KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > metamata > MMetricsStreamHandler


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
19
20 import java.io.BufferedReader JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.io.InputStreamReader JavaDoc;
24 import java.io.OutputStream JavaDoc;
25 import java.io.OutputStreamWriter JavaDoc;
26 import java.text.DecimalFormat JavaDoc;
27 import java.text.NumberFormat JavaDoc;
28 import java.text.ParseException JavaDoc;
29 import java.util.Date JavaDoc;
30 import java.util.EmptyStackException JavaDoc;
31 import java.util.Enumeration JavaDoc;
32 import java.util.Stack JavaDoc;
33 import java.util.Vector JavaDoc;
34 import javax.xml.transform.OutputKeys JavaDoc;
35 import javax.xml.transform.Transformer JavaDoc;
36 import javax.xml.transform.TransformerFactory JavaDoc;
37 import javax.xml.transform.sax.SAXTransformerFactory JavaDoc;
38 import javax.xml.transform.sax.TransformerHandler JavaDoc;
39 import javax.xml.transform.stream.StreamResult JavaDoc;
40 import org.apache.tools.ant.BuildException;
41 import org.apache.tools.ant.Project;
42 import org.apache.tools.ant.Task;
43 import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
44 import org.apache.tools.ant.util.DateUtils;
45 import org.xml.sax.Attributes JavaDoc;
46 import org.xml.sax.SAXException JavaDoc;
47 import org.xml.sax.helpers.AttributesImpl JavaDoc;
48
49 /**
50  * A handy metrics handler. Most of this code was done only with the
51  * screenshots on the documentation since the evaluation version as
52  * of this writing does not allow to save metrics or to run it via
53  * command line.
54  * <p>
55  * This class can be used to transform a text file or to process the
56  * output stream directly.
57  *
58  */

59 public class MMetricsStreamHandler implements ExecuteStreamHandler {
60
61     /** CLASS construct, it should be named something like 'MyClass' */
62     private static final String JavaDoc CLASS = "class";
63
64     /** package construct, it should be look like 'com.mycompany.something' */
65     private static final String JavaDoc PACKAGE = "package";
66
67     /** FILE construct, it should look like something 'MyClass.java' or 'MyClass.class' */
68     private static final String JavaDoc FILE = "file";
69
70     /** METHOD construct, it should looke like something 'doSomething(...)' or 'doSomething()' */
71     private static final String JavaDoc METHOD = "method";
72
73     private static final String JavaDoc[] ATTRIBUTES = {
74         "name", "vg", "loc", "dit", "noa", "nrm", "nlm", "wmc",
75         "rfc", "dac", "fanout", "cbo", "lcom", "nocl"};
76
77     /** reader for stdout */
78     private InputStream JavaDoc metricsOutput;
79
80     /**
81      * this is where the XML output will go, should mostly be a file
82      * the caller is responsible for flushing and closing this stream
83      */

84     private OutputStream JavaDoc xmlOutputStream;
85
86     /** metrics handler */
87     private TransformerHandler JavaDoc metricsHandler;
88
89     /** the task */
90     private Task task;
91
92     /**
93      * the stack where are stored the metrics element so that they we can
94      * know if we have to close an element or not.
95      */

96     private Stack JavaDoc stack = new Stack JavaDoc();
97
98     /** initialize this handler */
99     MMetricsStreamHandler(Task task, OutputStream JavaDoc xmlOut) {
100         this.task = task;
101         this.xmlOutputStream = xmlOut;
102     }
103
104     /** Ignore. */
105     public void setProcessInputStream(OutputStream JavaDoc p1) throws IOException JavaDoc {
106     }
107
108     /** Ignore. */
109     public void setProcessErrorStream(InputStream JavaDoc p1) throws IOException JavaDoc {
110     }
111
112     /** Set the inputstream */
113     public void setProcessOutputStream(InputStream JavaDoc is) throws IOException JavaDoc {
114         metricsOutput = is;
115     }
116
117     public void start() throws IOException JavaDoc {
118         // create the transformer handler that will be used to serialize
119
// the output.
120
TransformerFactory JavaDoc factory = TransformerFactory.newInstance();
121         if (!factory.getFeature(SAXTransformerFactory.FEATURE)) {
122             throw new IllegalStateException JavaDoc("Invalid Transformer factory feature");
123         }
124         try {
125             metricsHandler = ((SAXTransformerFactory JavaDoc) factory).newTransformerHandler();
126             metricsHandler.setResult(new StreamResult JavaDoc(new OutputStreamWriter JavaDoc(xmlOutputStream, "UTF-8")));
127             Transformer JavaDoc transformer = metricsHandler.getTransformer();
128             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
129
130             // start the document with a 'metrics' root
131
final Date JavaDoc now = new Date JavaDoc();
132             metricsHandler.startDocument();
133             AttributesImpl JavaDoc attr = new AttributesImpl JavaDoc();
134             attr.addAttribute("", "company", "company", "CDATA", "metamata");
135             attr.addAttribute("", "snapshot_created", "snapshot_created", "CDATA",
136                     DateUtils.format(now, DateUtils.ISO8601_DATETIME_PATTERN));
137             // attr.addAttribute("", "elapsed_time", "elapsed_time", "CDATA",
138
// String.valueOf(now.getTime() - program_start.getTime()));
139
attr.addAttribute("", "program_start", "program_start", "CDATA",
140                     DateUtils.format(new Date JavaDoc(), DateUtils.ISO8601_DATETIME_PATTERN));
141             metricsHandler.startElement("", "metrics", "metrics", attr);
142
143             // now parse the whole thing
144
parseOutput();
145
146         } catch (Exception JavaDoc e) {
147             throw new BuildException(e);
148         }
149     }
150
151     /**
152      * Pretty dangerous business here.
153      */

154     public void stop() {
155         try {
156             // we need to pop everything and close elements that have not been
157
// closed yet.
158
while (stack.size() > 0) {
159                 ElementEntry elem = (ElementEntry) stack.pop();
160                 metricsHandler.endElement("", elem.getType(), elem.getType());
161             }
162             // close the root
163
metricsHandler.endElement("", "metrics", "metrics");
164             // document is finished for good
165
metricsHandler.endDocument();
166         } catch (SAXException JavaDoc e) {
167             e.printStackTrace();
168             throw new IllegalStateException JavaDoc(e.getMessage());
169         }
170     }
171
172     /** read each line and process it */
173     protected void parseOutput() throws IOException JavaDoc, SAXException JavaDoc {
174         BufferedReader JavaDoc br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(metricsOutput));
175         String JavaDoc line = null;
176         while ((line = br.readLine()) != null) {
177             processLine(line);
178         }
179     }
180
181     /**
182      * Process a metrics line. If the metrics is invalid and that this is not
183      * the header line, it is display as info.
184      * @param line the line to process, it is normally a line full of metrics.
185      */

186     protected void processLine(String JavaDoc line) throws SAXException JavaDoc {
187         if (line.startsWith("Construct\tV(G)\tLOC\tDIT\tNOA\tNRM\tNLM\tWMC\tRFC\tDAC\tFANOUT\tCBO\tLCOM\tNOCL")) {
188             return;
189         }
190         try {
191             MetricsElement elem = MetricsElement.parse(line);
192             startElement(elem);
193         } catch (ParseException JavaDoc e) {
194             //e.printStackTrace();
195
// invalid lines are sent to the output as information, it might be anything,
196
task.log(line, Project.MSG_INFO);
197         }
198     }
199
200     /**
201      * Start a new construct. Elements are popped until we are on the same
202      * parent node, then the element type is guessed and pushed on the
203      * stack.
204      * @param elem the element to process.
205      * @throws SAXException thrown if there is a problem when sending SAX events.
206      */

207     protected void startElement(MetricsElement elem) throws SAXException JavaDoc {
208         // if there are elements in the stack we possibly need to close one or
209
// more elements previous to this one until we got its parent
210
int indent = elem.getIndent();
211         if (stack.size() > 0) {
212             ElementEntry previous = (ElementEntry) stack.peek();
213             // close nodes until you got the parent.
214
try {
215                 while (indent <= previous.getIndent() && stack.size() > 0) {
216                     stack.pop();
217                     metricsHandler.endElement("", previous.getType(), previous.getType());
218                     previous = (ElementEntry) stack.peek();
219                 }
220             } catch (EmptyStackException JavaDoc ignored) {
221             }
222         }
223
224         // ok, now start the new construct
225
String JavaDoc type = getConstructType(elem);
226         Attributes JavaDoc attrs = createAttributes(elem);
227         metricsHandler.startElement("", type, type, attrs);
228
229         // make sure we keep track of what we did, that's history
230
stack.push(new ElementEntry(type, indent));
231     }
232
233     /**
234      * return the construct type of the element. We can hardly recognize the
235      * type of a metrics element, so we are kind of forced to do some black
236      * magic based on the name and indentation to recognize the type.
237      * @param elem the metrics element to guess for its type.
238      * @return the type of the metrics element, either PACKAGE, FILE, CLASS or
239      * METHOD.
240      */

241     protected String JavaDoc getConstructType(MetricsElement elem) {
242         // ok no doubt, it's a file
243
if (elem.isCompilationUnit()) {
244             return FILE;
245         }
246
247         // same, we're sure it's a method
248
if (elem.isMethod()) {
249             return METHOD;
250         }
251
252         // if it's empty, and none of the above it should be a package
253
if (stack.size() == 0) {
254             return PACKAGE;
255         }
256
257         // ok, this is now black magic time, we will guess the type based on
258
// the previous type and its indent...
259
final ElementEntry previous = (ElementEntry) stack.peek();
260         final String JavaDoc prevType = previous.getType();
261         final int prevIndent = previous.getIndent();
262         final int indent = elem.getIndent();
263         // we're just under a file with a bigger indent so it's a class
264
if (prevType.equals(FILE) && indent > prevIndent) {
265             return CLASS;
266         }
267
268         // we're just under a class with a greater or equals indent, it's a class
269
// (there might be several classes in a compilation unit and inner classes as well)
270
if (prevType.equals(CLASS) && indent >= prevIndent) {
271             return CLASS;
272         }
273
274         // we assume the other are package
275
return PACKAGE;
276     }
277
278
279     /**
280      * Create all attributes of a MetricsElement skipping those who have an
281      * empty string
282      */

283     protected Attributes JavaDoc createAttributes(MetricsElement elem) {
284         AttributesImpl JavaDoc impl = new AttributesImpl JavaDoc();
285         int i = 0;
286         String JavaDoc name = ATTRIBUTES[i++];
287         impl.addAttribute("", name, name, "CDATA", elem.getName());
288         Enumeration JavaDoc metrics = elem.getMetrics();
289         for (; metrics.hasMoreElements(); i++) {
290             String JavaDoc value = (String JavaDoc) metrics.nextElement();
291             if (value.length() > 0) {
292                 name = ATTRIBUTES[i];
293                 impl.addAttribute("", name, name, "CDATA", value);
294             }
295         }
296         return impl;
297     }
298
299     /**
300      * helper class to keep track of elements via its type and indent
301      * that's all we need to guess a type.
302      */

303     private static final class ElementEntry {
304         private String JavaDoc type;
305         private int indent;
306
307         ElementEntry(String JavaDoc type, int indent) {
308             this.type = type;
309             this.indent = indent;
310         }
311
312         public String JavaDoc getType() {
313             return type;
314         }
315
316         public int getIndent() {
317             return indent;
318         }
319     }
320 }
321
322 class MetricsElement {
323
324     private static final NumberFormat JavaDoc METAMATA_NF;
325
326     private static final NumberFormat JavaDoc NEUTRAL_NF;
327
328     static {
329         METAMATA_NF = NumberFormat.getInstance();
330         METAMATA_NF.setMaximumFractionDigits(1);
331         NEUTRAL_NF = NumberFormat.getInstance();
332         if (NEUTRAL_NF instanceof DecimalFormat JavaDoc) {
333             ((DecimalFormat JavaDoc) NEUTRAL_NF).applyPattern("###0.###;-###0.###");
334         }
335         NEUTRAL_NF.setMaximumFractionDigits(1);
336     }
337
338     private int indent;
339
340     private String JavaDoc construct;
341
342     private Vector JavaDoc metrics;
343
344     MetricsElement(int indent, String JavaDoc construct, Vector JavaDoc metrics) {
345         this.indent = indent;
346         this.construct = construct;
347         this.metrics = metrics;
348     }
349
350     public int getIndent() {
351         return indent;
352     }
353
354     public String JavaDoc getName() {
355         return construct;
356     }
357
358     public Enumeration JavaDoc getMetrics() {
359         return metrics.elements();
360     }
361
362     public boolean isCompilationUnit() {
363         return (construct.endsWith(".java") || construct.endsWith(".class"));
364     }
365
366     public boolean isMethod() {
367         return (construct.endsWith("(...)") || construct.endsWith("()"));
368     }
369
370     public static MetricsElement parse(String JavaDoc line) throws ParseException JavaDoc {
371         final Vector JavaDoc metrics = new Vector JavaDoc();
372         int pos;
373
374         // i'm using indexOf since I need to know if there are empty strings
375
// between tabs and I find it easier than with StringTokenizer
376
while ((pos = line.indexOf('\t')) != -1) {
377             String JavaDoc token = line.substring(0, pos);
378             // only parse what coudl be a valid number. ie not constructs nor no value
379
/*if (metrics.size() != 0 || token.length() != 0) {
380                 Number num = METAMATA_NF.parse(token); // parse with Metamata NF
381                 token = NEUTRAL_NF.format(num.doubleValue()); // and format with a neutral NF
382             }*/

383             metrics.addElement(token);
384             line = line.substring(pos + 1);
385         }
386         metrics.addElement(line);
387
388         // there should be exactly 14 tokens (1 name + 13 metrics), if not, there is a problem !
389
if (metrics.size() != 14) {
390             throw new ParseException JavaDoc("Could not parse the following line as "
391                 + "a metrics: -->" + line + "<--", -1);
392         }
393
394         // remove the first token it's made of the indentation string and the
395
// construct name, we'll need all this to figure out what type of
396
// construct it is since we lost all semantics :(
397
// (#indent[/]*)(#construct.*)
398
String JavaDoc name = (String JavaDoc) metrics.elementAt(0);
399         metrics.removeElementAt(0);
400         int indent = 0;
401         pos = name.lastIndexOf('/');
402         if (pos != -1) {
403             name = name.substring(pos + 1);
404             indent = pos + 1; // indentation is last position of token + 1
405
}
406         return new MetricsElement(indent, name, metrics);
407     }
408 }
409
410
Popular Tags