KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > sitraka > XMLReport


1 /*
2  * Copyright 2001-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.sitraka;
18
19 import java.io.File JavaDoc;
20 import java.io.FileInputStream JavaDoc;
21 import java.util.Enumeration JavaDoc;
22 import java.util.Hashtable JavaDoc;
23 import java.util.NoSuchElementException JavaDoc;
24 import java.util.Vector JavaDoc;
25 import javax.xml.parsers.DocumentBuilder JavaDoc;
26 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
27 import org.apache.tools.ant.Project;
28 import org.apache.tools.ant.Task;
29 import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassFile;
30 import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassPathLoader;
31 import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.MethodInfo;
32 import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.Utils;
33 import org.w3c.dom.Document JavaDoc;
34 import org.w3c.dom.Element JavaDoc;
35 import org.w3c.dom.Node JavaDoc;
36 import org.w3c.dom.NodeList JavaDoc;
37 import org.xml.sax.InputSource JavaDoc;
38
39 /**
40  * Little hack to process XML report from JProbe. It will fix
41  * some reporting errors from JProbe 3.0 and makes use of a reference
42  * classpath to add classes/methods that were not reported by JProbe
43  * as being used (ie loaded)
44  *
45  */

46 public class XMLReport {
47     /** task caller, can be null, used for logging purpose */
48     private Task task;
49
50     /** the XML file to process just from CovReport */
51     private File JavaDoc file;
52
53     /** jprobe home path. It is used to get the DTD */
54     private File JavaDoc jprobeHome;
55
56     /** parsed document */
57     private Document JavaDoc report;
58
59     /**
60      * mapping of class names to <code>ClassFile</code>s from the reference
61      * classpath. It is used to filter the JProbe report.
62      */

63     private Hashtable JavaDoc classFiles;
64
65     /** mapping package name / package node for faster access */
66     private Hashtable JavaDoc pkgMap;
67
68     /** mapping classname / class node for faster access */
69     private Hashtable JavaDoc classMap;
70
71     /** method filters */
72     private ReportFilters filters;
73
74     /** create a new XML report, logging will be on stdout */
75     public XMLReport(File JavaDoc file) {
76         this(null, file);
77     }
78
79     /** create a new XML report, logging done on the task */
80     public XMLReport(Task task, File JavaDoc file) {
81         this.file = file;
82         this.task = task;
83     }
84
85     /** set the JProbe home path. Used to get the DTD */
86     public void setJProbehome(File JavaDoc home) {
87         jprobeHome = home;
88     }
89
90     /** set the */
91     public void setReportFilters(ReportFilters filters) {
92         this.filters = filters;
93     }
94
95
96     /** create node maps so that we can access node faster by their name */
97     protected void createNodeMaps() {
98         pkgMap = new Hashtable JavaDoc();
99         classMap = new Hashtable JavaDoc();
100         // create a map index of all packages by their name
101
// @todo can be done faster by direct access.
102
NodeList JavaDoc packages = report.getElementsByTagName("package");
103         final int pkglen = packages.getLength();
104         log("Indexing " + pkglen + " packages");
105         for (int i = pkglen - 1; i > -1; i--) {
106             Element JavaDoc pkg = (Element JavaDoc) packages.item(i);
107             String JavaDoc pkgname = pkg.getAttribute("name");
108
109             int nbclasses = 0;
110             // create a map index of all classes by their fully
111
// qualified name.
112
NodeList JavaDoc classes = pkg.getElementsByTagName("class");
113             final int classlen = classes.getLength();
114             log("Indexing " + classlen + " classes in package " + pkgname);
115             for (int j = classlen - 1; j > -1; j--) {
116                 Element JavaDoc clazz = (Element JavaDoc) classes.item(j);
117                 String JavaDoc classname = clazz.getAttribute("name");
118                 if (pkgname != null && pkgname.length() != 0) {
119                     classname = pkgname + "." + classname;
120                 }
121
122                 int nbmethods = 0;
123                 NodeList JavaDoc methods = clazz.getElementsByTagName("method");
124                 final int methodlen = methods.getLength();
125                 for (int k = methodlen - 1; k > -1; k--) {
126                     Element JavaDoc meth = (Element JavaDoc) methods.item(k);
127                     StringBuffer JavaDoc methodname = new StringBuffer JavaDoc(meth.getAttribute("name"));
128                     methodname.delete(methodname.toString().indexOf("("),
129                         methodname.toString().length());
130                     String JavaDoc signature = classname + "." + methodname + "()";
131                     if (filters.accept(signature)) {
132                         log("kept method:" + signature);
133                         nbmethods++;
134                     } else {
135                         clazz.removeChild(meth);
136                     }
137                 }
138                 // if we don't keep any method, we don't keep the class
139
if (nbmethods != 0 && classFiles.containsKey(classname)) {
140                     log("Adding class '" + classname + "'");
141                     classMap.put(classname, clazz);
142                     nbclasses++;
143                 } else {
144                     pkg.removeChild(clazz);
145                 }
146             }
147             if (nbclasses != 0) {
148                 log("Adding package '" + pkgname + "'");
149                 pkgMap.put(pkgname, pkg);
150             } else {
151                 pkg.getParentNode().removeChild(pkg);
152             }
153         }
154         log("Indexed " + classMap.size() + " classes in " + pkgMap.size() + " packages");
155     }
156
157     /** create the whole new document */
158     public Document JavaDoc createDocument(String JavaDoc[] classPath) throws Exception JavaDoc {
159
160         // Iterate over the classpath to identify reference classes
161
classFiles = new Hashtable JavaDoc();
162         ClassPathLoader cpl = new ClassPathLoader(classPath);
163         Enumeration JavaDoc e = cpl.loaders();
164         while (e.hasMoreElements()) {
165             ClassPathLoader.FileLoader fl = (ClassPathLoader.FileLoader) e.nextElement();
166             ClassFile[] classes = fl.getClasses();
167             log("Processing " + classes.length + " classes in " + fl.getFile());
168             // process all classes
169
for (int i = 0; i < classes.length; i++) {
170                 classFiles.put(classes[i].getFullName(), classes[i]);
171             }
172         }
173
174         // Load the JProbe coverage XML report
175
DocumentBuilder JavaDoc dbuilder = newBuilder();
176         InputSource JavaDoc is = new InputSource JavaDoc(new FileInputStream JavaDoc(file));
177         if (jprobeHome != null) {
178             File JavaDoc dtdDir = new File JavaDoc(jprobeHome, "dtd");
179             is.setSystemId("file:///" + dtdDir.getAbsolutePath() + "/");
180         }
181         report = dbuilder.parse(is);
182         report.normalize();
183
184         // create maps for faster node access (also filters out unwanted nodes)
185
createNodeMaps();
186
187         // Make sure each class from the reference path ends up in the report
188
Enumeration JavaDoc classes = classFiles.elements();
189         while (classes.hasMoreElements()) {
190             ClassFile cf = (ClassFile) classes.nextElement();
191             serializeClass(cf);
192         }
193         // update the document with the stats
194
update();
195         return report;
196     }
197
198     /**
199      * JProbe does not put the java.lang prefix for classes
200      * in this package, so used this nice method so that
201      * I have the same signature for methods
202      */

203     protected String JavaDoc getMethodSignature(MethodInfo method) {
204         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(method.getName());
205         buf.append("(");
206         String JavaDoc[] params = method.getParametersType();
207         for (int i = 0; i < params.length; i++) {
208             String JavaDoc type = params[i];
209             int pos = type.lastIndexOf('.');
210             if (pos != -1) {
211                 String JavaDoc pkg = type.substring(0, pos);
212                 if ("java.lang".equals(pkg)) {
213                     params[i] = type.substring(pos + 1);
214                 }
215             }
216             buf.append(params[i]);
217             if (i != params.length - 1) {
218                 buf.append(", ");
219             }
220         }
221         buf.append(")");
222         return buf.toString();
223     }
224
225     /**
226      * Convert to a CovReport-like signature - &lt;classname&gt;&#046;&lt;method&gt;().
227      */

228     protected String JavaDoc getMethodSignature(ClassFile clazz, MethodInfo method) {
229         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(clazz.getFullName());
230         buf.append(".");
231         buf.append(method.getName());
232         buf.append("()");
233         return buf.toString();
234     }
235
236     /**
237      * Do additional work on an element to remove abstract methods that
238      * are reported by JProbe 3.0
239      */

240     protected void removeAbstractMethods(ClassFile classFile, Element JavaDoc classNode) {
241         MethodInfo[] methods = classFile.getMethods();
242         Hashtable JavaDoc methodNodeList = getMethods(classNode);
243         // assert xmlMethods.size() == methods.length()
244
final int size = methods.length;
245         for (int i = 0; i < size; i++) {
246             MethodInfo method = methods[i];
247             String JavaDoc methodSig = getMethodSignature(method);
248             Element JavaDoc methodNode = (Element JavaDoc) methodNodeList.get(methodSig);
249             if (methodNode != null
250                 && Utils.isAbstract(method.getAccessFlags())) {
251                 log("\tRemoving abstract method " + methodSig);
252                 classNode.removeChild(methodNode);
253             }
254         }
255     }
256
257     /** create an empty method element with its cov.data values */
258     protected Element JavaDoc createMethodElement(MethodInfo method) {
259         String JavaDoc methodsig = getMethodSignature(method);
260         Element JavaDoc methodElem = report.createElement("method");
261         methodElem.setAttribute("name", methodsig);
262         // create the method cov.data element
263
Element JavaDoc methodData = report.createElement("cov.data");
264         methodElem.appendChild(methodData);
265         methodData.setAttribute("calls", "0");
266         methodData.setAttribute("hit_lines", "0");
267         methodData.setAttribute("total_lines", String.valueOf(method.getNumberOfLines()));
268         return methodElem;
269     }
270
271     /** create an empty package element with its default cov.data (0) */
272     protected Element JavaDoc createPackageElement(String JavaDoc pkgname) {
273         Element JavaDoc pkgElem = report.createElement("package");
274         pkgElem.setAttribute("name", pkgname);
275         // create the package cov.data element / default
276
// must be updated at the end of the whole process
277
Element JavaDoc pkgData = report.createElement("cov.data");
278         pkgElem.appendChild(pkgData);
279         pkgData.setAttribute("calls", "0");
280         pkgData.setAttribute("hit_methods", "0");
281         pkgData.setAttribute("total_methods", "0");
282         pkgData.setAttribute("hit_lines", "0");
283         pkgData.setAttribute("total_lines", "0");
284         return pkgElem;
285     }
286
287     /** create an empty class element with its default cov.data (0) */
288     protected Element JavaDoc createClassElement(ClassFile classFile) {
289         // create the class element
290
Element JavaDoc classElem = report.createElement("class");
291         classElem.setAttribute("name", classFile.getName());
292         // source file possibly does not exist in the bytecode
293
if (null != classFile.getSourceFile()) {
294             classElem.setAttribute("source", classFile.getSourceFile());
295         }
296         // create the cov.data elem
297
Element JavaDoc classData = report.createElement("cov.data");
298         classElem.appendChild(classData);
299         // create the class cov.data element
300
classData.setAttribute("calls", "0");
301         classData.setAttribute("hit_methods", "0");
302         classData.setAttribute("total_methods", "0");
303         classData.setAttribute("hit_lines", "0");
304         classData.setAttribute("total_lines", "0");
305         return classElem;
306     }
307
308     /** serialize a classfile into XML */
309     protected void serializeClass(ClassFile classFile) {
310         // the class already is reported so ignore it
311
String JavaDoc fullclassname = classFile.getFullName();
312         log("Looking for '" + fullclassname + "'");
313         Element JavaDoc clazz = (Element JavaDoc) classMap.get(fullclassname);
314
315         // ignore classes that are already reported, all the information is
316
// already there.
317
if (clazz != null) {
318             log("Ignoring " + fullclassname);
319             removeAbstractMethods(classFile, clazz);
320             return;
321         }
322
323         // ignore interfaces files, there is no code in there to cover.
324
if (Utils.isInterface(classFile.getAccess())) {
325             return;
326         }
327
328         Vector JavaDoc methods = getFilteredMethods(classFile);
329         // no need to process, there are no methods to add for this class.
330
if (methods.size() == 0) {
331             return;
332         }
333
334         String JavaDoc pkgname = classFile.getPackage();
335         // System.out.println("Looking for package " + pkgname);
336
Element JavaDoc pkgElem = (Element JavaDoc) pkgMap.get(pkgname);
337         if (pkgElem == null) {
338             pkgElem = createPackageElement(pkgname);
339             report.getDocumentElement().appendChild(pkgElem);
340             pkgMap.put(pkgname, pkgElem); // add the pkg to the map
341
}
342         // this is a brand new class, so we have to create a new node
343

344         // create the class element
345
Element JavaDoc classElem = createClassElement(classFile);
346         pkgElem.appendChild(classElem);
347
348         int total_lines = 0;
349         int total_methods = 0;
350         final int count = methods.size();
351         for (int i = 0; i < count; i++) {
352             // create the method element
353
MethodInfo method = (MethodInfo) methods.elementAt(i);
354             if (Utils.isAbstract(method.getAccessFlags())) {
355                 continue; // no need to report abstract methods
356
}
357             Element JavaDoc methodElem = createMethodElement(method);
358             classElem.appendChild(methodElem);
359             total_lines += method.getNumberOfLines();
360             total_methods++;
361         }
362         // create the class cov.data element
363
Element JavaDoc classData = getCovDataChild(classElem);
364         classData.setAttribute("total_methods", String.valueOf(total_methods));
365         classData.setAttribute("total_lines", String.valueOf(total_lines));
366
367         // add itself to the node map
368
classMap.put(fullclassname, classElem);
369     }
370
371     protected Vector JavaDoc getFilteredMethods(ClassFile classFile) {
372         MethodInfo[] methodlist = classFile.getMethods();
373         Vector JavaDoc methods = new Vector JavaDoc(methodlist.length);
374         for (int i = 0; i < methodlist.length; i++) {
375             MethodInfo method = methodlist[i];
376             String JavaDoc signature = getMethodSignature(classFile, method);
377             if (filters.accept(signature)) {
378                 methods.addElement(method);
379                 log("keeping " + signature);
380             } else {
381 // log("discarding " + signature);
382
}
383         }
384         return methods;
385     }
386
387
388     /** update the count of the XML, that is accumulate the stats on
389      * methods, classes and package so that the numbers are valid
390      * according to the info that was appended to the XML.
391      */

392     protected void update() {
393         int calls = 0;
394         int hit_methods = 0;
395         int total_methods = 0;
396         int hit_lines = 0;
397         int total_lines = 0;
398
399         // use the map for access, all nodes should be there
400
Enumeration JavaDoc e = pkgMap.elements();
401         while (e.hasMoreElements()) {
402             Element JavaDoc pkgElem = (Element JavaDoc) e.nextElement();
403             String JavaDoc pkgname = pkgElem.getAttribute("name");
404             Element JavaDoc[] classes = getClasses(pkgElem);
405             int pkg_calls = 0;
406             int pkg_hit_methods = 0;
407             int pkg_total_methods = 0;
408             int pkg_hit_lines = 0;
409             int pkg_total_lines = 0;
410             //System.out.println("Processing package '" + pkgname + "': "
411
// + classes.length + " classes");
412
for (int j = 0; j < classes.length; j++) {
413                 Element JavaDoc clazz = classes[j];
414                 String JavaDoc classname = clazz.getAttribute("name");
415                 if (pkgname != null && pkgname.length() != 0) {
416                     classname = pkgname + "." + classname;
417                 }
418                 // there's only cov.data as a child so bet on it
419
Element JavaDoc covdata = getCovDataChild(clazz);
420                 try {
421                     pkg_calls += Integer.parseInt(covdata.getAttribute("calls"));
422                     pkg_hit_methods += Integer.parseInt(covdata.getAttribute("hit_methods"));
423                     pkg_total_methods += Integer.parseInt(covdata.getAttribute("total_methods"));
424                     pkg_hit_lines += Integer.parseInt(covdata.getAttribute("hit_lines"));
425                     pkg_total_lines += Integer.parseInt(covdata.getAttribute("total_lines"));
426                 } catch (NumberFormatException JavaDoc ex) {
427                     log("Error parsing '" + classname + "' (" + j + "/"
428                         + classes.length + ") in package '" + pkgname + "'");
429                     throw ex;
430                 }
431             }
432             Element JavaDoc covdata = getCovDataChild(pkgElem);
433             covdata.setAttribute("calls", String.valueOf(pkg_calls));
434             covdata.setAttribute("hit_methods", String.valueOf(pkg_hit_methods));
435             covdata.setAttribute("total_methods", String.valueOf(pkg_total_methods));
436             covdata.setAttribute("hit_lines", String.valueOf(pkg_hit_lines));
437             covdata.setAttribute("total_lines", String.valueOf(pkg_total_lines));
438             calls += pkg_calls;
439             hit_methods += pkg_hit_methods;
440             total_methods += pkg_total_methods;
441             hit_lines += pkg_hit_lines;
442             total_lines += pkg_total_lines;
443         }
444         Element JavaDoc covdata = getCovDataChild(report.getDocumentElement());
445         covdata.setAttribute("calls", String.valueOf(calls));
446         covdata.setAttribute("hit_methods", String.valueOf(hit_methods));
447         covdata.setAttribute("total_methods", String.valueOf(total_methods));
448         covdata.setAttribute("hit_lines", String.valueOf(hit_lines));
449         covdata.setAttribute("total_lines", String.valueOf(total_lines));
450     }
451
452     protected Element JavaDoc getCovDataChild(Element JavaDoc parent) {
453         NodeList JavaDoc children = parent.getChildNodes();
454         int len = children.getLength();
455         for (int i = 0; i < len; i++) {
456             Node JavaDoc child = children.item(i);
457             if (child.getNodeType() == Node.ELEMENT_NODE) {
458                 Element JavaDoc elem = (Element JavaDoc) child;
459                 if ("cov.data".equals(elem.getNodeName())) {
460                     return elem;
461                 }
462             }
463         }
464         throw new NoSuchElementException JavaDoc("Could not find 'cov.data' "
465             + "element in parent '" + parent.getNodeName() + "'");
466     }
467
468     protected Hashtable JavaDoc getMethods(Element JavaDoc clazz) {
469         Hashtable JavaDoc map = new Hashtable JavaDoc();
470         NodeList JavaDoc children = clazz.getChildNodes();
471         int len = children.getLength();
472         for (int i = 0; i < len; i++) {
473             Node JavaDoc child = children.item(i);
474             if (child.getNodeType() == Node.ELEMENT_NODE) {
475                 Element JavaDoc elem = (Element JavaDoc) child;
476                 if ("method".equals(elem.getNodeName())) {
477                     String JavaDoc name = elem.getAttribute("name");
478                     map.put(name, elem);
479                 }
480             }
481         }
482         return map;
483     }
484
485     protected Element JavaDoc[] getClasses(Element JavaDoc pkg) {
486         Vector JavaDoc v = new Vector JavaDoc();
487         NodeList JavaDoc children = pkg.getChildNodes();
488         int len = children.getLength();
489         for (int i = 0; i < len; i++) {
490             Node JavaDoc child = children.item(i);
491             if (child.getNodeType() == Node.ELEMENT_NODE) {
492                 Element JavaDoc elem = (Element JavaDoc) child;
493                 if ("class".equals(elem.getNodeName())) {
494                     v.addElement(elem);
495                 }
496             }
497         }
498         Element JavaDoc[] elems = new Element JavaDoc[v.size()];
499         v.copyInto(elems);
500         return elems;
501
502     }
503
504     protected Element JavaDoc[] getPackages(Element JavaDoc snapshot) {
505         Vector JavaDoc v = new Vector JavaDoc();
506         NodeList JavaDoc children = snapshot.getChildNodes();
507         int len = children.getLength();
508         for (int i = 0; i < len; i++) {
509             Node JavaDoc child = children.item(i);
510             if (child.getNodeType() == Node.ELEMENT_NODE) {
511                 Element JavaDoc elem = (Element JavaDoc) child;
512                 if ("package".equals(elem.getNodeName())) {
513                     v.addElement(elem);
514                 }
515             }
516         }
517         Element JavaDoc[] elems = new Element JavaDoc[v.size()];
518         v.copyInto(elems);
519         return elems;
520     }
521
522     private static DocumentBuilder JavaDoc newBuilder() {
523         try {
524             DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
525             factory.setIgnoringComments(true);
526             factory.setValidating(false);
527             return factory.newDocumentBuilder();
528         } catch (Exception JavaDoc e) {
529             throw new ExceptionInInitializerError JavaDoc(e);
530         }
531     }
532
533     public void log(String JavaDoc message) {
534         if (task == null) {
535             //System.out.println(message);
536
} else {
537             task.log(message, Project.MSG_DEBUG);
538         }
539     }
540
541 }
542
543
Popular Tags