| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import com.sun.javadoc.AnnotationDesc; |
| import com.sun.javadoc.AnnotationTypeDoc; |
| import com.sun.javadoc.AnnotationValue; |
| import com.sun.javadoc.ClassDoc; |
| import com.sun.javadoc.ConstructorDoc; |
| import com.sun.javadoc.Doc; |
| import com.sun.javadoc.ExecutableMemberDoc; |
| import com.sun.javadoc.LanguageVersion; |
| import com.sun.javadoc.MethodDoc; |
| import com.sun.javadoc.PackageDoc; |
| import com.sun.javadoc.Parameter; |
| import com.sun.javadoc.ParameterizedType; |
| import com.sun.javadoc.RootDoc; |
| import com.sun.javadoc.SourcePosition; |
| import com.sun.javadoc.Tag; |
| import com.sun.javadoc.Type; |
| import com.sun.javadoc.TypeVariable; |
| import com.sun.javadoc.AnnotationDesc.ElementValuePair; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /* |
| */ |
| public class TestCoverageDoclet { |
| |
| public static final int TYPE_FIELD = 0; |
| public static final int TYPE_METHOD = 1; |
| public static final int TYPE_CLASS = 2; |
| public static final int TYPE_PACKAGE = 3; |
| public static final int TYPE_ROOT = 4; |
| public static final int VALUE_RED = 0; |
| public static final int VALUE_YELLOW = 1; |
| public static final int VALUE_GREEN = 2; |
| public static final String[] COLORS = { "#ffa0a0", "#ffffa0", "#a0ffa0" }; |
| public static final String[] TYPES = { "Field", "Method", "Class", "Package", "All packages" }; |
| |
| /** |
| * Holds our basic output directory. |
| */ |
| private File directory; |
| |
| private Map<ExecutableMemberDoc, AnnotationPointer> resolved = |
| new HashMap<ExecutableMemberDoc, AnnotationPointer>(8192); |
| |
| /** |
| * Helper class for comparing element with each other, in oder to determine |
| * an order. Uses lexicographic order of names. |
| */ |
| private class DocComparator implements Comparator<Doc> { |
| public int compare(Doc elem1, Doc elem2) { |
| return elem1.name().compareTo(elem2.name()); |
| } |
| |
| public boolean equals(Doc elem) { |
| return this == elem; |
| } |
| } |
| |
| private class MemberComparator implements Comparator<ExecutableMemberDoc> { |
| public int compare(ExecutableMemberDoc mem1, ExecutableMemberDoc mem2) { |
| return mem1.toString().compareTo(mem2.toString()); |
| } |
| } |
| |
| class MyStats { |
| private String name; |
| private String link; |
| private int elemCnt = 0; |
| private int[] ryg = new int[3]; |
| private String extra; |
| |
| public MyStats(int type, String name, String link) { |
| this.name = name; |
| this.link = link; |
| } |
| |
| public void add(MyStats subStats) { |
| elemCnt++; |
| for (int i = 0; i < ryg.length; i++) { |
| ryg[i]+= subStats.ryg[i]; |
| } |
| } |
| |
| public int getCountFor(int color) { |
| return ryg[color]; |
| } |
| |
| public String getStat() { |
| float coverage = (float)(ryg[1]+ryg[2]) / (float)(ryg[0]+ryg[1]+ryg[2]); |
| return "red: "+ryg[0]+", yellow:"+ryg[1]+", green:"+ryg[2]+",coverage:"+coverage; |
| } |
| |
| public void inc(int color) { |
| ryg[color]++; |
| } |
| |
| public String getLink() { |
| return link; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public String getExtra() { |
| return extra; |
| } |
| |
| public void setExtra(String extra) { |
| this.extra = extra; |
| } |
| } |
| |
| /** |
| * Holds our comparator instance for everything. |
| */ |
| private DocComparator comparator = new DocComparator(); |
| private MemberComparator membercomparator = new MemberComparator(); |
| |
| /** |
| * Creates a new instance of the TestProgressDoclet for a given target |
| * directory. |
| */ |
| public TestCoverageDoclet(String directory) { |
| this.directory = new File(directory); |
| } |
| |
| /** |
| * Opens a new output file and writes the usual HTML header. Directories |
| * are created on demand. |
| */ |
| private PrintWriter openFile(String name, String title) throws IOException { |
| File file = new File(directory, name); |
| File parent = file.getParentFile(); |
| parent.mkdirs(); |
| |
| PrintWriter printer = new PrintWriter(new FileOutputStream(file)); |
| |
| printer.println("<html>"); |
| printer.println(" <head>"); |
| printer.println(" <title>" + title + "</title>"); |
| printer.println("<style type=\"text/css\">\n"+ |
| "body { }\n"+ |
| "table {border-width: 0px; border: solid; border-collapse: collapse;}\n"+ |
| "table tr td { vertical-align:top; padding:3px; border: 1px solid black;}\n"+ |
| "</style>"); |
| printer.println(" </head>"); |
| printer.println(" <body>"); |
| printer.println(" <h1>" + title + "</h1>"); |
| |
| return printer; |
| } |
| |
| /** |
| * Closes the given output file, writing the usual HTML footer before. |
| */ |
| private void closeFile(PrintWriter printer) { |
| printer.println(" </body>"); |
| printer.println("</html>"); |
| printer.flush(); |
| printer.close(); |
| } |
| |
| private class TablePrinter { |
| private PrintWriter pr; |
| |
| public TablePrinter(PrintWriter pr) { |
| this.pr = pr; |
| } |
| |
| public void printRow(int color, String... columns) { |
| String colo = COLORS[color]; |
| pr.print("<tr style=\"background-color:"+colo+"\">"); |
| for (String col : columns) { |
| pr.print("<td>"+col+"</td>"); |
| } |
| pr.print("</tr>"); |
| } |
| |
| public void printRow(String... columns) { |
| printRow(1, columns); |
| } |
| |
| public void printPlain(String val) { |
| pr.print(val); |
| } |
| |
| } |
| |
| /** |
| * Processes the whole list of classes that JavaDoc knows about. |
| */ |
| private void process(RootDoc root) throws IOException { |
| |
| // 1. traverse all test-classes (those extending JUnit's TestCase) |
| // and collect the annotation info. Print which test classes |
| // need annotating |
| PrintWriter pr = openFile("test-annotation.html", "test class annotation coverage"); |
| TablePrinter printer = new TablePrinter(pr); |
| printer.printPlain("<table>"); |
| printer.printRow("className", "annotated methods", "total methods", "percentage"); |
| |
| ClassDoc[] classes = root.classes(); |
| Arrays.sort(classes, new Comparator<ClassDoc>() { |
| public int compare(ClassDoc c1, ClassDoc c2) { |
| return c1.toString().compareTo(c2.toString()); |
| }}); |
| for (ClassDoc classDoc : classes) { |
| if (extendsJUnitTestCase(classDoc)) { |
| processTestClass(classDoc, printer); |
| } |
| } |
| printer.printPlain("</table>"); |
| closeFile(pr); |
| //dumpInfo(); |
| |
| // 2. traverse all "normal" (non-junit) source files, for each method |
| // get its status and propagate it up the tree |
| MyStats stats = new MyStats(TYPE_ROOT, "All", "aaa.html"); |
| PrintWriter aprinter = openFile("index.html", "All packages"); |
| aprinter.println("Generated " + new Date().toString()); |
| aprinter.println("<br/><a href=\"test-annotation.html\">annotation progress of test classes</a><br/>"); |
| aprinter.println("<br/><a href=\"hidden-doc.html\">hidden classes and methods</a><br/>"); |
| aprinter.println("<br/><a href=\"interfaces.html\">interfaces</a><br/>"); |
| aprinter.println("<h2>Packages</h2>"); |
| aprinter.println("<table>"); |
| |
| PrintWriter hiddenDocPr = openFile("hidden-doc.html", "hidden classes and methods list"); |
| TablePrinter hiddenDocPrinter = new TablePrinter(hiddenDocPr); |
| hiddenDocPrinter.printPlain("<table>"); |
| hiddenDocPrinter.printRow("Package Name", "Class Name", "Method Name"); |
| |
| PrintWriter interfacePr = openFile("interfaces.html", "interface list"); |
| TablePrinter interfacePrinter = new TablePrinter(interfacePr); |
| interfacePrinter.printPlain("<table>"); |
| interfacePrinter.printRow("packageName", "className"); |
| |
| PackageDoc[] packages = root.specifiedPackages(); |
| Arrays.sort(packages, comparator); |
| for (PackageDoc pack : packages) { |
| if (pack.allClasses().length != 0) { |
| |
| if (pack.name().endsWith(".cts")) { |
| // Skip the cts test packages |
| // System.out.println(">>>>>>>>>>>Skip package: " + pack.name()); |
| } else { |
| MyStats subStat = processPackage(pack, hiddenDocPrinter, interfacePrinter); |
| |
| System.out.println("package " + pack.name() + " has " + subStat.getCountFor(0) + " red."); |
| printStats(aprinter, subStat, true); |
| stats.add(subStat); |
| } |
| } |
| } |
| |
| |
| System.out.println("Total has " + stats.getCountFor(0) + " red."); |
| |
| interfacePrinter.printPlain("</table>"); |
| closeFile(interfacePr); |
| |
| hiddenDocPrinter.printPlain("</table>"); |
| closeFile(hiddenDocPr); |
| |
| aprinter.println("</table>"); |
| aprinter.println("<h2>Summary</h2>"); |
| aprinter.println("<table>"); |
| printStats(aprinter, stats, false); |
| aprinter.println("</table>"); |
| |
| closeFile(aprinter); |
| } |
| |
| /*private void processTargetClass(ClassDoc classDoc) { |
| System.out.println("class:"+classDoc); |
| // show all public/protected constructors |
| for (ExecutableMemberDoc constr : classDoc.constructors()) { |
| if (constr.isPublic() || constr.isProtected()) { |
| processTargetMC(constr); |
| } |
| } |
| // show all public/protected methods |
| for (ExecutableMemberDoc method : classDoc.methods()) { |
| if (method.isPublic() || method.isProtected()) { |
| processTargetMC(method); |
| } |
| } |
| }*/ |
| |
| /*private void dumpInfo() { |
| for (Map.Entry<ExecutableMemberDoc, AnnotationPointer> entry : resolved.entrySet()) { |
| ExecutableMemberDoc mdoc = entry.getKey(); |
| AnnotationPointer ap = entry.getValue(); |
| System.out.println("----- entry -----------------------"); |
| System.out.println("target:"+mdoc.toString()); |
| System.out.println("="); |
| for (MethodDoc meth : ap.testMethods) { |
| System.out.println("test method:"+meth); |
| } |
| } |
| }*/ |
| |
| private void processTestClass(ClassDoc classDoc, TablePrinter printer) { |
| // System.out.println("Processing >>> " + classDoc); |
| // collects all testinfo-annotation info of this class |
| ClassDoc targetClass = null; |
| // get the class annotation which names the default test target class |
| AnnotationDesc[] cAnnots = classDoc.annotations(); |
| for (AnnotationDesc cAnnot : cAnnots) { |
| |
| AnnotationTypeDoc atype = cAnnot.annotationType(); |
| if (atype.toString().equals("dalvik.annotation.TestTargetClass")) { |
| // single member annot with one child 'value' |
| ElementValuePair[] cpairs = cAnnot.elementValues(); |
| ElementValuePair evp = cpairs[0]; |
| AnnotationValue av = evp.value(); |
| Object obj = av.value(); |
| |
| // value must be a class doc |
| if (obj instanceof ClassDoc) { |
| targetClass = (ClassDoc) obj; |
| } else if (obj instanceof ParameterizedType) { |
| targetClass = ((ParameterizedType)obj).asClassDoc(); |
| } |
| else throw new RuntimeException("annotation elem value is of type "+obj.getClass().getName()); |
| } |
| } |
| |
| // now visit all methods (junit test methods - therefore we need not visit the constructors |
| AnnotStat ast = new AnnotStat(); |
| |
| //System.out.println("checking:"+classDoc.qualifiedName()); |
| |
| MethodDoc[] methods = classDoc.methods(); |
| String note = ""; |
| if (targetClass == null) { |
| note += "<br/>targetClass annotation missing!<br/>"; |
| } |
| |
| for (MethodDoc methodDoc : methods) { |
| // ignore if it is not a junit test method |
| if (!methodDoc.name().startsWith("test")) continue; |
| if (classDoc.qualifiedName().equals("tests.api.java.io.BufferedInputStreamTest")) { |
| //System.out.println("method: "+methodDoc.toString()); |
| } |
| |
| if (targetClass == null) { |
| // if the targetClass is missing, count all methods as non-annotated |
| ast.incMethodCnt(false); |
| } else { |
| String error = processTestMethod(methodDoc, ast, targetClass); |
| if (error != null) { |
| note+="<br/><b>E:</b> "+error; |
| } |
| } |
| } |
| |
| int man = ast.cntMethodWithAnnot; |
| int mto = ast.cntAllMethods; |
| float perc = mto==0? 100f : ((float)man)/mto * 100f; |
| |
| printer.printRow(man==mto && note.equals("")? 2:0, classDoc.qualifiedName(), ""+ast.cntMethodWithAnnot, ""+ast.cntAllMethods, |
| ""+perc+ note); |
| |
| } |
| |
| private class AnnotStat { |
| int cntMethodWithAnnot = 0; |
| int cntAllMethods = 0; |
| /** |
| * @param correctAnnot |
| */ |
| public void incMethodCnt(boolean correctAnnot) { |
| cntAllMethods++; |
| if (correctAnnot) { |
| cntMethodWithAnnot++; |
| } |
| } |
| } |
| |
| // points from one targetMethod to 0..n testMethods which test the target method |
| private class AnnotationPointer { |
| AnnotationPointer(ExecutableMemberDoc targetMethod) { |
| this.targetMethod = targetMethod; |
| } |
| |
| final ExecutableMemberDoc targetMethod; |
| List<MethodDoc> testMethods = new ArrayList<MethodDoc>(); |
| |
| public void addTestMethod(MethodDoc testMethod) { |
| if (testMethods.contains(testMethod)) { |
| System.out.println("warn: testMethod refers more than once to the targetMethod, testMethod="+testMethod); |
| } else { |
| testMethods.add(testMethod); |
| } |
| } |
| } |
| |
| private String processTestMethod(MethodDoc methodDoc, AnnotStat ast, ClassDoc targetClass) { |
| //System.out.println("processing method: " + methodDoc); |
| // get all per-method-annotation |
| boolean correctAnnot = false; |
| AnnotationDesc[] annots = methodDoc.annotations(); |
| for (AnnotationDesc annot : annots) { |
| if (annot.annotationType().toString().equals("dalvik.annotation.TestInfo")) { |
| ElementValuePair[] pairs = annot.elementValues(); |
| for (ElementValuePair kv : pairs) { |
| if (kv.element().qualifiedName().equals("dalvik.annotation.TestInfo.targets")) { |
| // targets is an [] type |
| AnnotationValue[] targets = (AnnotationValue[]) kv.value().value(); |
| for (AnnotationValue tval : targets) { |
| // the test targets must be annotations themselves |
| AnnotationDesc targetAnnot = (AnnotationDesc) tval.value(); |
| ExecutableMemberDoc targetMethod = getTargetMethod(targetAnnot, targetClass); |
| if (targetMethod != null) { |
| AnnotationPointer tar = getAnnotationPointer(targetMethod, true); |
| tar.addTestMethod(methodDoc); |
| correctAnnot = true; |
| } else { |
| ast.incMethodCnt(false); |
| return "error: could not resolve targetMethod for class "+targetClass+", annotation was:"+targetAnnot+", testMethod = "+methodDoc.toString(); |
| } |
| } |
| } |
| } |
| } // else some other annotation |
| } |
| ast.incMethodCnt(correctAnnot); |
| return null; |
| } |
| |
| private AnnotationPointer getAnnotationPointer(ExecutableMemberDoc targetMethod, boolean create) { |
| AnnotationPointer ap = resolved.get(targetMethod); |
| if (create && ap == null) { |
| ap = new AnnotationPointer(targetMethod); |
| resolved.put(targetMethod, ap); |
| } |
| return ap; |
| } |
| |
| private ExecutableMemberDoc getTargetMethod(AnnotationDesc targetAnnot, |
| ClassDoc targetClass) { |
| // targetAnnot like @android.annotation.TestTarget(methodName="group", methodArgs=int.class) |
| ElementValuePair[] pairs = targetAnnot.elementValues(); |
| String methodName = null; |
| String args = ""; |
| for (ElementValuePair kval : pairs) { |
| if (kval.element().name().equals("methodName")) { |
| methodName = (String) kval.value().value(); |
| } else if (kval.element().name().equals("methodArgs")) { |
| AnnotationValue[] vals = (AnnotationValue[]) kval.value().value(); |
| for (int i = 0; i < vals.length; i++) { |
| AnnotationValue arg = vals[i]; |
| String argV; |
| if (arg.value() instanceof ClassDoc) { |
| ClassDoc cd = (ClassDoc)arg.value(); |
| argV = cd.qualifiedName(); |
| } else { // primitive type or array type |
| // is there a nicer way to do this? |
| argV = arg.toString(); |
| } |
| // strip .class out of args since signature does not contain those |
| if (argV.endsWith(".class")) { |
| argV = argV.substring(0, argV.length()-6); |
| } |
| args+= (i>0? ",":"") + argV; |
| } |
| } |
| } |
| // both methodName and methodArgs != null because of Annotation definition |
| |
| String refSig = methodName+"("+args+")"; |
| //System.out.println("Check " + refSig); |
| // find the matching method in the target class |
| // check all methods |
| for (ExecutableMemberDoc mdoc : targetClass.methods()) { |
| if (equalsSignature(mdoc, refSig)) { |
| return mdoc; |
| } |
| } |
| // check constructors, too |
| for (ExecutableMemberDoc mdoc : targetClass.constructors()) { |
| if (equalsSignature(mdoc, refSig)) { |
| return mdoc; |
| } |
| } |
| return null; |
| } |
| |
| private boolean equalsSignature(ExecutableMemberDoc mdoc, String refSignature) { |
| Parameter[] params = mdoc.parameters(); |
| String targs = ""; |
| for (int i = 0; i < params.length; i++) { |
| Parameter parameter = params[i]; |
| // check for generic type types |
| Type ptype = parameter.type(); |
| TypeVariable typeVar = ptype.asTypeVariable(); |
| String ptname; |
| if (typeVar != null) { |
| ptname = "java.lang.Object"; // the default fallback |
| Type[] bounds = typeVar.bounds(); |
| if (bounds.length > 0) { |
| ClassDoc typeClass = bounds[0].asClassDoc(); |
| ptname = typeClass.qualifiedName(); |
| } |
| } else { |
| // regular var |
| //ptname = parameter.type().qualifiedTypeName(); |
| ptname = parameter.type().toString(); |
| |
| //System.out.println("quali:"+ptname); |
| //ptname = parameter.typeName(); |
| // omit type signature |
| ptname = ptname.replaceAll("<.*>",""); |
| } |
| targs+= (i>0? ",":"") + ptname; |
| } |
| String testSig = mdoc.name()+"("+targs+")"; |
| |
| //return testSig.equals(refSignature); |
| if (testSig.equals(refSignature)) { |
| //System.out.println("found: Sig:"+testSig); |
| return true; |
| } else { |
| //System.out.println("no match: ref = "+refSignature+", test = "+testSig); |
| return false; |
| } |
| } |
| |
| private boolean extendsJUnitTestCase(ClassDoc classDoc) { |
| //junit.framework.TestCase.java |
| ClassDoc curClass = classDoc; |
| while ((curClass = curClass.superclass()) != null) { |
| if (curClass.toString().equals("junit.framework.TestCase")) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Processes the details of a single package. |
| * @param hiddenDocPrinter |
| * @param excludedClassPrinter |
| * @param interfacePrinter |
| */ |
| private MyStats processPackage(PackageDoc pack, TablePrinter hiddenDocPrinter, |
| TablePrinter interfacePrinter) throws IOException { |
| String file = getPackageDir(pack) + "/package.html"; |
| PrintWriter printer = openFile(file, "Package " + pack.name()); |
| |
| MyStats stats = new MyStats(TYPE_PACKAGE, pack.name(), file); |
| printer.println("<table>"); |
| |
| ClassDoc[] classes = pack.allClasses(); |
| Arrays.sort(classes, comparator); |
| for (ClassDoc clazz : classes) { |
| if (extendsJUnitTestCase(clazz)) { |
| printer.println("<tr><td>ignored(junit):"+clazz.name()+"</td></tr>"); |
| } else if (isHiddenClass(clazz)) { |
| hiddenDocPrinter.printRow(pack.name(), clazz.name(), "*"); |
| } else if (clazz.isInterface()) { |
| interfacePrinter.printRow(pack.name(), clazz.name()); |
| } else { |
| MyStats subStats = processClass(clazz, hiddenDocPrinter); |
| printStats(printer, subStats, true); |
| stats.add(subStats); |
| } |
| } |
| printer.println("</table>"); |
| closeFile(printer); |
| return stats; |
| } |
| |
| private boolean isHiddenClass(ClassDoc clazz) { |
| if (clazz == null) { |
| return false; |
| } |
| |
| if (isHiddenDoc(clazz)) { |
| return true; |
| } |
| |
| // If outter class is hidden, this class should be hidden as well |
| return isHiddenClass(clazz.containingClass()); |
| } |
| |
| private boolean isHiddenDoc(Doc doc) { |
| // Since currently we have two kinds of annotations to mark a class as hide: |
| // 1. @hide |
| // 2. {@hide} |
| // So we should consider both conditions. |
| for (Tag t : doc.tags()) { |
| if (t.name().equals("@hide")) { |
| return true; |
| } |
| } |
| |
| for (Tag t : doc.inlineTags()) { |
| if (t.name().equals("@hide")) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private MyStats processClass(ClassDoc clazz, TablePrinter hiddenDocPrinter) throws IOException { |
| //System.out.println("Process source class: " + clazz); |
| String file = getPackageDir(clazz.containingPackage()) + "/" + clazz.name() + ".html"; |
| PrintWriter printer = openFile(file, "Class " + clazz.name()); |
| |
| String packageName = clazz.containingPackage().name(); |
| String className = clazz.name(); |
| |
| MyStats stats = new MyStats(TYPE_CLASS, className, className+".html"); |
| printer.println("<table><tr><td>name</td><td>tested by</td></tr>"); |
| ConstructorDoc[] constructors = clazz.constructors(); |
| Arrays.sort(constructors, comparator); |
| for (ConstructorDoc constructor : constructors) { |
| //System.out.println("constructor: " + constructor); |
| if (isHiddenDoc(constructor)) { |
| hiddenDocPrinter.printRow(packageName, className, constructor.name()); |
| } else if (!isGeneratedConstructor(constructor)) { |
| MyStats subStat = processElement(constructor); |
| printStats(printer, subStat, false); |
| stats.add(subStat); |
| } |
| } |
| |
| MethodDoc[] methods = clazz.methods(); |
| Arrays.sort(methods, comparator); |
| for (MethodDoc method : methods) { |
| //System.out.println("method: " + method); |
| if ("finalize".equals(method.name())) { |
| // Skip finalize method |
| } else if (isHiddenDoc(method)) { |
| hiddenDocPrinter.printRow(packageName, className, method.name()); |
| } else if (method.isAbstract()) { |
| // Skip abstract method |
| } else { |
| MyStats subStat = processElement(method); |
| printStats(printer, subStat, false); |
| stats.add(subStat); |
| } |
| } |
| |
| printer.println("</table>"); |
| closeFile(printer); |
| return stats; |
| } |
| |
| /** |
| * Determines whether a constructor has been automatically generated and is |
| * thus not present in the original source. The only way to find out seems |
| * to compare the source position against the one of the class. If they're |
| * equal, the constructor does not exist. It's a bit hacky, but it works. |
| */ |
| private boolean isGeneratedConstructor(ConstructorDoc doc) { |
| SourcePosition constPos = doc.position(); |
| SourcePosition classPos = doc.containingClass().position(); |
| |
| return ("" + constPos).equals("" + classPos); |
| } |
| |
| /** |
| * Processes a single method/constructor. |
| */ |
| private MyStats processElement(ExecutableMemberDoc method) { |
| //int color = getColor(doc) |
| //derived.add(subStats) |
| AnnotationPointer ap = getAnnotationPointer(method, false); |
| MyStats stats = new MyStats(TYPE_METHOD, "<b>"+method.name() + "</b> "+method.signature(), null); |
| int refCnt = 0; |
| if (ap != null) { |
| refCnt = ap.testMethods.size(); |
| String by = ""; |
| List<MethodDoc> testM = ap.testMethods; |
| Collections.sort(testM, membercomparator); |
| for (MethodDoc teme : testM) { |
| by+= "<br/>"+teme.toString(); |
| } |
| stats.setExtra(by); |
| } // else this class has no single test that targets one of its method |
| |
| if (refCnt == 0) { |
| stats.inc(VALUE_RED); |
| } else if (refCnt == 1) { |
| stats.inc(VALUE_YELLOW); |
| } else { |
| stats.inc(VALUE_GREEN); |
| } |
| return stats; |
| } |
| |
| /** |
| * Prints a single row to a stats table. |
| */ |
| private void printStats(PrintWriter printer, MyStats info, boolean wantLink) { |
| int red = info.getCountFor(VALUE_RED); |
| int yellow = info.getCountFor(VALUE_YELLOW); |
| |
| printer.println("<tr>"); |
| |
| // rule for coloring: |
| // if red > 0 -> red |
| // if yellow > 0 -> yellow |
| // else green |
| int color; |
| if (red > 0) { |
| color = VALUE_RED; |
| } else if (yellow > 0) { |
| color = VALUE_YELLOW; |
| } else { |
| color = VALUE_GREEN; |
| } |
| |
| printer.println("<td bgcolor=\""+COLORS[color]+"\">"); |
| String link = info.getLink(); |
| if (wantLink && link != null) { |
| printer.print("<a href=\"" + link + "\">" + info.getName() + "</a>"); |
| } else { |
| printer.print(info.getName()); |
| } |
| printer.println(" ("+info.getStat()+") </td>"); |
| if (info.getExtra()!=null) { |
| printer.println("<td>"+info.getExtra()+"</td>"); |
| } |
| printer.println("</tr>"); |
| } |
| |
| /** |
| * Returns the directory for a given package. Basically converts embedded |
| * dots in the name into slashes. |
| */ |
| private File getPackageDir(PackageDoc pack) { |
| if (pack == null || pack.name() == null || "".equals(pack.name())) { |
| return new File("."); |
| } else { |
| return new File(pack.name().replace('.', '/')); |
| } |
| } |
| |
| /** |
| * Called by JavaDoc to find our which command line arguments are supported |
| * and how many parameters they take. Part of the JavaDoc API. |
| */ |
| public static int optionLength(String option) { |
| if ("-d".equals(option)) { |
| return 2; |
| } else { |
| return 0; |
| } |
| } |
| |
| /** |
| * Called by JavaDoc to query a specific command line argument. Part of the |
| * JavaDoc API. |
| */ |
| private static String getOption(RootDoc root, String option, int index, String defValue) { |
| String[][] allOptions = root.options(); |
| for (int i = 0; i < allOptions.length; i++) { |
| if (allOptions[i][0].equals(option)) { |
| return allOptions[i][index]; |
| } |
| } |
| return defValue; |
| } |
| |
| /** |
| * Called by JavaDoc to find out which Java version we claim to support. |
| * Part of the JavaDoc API. |
| */ |
| public static LanguageVersion languageVersion() { |
| return LanguageVersion.JAVA_1_5; |
| } |
| |
| /** |
| * The main entry point called by JavaDoc after all required information has |
| * been collected. Part of the JavaDoc API. |
| */ |
| public static boolean start(RootDoc root) { |
| try { |
| String target = getOption(root, "-d", 1, "."); |
| TestCoverageDoclet doclet = new TestCoverageDoclet(target); |
| doclet.process(root); |
| |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| return false; |
| } |
| return true; |
| } |
| |
| } |