1. /*
  2. * Copyright 2001-2002,2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs.optional.metamata;
  18. import java.io.File;
  19. import java.io.FileOutputStream;
  20. import java.io.IOException;
  21. import java.io.OutputStream;
  22. import java.util.Vector;
  23. import org.apache.tools.ant.BuildException;
  24. import org.apache.tools.ant.Project;
  25. import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
  26. import org.apache.tools.ant.taskdefs.LogStreamHandler;
  27. import org.apache.tools.ant.types.FileSet;
  28. import org.apache.tools.ant.types.Path;
  29. /**
  30. * Invokes the Metamata Audit/ Webgain Quality Analyzer on a set of Java files.
  31. * <p>
  32. * <i>maudit</i> performs static analysis of the Java source code and byte
  33. * code files to find and report errors of style and potential problems related
  34. * to performance, maintenance and robustness. As a convenience, a stylesheet
  35. * is given in <tt>etc</tt> directory, so that an HTML report can be generated
  36. * from the XML file.
  37. *
  38. */
  39. public class MAudit extends AbstractMetamataTask {
  40. /* As of Metamata 2.0, the command line of MAudit is as follows:
  41. Usage
  42. maudit <option>... <path>... [-unused <search-path>...]
  43. Parameters
  44. path File or directory to audit.
  45. search-path File or directory to search for declaration uses.
  46. Options
  47. -arguments -A <file> Includes command line arguments from file.
  48. -classpath -cp <path> Sets class path (also source path unless one
  49. explicitly set). Overrides METAPATH/CLASSPATH.
  50. -exit -x Exits after the first error.
  51. -fix -f Automatically fixes certain errors.
  52. -fullpath Prints full path for locations.
  53. -help -h Prints help and exits.
  54. -list -l Creates listing file for each audited file.
  55. -offsets -off Offset and length for locations.
  56. -output -o <file> Prints output to file.
  57. -quiet -q Suppresses copyright and summary messages.
  58. -sourcepath <path> Sets source path. Overrides SOURCEPATH.
  59. -tab -t Prints a tab character after first argument.
  60. -unused -u Finds declarations unused in search paths.
  61. -verbose -v Prints all messages.
  62. -version -V Prints version and exits.
  63. */
  64. //---------------------- PUBLIC METHODS ------------------------------------
  65. /** pattern used by maudit to report the error for a file */
  66. /** RE does not seems to support regexp pattern with comments so i'm stripping it*/
  67. // (?:file:)?((?#filepath).+):((?#line)\\d+)\\s*:\\s+((?#message).*)
  68. static final String AUDIT_PATTERN = "(?:file:)?(.+):(\\d+)\\s*:\\s+(.*)";
  69. private File outFile = null;
  70. private Path searchPath = null;
  71. private Path rulesPath = null;
  72. private boolean fix = false;
  73. private boolean list = false;
  74. private boolean unused = false;
  75. // add a bunch of undocumented options for the task
  76. private boolean quiet = false;
  77. private boolean exit = false;
  78. private boolean offsets = false;
  79. private boolean verbose = false;
  80. private boolean fullsemanticize = false;
  81. /** default constructor */
  82. public MAudit() {
  83. super("com.metamata.gui.rc.MAudit");
  84. }
  85. /**
  86. * The XML file to which the Audit result should be written to; required
  87. */
  88. public void setTofile(File outFile) {
  89. this.outFile = outFile;
  90. }
  91. /**
  92. * Automatically fix certain errors
  93. * (those marked as fixable in the manual);
  94. * optional, default=false
  95. */
  96. public void setFix(boolean flag) {
  97. this.fix = flag;
  98. }
  99. /**
  100. * Creates listing file for each audited file; optional, default false.
  101. * When set, a .maudit file will be generated in the
  102. * same location as the source file.
  103. */
  104. public void setList(boolean flag) {
  105. this.list = flag;
  106. }
  107. /**
  108. * Finds declarations unused in search paths; optional, default false.
  109. * It will look for unused global declarations
  110. * in the source code within a use domain specified by the
  111. * <tt>searchpath</tt> element.
  112. */
  113. public void setUnused(boolean flag) {
  114. this.unused = flag;
  115. }
  116. /**
  117. * flag to suppress copyright and summary messages; default false.
  118. * internal/testing only
  119. * @ant.attribute ignore="true"
  120. */
  121. public void setQuiet(boolean flag) {
  122. this.quiet = flag;
  123. }
  124. /**
  125. * flag to tell the task to exit after the first error.
  126. * internal/testing only
  127. * @ant.attribute ignore="true"
  128. */
  129. public void setExit(boolean flag) {
  130. this.exit = flag;
  131. }
  132. /**
  133. * internal/testing only
  134. * @ant.attribute ignore="true"
  135. */
  136. public void setOffsets(boolean flag) {
  137. this.offsets = flag;
  138. }
  139. /**
  140. * flag to print all messages; optional, default false.
  141. * internal/testing only
  142. * @ant.attribute ignore="true"
  143. */
  144. public void setVerbose(boolean flag) {
  145. this.verbose = flag;
  146. }
  147. /**
  148. * internal/testing only
  149. * @ant.attribute ignore="true"
  150. */
  151. public void setFullsemanticize(boolean flag) {
  152. this.fullsemanticize = flag;
  153. }
  154. /**
  155. * classpath for additional audit rules
  156. * these must be placed before metamata.jar !!
  157. */
  158. public Path createRulespath() {
  159. if (rulesPath == null) {
  160. rulesPath = new Path(getProject());
  161. }
  162. return rulesPath;
  163. }
  164. /**
  165. * search path to use for unused global declarations;
  166. * required when <tt>unused</tt> is set.
  167. */
  168. public Path createSearchpath() {
  169. if (searchPath == null) {
  170. searchPath = new Path(getProject());
  171. }
  172. return searchPath;
  173. }
  174. /**
  175. * create the option vector for the command
  176. */
  177. protected Vector getOptions() {
  178. Vector options = new Vector(512);
  179. // add the source path automatically from the fileset.
  180. // to avoid redundancy...
  181. for (int i = 0; i < fileSets.size(); i++) {
  182. FileSet fs = (FileSet) fileSets.elementAt(i);
  183. Path path = createSourcepath();
  184. File dir = fs.getDir(getProject());
  185. path.setLocation(dir);
  186. }
  187. // there is a bug in Metamata 2.0 build 37. The sourcepath argument does
  188. // not work. So we will use the sourcepath prepended to classpath. (order
  189. // is important since Metamata looks at .class and .java)
  190. if (sourcePath != null) {
  191. sourcePath.append(classPath); // srcpath is prepended
  192. classPath = sourcePath;
  193. sourcePath = null; // prevent from using -sourcepath
  194. }
  195. // don't forget to modify the pattern if you change the options reporting
  196. if (classPath != null) {
  197. options.addElement("-classpath");
  198. options.addElement(classPath.toString());
  199. }
  200. // suppress copyright msg when running, we will let it so that this
  201. // will be the only output to the console if in xml mode
  202. if (quiet) {
  203. options.addElement("-quiet");
  204. }
  205. if (fullsemanticize) {
  206. options.addElement("-full-semanticize");
  207. }
  208. if (verbose) {
  209. options.addElement("-verbose");
  210. }
  211. if (offsets) {
  212. options.addElement("-offsets");
  213. }
  214. if (exit) {
  215. options.addElement("-exit");
  216. }
  217. if (fix) {
  218. options.addElement("-fix");
  219. }
  220. options.addElement("-fullpath");
  221. // generate .maudit files much more detailed than the report
  222. // I don't like it very much, I think it could be interesting
  223. // to get all .maudit files and include them in the XML.
  224. if (list) {
  225. options.addElement("-list");
  226. }
  227. if (sourcePath != null) {
  228. options.addElement("-sourcepath");
  229. options.addElement(sourcePath.toString());
  230. }
  231. addAllVector(options, includedFiles.keys());
  232. if (unused) {
  233. options.addElement("-unused");
  234. options.addElement(searchPath.toString());
  235. }
  236. return options;
  237. }
  238. /**
  239. * validate the settings
  240. */
  241. protected void checkOptions() throws BuildException {
  242. super.checkOptions();
  243. if (unused && searchPath == null) {
  244. throw new BuildException("'searchpath' element must be set when "
  245. + "looking for 'unused' declarations.");
  246. }
  247. if (!unused && searchPath != null) {
  248. log("'searchpath' element ignored. 'unused' attribute is disabled.",
  249. Project.MSG_WARN);
  250. }
  251. if (rulesPath != null) {
  252. cmdl.createClasspath(getProject()).addExisting(rulesPath);
  253. }
  254. }
  255. protected ExecuteStreamHandler createStreamHandler() throws BuildException {
  256. // if we didn't specify a file, then use a screen report
  257. if (outFile == null) {
  258. return new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_ERR);
  259. }
  260. ExecuteStreamHandler handler = null;
  261. OutputStream out = null;
  262. try {
  263. out = new FileOutputStream(outFile);
  264. handler = new MAuditStreamHandler(this, out);
  265. } catch (IOException e) {
  266. throw new BuildException(e);
  267. } finally {
  268. if (out == null) {
  269. try {
  270. out.close();
  271. } catch (IOException e) {
  272. }
  273. }
  274. }
  275. return handler;
  276. }
  277. protected void cleanUp() throws BuildException {
  278. super.cleanUp();
  279. // at this point if -list is used, we should move
  280. // the .maudit file since we cannot choose their location :(
  281. // the .maudit files match the .java files
  282. // we'll use includedFiles to get the .maudit files.
  283. /*if (out != null) {
  284. // close it if not closed by the handler...
  285. }*/
  286. }
  287. }