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.FileInputStream;
  20. import java.io.FileOutputStream;
  21. import java.io.IOException;
  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.EnumeratedAttribute;
  28. import org.apache.tools.ant.types.Path;
  29. /**
  30. * Computes the metrics of a set of Java files and write the results to an XML
  31. * file. As a convenience, a stylesheet is given in <tt>etc</tt> directory,
  32. * so that an HTML report can be generated from the XML file.
  33. * <p>
  34. * You will not be able to use this task with the evaluation version since
  35. * as of Metamata 2.0, Metrics does not support command line :-(
  36. *
  37. *
  38. */
  39. public class MMetrics extends AbstractMetamataTask {
  40. /*
  41. The command line options as of Metamata 2.0 are as follows:
  42. Usage
  43. mmetrics <option>... <path>...
  44. Parameters
  45. path File or directory to measure.
  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. -compilation-units Measure compilation units.
  51. -files Measure compilation units.
  52. -format -f <format> Sets output format, default output file type.
  53. -help -h Prints help and exits.
  54. -indent -i <string> Sets string used to indent labels one level.
  55. -methods Measure methods, types, and compilation units.
  56. -output -o <file> Sets output file name.
  57. -quiet -q Suppresses copyright message.
  58. -sourcepath <path> Sets source path. Overrides SOURCEPATH.
  59. -types Measure types and compilation units.
  60. -verbose -v Prints all messages.
  61. -version -V Prints version and exits.
  62. Format Options
  63. comma csv Format output as comma-separated text.
  64. html htm Format output as an HTML table.
  65. tab tab-separated tsv Format output as tab-separated text.
  66. text txt Format output as space-aligned text.
  67. */
  68. /** the granularity mode. Should be one of 'files', 'methods' and 'types'. */
  69. private String granularity = null;
  70. /** the XML output file */
  71. private File outFile = null;
  72. /** the location of the temporary txt report */
  73. private File tmpFile;
  74. private Path path = null;
  75. //--------------------------- PUBLIC METHODS -------------------------------
  76. /** default constructor */
  77. public MMetrics() {
  78. super("com.metamata.sc.MMetrics");
  79. }
  80. /**
  81. * Attributes for granularity.
  82. */
  83. public static class GranularityAttribute extends EnumeratedAttribute {
  84. public String[] getValues() {
  85. return new String[]{"compilation-units", "files", "methods", "types", "packages"};
  86. }
  87. }
  88. /**
  89. * set the granularity of the audit. Should be one of 'files', 'methods'
  90. * or 'types'.
  91. * @param granularity the audit reporting mode.
  92. */
  93. public void setGranularity(GranularityAttribute granularity) {
  94. this.granularity = granularity.getValue();
  95. }
  96. /**
  97. * Set the output XML file
  98. * @param file the xml file to write the XML report to.
  99. */
  100. public void setTofile(File file) {
  101. this.outFile = file;
  102. }
  103. /**
  104. * Set a new path (directory) to measure metrics from.
  105. * @return the path instance to use.
  106. */
  107. public Path createPath() {
  108. if (path == null) {
  109. path = new Path(getProject());
  110. }
  111. return path;
  112. }
  113. //------------------- PROTECTED / PRIVATE METHODS --------------------------
  114. // check for existing options and outfile, all other are optional
  115. protected void checkOptions() throws BuildException {
  116. super.checkOptions();
  117. if (outFile == null) {
  118. throw new BuildException("Output XML file must be set via 'tofile' attribute.");
  119. }
  120. if (path == null && fileSets.size() == 0) {
  121. throw new BuildException("Must set either paths (path element) "
  122. + "or files (fileset element)");
  123. }
  124. // I don't accept dirs and files at the same time,
  125. // I cannot recognize the semantic in the result
  126. if (path != null && fileSets.size() > 0) {
  127. throw new BuildException("Cannot set paths (path element) and "
  128. + "files (fileset element) at the same time");
  129. }
  130. tmpFile = createTmpFile();
  131. }
  132. protected void execute0(ExecuteStreamHandler handler) throws BuildException {
  133. super.execute0(handler);
  134. transformFile();
  135. }
  136. /**
  137. * transform the generated file via the handler
  138. * This function can either be called if the result is written to the output
  139. * file via -output or we could use the handler directly on stdout if not.
  140. * @see #createStreamHandler()
  141. */
  142. protected void transformFile() throws BuildException {
  143. FileInputStream tmpStream = null;
  144. try {
  145. tmpStream = new FileInputStream(tmpFile);
  146. } catch (IOException e) {
  147. throw new BuildException("Error reading temporary file: " + tmpFile, e);
  148. }
  149. FileOutputStream xmlStream = null;
  150. try {
  151. xmlStream = new FileOutputStream(outFile);
  152. ExecuteStreamHandler xmlHandler = new MMetricsStreamHandler(this, xmlStream);
  153. xmlHandler.setProcessOutputStream(tmpStream);
  154. xmlHandler.start();
  155. xmlHandler.stop();
  156. } catch (IOException e) {
  157. throw new BuildException("Error creating output file: " + outFile, e);
  158. } finally {
  159. if (xmlStream != null) {
  160. try {
  161. xmlStream.close();
  162. } catch (IOException ignored) {
  163. }
  164. }
  165. if (tmpStream != null) {
  166. try {
  167. tmpStream.close();
  168. } catch (IOException ignored) {
  169. }
  170. }
  171. }
  172. }
  173. /** cleanup the temporary txt report */
  174. protected void cleanUp() throws BuildException {
  175. try {
  176. super.cleanUp();
  177. } finally {
  178. if (tmpFile != null) {
  179. tmpFile.delete();
  180. tmpFile = null;
  181. }
  182. }
  183. }
  184. /**
  185. * if the report is transform via a temporary txt file we should use a
  186. * a normal logger here, otherwise we could use the metrics handler
  187. * directly to capture and transform the output on stdout to XML.
  188. */
  189. protected ExecuteStreamHandler createStreamHandler() {
  190. // write the report directtly to an XML stream
  191. // return new MMetricsStreamHandler(this, xmlStream);
  192. return new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_INFO);
  193. }
  194. protected Vector getOptions() {
  195. Vector options = new Vector(512);
  196. // there is a bug in Metamata 2.0 build 37. The sourcepath argument does
  197. // not work. So we will use the sourcepath prepended to classpath. (order
  198. // is important since Metamata looks at .class and .java)
  199. if (sourcePath != null) {
  200. sourcePath.append(classPath); // srcpath is prepended
  201. classPath = sourcePath;
  202. sourcePath = null; // prevent from using -sourcepath
  203. }
  204. // don't forget to modify the pattern if you change the options reporting
  205. if (classPath != null) {
  206. options.addElement("-classpath");
  207. options.addElement(classPath.toString());
  208. }
  209. options.addElement("-output");
  210. options.addElement(tmpFile.toString());
  211. options.addElement("-" + granularity);
  212. // display the metamata copyright
  213. // options.addElement( "-quiet");
  214. options.addElement("-format");
  215. // need this because that's what the handler is using, it's
  216. // way easier to process than any other separator
  217. options.addElement("tab");
  218. // specify a / as the indent character, used by the handler.
  219. options.addElement("-i");
  220. options.addElement("/");
  221. // directories
  222. String[] dirs = path.list();
  223. for (int i = 0; i < dirs.length; i++) {
  224. options.addElement(dirs[i]);
  225. }
  226. // files next.
  227. addAllVector(options, includedFiles.keys());
  228. return options;
  229. }
  230. }