diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReport.java b/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReport.java index f6c3bc0d9..c43872f96 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReport.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReport.java @@ -53,33 +53,32 @@ */ package org.apache.tools.ant.taskdefs.optional.sitraka; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassFile; +import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassPathLoader; +import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.MethodInfo; +import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.Utils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.Transformer; import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; - -import org.w3c.dom.Document; -import org.w3c.dom.NodeList; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.xml.sax.InputSource; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.util.Hashtable; import java.util.Enumeration; -import java.util.Vector; +import java.util.Hashtable; import java.util.NoSuchElementException; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassPathLoader; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassFile; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.MethodInfo; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.MethodInfoList; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.Utils; -import org.apache.tools.ant.Task; -import org.apache.tools.ant.Project; +import java.util.Vector; /** * Little hack to process XML report from JProbe. It will fix @@ -90,498 +89,488 @@ import org.apache.tools.ant.Project; * @author Stephane Bailliez */ public class XMLReport { - /** task caller, can be null, used for logging purpose */ - protected Task task; - - /** the XML file to process just from CovReport */ - protected File file; - - /** jprobe home path. It is used to get the DTD */ - protected File jprobeHome; - - /** parsed document */ - protected Document report; - - /** mapping package name / package node for faster access */ - protected Hashtable pkgMap; - - /** mapping classname / class node for faster access */ - protected Hashtable classMap; - - /** method filters */ - protected ReportFilters filters; - - /** create a new XML report, logging will be on stdout */ - public XMLReport(File file){ - this(null, file); - } - - /** create a new XML report, logging done on the task */ - public XMLReport(Task task, File file){ - this.file = file; - this.task = task; - } - - /** set the JProbe home path. Used to get the DTD */ - public void setJProbehome(File home){ - jprobeHome = home; - } - - /** set the */ - public void setReportFilters(ReportFilters filters){ - this.filters = filters; - } - - - - /** create node maps so that we can access node faster by their name */ - protected void createNodeMaps(){ - pkgMap = new Hashtable(); - classMap = new Hashtable(); - // create a map index of all packages by their name - // @todo can be done faster by direct access. - NodeList packages = report.getElementsByTagName("package"); - final int pkglen = packages.getLength(); - log("Indexing " + pkglen + " packages"); - for (int i = pkglen-1; i > -1 ; i--){ - Element pkg = (Element)packages.item(i); - String pkgname = pkg.getAttribute("name"); - - int nbclasses = 0; - // create a map index of all classes by their fully - // qualified name. - NodeList classes = pkg.getElementsByTagName("class"); - final int classlen = classes.getLength(); - log("Indexing " + classlen + " classes in package " + pkgname); - for (int j = classlen-1; j > -1 ; j--){ - Element clazz = (Element)classes.item(j); - String classname = clazz.getAttribute("name"); - if (pkgname != null && pkgname.length() != 0){ - classname = pkgname + "." + classname; - } - - int nbmethods = 0; - NodeList methods = clazz.getElementsByTagName("method"); - final int methodlen = methods.getLength(); - for (int k = methodlen-1; k > -1; k--){ - Element meth = (Element)methods.item(k); - StringBuffer methodname = new StringBuffer(meth.getAttribute("name")); - methodname.delete(methodname.toString().indexOf("(") , methodname.toString().length()); - String signature = classname + "." + methodname + "()"; - if (filters.accept(signature)){ - log("keeped method:" + signature); - nbmethods++; - } - else { - clazz.removeChild(meth); - } - } - // if we don't keep any method, we don't keep the class - if (nbmethods != 0){ - log("Adding class '" + classname + "'"); - classMap.put(classname, clazz); - nbclasses++; - } - else { - pkg.removeChild(clazz); - } - } - if (nbclasses != 0){ - log("Adding package '" + pkgname + "'"); - pkgMap.put(pkgname, pkg); - } - else { - pkg.getParentNode().removeChild(pkg); - } - } - log("Indexed " + classMap.size() + " classes in " + pkgMap.size() + " packages"); - } - - /** create the whole new document */ - public Document createDocument(String[] classPath) throws Exception { - - DocumentBuilder dbuilder = newBuilder(); - InputSource is = new InputSource( new FileInputStream(file) ); - if (jprobeHome != null){ - File dtdDir = new File(jprobeHome, "Dtd/snapshot.dtd"); - is.setSystemId( "file:///" + dtdDir.getAbsolutePath() ); - } - report = dbuilder.parse( is ); - report.normalize(); - - // create maps for faster node access - createNodeMaps(); - - // iterate over the classpath... - ClassPathLoader cpl = new ClassPathLoader(classPath); - Enumeration enum = cpl.loaders(); - while ( enum.hasMoreElements() ){ - ClassPathLoader.FileLoader fl = (ClassPathLoader.FileLoader)enum.nextElement(); - ClassFile[] classes = fl.getClasses(); - log("Processing " + classes.length + " classes in " + fl.getFile()); - // process all classes - for (int i = 0; i < classes.length; i++){ - serializeClass(classes[i]); - } - } - // update the document with the stats - update(); - return report; - } - - /** - * JProbe does not put the java.lang prefix for classes - * in this package, so used this nice method so that - * I have the same signature for methods - */ - protected String getMethodSignature(MethodInfo method){ - StringBuffer buf = new StringBuffer(method.getName()); - buf.append("("); - String[] params = method.getParametersType(); - for (int i = 0; i < params.length; i++){ - String type = params[i]; - int pos = type.lastIndexOf('.'); - if (pos != -1){ - String pkg = type.substring(0, pos); - if ("java.lang".equals(pkg)){ - params[i] = type.substring(pos + 1); - } - } - buf.append(params[i]); - if (i != params.length - 1){ - buf.append(", "); - } - } - buf.append(")"); - return buf.toString(); - } - - /** - * Convert to a CovReport-like signature ie, .() - */ - protected String getMethodSignature(ClassFile clazz, MethodInfo method){ - StringBuffer buf = new StringBuffer(clazz.getFullName()); - buf.append("."); - buf.append(method.getName()); - buf.append("()"); - return buf.toString(); - } - - /** - * Do additional work on an element to remove abstract methods that - * are reported by JProbe 3.0 - */ - protected void removeAbstractMethods(ClassFile classFile, Element classNode){ - MethodInfoList methods = classFile.getMethods(); - Hashtable methodNodeList = getMethods(classNode); - // assert xmlMethods.size() == methods.length() - final int size = methods.length(); - for (int i = 0; i < size; i++){ - MethodInfo method = methods.getMethod(i); - String methodSig = getMethodSignature(method); - Element methodNode = (Element)methodNodeList.get(methodSig); - if ( methodNode != null && - Utils.isAbstract(method.getAccessFlags())) { - log("\tRemoving method " + methodSig); - classNode.removeChild(methodNode); - } - } - } - - /** create an empty method element with its cov.data values */ - protected Element createMethodElement(MethodInfo method){ - String methodsig = getMethodSignature(method); - Element methodElem = report.createElement("method"); - methodElem.setAttribute("name", methodsig); - // create the method cov.data element - Element methodData = report.createElement("cov.data"); - methodElem.appendChild(methodData); - methodData.setAttribute("calls", "0"); - methodData.setAttribute("hit_lines", "0"); - methodData.setAttribute("total_lines", String.valueOf(method.getNumberOfLines())); - return methodElem; - } - - /** create an empty package element with its default cov.data (0) */ - protected Element createPackageElement(String pkgname){ - Element pkgElem = report.createElement("package"); - pkgElem.setAttribute("name", pkgname); - // create the package cov.data element / default - // must be updated at the end of the whole process - Element pkgData = report.createElement("cov.data"); - pkgElem.appendChild(pkgData); - pkgData.setAttribute("calls", "0"); - pkgData.setAttribute("hit_methods", "0"); - pkgData.setAttribute("total_methods", "0"); - pkgData.setAttribute("hit_lines", "0"); - pkgData.setAttribute("total_lines", "0"); - return pkgElem; - } - - /** create an empty class element with its default cov.data (0) */ - protected Element createClassElement(ClassFile classFile){ - // create the class element - Element classElem = report.createElement("class"); - classElem.setAttribute("name", classFile.getName()); - classElem.setAttribute("source", classFile.getSourceFile()); - // create the cov.data elem - Element classData = report.createElement("cov.data"); - classElem.appendChild(classData); - // create the class cov.data element - classData.setAttribute("calls", "0"); - classData.setAttribute("hit_methods", "0"); - classData.setAttribute("total_methods", "0"); - classData.setAttribute("hit_lines", "0"); - classData.setAttribute("total_lines", "0"); - return classElem; - } - - /** serialize a classfile into XML */ - protected void serializeClass(ClassFile classFile){ - // the class already is reported so ignore it - String fullclassname = classFile.getFullName(); - log("Looking for '" + fullclassname + "'"); - Element clazz = (Element)classMap.get(fullclassname); - - // ignore classes that are already reported, all the information is - // already there. - if ( clazz != null ){ - log("Ignoring " + fullclassname); - removeAbstractMethods(classFile, clazz); - return; - } - - // ignore interfaces files, there is no code in there to cover. - if (Utils.isInterface(classFile.getAccess())){ - return; - } - - Vector methods = getFilteredMethods(classFile); - // no need to process, there are no methods to add for this class. - if (methods.size() == 0){ - return; - } - - String pkgname = classFile.getPackage(); - // System.out.println("Looking for package " + pkgname); - Element pkgElem = (Element)pkgMap.get(pkgname); - if (pkgElem == null){ - pkgElem = createPackageElement(pkgname); - report.getDocumentElement().appendChild(pkgElem); - pkgMap.put(pkgname, pkgElem); // add the pkg to the map - } - // this is a brand new class, so we have to create a new node - - // create the class element - Element classElem = createClassElement(classFile); - pkgElem.appendChild(classElem); - - int total_lines = 0; - int total_methods = 0; - for (int i = 0; i < methods.size(); i++){ - // create the method element - MethodInfo method = (MethodInfo)methods.elementAt(i); - if ( Utils.isAbstract(method.getAccessFlags() ) ){ - continue; // no need to report abstract methods - } - Element methodElem = createMethodElement(method); - classElem.appendChild(methodElem); - total_lines += method.getNumberOfLines(); - total_methods++; - } - // create the class cov.data element - Element classData = getCovDataChild(classElem); - classData.setAttribute("total_methods", String.valueOf(total_methods)); - classData.setAttribute("total_lines", String.valueOf(total_lines)); - - // add itself to the node map - classMap.put(fullclassname, classElem); - } - - protected Vector getFilteredMethods(ClassFile classFile){ - Vector methods = new Vector(); - MethodInfoList methodlist = classFile.getMethods(); - for (int i = 0; i < methodlist.length(); i++){ - MethodInfo method = methodlist.getMethod(i); - String signature = getMethodSignature(classFile, method); - if (filters.accept(signature)){ - methods.addElement(method); - log("keeping " + signature); - } else { + /** task caller, can be null, used for logging purpose */ + protected Task task; + + /** the XML file to process just from CovReport */ + protected File file; + + /** jprobe home path. It is used to get the DTD */ + protected File jprobeHome; + + /** parsed document */ + protected Document report; + + /** mapping of class names to ClassFiles from the reference classpath. It is used to filter the JProbe report. */ + protected Hashtable classFiles; + + /** mapping package name / package node for faster access */ + protected Hashtable pkgMap; + + /** mapping classname / class node for faster access */ + protected Hashtable classMap; + + /** method filters */ + protected ReportFilters filters; + + /** create a new XML report, logging will be on stdout */ + public XMLReport(File file) { + this(null, file); + } + + /** create a new XML report, logging done on the task */ + public XMLReport(Task task, File file) { + this.file = file; + this.task = task; + } + + /** set the JProbe home path. Used to get the DTD */ + public void setJProbehome(File home) { + jprobeHome = home; + } + + /** set the */ + public void setReportFilters(ReportFilters filters) { + this.filters = filters; + } + + + /** create node maps so that we can access node faster by their name */ + protected void createNodeMaps() { + pkgMap = new Hashtable(); + classMap = new Hashtable(); + // create a map index of all packages by their name + // @todo can be done faster by direct access. + NodeList packages = report.getElementsByTagName("package"); + final int pkglen = packages.getLength(); + log("Indexing " + pkglen + " packages"); + for (int i = pkglen - 1; i > -1; i--) { + Element pkg = (Element) packages.item(i); + String pkgname = pkg.getAttribute("name"); + + int nbclasses = 0; + // create a map index of all classes by their fully + // qualified name. + NodeList classes = pkg.getElementsByTagName("class"); + final int classlen = classes.getLength(); + log("Indexing " + classlen + " classes in package " + pkgname); + for (int j = classlen - 1; j > -1; j--) { + Element clazz = (Element) classes.item(j); + String classname = clazz.getAttribute("name"); + if (pkgname != null && pkgname.length() != 0) { + classname = pkgname + "." + classname; + } + + int nbmethods = 0; + NodeList methods = clazz.getElementsByTagName("method"); + final int methodlen = methods.getLength(); + for (int k = methodlen - 1; k > -1; k--) { + Element meth = (Element) methods.item(k); + StringBuffer methodname = new StringBuffer(meth.getAttribute("name")); + methodname.delete(methodname.toString().indexOf("("), methodname.toString().length()); + String signature = classname + "." + methodname + "()"; + if (filters.accept(signature)) { + log("kept method:" + signature); + nbmethods++; + } else { + clazz.removeChild(meth); + } + } + // if we don't keep any method, we don't keep the class + if (nbmethods != 0 && classFiles.containsKey(classname)) { + log("Adding class '" + classname + "'"); + classMap.put(classname, clazz); + nbclasses++; + } else { + pkg.removeChild(clazz); + } + } + if (nbclasses != 0) { + log("Adding package '" + pkgname + "'"); + pkgMap.put(pkgname, pkg); + } else { + pkg.getParentNode().removeChild(pkg); + } + } + log("Indexed " + classMap.size() + " classes in " + pkgMap.size() + " packages"); + } + + /** create the whole new document */ + public Document createDocument(String[] classPath) throws Exception { + + // Iterate over the classpath to identify reference classes + classFiles = new Hashtable(); + ClassPathLoader cpl = new ClassPathLoader(classPath); + Enumeration enum = cpl.loaders(); + while (enum.hasMoreElements()) { + ClassPathLoader.FileLoader fl = (ClassPathLoader.FileLoader) enum.nextElement(); + ClassFile[] classes = fl.getClasses(); + log("Processing " + classes.length + " classes in " + fl.getFile()); + // process all classes + for (int i = 0; i < classes.length; i++) { + classFiles.put(classes[i].getFullName(), classes[i]); + } + } + + // Load the JProbe coverage XML report + DocumentBuilder dbuilder = newBuilder(); + InputSource is = new InputSource(new FileInputStream(file)); + if (jprobeHome != null) { + File dtdDir = new File(jprobeHome, "Dtd"); + is.setSystemId("file:///" + dtdDir.getAbsolutePath() + "/"); + } + report = dbuilder.parse(is); + report.normalize(); + + // create maps for faster node access (also filters out unwanted nodes) + createNodeMaps(); + + // Make sure each class from the reference path ends up in the report + Enumeration classes = classFiles.elements(); + while (classes.hasMoreElements()) { + ClassFile cf = (ClassFile) classes.nextElement(); + serializeClass(cf); + } + // update the document with the stats + update(); + return report; + } + + /** + * JProbe does not put the java.lang prefix for classes + * in this package, so used this nice method so that + * I have the same signature for methods + */ + protected String getMethodSignature(MethodInfo method) { + StringBuffer buf = new StringBuffer(method.getName()); + buf.append("("); + String[] params = method.getParametersType(); + for (int i = 0; i < params.length; i++) { + String type = params[i]; + int pos = type.lastIndexOf('.'); + if (pos != -1) { + String pkg = type.substring(0, pos); + if ("java.lang".equals(pkg)) { + params[i] = type.substring(pos + 1); + } + } + buf.append(params[i]); + if (i != params.length - 1) { + buf.append(", "); + } + } + buf.append(")"); + return buf.toString(); + } + + /** + * Convert to a CovReport-like signature ie, .() + */ + protected String getMethodSignature(ClassFile clazz, MethodInfo method) { + StringBuffer buf = new StringBuffer(clazz.getFullName()); + buf.append("."); + buf.append(method.getName()); + buf.append("()"); + return buf.toString(); + } + + /** + * Do additional work on an element to remove abstract methods that + * are reported by JProbe 3.0 + */ + protected void removeAbstractMethods(ClassFile classFile, Element classNode) { + MethodInfo[] methods = classFile.getMethods(); + Hashtable methodNodeList = getMethods(classNode); + // assert xmlMethods.size() == methods.length() + final int size = methods.length; + for (int i = 0; i < size; i++) { + MethodInfo method = methods[i]; + String methodSig = getMethodSignature(method); + Element methodNode = (Element) methodNodeList.get(methodSig); + if (methodNode != null && + Utils.isAbstract(method.getAccessFlags())) { + log("\tRemoving method " + methodSig); + classNode.removeChild(methodNode); + } + } + } + + /** create an empty method element with its cov.data values */ + protected Element createMethodElement(MethodInfo method) { + String methodsig = getMethodSignature(method); + Element methodElem = report.createElement("method"); + methodElem.setAttribute("name", methodsig); + // create the method cov.data element + Element methodData = report.createElement("cov.data"); + methodElem.appendChild(methodData); + methodData.setAttribute("calls", "0"); + methodData.setAttribute("hit_lines", "0"); + methodData.setAttribute("total_lines", String.valueOf(method.getNumberOfLines())); + return methodElem; + } + + /** create an empty package element with its default cov.data (0) */ + protected Element createPackageElement(String pkgname) { + Element pkgElem = report.createElement("package"); + pkgElem.setAttribute("name", pkgname); + // create the package cov.data element / default + // must be updated at the end of the whole process + Element pkgData = report.createElement("cov.data"); + pkgElem.appendChild(pkgData); + pkgData.setAttribute("calls", "0"); + pkgData.setAttribute("hit_methods", "0"); + pkgData.setAttribute("total_methods", "0"); + pkgData.setAttribute("hit_lines", "0"); + pkgData.setAttribute("total_lines", "0"); + return pkgElem; + } + + /** create an empty class element with its default cov.data (0) */ + protected Element createClassElement(ClassFile classFile) { + // create the class element + Element classElem = report.createElement("class"); + classElem.setAttribute("name", classFile.getName()); + classElem.setAttribute("source", classFile.getSourceFile()); + // create the cov.data elem + Element classData = report.createElement("cov.data"); + classElem.appendChild(classData); + // create the class cov.data element + classData.setAttribute("calls", "0"); + classData.setAttribute("hit_methods", "0"); + classData.setAttribute("total_methods", "0"); + classData.setAttribute("hit_lines", "0"); + classData.setAttribute("total_lines", "0"); + return classElem; + } + + /** serialize a classfile into XML */ + protected void serializeClass(ClassFile classFile) { + // the class already is reported so ignore it + String fullclassname = classFile.getFullName(); + log("Looking for '" + fullclassname + "'"); + Element clazz = (Element) classMap.get(fullclassname); + + // ignore classes that are already reported, all the information is + // already there. + if (clazz != null) { + log("Ignoring " + fullclassname); + removeAbstractMethods(classFile, clazz); + return; + } + + // ignore interfaces files, there is no code in there to cover. + if (Utils.isInterface(classFile.getAccess())) { + return; + } + + Vector methods = getFilteredMethods(classFile); + // no need to process, there are no methods to add for this class. + if (methods.size() == 0) { + return; + } + + String pkgname = classFile.getPackage(); + // System.out.println("Looking for package " + pkgname); + Element pkgElem = (Element) pkgMap.get(pkgname); + if (pkgElem == null) { + pkgElem = createPackageElement(pkgname); + report.getDocumentElement().appendChild(pkgElem); + pkgMap.put(pkgname, pkgElem); // add the pkg to the map + } + // this is a brand new class, so we have to create a new node + + // create the class element + Element classElem = createClassElement(classFile); + pkgElem.appendChild(classElem); + + int total_lines = 0; + int total_methods = 0; + for (int i = 0; i < methods.size(); i++) { + // create the method element + MethodInfo method = (MethodInfo) methods.elementAt(i); + if (Utils.isAbstract(method.getAccessFlags())) { + continue; // no need to report abstract methods + } + Element methodElem = createMethodElement(method); + classElem.appendChild(methodElem); + total_lines += method.getNumberOfLines(); + total_methods++; + } + // create the class cov.data element + Element classData = getCovDataChild(classElem); + classData.setAttribute("total_methods", String.valueOf(total_methods)); + classData.setAttribute("total_lines", String.valueOf(total_lines)); + + // add itself to the node map + classMap.put(fullclassname, classElem); + } + + protected Vector getFilteredMethods(ClassFile classFile) { + Vector methods = new Vector(); + MethodInfo[] methodlist = classFile.getMethods(); + for (int i = 0; i < methodlist.length; i++) { + MethodInfo method = methodlist[i]; + String signature = getMethodSignature(classFile, method); + if (filters.accept(signature)) { + methods.addElement(method); + log("keeping " + signature); + } else { // log("discarding " + signature); - } - } - return methods; - } - - - /** update the count of the XML, that is accumulate the stats on - * methods, classes and package so that the numbers are valid - * according to the info that was appended to the XML. - */ - protected void update(){ - int calls = 0; - int hit_methods = 0; - int total_methods = 0; - int hit_lines = 0; - int total_lines = 0; - - // use the map for access, all nodes should be there - Enumeration enum = pkgMap.elements(); - while ( enum.hasMoreElements() ){ - Element pkgElem = (Element)enum.nextElement(); - String pkgname = pkgElem.getAttribute("name"); - Element[] classes = getClasses(pkgElem); - int pkg_calls = 0; - int pkg_hit_methods = 0; - int pkg_total_methods = 0; - int pkg_hit_lines = 0; - int pkg_total_lines = 0; - //System.out.println("Processing package '" + pkgname + "': " + classes.length + " classes"); - for (int j = 0; j < classes.length; j++){ - Element clazz = classes[j]; - String classname = clazz.getAttribute("name"); - if (pkgname != null && pkgname.length() != 0){ - classname = pkgname + "." + classname; - } - // there's only cov.data as a child so bet on it - Element covdata = getCovDataChild(clazz); - try { - pkg_calls += Integer.parseInt(covdata.getAttribute("calls")); - pkg_hit_methods += Integer.parseInt(covdata.getAttribute("hit_methods")); - pkg_total_methods += Integer.parseInt(covdata.getAttribute("total_methods")); - pkg_hit_lines += Integer.parseInt(covdata.getAttribute("hit_lines")); - pkg_total_lines += Integer.parseInt(covdata.getAttribute("total_lines")); - } catch (NumberFormatException e){ - System.err.println("Error parsing '" + classname + "' (" + j + "/" + classes.length + ") in package '" + pkgname + "'"); - throw e; - } - } - Element covdata = getCovDataChild(pkgElem); - covdata.setAttribute("calls", String.valueOf(pkg_calls)); - covdata.setAttribute("hit_methods", String.valueOf(pkg_hit_methods)); - covdata.setAttribute("total_methods", String.valueOf(pkg_total_methods)); - covdata.setAttribute("hit_lines", String.valueOf(pkg_hit_lines)); - covdata.setAttribute("total_lines", String.valueOf(pkg_total_lines)); - calls += pkg_calls; - hit_methods += pkg_hit_methods; - total_methods += pkg_total_methods; - hit_lines += pkg_hit_lines; - total_lines += pkg_total_lines; - } - Element covdata = getCovDataChild(report.getDocumentElement()); - covdata.setAttribute("calls", String.valueOf(calls)); - covdata.setAttribute("hit_methods", String.valueOf(hit_methods)); - covdata.setAttribute("total_methods", String.valueOf(total_methods)); - covdata.setAttribute("hit_lines", String.valueOf(hit_lines)); - covdata.setAttribute("total_lines", String.valueOf(total_lines)); - } - - protected Element getCovDataChild(Element parent){ - NodeList children = parent.getChildNodes(); - int len = children.getLength(); - for (int i = 0; i < len; i++){ - Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE){ - Element elem = (Element)child; - if ("cov.data".equals(elem.getNodeName())){ - return elem; - } - } - } - throw new NoSuchElementException("Could not find 'cov.data' element in parent '" + parent.getNodeName() + "'"); - } - - protected Hashtable getMethods(Element clazz){ - Hashtable map = new Hashtable(); - NodeList children = clazz.getChildNodes(); - int len = children.getLength(); - for (int i = 0; i < len; i++){ - Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE){ - Element elem = (Element)child; - if ("method".equals(elem.getNodeName())){ - String name = elem.getAttribute("name"); - map.put(name, elem); - } - } - } - return map; - } - - protected Element[] getClasses(Element pkg){ - Vector v = new Vector(); - NodeList children = pkg.getChildNodes(); - int len = children.getLength(); - for (int i = 0; i < len; i++){ - Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE){ - Element elem = (Element)child; - if ("class".equals(elem.getNodeName())){ - v.addElement(elem); - } - } - } - Element[] elems = new Element[v.size()]; - v.copyInto(elems); - return elems; - - } - - protected Element[] getPackages(Element snapshot){ - Vector v = new Vector(); - NodeList children = snapshot.getChildNodes(); - int len = children.getLength(); - for (int i = 0; i < len; i++){ - Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE){ - Element elem = (Element)child; - if ("package".equals(elem.getNodeName())){ - v.addElement(elem); - } - } - } - Element[] elems = new Element[v.size()]; - v.copyInto(elems); - return elems; - } - - private static DocumentBuilder newBuilder() { - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setIgnoringComments(true); - factory.setValidating(false); - return factory.newDocumentBuilder(); - } catch (Exception e){ - throw new ExceptionInInitializerError(e); - } - } - - public void log(String message){ - if (task == null){ - //System.out.println(message); - } else { - task.log(message, Project.MSG_DEBUG); - } - } - - public static void main(String[] args) throws Exception { - File reportFile = new File( XMLReport.class.getResource("covreport-test.xml").getFile() ); - XMLReport report = new XMLReport( reportFile ); - report.setJProbehome(new File("d:/Program Files/JProbe")); - ReportFilters filters = new ReportFilters(); - ReportFilters.Include incl = new ReportFilters.Include(); - incl.setClass("*"); - incl.setMethod("set*"); - filters.addInclude(incl); - report.setReportFilters(filters); - Document doc = report.createDocument( new String[]{"Z:/imediation/ichannel/sources/toolkit/lib/imtoolkit.jar"} ); - TransformerFactory tfactory = TransformerFactory.newInstance(); - Transformer transformer = tfactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.METHOD, "xml"); - transformer.transform(new DOMSource(doc), new StreamResult( new FileOutputStream( "d:/tmp/snapshot_merge.xml"))); - } + } + } + return methods; + } + + + /** update the count of the XML, that is accumulate the stats on + * methods, classes and package so that the numbers are valid + * according to the info that was appended to the XML. + */ + protected void update() { + int calls = 0; + int hit_methods = 0; + int total_methods = 0; + int hit_lines = 0; + int total_lines = 0; + + // use the map for access, all nodes should be there + Enumeration enum = pkgMap.elements(); + while (enum.hasMoreElements()) { + Element pkgElem = (Element) enum.nextElement(); + String pkgname = pkgElem.getAttribute("name"); + Element[] classes = getClasses(pkgElem); + int pkg_calls = 0; + int pkg_hit_methods = 0; + int pkg_total_methods = 0; + int pkg_hit_lines = 0; + int pkg_total_lines = 0; + //System.out.println("Processing package '" + pkgname + "': " + classes.length + " classes"); + for (int j = 0; j < classes.length; j++) { + Element clazz = classes[j]; + String classname = clazz.getAttribute("name"); + if (pkgname != null && pkgname.length() != 0) { + classname = pkgname + "." + classname; + } + // there's only cov.data as a child so bet on it + Element covdata = getCovDataChild(clazz); + try { + pkg_calls += Integer.parseInt(covdata.getAttribute("calls")); + pkg_hit_methods += Integer.parseInt(covdata.getAttribute("hit_methods")); + pkg_total_methods += Integer.parseInt(covdata.getAttribute("total_methods")); + pkg_hit_lines += Integer.parseInt(covdata.getAttribute("hit_lines")); + pkg_total_lines += Integer.parseInt(covdata.getAttribute("total_lines")); + } catch (NumberFormatException e) { + System.err.println("Error parsing '" + classname + "' (" + j + "/" + classes.length + ") in package '" + pkgname + "'"); + throw e; + } + } + Element covdata = getCovDataChild(pkgElem); + covdata.setAttribute("calls", String.valueOf(pkg_calls)); + covdata.setAttribute("hit_methods", String.valueOf(pkg_hit_methods)); + covdata.setAttribute("total_methods", String.valueOf(pkg_total_methods)); + covdata.setAttribute("hit_lines", String.valueOf(pkg_hit_lines)); + covdata.setAttribute("total_lines", String.valueOf(pkg_total_lines)); + calls += pkg_calls; + hit_methods += pkg_hit_methods; + total_methods += pkg_total_methods; + hit_lines += pkg_hit_lines; + total_lines += pkg_total_lines; + } + Element covdata = getCovDataChild(report.getDocumentElement()); + covdata.setAttribute("calls", String.valueOf(calls)); + covdata.setAttribute("hit_methods", String.valueOf(hit_methods)); + covdata.setAttribute("total_methods", String.valueOf(total_methods)); + covdata.setAttribute("hit_lines", String.valueOf(hit_lines)); + covdata.setAttribute("total_lines", String.valueOf(total_lines)); + } + + protected Element getCovDataChild(Element parent) { + NodeList children = parent.getChildNodes(); + int len = children.getLength(); + for (int i = 0; i < len; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Element elem = (Element) child; + if ("cov.data".equals(elem.getNodeName())) { + return elem; + } + } + } + throw new NoSuchElementException("Could not find 'cov.data' element in parent '" + parent.getNodeName() + "'"); + } + + protected Hashtable getMethods(Element clazz) { + Hashtable map = new Hashtable(); + NodeList children = clazz.getChildNodes(); + int len = children.getLength(); + for (int i = 0; i < len; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Element elem = (Element) child; + if ("method".equals(elem.getNodeName())) { + String name = elem.getAttribute("name"); + map.put(name, elem); + } + } + } + return map; + } + + protected Element[] getClasses(Element pkg) { + Vector v = new Vector(); + NodeList children = pkg.getChildNodes(); + int len = children.getLength(); + for (int i = 0; i < len; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Element elem = (Element) child; + if ("class".equals(elem.getNodeName())) { + v.addElement(elem); + } + } + } + Element[] elems = new Element[v.size()]; + v.copyInto(elems); + return elems; + + } + + protected Element[] getPackages(Element snapshot) { + Vector v = new Vector(); + NodeList children = snapshot.getChildNodes(); + int len = children.getLength(); + for (int i = 0; i < len; i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Element elem = (Element) child; + if ("package".equals(elem.getNodeName())) { + v.addElement(elem); + } + } + } + Element[] elems = new Element[v.size()]; + v.copyInto(elems); + return elems; + } + + private static DocumentBuilder newBuilder() { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setIgnoringComments(true); + factory.setValidating(false); + return factory.newDocumentBuilder(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + public void log(String message) { + if (task == null) { + //System.out.println(message); + } else { + task.log(message, Project.MSG_DEBUG); + } + } } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassFile.java b/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassFile.java index 7fa657950..75f4d3b19 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassFile.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassFile.java @@ -53,154 +53,132 @@ */ package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode; +import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.DataInputStream; -import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPool; import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ClassCPInfo; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.AttributeInfoList; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.SourceFile; +import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPool; +import org.apache.tools.ant.taskdefs.optional.depend.constantpool.Utf8CPInfo; import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.AttributeInfo; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.SourceDir; import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.LineNumberTable; /** * Object representing a class. * + * Information are kept to the strict minimum for JProbe reports so + * that not too many objects are created for a class, otherwise the + * JVM can quickly run out of memory when analyzing a great deal of + * classes and keeping them in memory for global analysis. + * * @author Stephane Bailliez */ -public class ClassFile { - - protected ConstantPool constantPool; - - protected InterfaceList interfaces; - - protected FieldInfoList fields; - - protected MethodInfoList methods; - - protected String sourceDir; - - protected String sourceFile; - - protected int access_flags; - - protected int this_class; - - protected int super_class; - - protected boolean isSynthetic; - - protected boolean isDeprecated; - - public ClassFile(InputStream is) throws IOException { - DataInputStream dis = new DataInputStream(is); - constantPool = new ConstantPool(); - - int magic = dis.readInt(); // 0xCAFEBABE - int minor = dis.readShort(); - int major = dis.readShort(); - - constantPool.read(dis); - constantPool.resolve(); - - access_flags = dis.readShort(); - this_class = dis.readShort(); - super_class = dis.readShort(); - - interfaces = new InterfaceList(constantPool); - interfaces.read(dis); - //System.out.println(interfaces.toString()); - - fields = new FieldInfoList(constantPool); - fields.read(dis); - //System.out.println(fields.toString()); - - methods = new MethodInfoList(constantPool); - methods.read(dis); - //System.out.println(methods.toString()); - - AttributeInfoList attributes = new AttributeInfoList(constantPool); - attributes.read(dis); - SourceFile srcFile = (SourceFile)attributes.getAttribute(AttributeInfo.SOURCE_FILE); - if (srcFile != null){ - sourceFile = srcFile.getValue(); - } - SourceDir srcDir = (SourceDir)attributes.getAttribute(AttributeInfo.SOURCE_DIR); - if (srcDir != null){ - sourceDir = srcDir.getValue(); - } - isSynthetic = attributes.getAttribute(AttributeInfo.SYNTHETIC) != null; - isDeprecated = attributes.getAttribute(AttributeInfo.DEPRECATED) != null; - } - - public int getAccess(){ - return access_flags; - } - public InterfaceList getInterfaces(){ - return interfaces; - } - public String getSourceFile(){ - return sourceFile; - } - public String getSourceDir(){ - return sourceDir; - } - public boolean isSynthetic() { - return isSynthetic; - } - public boolean isDeprecated() { - return isDeprecated; - } - public MethodInfoList getMethods(){ - return methods; - } - public FieldInfoList getFields(){ - return fields; - } - public String getSuperName(){ - return Utils.getUTF8Value(constantPool, super_class); - } - public String getFullName(){ - return ((ClassCPInfo)constantPool.getEntry(this_class)).getClassName().replace('/','.'); - } - public String getName(){ - String name = getFullName(); - int pos = name.lastIndexOf('.'); - if (pos == -1){ - return ""; - } - return name.substring(pos + 1); - } - public String getPackage(){ - String name = getFullName(); - int pos = name.lastIndexOf('.'); - if (pos == -1){ - return ""; - } - return name.substring(0, pos); - } - - /** dirty test method, move it into a testcase */ - public static void main(String[] args) throws Exception { - System.out.println("loading classfile..."); - InputStream is = ClassLoader.getSystemResourceAsStream("java/util/Vector.class"); - ClassFile clazzfile = new ClassFile(is); - System.out.println("Class name: " + clazzfile.getName()); - MethodInfoList methods = clazzfile.getMethods(); - for (int i = 0; i < methods.length(); i++){ - MethodInfo method = methods.getMethod(i); - System.out.println("Method: " + method.getFullSignature()); - System.out.println("line: " + method.getNumberOfLines()); - LineNumberTable lnt = method.getCode().getLineNumberTable(); - } - } - +public final class ClassFile { + + private MethodInfo[] methods; + + private String sourceFile; + + private String fullname; + + private int access_flags; + + public ClassFile(InputStream is) throws IOException { + DataInputStream dis = new DataInputStream(is); + ConstantPool constantPool = new ConstantPool(); + + int magic = dis.readInt(); // 0xCAFEBABE + int minor = dis.readShort(); + int major = dis.readShort(); + + constantPool.read(dis); + constantPool.resolve(); + + // class information + access_flags = dis.readShort(); + int this_class = dis.readShort(); + fullname = ((ClassCPInfo) constantPool.getEntry(this_class)).getClassName().replace('/', '.'); + int super_class = dis.readShort(); + + // skip interfaces... + int count = dis.readShort(); + dis.skipBytes(count * 2); // short + + // skip fields... + int numFields = dis.readShort(); + for (int i = 0; i < numFields; i++) { + // 3 short: access flags, name index, descriptor index + dis.skip(2 * 3); + // attribute list... + int attributes_count = dis.readUnsignedShort(); + for (int j = 0; j < attributes_count; j++) { + dis.skipBytes(2); // skip attr_id (short) + int len = dis.readInt(); + dis.skipBytes(len); + } + } + + // read methods + int method_count = dis.readShort(); + methods = new MethodInfo[method_count]; + for (int i = 0; i < method_count; i++) { + methods[i] = new MethodInfo(); + methods[i].read(constantPool, dis); + } + + // get interesting attributes. + int attributes_count = dis.readUnsignedShort(); + for (int j = 0; j < attributes_count; j++) { + int attr_id = dis.readShort(); + int len = dis.readInt(); + String attr_name = Utils.getUTF8Value(constantPool, attr_id); + if (AttributeInfo.SOURCE_FILE.equals(attr_name)) { + int name_index = dis.readShort(); + sourceFile = ((Utf8CPInfo) constantPool.getEntry(name_index)).getValue(); + } else { + dis.skipBytes(len); + } + } + } + + public int getAccess() { + return access_flags; + } + + public String getSourceFile() { + return sourceFile; + } + + public MethodInfo[] getMethods() { + return methods; + } + + public String getFullName() { + return fullname; + } + + public String getName() { + String name = getFullName(); + int pos = name.lastIndexOf('.'); + if (pos == -1) { + return ""; + } + return name.substring(pos + 1); + } + + public String getPackage() { + String name = getFullName(); + int pos = name.lastIndexOf('.'); + if (pos == -1) { + return ""; + } + return name.substring(0, pos); + } + } - + diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/MethodInfo.java b/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/MethodInfo.java index a7673b218..d822e33aa 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/MethodInfo.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/MethodInfo.java @@ -53,15 +53,11 @@ */ package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode; -import java.io.IOException; import java.io.DataInputStream; +import java.io.IOException; import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPool; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.Code; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.Exceptions; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.AttributeInfoList; import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.AttributeInfo; -import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.LineNumberTable; /** * Method info structure. @@ -69,104 +65,119 @@ import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.LineNu * * @author Stephane Bailliez */ -public class MethodInfo { - protected ConstantPool constantPool; - protected int access_flags; - protected int name_index; - protected int descriptor_index; - protected Code code; - protected boolean deprecated; - protected boolean synthetic; - protected Exceptions exceptions; - public MethodInfo(ConstantPool pool){ - constantPool = pool; - } - - public void read(DataInputStream dis) throws IOException { - access_flags = dis.readShort(); - name_index = dis.readShort(); - descriptor_index = dis.readShort(); - AttributeInfoList attrlist = new AttributeInfoList(constantPool); - attrlist.read(dis); - code = (Code)attrlist.getAttribute(AttributeInfo.CODE); - synthetic = attrlist.getAttribute(AttributeInfo.SYNTHETIC) != null; - deprecated = attrlist.getAttribute(AttributeInfo.DEPRECATED) != null; - exceptions = (Exceptions)attrlist.getAttribute(AttributeInfo.EXCEPTIONS); - } - - public int getAccessFlags(){ - return access_flags; - } - - public String getName(){ - return Utils.getUTF8Value(constantPool, name_index); - } - - public String getDescriptor(){ - return Utils.getUTF8Value(constantPool, descriptor_index); - } - - public String getFullSignature(){ - return getReturnType() + " " + getShortSignature(); - } - - public String getShortSignature(){ - StringBuffer buf = new StringBuffer(getName()); - buf.append("("); - String[] params = getParametersType(); - for (int i = 0; i < params.length; i++){ - buf.append(params[i]); - if (i != params.length - 1){ - buf.append(", "); - } - } - buf.append(")"); - return buf.toString(); - } - - public String getReturnType(){ - return Utils.getMethodReturnType(getDescriptor()); - } - - public String[] getParametersType(){ - return Utils.getMethodParams(getDescriptor()); - } - - public Code getCode(){ - return code; - } - - public int getNumberOfLines(){ - int len = -1; - if (code != null){ - LineNumberTable lnt = code.getLineNumberTable(); - if (lnt != null){ - len = lnt.length(); - } - } - return len; - } - - public boolean isDeprecated(){ - return deprecated; - } - - public boolean isSynthetic(){ - return synthetic; - } - - public String getAccess(){ - return Utils.getMethodAccess(access_flags); - } - - public String toString(){ - StringBuffer sb = new StringBuffer(); - sb.append("Method: ").append(getAccess()).append(" "); - sb.append(getFullSignature()); - sb.append(" synthetic:").append(synthetic); - sb.append(" deprecated:").append(deprecated); - return sb.toString(); - } +public final class MethodInfo { + private int access_flags; + private int loc = -1; + private String name; + private String descriptor; + + public MethodInfo() { + } + + public void read(ConstantPool constantPool, DataInputStream dis) throws IOException { + access_flags = dis.readShort(); + + int name_index = dis.readShort(); + name = Utils.getUTF8Value(constantPool, name_index); + + int descriptor_index = dis.readShort(); + descriptor = Utils.getUTF8Value(constantPool, descriptor_index); + + int attributes_count = dis.readUnsignedShort(); + for (int i = 0; i < attributes_count; i++) { + int attr_id = dis.readShort(); + String attr_name = Utils.getUTF8Value(constantPool, attr_id); + int len = dis.readInt(); + if (AttributeInfo.CODE.equals(attr_name)) { + readCode(constantPool, dis); + } else { + dis.skipBytes(len); + } + } + + } + + protected void readCode(ConstantPool constantPool, DataInputStream dis) throws IOException { + // skip max_stack (short), max_local (short) + dis.skipBytes(2*2); + + // skip bytecode... + int bytecode_len = dis.readInt(); + dis.skip(bytecode_len); + + // skip exceptions... 1 exception = 4 short. + int exception_count = dis.readShort(); + dis.skipBytes(exception_count * 4 * 2); + + // read attributes... + int attributes_count = dis.readUnsignedShort(); + for (int i = 0; i < attributes_count; i++) { + int attr_id = dis.readShort(); + String attr_name = Utils.getUTF8Value(constantPool, attr_id); + int len = dis.readInt(); + if (AttributeInfo.LINE_NUMBER_TABLE.equals(attr_name)) { + // we're only interested in lines of code... + loc = dis.readShort(); + // skip the table which is 2*loc*short + dis.skip(loc * 2 * 2); + } else { + dis.skipBytes(len); + } + } + } + + public int getAccessFlags() { + return access_flags; + } + + public String getName() { + return name; + } + + public String getDescriptor() { + return descriptor; + } + + public String getFullSignature() { + return getReturnType() + " " + getShortSignature(); + } + + public String getShortSignature() { + StringBuffer buf = new StringBuffer(getName()); + buf.append("("); + String[] params = getParametersType(); + for (int i = 0; i < params.length; i++) { + buf.append(params[i]); + if (i != params.length - 1) { + buf.append(", "); + } + } + buf.append(")"); + return buf.toString(); + } + + public String getReturnType() { + return Utils.getMethodReturnType(getDescriptor()); + } + + public String[] getParametersType() { + return Utils.getMethodParams(getDescriptor()); + } + + public int getNumberOfLines() { + return loc; + } + + public String getAccess() { + return Utils.getMethodAccess(access_flags); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("Method: ").append(getAccess()).append(" "); + sb.append(getFullSignature()); + return sb.toString(); + } }