keep previous history
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index edb6b1b..8cba973 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -47,6 +47,7 @@
     <classpathentry kind="src" path="cts/tests/tests/view/src"/>
     <classpathentry kind="src" path="cts/tests/tests/webkit/src"/>
     <classpathentry kind="src" path="cts/tests/tests/widget/src"/>
+    <classpathentry kind="src" path="cts/tools/annotation-helper/src"/>
     <classpathentry kind="src" path="cts/tools/cts-api-coverage/src"/>
     <classpathentry kind="src" path="cts/tools/cts-reference-app-lib/src"/>
     <classpathentry kind="src" path="cts/tools/dasm/src"/>
@@ -58,6 +59,7 @@
     <classpathentry kind="src" path="cts/tools/signature-tools/src"/>
     <classpathentry kind="src" path="cts/tools/signature-tools/test"/>
     <classpathentry kind="src" path="cts/tools/spec-progress/src"/>
+    <classpathentry kind="src" path="cts/tools/test-progress-new/src"/>
     <classpathentry kind="src" path="cts/tools/utils"/>
     <classpathentry kind="src" path="cts/tools/vm-tests/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsAccessibilityServiceTestCases_intermediates/src/src"/>
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 6292d7a..c7b25b2 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -2,15 +2,24 @@
 /* These tests consistently fail on GRH78. */
 { name: "android.location.cts.GeocoderTest#testGetFromLocation" },
 { name: "android.location.cts.GeocoderTest#testGetFromLocationName" },
+{ name: "android.net.cts.ListeningPortsTest#testNoListeningUdp6Ports" },
 { name: "android.webkit.cts.WebSettingsTest#testSetAppCacheEnabled" },
 { name: "android.net.cts.SSLCertificateSocketFactoryTest" },
 
 {
+  description: "testGetDuration causes testOnKeyDown to fail for some reason",
+  name: "android.widget.cts.VideoViewTest#testOnKeyDown"
+},
+
+{
   description: "Flaky tests that need to be rewritten or deleted.",
   names: [
     "android.media.cts.MediaRecorderTest#testSetCamera",
     "android.webkit.cts.CacheManagerTest#testCacheFile",
-    "android.widget.cts.AutoCompleteTextViewTest#testOnFilterComplete"
+    "android.widget.cts.AutoCompleteTextViewTest#testOnFilterComplete",
+    "android.util.cts.EventLogTest#testReadEvents",
+    "android.util.cts.EventLogTest#testWriteEvent",
+    "android.util.cts.EventLogTest#testWriteEventWithOversizeValue"
   ]
 },
 
@@ -19,17 +28,29 @@
   name: "android.provider.cts.MediaStore_Audio_Playlists_MembersTest"
 },
 
+/* These tests pass when executed individually but fail when running CTS as a whole on GRH78. */
 {
-  description: "These tests pass when executed individually but fail when running CTS as a whole on GRH78.",
-  bug: 3184701,
-  names: [
-    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection",
-    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection",
-    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testConsequentProxyConnection",
-    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection_doOutput",
-    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnectionFailed",
-    "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection_Not_Found_Response"
-  ]
+  name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection",
+  bug: 3184701
+},
+{
+  name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection",
+  bug: 3184701
+},
+{
+  name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testConsequentProxyConnection",
+  bug: 3184701
+},
+{
+  name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnection_doOutput",
+  bug: 3184701
+},
+{
+  name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyAuthConnectionFailed",
+  bug: 3184701
+},
+{
+  name: "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testProxyConnection_Not_Found_Response",
+  bug: 3184701
 }
-
 ]
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 8483fbb..57ce365 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -62,8 +62,7 @@
                     "44010",    // NTT DOCOMO
                     "45005",    // SKT Mobility
                     "45002",    // SKT Mobility
-                    "45008",    // KT Mobility
-                    "45006"     // LGT
+                    "45008"     // KT Mobility
             );
 
     // List of network operators that doesn't support Data(binary) SMS message
diff --git a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
index 44d48a5..6b9aa84 100644
--- a/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/VideoViewTest.java
@@ -31,6 +31,7 @@
 import android.media.MediaPlayer.OnErrorListener;
 import android.media.MediaPlayer.OnPreparedListener;
 import android.test.ActivityInstrumentationTestCase2;
+import android.view.KeyEvent;
 import android.view.View.MeasureSpec;
 import android.view.animation.cts.DelayedCheck;
 import android.widget.MediaController;
@@ -309,6 +310,89 @@
     }
 
     @TestTargetNew(
+        level = TestLevel.NOT_NECESSARY,
+        method = "onTouchEvent",
+        args = {android.view.MotionEvent.class}
+    )
+    public void testOnTouchEvent() {
+        // onTouchEvent() is implementation details, do NOT test
+    }
+
+    @TestTargetNew(
+        level = TestLevel.SUFFICIENT,
+        method = "onKeyDown",
+        args = {int.class, android.view.KeyEvent.class}
+    )
+    public void testOnKeyDown() throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mVideoView.setVideoPath(mVideoPath);
+                mVideoView.requestFocus();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertFalse(mVideoView.isPlaying());
+        sendKeys(KeyEvent.KEYCODE_HEADSETHOOK);
+        // video should be played.
+        new DelayedCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mVideoView.isPlaying();
+            }
+        }.run();
+        assertFalse(mMediaController.isShowing());
+
+        sendKeys(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        new DelayedCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return !mVideoView.isPlaying();
+            }
+        }.run();
+        // MediaController should show
+        assertTrue(mMediaController.isShowing());
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mVideoView.start();
+            }
+        });
+        new DelayedCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mVideoView.isPlaying();
+            }
+        }.run();
+
+        sendKeys(KeyEvent.KEYCODE_MEDIA_STOP);
+        new DelayedCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return !mVideoView.isPlaying();
+            }
+        }.run();
+    }
+
+    @TestTargetNew(
+        level = TestLevel.NOT_NECESSARY,
+        method = "onMeasure",
+        args = {int.class, int.class}
+    )
+    public void testOnMeasure() {
+        // Do not test onMeasure(), implementation details
+    }
+
+    @TestTargetNew(
+        level = TestLevel.NOT_NECESSARY,
+        method = "onTrackballEvent",
+        args = {android.view.MotionEvent.class}
+    )
+    public void testOnTrackballEvent() {
+        // Do not test onTrackballEvent(), implementation details
+    }
+
+    @TestTargetNew(
         level = TestLevel.COMPLETE,
         method = "getDuration",
         args = {}
diff --git a/tools/annotation-helper/.classpath b/tools/annotation-helper/.classpath
new file mode 100644
index 0000000..bece0f3
--- /dev/null
+++ b/tools/annotation-helper/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry exported="true" kind="lib" path="bin/"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/annotation-helper/.project b/tools/annotation-helper/.project
new file mode 100644
index 0000000..ea4be4f
--- /dev/null
+++ b/tools/annotation-helper/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>spechelper</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/annotation-helper/.settings/org.eclipse.jdt.core.prefs b/tools/annotation-helper/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c4095e1
--- /dev/null
+++ b/tools/annotation-helper/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,57 @@
+#Wed Jun 04 14:49:33 CEST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=ignore
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=ignore
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=ignore
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=ignore
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=ignore
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.processAnnotations=disabled
diff --git a/tools/annotation-helper/META-INF/MANIFEST.MF b/tools/annotation-helper/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1f39a53
--- /dev/null
+++ b/tools/annotation-helper/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Spechelper Plug-in
+Bundle-SymbolicName: spechelper; singleton:=true
+Bundle-Version: 1.0.0
+Bundle-Activator: spechelper.Activator
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime
+Eclipse-LazyStart: false
+Import-Package: org.eclipse.jdt.core,
+ org.eclipse.jdt.core.compiler,
+ org.eclipse.jdt.core.dom,
+ org.eclipse.jdt.core.util,
+ org.eclipse.jdt.internal.ui,
+ org.eclipse.jdt.internal.ui.javaeditor,
+ org.eclipse.jdt.ui,
+ org.eclipse.jdt.ui.text.java,
+ org.eclipse.jface.text,
+ org.eclipse.jface.text.contentassist,
+ org.eclipse.ui.texteditor
+Bundle-ClassPath: bin/
diff --git a/tools/annotation-helper/README.txt b/tools/annotation-helper/README.txt
new file mode 100644
index 0000000..8ff7eec
--- /dev/null
+++ b/tools/annotation-helper/README.txt
@@ -0,0 +1 @@
+for information, read the javadoc under src/spechelper/SimpleComputer.java
diff --git a/tools/annotation-helper/build.properties b/tools/annotation-helper/build.properties
new file mode 100644
index 0000000..e5dc709
--- /dev/null
+++ b/tools/annotation-helper/build.properties
@@ -0,0 +1,9 @@
+output.. = bin/
+bin.includes = plugin.xml,\
+               META-INF/,\
+               bin/,\
+               build.properties,\
+               feature.xml
+src.includes = src/,\
+               plugin.xml,\
+               feature.xml
diff --git a/tools/annotation-helper/plugin.xml b/tools/annotation-helper/plugin.xml
new file mode 100644
index 0000000..6cc641d
--- /dev/null
+++ b/tools/annotation-helper/plugin.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+	<extension 
+		point="org.eclipse.jdt.ui.javaCompletionProposalComputer" 
+		id="WordCompletionProposalComputer"
+		name="Noser Annotation Completion Proposal Computer">
+
+		<javaCompletionProposalComputer 
+		    activate="true"
+			class="spechelper.SimpleComputer"
+			categoryId="org.eclipse.ui.texteditor.textual_proposals">
+		</javaCompletionProposalComputer>
+	</extension>
+</plugin>
diff --git a/tools/annotation-helper/src/spechelper/MethodSelector.java b/tools/annotation-helper/src/spechelper/MethodSelector.java
new file mode 100644
index 0000000..a0b4bba
--- /dev/null
+++ b/tools/annotation-helper/src/spechelper/MethodSelector.java
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+package spechelper;
+
+import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeParameter;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.ui.JavaElementLabelProvider;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 
+ */
+public class MethodSelector {
+
+    public String obtainReplacement(String buffer) {
+        IMethod method = selectMethod();
+        // if user did cancel the selection
+        if (method == null) {
+            return null;
+        }
+
+        // see if we are already in a annotation:
+        // if yes -> only dump the testtarget annotation, not the complete
+        // TestInfo
+        // (could not easily find this out with CompilationUnit, since inserting
+        // a :
+        // broke the AST - maybe use WorkingCopy and so on,
+        // but for now: do it with simple String analysis
+        boolean shortOnly = false;
+        int annotPos = buffer.lastIndexOf("@TestInfo");
+        // the latest annotation - count "(" ")" pairs - if not the same count
+        // we assume to be in the annotation (H: code compiles fine)
+        if (annotPos != -1) {
+            String sub = buffer.substring(annotPos);
+            // only consider the latest 6 lines for the annotation to occur
+            // (6 = range within which the annotation @TestTarget
+            // must occur, but out of range to reach the annotation from the
+            // previous method - ah i'd prefer working with compilationUnit...
+            String[] lines = sub.split("\n");
+            for (int i = lines.length - 6; i < lines.length; i++) {
+                String line = lines[i];
+                if (line.contains("@TestTarget")) {
+                    shortOnly = true;
+                }
+            }
+        }
+
+        return generateAnnotation(shortOnly, method);
+    }
+
+
+    private String generateAnnotation(boolean shortOnly, IMethod method) {
+        String[] ptypes = method.getParameterTypes();
+        String param = "";
+        for (int i = 0; i < ptypes.length; i++) {
+            String ptype = ptypes[i];
+            String sig = Signature.toString(ptype);
+            // kind of a hack: convert all Generic Type args to Object, or to
+            // its bound Type
+            if (sig.length() == 1) {
+                ITypeParameter tps = method.getTypeParameter(sig);
+                sig = "Object";
+
+                if (tps != null && tps.exists()) {
+                    try {
+                        String[] bounds = tps.getBounds();
+                        if (bounds.length > 0) {
+                            sig = bounds[0];
+                        }
+                    } catch (JavaModelException e) {
+                        e.printStackTrace();
+                    }
+
+                }
+            }
+            // omit type signature
+            sig = sig.replaceAll("<.*>", "");
+            param += (i > 0 ? ", " : "") + sig + ".class";
+        }
+        String IND = "    ";
+
+        String targ = "@TestTarget(\n" + IND + "      methodName = \""
+                + method.getElementName() + "\",\n" + IND
+                + "      methodArgs = {" + param + "}\n" + IND + "    )\n";
+
+        String s;
+        if (shortOnly) {
+            s = targ;
+        } else {
+
+            s = "@TestInfo(\n" + IND + "  status = TestStatus.TBR,\n" + IND
+                    + "  notes = \"\",\n" + IND + "  targets = {\n" + IND
+                    + "    " + targ + IND + "})";
+        }
+        return s;
+    }
+
+    private IMethod selectMethod() {
+        IEditorPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+                .getActivePage().getActiveEditor();
+        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+                .getShell();
+        IEditorInput ei = part.getEditorInput();
+        final ICompilationUnit cu = JavaPlugin.getDefault()
+                .getWorkingCopyManager().getWorkingCopy(ei);
+        // cu != null since we register only for java/javadoc completion
+        // proposals
+        ASTParser parser = ASTParser.newParser(AST.JLS3);
+        parser.setSource(cu);
+        parser.setResolveBindings(true);
+        CompilationUnit unit = (CompilationUnit) parser.createAST(null);
+
+        class MHolder {
+            IMethod method;
+        }
+        final MHolder mholder = new MHolder();
+
+        class FHolder {
+            boolean foundClassAnnotation;
+        }
+        final FHolder fholder = new FHolder();
+
+        unit.accept(new ASTVisitor() {
+            public boolean visit(SingleMemberAnnotation node) {
+                String name = node.getTypeName().getFullyQualifiedName();
+                if (!name.equals("TestTargetClass")) {
+                    return false;
+                }
+                fholder.foundClassAnnotation = true;
+                Expression targetClassE = node.getValue();
+                ITypeBinding ty = targetClassE.resolveTypeBinding();
+                if (ty == null) {
+                    return false;
+                }
+                ITypeBinding[] classTypes = ty.getTypeArguments();
+                if (classTypes.length > 0) {
+                    ITypeBinding tp = classTypes[0];
+                    String qname = tp.getQualifiedName();
+                    System.out.println("qname:" + qname);
+                    IJavaProject myProject = cu.getJavaProject();
+                    try {
+                        IType myType = myProject.findType(qname);
+                        if (myType != null) {
+                            Shell parent = PlatformUI.getWorkbench()
+                                    .getActiveWorkbenchWindow().getShell();
+                            ElementListSelectionDialog dialog = new ElementListSelectionDialog(
+                                    parent,
+                                    new JavaElementLabelProvider(
+                                            JavaElementLabelProvider.SHOW_PARAMETERS
+                                                    | JavaElementLabelProvider.SHOW_OVERLAY_ICONS
+                                                    | JavaElementLabelProvider.SHOW_RETURN_TYPE));
+                            // restrict to public/protected methods only
+                            IMethod[] allMeth = myType.getMethods();
+                            List<IMethod> pubproMethods = new ArrayList<IMethod>();
+                            for (int i = 0; i < allMeth.length; i++) {
+                                IMethod method = allMeth[i];
+                                if ((method.getFlags() & (Flags.AccPublic | Flags.AccProtected)) != 0) {
+                                    pubproMethods.add(method);
+                                }
+                            }
+                            IMethod[] res = pubproMethods
+                                    .toArray(new IMethod[pubproMethods.size()]);
+                            dialog.setIgnoreCase(true);
+                            dialog.setBlockOnOpen(true);
+                            dialog.setElements(res);//
+                            dialog.setFilter("");
+                            dialog.setTitle(qname);
+                            if (dialog.open() != IDialogConstants.CANCEL_ID) {
+                                Object[] types = dialog.getResult();
+                                System.out.println("selected:" + types[0]);
+                                IMethod method = (IMethod) types[0];
+                                mholder.method = method;
+
+                            } else {
+                                // System.out.println("cancelled!!");
+                            }
+                        }
+                    } catch (JavaModelException e) {
+                        e.printStackTrace();
+                    }
+                }
+                return true;
+            }
+        });
+        if (!fholder.foundClassAnnotation) {
+            MessageDialog.openInformation(shell, "Class Annotation missing",
+                    "@TestTargetClass(...) is missing");
+            return null;
+        }
+        return mholder.method;
+    }
+}
diff --git a/tools/annotation-helper/src/spechelper/MyCompletion.java b/tools/annotation-helper/src/spechelper/MyCompletion.java
new file mode 100644
index 0000000..7a5c3c7
--- /dev/null
+++ b/tools/annotation-helper/src/spechelper/MyCompletion.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+package spechelper;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+public class MyCompletion implements ICompletionProposal {
+
+    private String m_displ;
+    private String m_replace;
+    private int m_offset;
+    private int m_replacelen;
+    private int m_cursorpos;
+    private Image m_img;
+    private IContextInformation m_context;
+    private String m_addinfo;
+    private String fBuffer;
+
+    public MyCompletion(String buffer, String replacementString,
+            int replacementOffset, int replacementLength, int cursorPosition,
+            Image image, String displayString,
+            IContextInformation contextInformation,
+            String additionalProposalInfo) {
+        m_replace = replacementString;
+        m_offset = replacementOffset;
+        m_replacelen = replacementLength;
+        m_cursorpos = cursorPosition;
+        m_img = image;
+        fBuffer = buffer;
+        m_displ = displayString;
+        m_context = contextInformation;
+        m_addinfo = additionalProposalInfo;
+    }
+
+    public void apply(IDocument document) {
+        try {
+            MethodSelector ms = new MethodSelector();
+            String replace = ms.obtainReplacement(fBuffer);
+            if (replace == null) {
+                m_cursorpos = 0;
+                return;
+            }
+            m_cursorpos = replace.length();
+            document.replace(m_offset, m_replacelen, replace);
+        } catch (BadLocationException x) {
+            // ignore
+        }
+    }
+
+    public Point getSelection(IDocument document) {
+        return new Point(m_offset + m_cursorpos, 0);
+    }
+
+    public IContextInformation getContextInformation() {
+        return m_context;
+    }
+
+    public Image getImage() {
+        return m_img;
+    }
+
+    public String getDisplayString() {
+        if (m_displ != null) return m_displ;
+        return m_replace;
+    }
+
+    public String getAdditionalProposalInfo() {
+        return m_addinfo;
+    }
+}
diff --git a/tools/annotation-helper/src/spechelper/SimpleComputer.java b/tools/annotation-helper/src/spechelper/SimpleComputer.java
new file mode 100644
index 0000000..757b1a8
--- /dev/null
+++ b/tools/annotation-helper/src/spechelper/SimpleComputer.java
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+package spechelper;
+
+import dalvik.annotation.TestTargetClass;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Pattern;
+
+/**
+ * <p>
+ * a plugin to auto-insert the following annotation constructs: 
+ * TestInfo.java, TestStatus.java, TestTarget.java, and TestTargetClass.java
+ * under 
+ * /android/device/dalvik/libcore/dalvik/src/main/java/dalvik/annotation/
+ * <p>
+ * usage:<br>
+ * - install export/plugins/spechelper_1.0.0.jar into your eclipse/plugin folder.<br>
+ * - restart eclipse<br>
+ * - open a java file<br>
+ * - insert the TestTargetClass annotation above the class declaration, e.g.
+ *   <code>@TestTargetClass(Pattern.class)</code><br>
+ * - insert a ":" one line above the signature of a method to be annotated,  
+ *   and press ctrl-space for eclipse autocompletion. a popup appears which
+ *   lists all target methods. choose one, and the annotation will be filled in
+ *   at the cursor position.<br>
+ * <p>  
+ *   to annotate more than one target method, simply add a comma after the
+ *   first TestTarget, press enter and insert a ":", press ctrl-space again.
+ *   
+ * <p>
+ *  a sample:  
+ *   
+<pre>
+package org.apache.harmony.tests.java.util.regex;
+
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestInfo;
+import dalvik.annotation.TestTarget;
+import dalvik.annotation.TestStatus;
+
+import junit.framework.TestCase;
+
+import java.util.regex.Pattern;
+
+@TestTargetClass(Pattern.class)
+
+public class PatternTest extends TestCase {
+    
+    // add ":", press ctrl-space here to let the eclipse plugin generate 
+    // the next few lines
+    @TestInfo(
+      status = TestStatus.TBR,
+      notes = "",
+      targets = {
+        @TestTarget(
+          methodName = "compile",
+          methodArgs = {String.class}
+        )
+    })
+    public void foo() {
+        //
+    }
+
+    @TestInfo(
+      status = TestStatus.TBR,
+      notes = "",
+      targets = {
+        @TestTarget(
+          methodName = "compile",
+          methodArgs = {String.class}
+        ),
+        // add ":", press ctrl-space here to insert another TestTarget
+    })
+    public void bar() {
+        //
+    }
+    
+    @TestInfo(
+      status = TestStatus.TBR,
+      notes = "",
+      targets = {
+        @TestTarget(
+          methodName = "compile",
+          methodArgs = {String.class}
+        ),
+        @TestTarget(
+          methodName = "split",
+          methodArgs = {CharSequence.class, int.class}
+        )
+
+    })
+    public void foobarsample() {
+        //
+    }
+    
+}
+</pre>
+ *   
+ *   
+ *
+ */
+public class SimpleComputer implements IJavaCompletionProposalComputer {
+
+    public List<ICompletionProposal> computeCompletionProposals(
+            ContentAssistInvocationContext context, IProgressMonitor monitor) {
+        List<ICompletionProposal> ret = new Vector<ICompletionProposal>();
+        try {
+            int offs = context.getInvocationOffset();
+            String buffer = context.getDocument().get(0, offs);
+            //System.out.println("buffer:'"+buffer+"'");
+            //System.out.println("offset:"+offs);
+            String keyWord = ":";
+            String keyWordInfo = "':': noser: autofills the annotation";
+
+            int idx = 0;
+            // find the replacement position
+            int klen = keyWord.length();
+            for (int i = 0; i < klen; i++) {
+                String test = keyWord.substring(0, klen - i);
+                if (buffer.endsWith(test)) {
+                    idx = klen - i;
+                    break;
+                }
+            }
+            if (idx != 0) {
+                System.out.println("idx:"+idx);
+                String replace ="hi there! a longer sample text\nnew line";
+                    ICompletionProposal ci = new MyCompletion(buffer, replace,
+                            context.getInvocationOffset() - idx, idx, replace
+                                    .length(), null, keyWordInfo, null, null);
+                    ret.add(ci);
+            }
+        } catch (BadLocationException e) {
+            e.printStackTrace();
+        }
+        return ret;
+    }
+
+
+    public List<ICompletionProposal> computeContextInformation(
+            ContentAssistInvocationContext context, IProgressMonitor monitor) {
+        return new Vector<ICompletionProposal>();
+    }
+
+    public String getErrorMessage() {
+        return "Error from SimpleComputer";
+    }
+
+    public void sessionEnded() {
+        //System.out.println("session ended");
+    }
+
+    public void sessionStarted() {
+        //System.out.println("session started");
+    }
+
+}
diff --git a/tools/annotation-helper/src/spechelper/Test.java b/tools/annotation-helper/src/spechelper/Test.java
new file mode 100644
index 0000000..01b11df
--- /dev/null
+++ b/tools/annotation-helper/src/spechelper/Test.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package spechelper;
+
+import java.util.Comparator;
+
+public class Test {
+    
+    public Test() {}
+    
+    public <E>  void bla(Comparable<Long> comp1, Comparator<Comparable<String>> comp2, E arg, int a, byte[] b, char[] c, double d, float f, boolean bo, int[][] ar, String[][] arr,
+            long l, String... strs) {
+    }
+    
+    public String fooaaa(int aasdfsdfsd) {
+        return null;
+    }
+    
+    private void priv() {}
+
+    protected void prot() {}
+    
+    void packloc() {}
+    
+    public void testInputfoo() {
+        Math.sin(0d);
+    }
+}
diff --git a/tools/test-progress-new/Android.mk b/tools/test-progress-new/Android.mk
new file mode 100644
index 0000000..2ee261d
--- /dev/null
+++ b/tools/test-progress-new/Android.mk
@@ -0,0 +1,29 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := test-progress-new
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/test-progress | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+include $(LOCAL_PATH)/src/Android.mk
diff --git a/tools/test-progress-new/etc/test-progress b/tools/test-progress-new/etc/test-progress
new file mode 100644
index 0000000..af7eec4
--- /dev/null
+++ b/tools/test-progress-new/etc/test-progress
@@ -0,0 +1,393 @@
+#!/bin/bash
+#
+# 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+# Parse the test type, by default it will run against all tests
+# FIXME:
+# This should be removed after vm-tests and core-tests have been integrated
+# in CTS.
+testType="all"
+if [ ! -z $1 ]; then
+    if [ $1 == "all" -o $1 == "dalvik" -o $1 == "android" ]; then
+        testType=$1
+        shift
+    fi
+fi
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+    opt=`expr "$1" : '-J\(.*\)'`
+    javaOpts="${javaOpts} -${opt}"
+    shift
+done
+
+#exec java $javaOpts -jar $libdir/hat.jar "$@"
+
+#######################################################################
+# Original content of invocation script follows. Uses values cleverly
+# deduced by the above code. If you want to use this for a different
+# set of packages, adjust both the list of source directories and the
+# list of packages.
+#######################################################################
+export CLASSES=$progdir/../framework/test-progress-new.jar
+export INPUT=$ANDROID_BUILD_TOP
+export OUTPUT=$ANDROID_BUILD_TOP/out/target/common/cts/test-progress-new
+
+TESTCOVERAGE_FLAGS="countdisabled,acceptcandx"
+
+if [[ ("$1" == "-f") && ("$2" != "") ]]; then
+	TESTCOVERAGE_FLAGS=$2
+	echo "running with flag '$TESTCOVERAGE_FLAGS'"
+else
+	if [ "$1" != "" ]; then
+	   export OUTPUT=$1
+	fi
+fi
+
+DALVIK_SOURCE_PATH="\
+$INPUT/dalvik/libcore/annotation/src/main/java:\
+$INPUT/dalvik/libcore/archive/src/main/java:\
+$INPUT/dalvik/libcore/auth/src/main/java:\
+$INPUT/dalvik/libcore/awt-kernel/src/main/java:\
+$INPUT/dalvik/libcore/concurrent/src/main/java:\
+$INPUT/dalvik/libcore/crypto/src/main/java:\
+$INPUT/dalvik/libcore/dalvik/src/main/java:\
+$INPUT/dalvik/libcore/icu/src/main/java:\
+$INPUT/dalvik/libcore/json/src/main/java:\
+$INPUT/dalvik/libcore/junit/src/main/java:\
+$INPUT/dalvik/libcore/logging/src/main/java:\
+$INPUT/dalvik/libcore/luni-kernel/src/main/java:\
+$INPUT/dalvik/libcore/luni/src/main/java:\
+$INPUT/dalvik/libcore/math/src/main/java:\
+$INPUT/dalvik/libcore/nio_char/src/main/java:\
+$INPUT/dalvik/libcore/nio/src/main/java:\
+$INPUT/dalvik/libcore/openssl/src/main/java:\
+$INPUT/dalvik/libcore/prefs/src/main/java:\
+$INPUT/dalvik/libcore/regex/src/main/java:\
+$INPUT/dalvik/libcore/security-kernel/src/main/java:\
+$INPUT/dalvik/libcore/security/src/main/java:\
+$INPUT/dalvik/libcore/sql/src/main/java:\
+$INPUT/dalvik/libcore/suncompat/src/main/java:\
+$INPUT/dalvik/libcore/text/src/main/java:\
+$INPUT/dalvik/libcore/xml/src/main/java:\
+$INPUT/dalvik/libcore/x-net/src/main/java:\
+$INPUT/tests/HttpClient:\
+$INPUT/dalvik/libcore/annotation/src/test/java:\
+$INPUT/dalvik/libcore/archive/src/test/java:\
+$INPUT/dalvik/libcore/crypto/src/test/java:\
+$INPUT/dalvik/libcore/crypto/src/test/java/support/common/java:\
+$INPUT/dalvik/libcore/dalvik/src/test/java:\
+$INPUT/dalvik/libcore/logging/src/test/java:\
+$INPUT/dalvik/libcore/luni-kernel/src/test/java:\
+$INPUT/dalvik/libcore/luni/src/test/java:\
+$INPUT/dalvik/libcore/math/src/test/java:\
+$INPUT/dalvik/libcore/nio_char/src/test/java:\
+$INPUT/dalvik/libcore/nio/src/test/java:\
+$INPUT/dalvik/libcore/prefs/src/test/java:\
+$INPUT/dalvik/libcore/regex/src/test/java:\
+$INPUT/dalvik/libcore/security/src/test/java:\
+$INPUT/dalvik/libcore/sql/src/test/java:\
+$INPUT/dalvik/libcore/support/src/test/java:\
+$INPUT/dalvik/libcore/text/src/test/java:\
+$INPUT/dalvik/libcore/xml/src/test/java:\
+$INPUT/dalvik/libcore/x-net/src/test/java"
+
+DALVIK_PACKAGE="\
+junit.framework \
+org.apache.harmony.annotation.tests.java.lang.annotation \
+org.apache.harmony.archive.tests.java.util.jar \
+org.apache.harmony.archive.tests.java.util.zip \
+org.apache.harmony.crypto.tests.javax.crypto \
+org.apache.harmony.crypto.tests.javax.crypto.spec \
+org.apache.harmony.crypto.tests.javax.crypto.interfaces \
+org.apache.harmony.logging.tests.java.util.logging \
+org.apache.harmony.luni.tests.internal.net.www.protocol.http \
+org.apache.harmony.luni.tests.internal.net.www.protocol.https \
+org.apache.harmony.luni.tests.java.io \
+org.apache.harmony.luni.tests.java.lang \
+org.apache.harmony.luni.tests.java.net \
+org.apache.harmony.luni.tests.java.util \
+org.apache.harmony.luni.tests.util \
+org.apache.harmony.math.tests.java.math \
+org.apache.harmony.nio_char.tests.java.nio.charset \
+org.apache.harmony.nio_char.tests.java.nio.charset.spi \
+org.apache.harmony.nio.tests.java.nio \
+org.apache.harmony.nio.tests.java.nio.channels \
+org.apache.harmony.nio.tests.java.nio.channels.spi \
+org.apache.harmony.prefs.tests.java.util.prefs \
+org.apache.harmony.regex.tests.java.util.regex \
+org.apache.harmony.security.tests.java.security \
+org.apache.harmony.sql.tests.java.sql \
+org.apache.harmony.sql.tests.javax.sql \
+tests.SQLite \
+org.apache.harmony.testframework.serialization \
+org.apache.harmony.text.tests.java.text \
+targets \
+tests.api.java.io \
+tests.api.java.lang \
+tests.api.java.lang.ref \
+tests.api.java.lang.reflect \
+tests.api.java.math \
+tests.api.java.net \
+tests.api.java.nio.charset \
+tests.api.java.security \
+tests.api.java.util \
+tests.api.javax.net \
+tests.api.javax.net.ssl \
+tests.api.javax.security.auth \
+tests.api.javax.security.cert \
+tests.api.javax.xml.parsers \
+tests.api.org.xml.sax \
+tests.api.org.xml.sax.ext \
+tests.api.org.xml.sax.helpers \
+tests.api.org.apache.harmony.kernel.dalvik \
+tests.java.security \
+tests.java.sql \
+tests.javax.sql \
+tests.org.w3c.dom \
+tests.security \
+tests.security.acl \
+tests.security.cert \
+tests.security.interfaces \
+tests.security.permissions \
+tests.security.spec \
+tests.sql \
+tests.support \
+tests.targets.security \
+tests.targets.security.cert \
+tests.xml \
+java.io \
+java.lang \
+java.lang.annotation \
+java.lang.ref \
+java.lang.reflect \
+java.math \
+java.net \
+java.nio \
+java.nio.channels \
+java.nio.channels.spi \
+java.nio.charset \
+java.nio.charset.spi \
+java.security \
+java.security.acl \
+java.security.cert \
+java.security.interfaces \
+java.security.spec \
+java.sql \
+java.text \
+java.util \
+java.util.jar \
+java.util.logging \
+java.util.prefs \
+java.util.regex \
+java.util.zip \
+javax.crypto \
+javax.crypto.interfaces \
+javax.crypto.spec \
+javax.net \
+javax.net.ssl \
+javax.security.auth \
+javax.security.auth.callback \
+javax.security.auth.login \
+javax.security.auth.x500 \
+javax.security.cert \
+javax.sql \
+javax.xml.parsers \
+org.xml.sax \
+org.xml.sax.ext \
+org.xml.sax.helpers"
+
+ANDROID_SOURCE_PATH="\
+${INPUT}/frameworks/base/core/java:\
+${INPUT}/frameworks/base/graphics/java:\
+${INPUT}/frameworks/base/location/java:\
+${INPUT}/frameworks/base/media/java:\
+${INPUT}/frameworks/base/opengl/java:\
+${INPUT}/frameworks/base/sax/java:\
+${INPUT}/frameworks/base/telephony/java:\
+${INPUT}/frameworks/base/wifi/java:\
+${INPUT}/cts/tests/tests/app/src:\
+${INPUT}/cts/tests/tests/content/src:\
+${INPUT}/cts/tests/tests/database/src:\
+${INPUT}/cts/tests/tests/graphics/src:\
+${INPUT}/cts/tests/tests/hardware/src:\
+${INPUT}/cts/tests/tests/location/src:\
+${INPUT}/cts/tests/tests/media/src:\
+${INPUT}/cts/tests/tests/net/src:\
+${INPUT}/cts/tests/tests/opengl/src:\
+${INPUT}/cts/tests/tests/os/src:\
+${INPUT}/cts/tests/tests/preference/src:\
+${INPUT}/cts/tests/tests/provider/src:\
+${INPUT}/cts/tests/tests/sax/src:\
+${INPUT}/cts/tests/tests/security/src:\
+${INPUT}/cts/tests/tests/speech/src:\
+${INPUT}/cts/tests/tests/telephony/src:\
+${INPUT}/cts/tests/tests/text/src:\
+${INPUT}/cts/tests/tests/util/src:\
+${INPUT}/cts/tests/tests/view/src:\
+${INPUT}/cts/tests/tests/webkit/src:\
+${INPUT}/cts/tests/tests/widget/src:\
+${INPUT}/cts/tests/src:\
+${INPUT}/frameworks/base/test-runner:\
+${INPUT}/dalvik/libcore/dalvik/src/main/java:\
+${INPUT}/dalvik/libcore/xml/src/main/java:\
+${INPUT}/dalvik/libcore/junit/src/main/java:"
+
+ANDROID_PACKAGE="\
+android.accessibilityservice \
+android.accessibilityservice.cts \
+android.accounts \
+android.accounts.cts \
+android.app \
+android.app.cts \
+android.bluetooth \
+android.content \
+android.content.cts \
+android.content.pm \
+android.content.pm.cts \
+android.content.res \
+android.content.res.cts \
+android.database \
+android.database.cts \
+android.database.sqlite \
+android.database.sqlite.cts \
+android.gesture \
+android.gesture.cts \
+android.graphics \
+android.graphics.cts \
+android.graphics.drawable \
+android.graphics.drawable.cts \
+android.graphics.drawable.shapes \
+android.graphics.drawable.shapes.cts \
+android.hardware \
+android.hardware.cts \
+android.location \
+android.location.cts \
+android.media \
+android.media.cts \
+android.net \
+android.net.cts \
+android.net.http \
+android.net.http.cts \
+android.net.wifi \
+android.net.wifi.cts \
+android.opengl \
+android.opengl.cts \
+android.os \
+android.os.cts \
+android.preference \
+android.preference.cts \
+android.provider \
+android.provider.cts \
+android.sax \
+android.sax.cts \
+android.security \
+android.security.cts \
+android.speech.srec \
+android.speech.srec.cts \
+android.speech.tts \
+android.speech.tts.cts \
+android.telephony \
+android.telephony.cdma \
+android.telephony.cts \
+android.telephony.gsm \
+android.telephony.gsm.cts \
+android.text \
+android.text.cts \
+android.text.format \
+android.text.format.cts \
+android.text.method \
+android.text.method.cts \
+android.text.style \
+android.text.style.cts \
+android.text.util \
+android.text.util.cts \
+android.util \
+android.util.cts \
+android.view \
+android.view.cts \
+android.view.accessibility \
+android.view.accessibility.cts \
+android.view.animation \
+android.view.animation.cts \
+android.view.inputmethod \
+android.view.inputmethod.cts \
+android.webkit \
+android.webkit.cts \
+android.widget \
+android.widget.cts"
+
+ALL_SOURCE_PATH="${DALVIK_SOURCE_PATH}:${ANDROID_SOURCE_PATH}"
+ALL_PACKAGE="${DALVIK_PACKAGE} ${ANDROID_PACKAGE}"
+
+SOURCE_PATH=""
+if [ ${testType} == "all" ]; then
+    SOURCE_PATH="${ALL_SOURCE_PATH} ${ALL_PACKAGE}"
+elif [ ${testType} == "dalvik" ]; then
+    SOURCE_PATH="${DALVIK_SOURCE_PATH} ${DALVIK_PACKAGE}"
+elif [ ${testType} == "android" ]; then
+    SOURCE_PATH="${ANDROID_SOURCE_PATH} ${ANDROID_PACKAGE}"
+fi
+
+javadoc -J-Xmx1024M -docletpath $CLASSES -doclet testprogress2.TestCoverageDoclet -f $TESTCOVERAGE_FLAGS -d $OUTPUT -sourcepath $SOURCE_PATH
+
+# left out on purpose:
+# org.w3c.dom packages: the tests in the xml module are superseded by the
+# official w3c test suite located in the dom module.
+#
+
+# left out on purpose:
+# concurrent packages: a full test suite exists (JCP JSR166 test suite) and it located at 
+# device/dalvik/libcore/concurrent/src/test/java/tests/api/java/util/concurrent
+#
+#java.util.concurrent \
+#java.util.concurrent.atomic \
+#java.util.concurrent.locks \
+#
+
+# left out on purpose: (100% coverage/jsr)
+# tests/api/java/util/concurrent
+# tests.api.java.util.concurrent
+
+
+# all test source path found by:
+# find . -type d | sed -e '/.svn/d' | grep "src/test/java" | sed -e 's/test\/java.*/test\/java/g' | sort -u
+
+# the list of packages (test only) was taken from:
+#  egrep -R "[ \.]TestCase" dalvik/* | sed -e '/.svn/d' | cut -d':' -f 1 | cut -d'.' -f1 | sed -e's/.*src\/test\/java\///g;s/\/[^\/]*$//g' | sort -u
+
+
diff --git a/tools/test-progress-new/src/Android.mk b/tools/test-progress-new/src/Android.mk
new file mode 100644
index 0000000..00d5904
--- /dev/null
+++ b/tools/test-progress-new/src/Android.mk
@@ -0,0 +1,27 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+#LOCAL_MODULE_TAGS := cts
+
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_CLASSPATH := \
+	$(HOST_JDK_TOOLS_JAR)
+
+LOCAL_MODULE:= test-progress-new
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/test-progress-new/src/testprogress2/AnnotationPointer.java b/tools/test-progress-new/src/testprogress2/AnnotationPointer.java
new file mode 100644
index 0000000..8f466de
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/AnnotationPointer.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package testprogress2;
+
+import com.sun.javadoc.ExecutableMemberDoc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// points from one targetMethod to 0..n testMethods which test the target method
+public class AnnotationPointer {
+    final ExecutableMemberDoc targetMethod;
+
+    private List<TestTargetNew> targets = new ArrayList<TestTargetNew>();
+
+    AnnotationPointer(ExecutableMemberDoc targetMethod) {
+        this.targetMethod = targetMethod;
+    }
+
+    public void addTestTargetNew(TestTargetNew testMethodInfo) {
+        /*
+         * if (testMethods.contains(testMethodInfo)) { throw new
+         * RuntimeException("warn: testMethod refers more than once to the
+         * targetMethod, testMethod="+testMethodInfo.getMethodDoc());
+         * //System.out.println("warn: testMethod refers more than once to the
+         * targetMethod, testMethod="+testMethod); } else {
+         */
+        targets.add(testMethodInfo);
+        // }
+    }
+
+    public List<TestTargetNew> getTargets() {
+        return targets;
+    }
+
+    public void addProxiesFrom(AnnotationPointer ap) {
+        List<TestTargetNew> t = ap.targets;
+        // clone the TestTargetNew and add to it a note from which
+        // target method it orignally stems
+        for (TestTargetNew ttn : t) {
+            TestTargetNew tnew = ttn.cloneMe("<b>(INDIRECTLY tested)</b>");
+            targets.add(tnew);
+        }
+    }
+}
diff --git a/tools/test-progress-new/src/testprogress2/ClassOriginator.java b/tools/test-progress-new/src/testprogress2/ClassOriginator.java
new file mode 100644
index 0000000..378cec7
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/ClassOriginator.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package testprogress2;
+
+import com.sun.javadoc.ClassDoc;
+
+/**
+ *
+ */
+public class ClassOriginator implements Originator {
+    private final ClassDoc mClassDoc;
+
+    private final String mComment;
+
+    ClassOriginator(ClassDoc classDoc, String comment) {
+        mClassDoc = classDoc;
+        mComment = comment;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see testprogress.Originator#asString()
+     */
+    public String asString() {
+        return (mComment != null ? mComment + " - " : "") + " -class- "
+                + mClassDoc.qualifiedName();
+    }
+
+    public boolean isDisabled() {
+        return false;
+    }
+
+    /**
+     * a reference from a class is never a to be fixed
+     */
+    public String getToBeFixed() {
+        return null;
+    }
+
+    /**
+     * a reference from a class is never a broken tests
+     */
+    public String getBrokenTest() {
+        return null;
+    }
+
+    /**
+     * a reference from a class is never a failure
+     */
+    public String getKnownFailure() {
+        return null;
+    }
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/MethodOriginator.java b/tools/test-progress-new/src/testprogress2/MethodOriginator.java
new file mode 100644
index 0000000..e46b436
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/MethodOriginator.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+package testprogress2;
+
+import com.sun.javadoc.AnnotationDesc;
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.MethodDoc;
+
+/**
+ *
+ */
+public class MethodOriginator implements Originator {
+    private final MethodDoc mMethod;
+
+    private final String mComment;
+
+    private final ClassDoc mClass;
+
+    private String knownFailure = null;
+
+    private String brokenTest = null;
+
+    private String toBeFixed = null;
+
+    /**
+     * @param testMethod
+     * @param clazz we need to provide the clazz since junit collects the
+     *            testMethods from all super classes - and thus MethodDoc's
+     *            class can point to a superclass. However, we want to know the
+     *            class where the TestTargetClass is pointing to the API class.
+     * @param comment
+     */
+    MethodOriginator(MethodDoc testMethod, ClassDoc clazz, String comment) {
+        mMethod = testMethod;
+        mComment = comment;
+        mClass = clazz;
+
+        AnnotationDesc[] annots = testMethod.annotations();
+        for (AnnotationDesc annot : annots) {
+            if (annot.annotationType().qualifiedName().equals(
+                    "dalvik.annotation.KnownFailure")) {
+                knownFailure = "<b>@KnownFailure:</b>"
+                        + (String)annot.elementValues()[0].value().value();
+            } else if (annot.annotationType().qualifiedName().equals(
+                    "dalvik.annotation.BrokenTest")) {
+                brokenTest = "<b>@BrokenTest:</b>"
+                        + (String)annot.elementValues()[0].value().value();
+            } else if (annot.annotationType().qualifiedName().equals(
+                    "dalvik.annotation.ToBeFixed")) {
+                String info = "N/A";
+                if (annot.elementValues().length > 0) {
+                    info = (String)annot.elementValues()[0].value().value();
+                }
+
+                toBeFixed = "<b>@ToBeFixed:</b>" + info;
+            }
+            // else some other annotation - ignore
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see testprogress.Originator#asString()
+     */
+    public String asString() {
+        return (mComment != null ? "comment:" + mComment + " - " : "")
+                + mClass.qualifiedName() + ": <b>" + mMethod.name() + "</b>"
+                + mMethod.signature()
+                + (brokenTest != null ? " [" + brokenTest + "]" : "")
+                + (toBeFixed != null ? " [" + toBeFixed + "]" : "")
+                + (knownFailure != null ? " [" + knownFailure + "]" : "");
+    }
+
+    public boolean isDisabled() {
+        return mMethod.name().startsWith("_test");
+    }
+
+    public String getBrokenTest() {
+        return brokenTest;
+    }
+
+    public String getToBeFixed() {
+        return toBeFixed;
+    }
+
+    public String getKnownFailure() {
+        return knownFailure;
+    }
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/Originator.java b/tools/test-progress-new/src/testprogress2/Originator.java
new file mode 100644
index 0000000..a6e593c
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/Originator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package testprogress2;
+
+public interface Originator {
+
+    /**
+     * @return
+     */
+    String asString();
+
+    /**
+     * whether the originating test is disabled
+     *
+     * @return true if the test is disabled (starts with _test...)
+     */
+    boolean isDisabled();
+
+    /**
+     * indicates if the test is to be fixed (annotated with @ToBeFixed
+     *
+     * @return a string containing the annotation, null otherwise
+     */
+    String getToBeFixed();
+
+    /**
+     * indicates if the test is broken (annotated with @BrokenTest
+     *
+     * @return a string containing the annotation, null otherwise
+     */
+    String getBrokenTest();
+
+    /**
+     * indicates if the test is a known failure (runs fine on a RI, annotated
+     * with @KnownFailure)
+     *
+     * @return a string containing the annotation, null otherwise
+     */
+    String getKnownFailure();
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/TestCoverageDoclet.java b/tools/test-progress-new/src/testprogress2/TestCoverageDoclet.java
new file mode 100644
index 0000000..a08a09b
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/TestCoverageDoclet.java
@@ -0,0 +1,1365 @@
+/*
+ * 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.
+ */
+
+package testprogress2;
+
+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.ParameterizedType;
+import com.sun.javadoc.RootDoc;
+import com.sun.javadoc.Tag;
+import com.sun.javadoc.AnnotationDesc.ElementValuePair;
+
+import testprogress2.TestMethodInformation.Color;
+import testprogress2.TestMethodInformation.Level;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+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;
+
+/**
+ * this doclet generates a .html report about the test coverage of the core api
+ * reading test method's annotations.
+ */
+public class TestCoverageDoclet {
+
+    /**
+     * color for the stats: green, yellow, red
+     */
+    public static final String[] COLORS = {
+            "#a0ffa0", "#ffffa0", "#ff8080"
+    };
+
+    // debugging output
+    private static final boolean DEBUG = false;
+
+    /**
+     * Holds our basic output directory.
+     */
+    private File directory;
+
+    private boolean _ignoreInterfacesAndAbstractMethods = false;
+
+    private boolean _doIncludeDisabledTests = false;
+
+    private boolean _acceptCompleteWithOtherStatus = false;
+
+    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());
+        }
+    }
+
+    /**
+     * Holds our comparator instance for everything.
+     */
+    private DocComparator classComparator = new DocComparator();
+
+    private MemberComparator memberComparator = new MemberComparator();
+
+    private Map<ClassDoc, List<TestTargetNew>> classToSpecialTargets =
+        new HashMap<ClassDoc, List<TestTargetNew>>();
+
+    /**
+     * Creates a new instance of the TestProgressDoclet for a given target
+     * directory.
+     *
+     * @param 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 filename, String title) {
+        File file = new File(directory, filename);
+        File parent = file.getParentFile();
+        parent.mkdirs();
+
+        PrintWriter printer;
+        try {
+            printer = new PrintWriter(new FileOutputStream(file));
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException("file not found:" + e.getMessage());
+        }
+
+        printer.println("<html>");
+        printer.println("  <head>");
+        printer.println("    <title>" + title + "</title>");
+        printer.println("<style type=\"text/css\">\n"
+                + "body, body table tr td { font-size:10pt;font-family: "
+                + " sans-serif; }\n" + "li { padding-bottom:2px }\n"
+                + "table { width:100%; 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(String... columns) {
+            pr.print("<tr style=\"background-color:white\">");
+            for (String col : columns) {
+                pr.print("<td>" + col + "</td>");
+            }
+            pr.print("</tr>");
+        }
+
+        public void printPlain(String val) {
+            pr.print(val);
+        }
+
+        public void printTableStart() {
+            pr.print("<table>");
+        }
+
+        public void printTableEnd() {
+            pr.print("</table>");
+        }
+
+        public void printPlainLn(String val) {
+            printPlain(val);
+            pr.print("<br>");
+        }
+    }
+
+    /**
+     * Processes the whole list of classes that JavaDoc knows about.
+     */
+    private void process(RootDoc root) {
+        System.out.println("V 0.9a");
+        String mode = getOption(root, "-f", 1, "dummy-see bash script");
+        System.out.println("mode: " + mode);
+
+        // standard mode is to include disabled tests
+        _doIncludeDisabledTests = mode.contains("countdisabled");
+        _acceptCompleteWithOtherStatus = mode.contains("acceptcandx");
+        if (_doIncludeDisabledTests) {
+            System.out.println("- including disabled tests");
+        } else {
+            System.out.println("- excluding disabled tests");
+        }
+        if (_acceptCompleteWithOtherStatus) {
+            System.out.println("- accepting complete tests with partial tests");
+        } else {
+            System.out.println("- not accepting complete tests with partial "
+                    + "tests");
+        }
+
+        // 1. traverse all test-classes (those extending JUnit's TestCase)
+        // and collect the annotation info.
+        // ===================================================================
+        System.out.println("stage 1 - get targets from all junit test methods");
+        PrintWriter pr = openFile("testcoverage/test-annotation.html",
+                "test class annotation coverage");
+        TablePrinter printer = new TablePrinter(pr);
+        printer.printTableStart();
+        printer.printRow("Test package name", "JUnit classes", "Meth", "Unt",
+                "Part", "Compl", "Disab", "Broken", "ToBeFixed", "KnownFail");
+
+        ColorStat totalStats = new ColorStat("All test packages", null);
+        PackageDoc[] packages = root.specifiedPackages();
+        Arrays.sort(packages, classComparator);
+        for (PackageDoc pack : packages) {
+            if (pack.allClasses().length != 0) {
+                ColorStat packageStat = processTestPackage(pack);
+                if (!packageStat.ignored) {
+                    // ignore those packages which have 0 tests in it
+                    printTestStats(printer, packageStat, true);
+                    totalStats.add(packageStat);
+                }
+            }
+        }
+        printer.printTableEnd();
+
+        printer.printPlainLn("<h2>Summary of all test packages</h2>");
+        printer.printTableStart();
+        printTestStats(printer, totalStats, false);
+        printer.printTableEnd();
+        closeFile(pr);
+
+        System.out.println("stage 2 - proxy test targets to abstract classes"
+                + "and interfaces");
+        // 1/2 - traverse all normal (non-junit) classes for interface
+        // and abstract methods implementation tests.
+        ClassDoc[] classes = root.classes();
+        for (ClassDoc classDoc : classes) {
+            if (!extendsJUnitTestCase(classDoc)) {
+                MethodDoc[] methods = classDoc.methods();
+                for (MethodDoc methodDoc : methods) {
+                    AnnotationPointer ap = getAnnotationPointer(methodDoc,
+                            false);
+                    if (ap != null) {
+                        // if there are already tests targeting this method,
+                        // search for abstract/interface methods which this
+                        // method
+                        // implements, and mark them as tested by this method,
+                        // too.
+                        List<MethodDoc> impls = implementingMethods(methodDoc);
+                        for (MethodDoc impl : impls) {
+                            AnnotationPointer apImpl = getAnnotationPointer(
+                                    impl, true);
+                            // add all tests which pointed to the original
+                            // method.
+                            apImpl.addProxiesFrom(ap);
+                        }
+                    }
+                }
+            }
+        }
+
+        // 2. traverse all "normal" (non-junit) source files
+        // ===================================================================
+        System.out.println("stage 3 - generating report for target api");
+        pr = openFile("index.html", "All target api packages");
+        printer = new TablePrinter(pr);
+        printer.printPlainLn("Generated " + new Date().toString()
+                + " - V0.9a<br>");
+        printer.printPlainLn("<a href=\"testcoverage/test-annotation.html\">"
+                + "annotation progress of test classes</a><br><br>");
+
+        printer.printTableStart();
+        printer.printRow("Package", "Classes", "Methods", "Untested",
+                "Partial", "Complete", "Disabled", "Broken", "ToBeFixed", "KnownFail");
+
+        totalStats = new ColorStat("All target packages", null);
+        packages = root.specifiedPackages();
+        Arrays.sort(packages, classComparator);
+
+        int classCount = 0;
+        for (PackageDoc pack : packages) {
+            if (pack.allClasses().length != 0) {
+                ColorStat packageStat = processPackage(pack);
+
+                if (!packageStat.ignored) {
+                    printStats(printer, packageStat, true);
+                    totalStats.add(packageStat);
+
+                    classCount += Integer.parseInt(packageStat.getExtra());
+                }
+            }
+        }
+        printer.printTableEnd();
+
+        totalStats.setExtra("" + classCount);
+        printer.printPlainLn("<h2>Summary of all target packages</h2>");
+        printer.printTableStart();
+        printStats(printer, totalStats, false);
+        printer.printTableEnd();
+        closeFile(pr);
+    }
+
+    /**
+     * Returns the interface(s) method(s) and the abstract method(s) that a
+     * given method implements, or an empty list if it does not implement
+     * anything.
+     */
+    private List<MethodDoc> implementingMethods(MethodDoc doc) {
+        List<MethodDoc> resultmethods = new ArrayList<MethodDoc>();
+        ClassDoc clazz = doc.containingClass();
+        implementedMethod0(resultmethods, doc, clazz, false);
+        return resultmethods;
+    }
+
+    /**
+     * Recursive helper method for finding out which interface methods or
+     * abstract methods a given method implements.
+     */
+    private void implementedMethod0(List<MethodDoc> resultmethods,
+            MethodDoc doc, ClassDoc testClass, boolean testMethods) {
+
+        // test all methods of this class
+        if (testMethods) {
+            MethodDoc[] methods = testClass.methods();
+            for (int j = 0; j < methods.length; j++) {
+                MethodDoc methodDoc = methods[j];
+                if ((methodDoc.isAbstract() || testClass.isInterface())
+                        && doc.overrides(methodDoc)) {
+                    resultmethods.add(methodDoc);
+                }
+            }
+        }
+
+        // test all implementing interfaces
+        ClassDoc[] ifs = testClass.interfaces();
+        for (int i = 0; i < ifs.length; i++) {
+            ClassDoc iface = ifs[i];
+            implementedMethod0(resultmethods, doc, iface, true);
+        }
+
+        // test the superclass
+        ClassDoc superclass = testClass.superclass();
+        if (superclass != null) {
+            implementedMethod0(resultmethods, doc, superclass, true);
+        }
+    }
+
+    private ColorStat processTestPackage(PackageDoc packageDoc) {
+        ColorStat stats = new ColorStat(packageDoc.name(),
+                getPackageBaseLink(packageDoc) + "/package.html");
+        if (hasHideFlag(packageDoc)) {
+            stats.ignored = true;
+            return stats;
+        }
+        String file = getPackageDir("testcoverage", packageDoc)
+                + "/package.html";
+        PrintWriter pr = openFile(file, "Test package " + packageDoc.name());
+        TablePrinter printer = new TablePrinter(pr);
+        printer.printTableStart();
+        printer.printRow("Class", "Extra", "Meth", "Unt", "Part", "Compl",
+                "Disab", "Broken", "ToBeFixed", "KnownFail");
+
+        ClassDoc[] classes = packageDoc.allClasses();
+        Arrays.sort(classes, classComparator);
+        int junitCnt = 0;
+        for (ClassDoc clazz : classes) {
+            if (extendsJUnitTestCase(clazz)) {
+                junitCnt++;
+                ColorStat subStats = processTestClass(clazz);
+                printTestStats(printer, subStats, true);
+                stats.add(subStats);
+            } else {
+                printer.printRow(clazz.name() + " ignored (no junit class): ",
+                        "", "", "", "", "", "", "", "");
+            }
+        }
+        printer.printTableEnd();
+
+        printer.printPlainLn("<h2>Test package summary</h2>");
+        printer.printTableStart();
+        printStats(printer, stats, false);
+        printer.printTableEnd();
+
+        closeFile(pr);
+        if (junitCnt == 0) {
+            if ((packageDoc.name().contains("tests.")
+                    || packageDoc.name().contains("junit.") || packageDoc
+                    .name().contains(".testframework"))
+                    && !(packageDoc.name().equals("junit.framework"))) {
+                System.err.println("warning!: no junit classes in package '"
+                        + packageDoc.name() + "' even though package name "
+                        + "contains tests.,junit. or .testframework");
+            }
+            stats = new ColorStat(packageDoc.name(),
+                    getPackageBaseLink(packageDoc) + "/package.html");
+            stats.incColor(TestMethodInformation.Color.GREEN);
+            stats.setExtra("Ignored since no Junit test and suites");
+            stats.ignored = true;
+        } else {
+            stats.setExtra("" + junitCnt);
+        }
+        return stats;
+    }
+
+    private ColorStat processPackage(PackageDoc packageDoc) {
+        ColorStat stats = new ColorStat(packageDoc.name(),
+                getPackageBaseLink(packageDoc) + "/package.html");
+        if (hasHideFlag(packageDoc)) {
+            stats.ignored = true;
+            return stats;
+        }
+        String file = getPackageDir("", packageDoc) + "/package.html";
+        PrintWriter pr = openFile(file, "Package " + packageDoc.name());
+        TablePrinter printer = new TablePrinter(pr);
+        printer.printTableStart();
+        printer.printRow("Class", "Extra", "Meth", "Unt", "Part", "Compl",
+                "Disab", "Broken", "ToBeFixed", "KnownFail");
+
+        ClassDoc[] classes = packageDoc.allClasses();
+        Arrays.sort(classes, classComparator);
+        int cnt = 0;
+        int junitCnt = 0;
+        for (ClassDoc clazz : classes) {
+            cnt++;
+            if (hasHideFlag(clazz)) {
+                // ignored since it has a @hide in the javadoc on the class
+                // level
+            } else if (extendsJUnitTestCase(clazz)) {
+                printer.printRow("ignored (junit class): " + clazz.name());
+                junitCnt++;
+            } else if (clazz.name().equals("AllTests")) {
+                printer.printRow("ignored (junit test suite class): "
+                        + clazz.name());
+                junitCnt++;
+            } else {
+                ColorStat subStats = processClass(clazz);
+                printStats(printer, subStats, true);
+                stats.add(subStats);
+            }
+        }
+        printer.printTableEnd();
+
+        printer.printPlainLn("<h2>Target package summary</h2>");
+        printer.printTableStart();
+        printStats(printer, stats, false);
+        printer.printTableEnd();
+
+        closeFile(pr);
+        if (junitCnt == cnt || packageDoc.name().contains("tests.")
+                || packageDoc.name().contains("junit.")
+                || packageDoc.name().contains(".testframework")
+                || packageDoc.name().endsWith(".cts")) {
+            // we only have junit classes -> mark green
+            stats = new ColorStat(packageDoc.name(),
+                    getPackageBaseLink(packageDoc) + "/package.html");
+            stats.incColor(TestMethodInformation.Color.GREEN);
+            stats
+                    .setExtra(junitCnt == cnt ? "Ignored since only Junit test and "
+                            + "suites"
+                            : "Ignored since \"tests.\" in name - recheck");
+            stats.ignored = true;
+        } else {
+            stats.setExtra("" + cnt);
+        }
+        return stats;
+    }
+
+    private boolean hasHideFlag(Doc doc) {
+        // workaround for the non-recognized @hide tag in package.html
+        if (doc instanceof PackageDoc) {
+            String comment = doc.getRawCommentText();
+            return comment != null && comment.contains("@hide");
+        } else {
+            Tag[] hideTags = doc.tags("hide");
+            return hideTags.length > 0;
+        }
+    }
+
+    private ColorStat processTestClass(ClassDoc clazz) {
+        String file = getPackageDir("testcoverage", clazz.containingPackage())
+                + "/" + clazz.name() + ".html";
+        PrintWriter pr = openFile(file, "Test class " + clazz.qualifiedName());
+        TablePrinter printer = new TablePrinter(pr);
+        ColorStat classStat = new ColorStat(clazz.name(), clazz.name()
+                + ".html");
+
+        TestTargetClass testTargetClass = getTargetClass(clazz);
+        ClassDoc targetClass = testTargetClass.targetClass;
+
+        String note = "Note:";
+        if (targetClass == null) {
+            note += "<br>targetClass annotation missing!<br>";
+        } else {
+            // add untested[] annotations to statistics
+            ClassOriginator co = new ClassOriginator(clazz, null);
+
+            AnnotationDesc[] annotsC = testTargetClass.untestedMethods
+                    .toArray(new AnnotationDesc[] {});
+            if (annotsC.length > 0) {
+                // we have "untested" refs
+                ColorStat classLevelStat = new ColorStat(clazz.name(), null);
+                TestMethodInformation tminfo = new TestMethodInformation(co,
+                        annotsC, targetClass);
+                if (tminfo.getError() != null) {
+                    printer.printPlainLn("<b>Error:</b>" + tminfo.getError());
+                    classLevelStat.incColor(Color.RED);
+                } else {
+                    linkTargets(tminfo.getTargets());
+                    classLevelStat.incColor(Color.GREEN);
+                }
+                classStat.add(classLevelStat);
+            }
+        }
+
+        printer.printPlainLn(note);
+
+        printer.printTableStart();
+        printer.printRow("Method", "Note", "Meth", "Unt", "Part", "Compl",
+                "Disab", "Broken", "ToBeFixed", "KnownFail");
+
+        int methodCnt = 0;
+        // also collects test... methods from all superclasses below TestCase,
+        // since those are called as well
+        List<MethodDoc> testMethods = collectAllTestMethods(clazz);
+        Collections.sort(testMethods, memberComparator);
+
+        for (MethodDoc testMethod : testMethods) {
+            methodCnt++;
+
+            // Make disabled tests visible in the report so we don't forget
+            // them.
+            boolean disTest = testMethod.name().startsWith("_test");
+
+            ColorStat methodStat = new ColorStat(testMethod.name(), null);
+            if (disTest) {
+                methodStat.incDisabledTestsCnt();
+            }
+            String comments = disTest ? "<b><span style=\"background:red\">"
+                    + "DISABLED</span></b>" : null;
+
+            MethodOriginator mo = new MethodOriginator(testMethod, clazz,
+                    comments);
+            AnnotationDesc[] annots = testMethod.annotations();
+            TestMethodInformation minfo = new TestMethodInformation(mo, annots,
+                    targetClass);
+            linkTargets(minfo.getTargets());
+
+            String extra = null;
+            if (comments != null) {
+                if (extra == null)
+                    extra = "";
+                extra += comments;
+            }
+
+            if (minfo.getError() != null) { // error case
+                if (extra == null)
+                    extra = "";
+                extra += "<b>Error:</b> " + minfo.getError() + "<br>";
+                methodStat.addMethodInfo(minfo);
+            } else {
+                if (mo.getKnownFailure() != null) {
+                    methodStat.incKnownFailureCnt();
+                    if (extra == null)
+                        extra = "";
+                    extra += mo.getKnownFailure();
+                }
+
+                // check for @BrokenTest
+                if (mo.getBrokenTest() != null) {
+                    // override with warning
+                    methodStat.incBrokenTestCnt();
+                    methodStat.incColor(Color.YELLOW);
+                    if (extra == null)
+                        extra = "";
+                    extra += mo.getBrokenTest();
+                }
+
+                // check for @ToBeFixed
+                if (mo.getToBeFixed() != null) {
+                    // override with warning
+                    methodStat.incToBeFixedCnt();
+                    methodStat.incColor(Color.YELLOW);
+                    if (extra == null) {
+                        extra = "";
+                    }
+
+                    extra += mo.getToBeFixed();
+                } else { // add regular stats
+                    methodStat.addMethodInfo(minfo);
+                }
+            }
+            if (extra != null) {
+                methodStat.setExtra(extra);
+            }
+
+            printTestStats(printer, methodStat, false);
+            classStat.add(methodStat);
+        }
+        printer.printTableEnd();
+
+        printer.printPlainLn("<h2>Test class summary</h2>");
+        printer.printTableStart();
+        printStats(printer, classStat, false);
+        printer.printTableEnd();
+
+        closeFile(pr);
+        classStat.setExtra("#methods: " + testMethods.size());
+        return classStat;
+    }
+
+    private void linkTargets(List<TestTargetNew> targets) {
+        for (TestTargetNew ttn : targets) {
+            if (ttn.getTargetMethod() != null) {
+                AnnotationPointer tar = getAnnotationPointer(ttn
+                        .getTargetMethod(), true);
+                tar.addTestTargetNew(ttn);
+            } else if (ttn.getTargetClass() != null) {
+                // some special target only pointing to a class, not a method.
+                addToClassTargets(ttn.getTargetClass(), ttn);
+            }
+        }
+    }
+
+    private boolean isGreen(TestMethodInformation.Level level) {
+        boolean lComplete = level == TestMethodInformation.Level.COMPLETE;
+        boolean lSufficient = level == TestMethodInformation.Level.SUFFICIENT;
+        boolean lPartialOk = level == TestMethodInformation.Level.PARTIAL_COMPLETE;
+        boolean lPartial = level == TestMethodInformation.Level.PARTIAL;
+        boolean lTodo = level == TestMethodInformation.Level.TODO;
+        boolean lNotFeasible = level == TestMethodInformation.Level.NOT_FEASIBLE;
+        boolean lNotNecessary = level == TestMethodInformation.Level.NOT_NECESSARY;
+
+        return lComplete || lPartialOk || lSufficient || lNotFeasible
+                || lNotNecessary;
+    }
+
+    private ColorStat processClass(ClassDoc clazz) {
+        String file = getPackageDir("", clazz.containingPackage()) + "/"
+                + clazz.name() + ".html";
+        String classDesc = getClassString(clazz);
+        PrintWriter pr = openFile(file, classDesc);
+        TablePrinter printer = new TablePrinter(pr);
+        printer.printPlain("<b>package " + clazz.containingPackage() + "</b>");
+        ColorStat classStats = new ColorStat(classDesc, clazz.name() + ".html");
+
+        // list special targets
+        List<TestTargetNew> classTargets = getTargetsFor(clazz);
+        if (classTargets != null) {
+            printer.printPlain("<h3>Class level tests</h3>");
+            printer.printPlain("<ul>");
+            for (TestTargetNew ttn : classTargets) {
+                String line = "<li>" + ttn.getOriginator().asString();
+                Level lev = ttn.getLevel();
+                line += " <font color=\""
+                        + (isGreen(lev) ? "green" : "red")
+                        + "\"><b>"
+                        + lev.name()
+                        + "</b></font>"
+                        + (ttn.getNotes() != null ? "<br>Notes: "
+                                + ttn.getNotes() : "") + "</li>";
+                printer.printPlain(line);
+            }
+            printer.printPlainLn("</ul>");
+        }
+
+        printer.printPlain("<h3>Method level tests</h3>");
+        printer.printTableStart();
+        printer.printRow("Method", "Tested by", "Meth", "Unt", "Part", "Compl",
+                "Disab", "Broken", "ToBeFixed", "KnownFail");
+        ConstructorDoc[] constructors = clazz.constructors();
+        Arrays.sort(constructors, classComparator);
+        int cnt = 0;
+        for (ConstructorDoc constructor : constructors) {
+            if (!hasHideFlag(constructor) && !hasHideFlag(clazz)) {
+                cnt++;
+                ColorStat memberStat = processElement(constructor);
+                printStats(printer, memberStat, false);
+                classStats.add(memberStat);
+            }
+        }
+
+        MethodDoc[] methods = clazz.methods();
+        Arrays.sort(methods, classComparator);
+        for (MethodDoc method : methods) {
+            if (!hasHideFlag(method) && !hasHideFlag(clazz)) {
+                cnt++;
+                ColorStat subStat = processElement(method);
+                printStats(printer, subStat, false);
+                classStats.add(subStat);
+            }
+        }
+        printer.printTableEnd();
+
+        printer.printPlainLn("<h2>Target class summary</h2>");
+        printer.printTableStart();
+        printStats(printer, classStats, false);
+        printer.printTableEnd();
+
+        closeFile(pr);
+        classStats.setExtra("#methods: " + cnt);
+
+        // mark as green
+        if (_ignoreInterfacesAndAbstractMethods && clazz.isInterface()) {
+            classStats = new ColorStat(clazz.name()
+                    + (clazz.isInterface() ? " (Interface)" : ""), clazz.name()
+                    + ".html");
+            int mcnt = clazz.methods().length;
+            // fake all methods to green
+            for (int i = 0; i < mcnt; i++) {
+                classStats.incColor(TestMethodInformation.Color.GREEN);
+            }
+            classStats.setExtra("Ignored since interface");
+        }
+        return classStats;
+    }
+
+    private class TestTargetClass {
+        ClassDoc targetClass;
+
+        List<AnnotationDesc> untestedMethods = new ArrayList<AnnotationDesc>();
+        // a List of @TestTargetNew annotations
+    }
+
+    private TestTargetClass getTargetClass(ClassDoc classDoc) {
+        // get the class annotation which names the default test target class
+        TestTargetClass ttc = new TestTargetClass();
+        ClassDoc targetClass = null;
+        AnnotationDesc[] cAnnots = classDoc.annotations();
+        for (AnnotationDesc cAnnot : cAnnots) {
+            AnnotationTypeDoc atype = cAnnot.annotationType();
+            if (atype.toString().equals("dalvik.annotation.TestTargetClass")) {
+                ElementValuePair[] cpairs = cAnnot.elementValues();
+                for (int i = 0; i < cpairs.length; i++) {
+                    ElementValuePair ev = cpairs[i];
+                    String elName = ev.element().name();
+                    if (elName.equals("value")) {
+                        // the target class
+                        AnnotationValue av = ev.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());
+                    } else if (elName.equals("untestedMethods")) {
+                        // TestTargetNew[] untestedMethods() default {};
+                        AnnotationValue[] targets = (AnnotationValue[])ev
+                                .value().value();
+                        for (AnnotationValue ttn : targets) {
+                            // each untested method must be a TestTargetNew
+                            AnnotationDesc ttnd = (AnnotationDesc)ttn.value();
+                            ttc.untestedMethods.add(ttnd);
+                        }
+                    }
+                }
+            }
+        }
+        ttc.targetClass = targetClass;
+        return ttc;
+    }
+
+    private List<MethodDoc> collectAllTestMethods(ClassDoc classDoc) {
+        List<MethodDoc> m = new ArrayList<MethodDoc>();
+
+        ClassDoc curCl = classDoc;
+        do {
+            m.addAll(getJunitTestMethods(curCl));
+        } while ((curCl = curCl.superclass()) != null
+                && !curCl.qualifiedName().equals("junit.framework.TestCase"));
+        return m;
+    }
+
+    private List<MethodDoc> getJunitTestMethods(ClassDoc classDoc) {
+        List<MethodDoc> cl = new ArrayList<MethodDoc>();
+        for (MethodDoc methodDoc : classDoc.methods()) {
+            if (methodDoc.isPublic()
+                    && (methodDoc.name().startsWith("test") || methodDoc.name()
+                            .startsWith("_test"))) {
+                cl.add(methodDoc);
+            }
+        }
+        return cl;
+    }
+
+    private class ColorStat {
+        private String name;
+
+        private String link;
+
+        private String extra;
+
+        public boolean ignored;
+
+        private int[] cntCol = new int[4];
+
+        private int disabledTestsCnt = 0;
+
+        private int brokenTestCnt = 0;
+
+        private int toBeFixedCnt = 0;
+
+        private int knownFailureCnt = 0;
+
+        public String getName() {
+            return name;
+        }
+
+        public String getLink() {
+            return link;
+        }
+
+        public ColorStat(String name, String link) {
+            this.name = name;
+            this.link = link;
+        }
+
+        public void add(ColorStat subStat) {
+            for (int i = 0; i < cntCol.length; i++) {
+                cntCol[i] += subStat.cntCol[i];
+            }
+            disabledTestsCnt += subStat.disabledTestsCnt;
+            brokenTestCnt += subStat.brokenTestCnt;
+            toBeFixedCnt += subStat.toBeFixedCnt;
+            knownFailureCnt += subStat.knownFailureCnt;
+        }
+
+        public void incDisabledTestsCnt() {
+            disabledTestsCnt++;
+        }
+
+        public void incBrokenTestCnt() {
+            brokenTestCnt++;
+        }
+
+        public void incToBeFixedCnt() {
+            toBeFixedCnt++;
+        }
+
+        public void incKnownFailureCnt() {
+            knownFailureCnt++;
+        }
+
+        public void incColor(TestMethodInformation.Color color) {
+            cntCol[color.ordinal()]++;
+        }
+
+        public int getColorCnt(TestMethodInformation.Color color) {
+            return cntCol[color.ordinal()];
+        }
+
+        public void addMethodInfo(TestMethodInformation minfo) {
+            TestMethodInformation.Color c = minfo.getColor();
+            int ord = c.ordinal();
+            cntCol[ord]++;
+        }
+
+        public void setExtra(String extra) {
+            this.extra = extra;
+        }
+
+        public String getExtra() {
+            return extra;
+        }
+
+        public int getDisabledTestsCnt() {
+            return disabledTestsCnt;
+        }
+
+        public int getBrokenTestCnt() {
+            return brokenTestCnt;
+        }
+
+        public int getToBeFixedCnt() {
+            return toBeFixedCnt;
+        }
+
+        public int getKnownFailureCnt() {
+            return knownFailureCnt;
+        }
+    }
+
+    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 void addToClassTargets(ClassDoc targetClass, TestTargetNew ttn) {
+        List<TestTargetNew> targets = classToSpecialTargets.get(targetClass);
+        if (targets == null) {
+            targets = new ArrayList<TestTargetNew>();
+            classToSpecialTargets.put(targetClass, targets);
+        }
+        targets.add(ttn);
+    }
+
+    private List<TestTargetNew> getTargetsFor(ClassDoc targetClass) {
+        return classToSpecialTargets.get(targetClass);
+    }
+
+    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 a single method/constructor.
+     */
+    private ColorStat processElement(ExecutableMemberDoc method) {
+        if (DEBUG) System.out.println("Processing " + method);
+        ColorStat memberStats = new ColorStat(getMethodString(method), null);
+        TestMethodInformation.Color c = TestMethodInformation.Color.RED;
+        // if flagged, consider abstract method ok also without tests
+        if (_ignoreInterfacesAndAbstractMethods && method instanceof MethodDoc
+                && ((MethodDoc)method).isAbstract()) {
+            c = TestMethodInformation.Color.GREEN;
+            memberStats.setExtra("ignored since abstract");
+        } else {
+            AnnotationPointer ap = getAnnotationPointer(method, false);
+            int testedByCnt = 0;
+            if (ap != null) {
+                List<TestTargetNew> targets = ap.getTargets();
+                testedByCnt = targets.size();
+                if (testedByCnt == 0) {
+                    throw new RuntimeException(
+                            "existing annotation pointer with no entries!, "
+                                    + "method:" + method);
+                }
+                // at least tested by one method
+                String by = "<ul>";
+                int completeTestCnt = 0;
+                int partialOkTestCnt = 0;
+                int partialTestCnt = 0;
+                int todoTestCnt = 0;
+                int notFeasableTestCnt = 0;
+                int notNecessaryTestCnt = 0;
+                int sufficientTestCnt = 0;
+                // int totalCnt = targets.size();
+
+                for (TestTargetNew target : targets) {
+                    Originator originator = target.getOriginator();
+                    boolean disabledTest = originator.isDisabled();
+                    boolean brokenTest = originator.getBrokenTest() != null;
+                    boolean toBeFixed = originator.getToBeFixed() != null;
+                    boolean knownFailure = originator.getKnownFailure() != null;
+                    by += "<li>" + originator.asString();
+                    TestMethodInformation.Level lev;
+                    if (target.isHavingProblems()) {
+                        lev = TestMethodInformation.Level.TODO;
+                    } else {
+                        lev = target.getLevel();
+                    }
+                    // TODO: improve: avoid c&p by adding ColorStat.addInfo
+                    // (originator) or similar
+                    if (disabledTest) {
+                        memberStats.incDisabledTestsCnt();
+                    }
+                    if (brokenTest) {
+                        memberStats.incBrokenTestCnt();
+                    }
+                    if (toBeFixed) {
+                        memberStats.incToBeFixedCnt();
+                    }
+                    if (knownFailure) {
+                        memberStats.incKnownFailureCnt();
+                    }
+
+                    boolean lComplete = lev == TestMethodInformation.Level.COMPLETE;
+                    boolean lSufficient = lev == TestMethodInformation.Level.SUFFICIENT;
+                    boolean lPartialOk = lev == TestMethodInformation.Level.PARTIAL_COMPLETE;
+                    boolean lPartial = lev == TestMethodInformation.Level.PARTIAL;
+                    boolean lTodo = lev == TestMethodInformation.Level.TODO;
+                    boolean lNotFeasible = lev == TestMethodInformation.Level.NOT_FEASIBLE;
+                    boolean lNotNecessary = lev == TestMethodInformation.Level.NOT_NECESSARY;
+
+                    by += " <font color=\""
+                            + (lComplete || lPartialOk || lSufficient
+                                    || lNotFeasible || lNotNecessary ? "green"
+                                    : "red")
+                            + "\"><b>"
+                            + lev.name()
+                            + "</b></font>"
+                            + (target.getNotes() != null ? "<br>Notes: "
+                                    + target.getNotes() : "");
+
+                    // only count tests when they are either ok (not disabled)
+                    // or if the doIncludeDisabledTests flag is set
+                    if ((_doIncludeDisabledTests || !disabledTest)
+                            && (!brokenTest) && (!toBeFixed)) {
+                        if (lComplete) {
+                            completeTestCnt++;
+                        } else if (lPartialOk) {
+                            partialOkTestCnt++;
+                        } else if (lPartial) {
+                            partialTestCnt++;
+                        } else if (lTodo) {
+                            todoTestCnt++;
+                        } else if (lSufficient) {
+                            sufficientTestCnt++;
+                        } else if (lNotFeasible) {
+                            notFeasableTestCnt++;
+                        } else if (lNotNecessary) {
+                            notNecessaryTestCnt++;
+                        }
+                    }
+
+                    if (toBeFixed) {
+                        partialTestCnt++;
+                    }
+
+                    if (DEBUG) {
+                        System.out.println("completeTestCnt: " + completeTestCnt
+                            + ", partialOkTestCnt: " + partialOkTestCnt
+                            + ", partialTestCnt: " + partialTestCnt
+                            + ", todoTestCnt: " + todoTestCnt
+                            + ", sufficientTestCnt: " + sufficientTestCnt
+                            + ", notFeasableTestCnt: " + notFeasableTestCnt
+                            + ", notNecessaryTestCnt: " + notNecessaryTestCnt);
+                    }
+                    by += "</li>";
+                }
+
+                String warnings = "";
+                // calculate the color
+                int singularTestCnt = notFeasableTestCnt + notNecessaryTestCnt;
+                boolean isAbstract = (method.containingClass().isInterface() ||
+                        (method instanceof MethodDoc) && ((MethodDoc)method).isAbstract());
+
+                if (_acceptCompleteWithOtherStatus
+                        && (completeTestCnt > 0 || sufficientTestCnt > 0)) {
+                    c = TestMethodInformation.Color.GREEN;
+                } else if (_acceptCompleteWithOtherStatus
+                        && (partialOkTestCnt > 1)) {
+                    c = TestMethodInformation.Color.GREEN;
+                } else {
+                    if (singularTestCnt > 0) {
+                        // we have tests which claim not_neccessary or
+                        // not_feasible
+                        if (targets.size() > singularTestCnt) {
+                            // other tests exist
+                            c = TestMethodInformation.Color.RED;
+                            warnings += "<b>WARNING:</b>NOT_FEASIBLE or "
+                                    + "NOT_NECESSARY together with other "
+                                    + "status!<br>";
+                        } else {
+                            // a collection of not_necessary and/or not_feasible
+                            if (notNecessaryTestCnt > 0
+                                    && notFeasableTestCnt > 0) {
+                                // a blend of both -> error
+                                warnings += "<b>WARNING:</b>both NOT_FEASIBLE "
+                                        + "and NOT_NECESSARY together!<br>";
+                                c = TestMethodInformation.Color.RED;
+                            } else { // just one sort of tests -> ok and
+                                // sufficent
+                                c = TestMethodInformation.Color.GREEN;
+                            }
+                        }
+                    } else if (todoTestCnt > 0) {
+                        c = TestMethodInformation.Color.RED;
+                    } else if (partialTestCnt > 0) {
+                        // at least one partial test
+                        c = TestMethodInformation.Color.YELLOW;
+                        if (completeTestCnt > 0 || sufficientTestCnt > 0) {
+                            if (_acceptCompleteWithOtherStatus) {
+                                // accept complete even if there are partials
+                                c = TestMethodInformation.Color.GREEN;
+                            } else if (completeTestCnt > 0) {
+                                // yellow+warning: mixed PARTIAL_COMPLETE and
+                                // COMPLETE status
+                                warnings += "<b>WARNING</b>: mixed PARTIAL "
+                                        + "and COMPLETE status<br>";
+                            }
+                        }
+                    } else if (partialOkTestCnt > 0 || sufficientTestCnt > 0) {
+                        // at least one partialOk test and no partial tests
+                        c = TestMethodInformation.Color.GREEN;
+                        if (partialOkTestCnt == 1) {
+                            // yellow: 1 PARTIAL_COMPLETE (we need either zero
+                            // or >=2 PARTIAL_OK tests)
+                            warnings += "<b>WARNING</b>: only one "
+                                    + "PARTIAL_COMPLETE status<br>";
+                            c = TestMethodInformation.Color.YELLOW;
+                        }
+                    } else if (completeTestCnt > 0 || singularTestCnt == 1) {
+                        // only complete tests
+                        c = TestMethodInformation.Color.GREEN;
+                    }
+
+                    if (completeTestCnt > 1 && !isAbstract
+                            && !_acceptCompleteWithOtherStatus) {
+                        // green+warning: >1 COMPLETE (we need either 0 or 1
+                        // COMPLETE, more would be strange, but possible)
+                        warnings += "<b>WARNING</b>: more than one "
+                                + "COMPLETE status<br>";
+                        if (c != TestMethodInformation.Color.RED) {
+                            c = TestMethodInformation.Color.YELLOW;
+                        }
+                    }
+                }
+                by = warnings + by;
+                memberStats.setExtra(by);
+            } else { // else this class has no single test that targets the
+                // current method
+                // handle special cases:
+
+                // can be ok if the current method is a default constructor
+                // which does not occur in the source code.
+                if (method.isConstructor() && method.signature().equals("()")) {
+                    if (method.position() != null) {
+                        // hacky stuff - the doclet should return null for a
+                        // default constructor,
+                        // but instead returns a source position pointing to the
+                        // same location as
+                        // the class.
+                        String constPos = method.position().toString();
+                        String classPos = method.containingClass().position()
+                                .toString();
+                        if (constPos.equals(classPos)) {
+                            // we have a default constructor not visible in
+                            // source code -> mark green, no testing needed
+                            c = TestMethodInformation.Color.GREEN;
+                            memberStats
+                                    .setExtra("automatically marked green "
+                                            + "since implicit default "
+                                            + "constructor");
+                        }
+                    } else {
+                        // should be called for default constructors according
+                        // to the doclet spec, but is never called.
+                        // spec:
+                        // "A default constructor returns null because it has no
+                        // location in the source file"
+                        // ??
+                        // what about default constructors being in the source
+                        // code?
+                        System.err.println("warning: doclet returned null for "
+                                + "source position: method:" + method);
+                    }
+                } else if (method.containingClass().superclass() != null
+                        && method.containingClass().superclass()
+                                .qualifiedName().equals("java.lang.Enum")) {
+                    // check enum types
+                    // <anyreturnclass> valueOf (java.lang.String) and
+                    // <anyreturnclass[]> values()
+                    // do not need to be tested, since they are autogenerated
+                    // by the compiler
+                    String sig = method.name() + method.signature();
+                    if (sig.equals("valueOf(java.lang.String)")
+                            || sig.equals("values()")) {
+                        c = TestMethodInformation.Color.GREEN;
+                        memberStats
+                                .setExtra("automatically marked green since "
+                                        + "generated by compiler for enums");
+                    }
+                }
+            }
+        }
+        memberStats.incColor(c);
+        return memberStats;
+    }
+
+    private String getMethodString(ExecutableMemberDoc method) {
+        String methodDesc = (method.isPublic() ? "public " : method
+                .isProtected() ? "protected " : method.isPrivate() ? "private "
+                : "");
+
+        return methodDesc + "<b>" + method.name() + "</b> "
+                + method.signature();
+    }
+
+    private String getClassString(ClassDoc clazz) {
+        return (clazz.isPublic() ? "public "
+                : clazz.isProtected() ? "protected "
+                        : clazz.isPrivate() ? "private " : "")
+                + (clazz.isInterface() ? "interface" : "class")
+                + " "
+                + clazz.name();
+    }
+
+    private void printTestStats(TablePrinter printer, ColorStat stat,
+            boolean wantLink) {
+        printStats(printer, stat, wantLink);
+    }
+
+    private void printStats(TablePrinter printer, ColorStat stat,
+            boolean wantLink) {
+        int redCnt = stat.getColorCnt(TestMethodInformation.Color.RED);
+        int yellowCnt = stat.getColorCnt(TestMethodInformation.Color.YELLOW);
+        int greenCnt = stat.getColorCnt(TestMethodInformation.Color.GREEN);
+        int disabledCnt = stat.getDisabledTestsCnt();
+        int brokenTestCnt = stat.getBrokenTestCnt();
+        int toBeFixedCnt = stat.getToBeFixedCnt();
+        int knownFailureCnt = stat.getKnownFailureCnt();
+
+        int total = redCnt + yellowCnt + greenCnt;
+
+        String link = stat.getLink();
+        String namePart;
+        if (wantLink && link != null) {
+            namePart = "<a href=\"" + link + "\">" + stat.getName() + "</a>";
+        } else {
+            namePart = stat.getName();
+        }
+
+        String extra = stat.getExtra() == null ? "" : stat.getExtra();
+
+        int totalDots = 120;
+
+        float toP = total == 0 ? 0 : (((float)totalDots) / total);
+
+        int redD = (int)(toP * redCnt);
+        if (redD == 0 && redCnt > 0) {
+            // never let red cut to zero;
+            redD = 1;
+        }
+        int yellowD = (int)(toP * yellowCnt);
+        if (yellowD == 0 && yellowCnt > 0) {
+            yellowD = 1;
+        }
+        int greenD = totalDots - redD - yellowD; // (int)(toP*greenCnt);
+
+        printer.printRow(namePart, extra, "" + total, "" + redCnt, ""
+                + yellowCnt, "" + greenCnt, "" + disabledCnt, ""
+                + brokenTestCnt, "" + toBeFixedCnt, "" + knownFailureCnt, ""
+                + (redCnt == 0 ? "" : "<span style=\"background:"
+                        + COLORS[TestMethodInformation.Color.RED.ordinal()]
+                        + "\">" + getDots(redD) + "</span>")
+                + (yellowCnt == 0 ? "" : "<span style=\"background:"
+                        + COLORS[TestMethodInformation.Color.YELLOW.ordinal()]
+                        + "\">" + getDots(yellowD) + "</span>")
+                + (greenCnt == 0 && total > 0 ? ""
+                        : "<span style=\"background:"
+                                + COLORS[TestMethodInformation.Color.GREEN
+                                        .ordinal()] + "\">" + getDots(greenD)
+                                + "</span>")
+                + "&nbsp;&nbsp;&nbsp;<span style=\"background:blue\">"
+                + getDots(total / 10) + "</span>");
+    }
+
+    private String getDots(int cnt) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < cnt; i++) {
+            sb.append("&nbsp;");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Returns the directory for a given package. Basically converts embedded
+     * dots in the name into slashes.
+     */
+
+    private String getPackageBaseLink(PackageDoc pack) {
+        return pack.name().replace('.', '/');
+    }
+
+    private File getPackageDir(String prefix, PackageDoc pack) {
+        if (pack == null || pack.name() == null || "".equals(pack.name())) {
+            return new File(prefix + "/" + ".");
+        } else {
+            return new File(prefix + "/" + 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.
+     *
+     * @param option the options
+     * @return an int
+     */
+    public static int optionLength(String option) {
+        if ("-d".equals(option)) {
+            return 2;
+        }
+        if ("-f".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.
+     *
+     * @return the version of the language
+     */
+    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.
+     *
+     * @param root
+     * @return whether an error occurred
+     */
+    public static boolean start(RootDoc root) {
+        try {
+            String target = getOption(root, "-d", 1, ".");
+            TestCoverageDoclet doclet = new TestCoverageDoclet(target);
+            doclet.process(root);
+
+            File file = new File(target, "index.html");
+            System.out.println("Please see complete report in " + 
+                    file.getAbsolutePath());
+            
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/TestMethodInformation.java b/tools/test-progress-new/src/testprogress2/TestMethodInformation.java
new file mode 100644
index 0000000..c609423
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/TestMethodInformation.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+package testprogress2;
+
+import com.sun.javadoc.AnnotationDesc;
+import com.sun.javadoc.AnnotationValue;
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.AnnotationDesc.ElementValuePair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * represents a list of testtargets annotations all belonging to one test method
+ * / one testclass together with its processed information.
+ */
+public class TestMethodInformation {
+    private boolean annotationExists = false;
+
+    private List<TestTargetNew> targets = new ArrayList<TestTargetNew>();
+
+    private String error = null;
+
+    private Color color = Color.RED;
+
+    public enum Level {
+        TODO, PARTIAL, PARTIAL_COMPLETE, COMPLETE, ADDITIONAL, NOT_NECESSARY, NOT_FEASIBLE, SUFFICIENT
+    }
+
+    public enum Color {
+        GREEN /* ready */, YELLOW /* work */, RED
+        /* missing essential stuff */
+    }
+
+    public TestMethodInformation(Originator originator,
+            AnnotationDesc[] annots, ClassDoc targetClass) {
+        // System.out.println("looking at "+testMethodDoc);
+        if (targetClass == null) {
+            addError("target class annotation missing!");
+            return;
+        }
+
+        for (AnnotationDesc annot : annots) {
+            if (annot.annotationType().qualifiedName().equals(
+                    "dalvik.annotation.TestTargets")) {
+                // multi target case
+                annotationExists = true;
+                ElementValuePair[] pairs = annot.elementValues();
+                if (pairs.length != 1
+                        && !pairs[0].element().qualifiedName().equals(
+                                "dalvik.annotation.TestTargets.value")) {
+                    throw new RuntimeException("TestTargets has mismatched "
+                            + "attributes");
+                }
+                AnnotationValue[] targets = (AnnotationValue[])pairs[0].value()
+                        .value();
+                for (AnnotationValue ttn : targets) {
+                    // the test targets must be annotations themselves
+                    AnnotationDesc ttnd = (AnnotationDesc)ttn.value();
+                    handleTestTargetNew(originator, ttnd, targetClass);
+                }
+            } else if (annot.annotationType().qualifiedName().equals(
+                    "dalvik.annotation.TestTargetNew")) {
+                // singular case
+                annotationExists = true;
+                handleTestTargetNew(originator, annot, targetClass);
+            } // else some other annotation - ignore
+        }
+
+        boolean targetsCorrect = true;
+        for (TestTargetNew ttn : targets) {
+            targetsCorrect &= (ttn.getTargetMethod() != null || ttn
+                    .getTargetClass() != null);
+        }
+
+        // calculate color of test method
+        if (annotationExists) {
+            if (targetsCorrect) {
+                color = Color.GREEN;
+            } // else incorrect targets
+        } else {
+            addError("no annotation!");
+        }
+    }
+
+    private void handleTestTargetNew(Originator originator, AnnotationDesc ttn,
+            ClassDoc targetClass) {
+        TestTargetNew testTarget = new TestTargetNew(originator, ttn,
+                targetClass);
+        if (testTarget.isHavingProblems()) {
+            // add to overall message
+            addError(testTarget.getNotes());
+        }
+        targets.add(testTarget);
+    }
+
+    private void addError(String err) {
+        if (error == null)
+            error = "";
+        error += err + " ; ";
+    }
+
+    /**
+     * @return the error
+     */
+    public String getError() {
+        return error;
+    }
+
+    public List<TestTargetNew> getTargets() {
+        return targets;
+    }
+
+    public Color getColor() {
+        return color;
+    }
+
+}
diff --git a/tools/test-progress-new/src/testprogress2/TestTargetNew.java b/tools/test-progress-new/src/testprogress2/TestTargetNew.java
new file mode 100644
index 0000000..6102614
--- /dev/null
+++ b/tools/test-progress-new/src/testprogress2/TestTargetNew.java
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ */
+
+package testprogress2;
+
+import com.sun.javadoc.AnnotationDesc;
+import com.sun.javadoc.AnnotationValue;
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.ExecutableMemberDoc;
+import com.sun.javadoc.FieldDoc;
+import com.sun.javadoc.Parameter;
+import com.sun.javadoc.ParameterizedType;
+import com.sun.javadoc.Type;
+import com.sun.javadoc.TypeVariable;
+import com.sun.javadoc.AnnotationDesc.ElementValuePair;
+
+import testprogress2.TestMethodInformation.Level;
+
+/**
+ * holder for a TestTargetNew annotation
+ */
+public class TestTargetNew {
+    private final Originator originator;
+
+    private Level level = null;
+
+    private String notes = null;
+
+    /*
+     * method or constructor of target class
+     */
+    private ExecutableMemberDoc targetMethod = null;
+
+    /*
+     * only set if the target points -only- to a class, not to a method. e.g for
+     * special "!..." targets
+     */
+    private ClassDoc targetClass = null;
+
+    /*
+     * read from annotation, e.g. foobar(java.lang.String)
+     */
+    private String readMethodSignature = null;
+
+    /*
+     * e.g. foobar
+     */
+    private String readMethodName = null;
+
+    /*
+     * read from annotation
+     */
+    private ClassDoc readTargetClass = null;
+
+    private boolean havingProblems = false;
+
+    private TestTargetNew(Originator originator) {
+        this.originator = originator;
+    }
+
+    /**
+     * @param originator the origin (class or method)
+     * @param ttn the annotation (testtargetnew)
+     * @param classLevelTargetClass the default target class as given in the
+     *            testtargetclass annotation
+     */
+    public TestTargetNew(Originator originator, AnnotationDesc ttn,
+            ClassDoc classLevelTargetClass) {
+        this.originator = originator;
+        parseTargetClassAndMethodSignature(ttn, classLevelTargetClass);
+        // post: readMethod, readMethodSignature and readTargetClass are now set
+
+        // test for artificial method targets
+        if (readMethodName.startsWith("!")) {
+            targetMethod = null;
+            targetClass = readTargetClass;
+            // level = Level.ADDITIONAL;
+            // notes already set
+            notes = "target: " + readMethodName
+                    + (notes != null ? ", " + "notes: " + notes : "");
+
+        } else if (level == Level.TODO) {
+            notes = "TODO :" + notes;
+            havingProblems = true;
+        } else {
+            // prepare method target:
+            // if the signature contains a "." then the prefix is used as a
+            // reference
+            // to an inner class. This is an alternative to using the clazz
+            // attribute in cases where the class is an inner protected class,
+            // because then the inner class is not visible for the compiler at
+            // the
+            // place of the annotation.
+            // e.g. clazz = Certificate.CertificateRep.class does not work,
+            // so we use clazz = Certificate.class (enclosing class), and method
+            // "Certificate.CertificateRep.<methodHere>", e.g.
+            // "CertificateRep.CertificateRep"
+            // to denote the constructor of the inner protected class
+            // CertificateRep
+            // within Certificate
+            int dotPos = readMethodName.lastIndexOf('.');
+            if (dotPos != -1) {
+                String prefixClassName = readMethodName.substring(0, dotPos);
+                readMethodName = readMethodName.substring(dotPos + 1);
+                ClassDoc[] iCs = readTargetClass.innerClasses();
+                for (ClassDoc iC : iCs) {
+                    if (iC.name().equals(prefixClassName)) {
+                        readTargetClass = iC;
+                        break;
+                    }
+                }
+            }
+
+            String methodAndSig = readMethodName + readMethodSignature;
+            ExecutableMemberDoc tmeth = findMethodSignatureIn(methodAndSig,
+                    readTargetClass);
+            // we need this double test for the note below
+            if (tmeth == null) {
+                // a) wrong signature or
+                // b) a testMethod in a superclass or superinterface, ok also
+                tmeth = findTargetMethodInSelfAndSupers(methodAndSig,
+                        readTargetClass);
+                if (tmeth != null) {
+                    if (notes == null)
+                        notes = "";
+                    notes += "- targetmethod (" + tmeth + ") was found in a "
+                            + "superclass/superinterface of the target<br>";
+                }
+            }
+            if (tmeth != null) {
+                // found
+                targetMethod = tmeth;
+            } else {
+                havingProblems = true;
+                notes = "From " + originator.asString()
+                        + " -> could not resolve " + "targetMethod for class "
+                        + readTargetClass + ", " + "annotation was:" + ttn
+                        + ", testMethodSig " + "= " + methodAndSig + "<br>";
+                System.err.println(">>> warning: " + notes);
+            }
+        }
+    }
+
+    private ExecutableMemberDoc findMethodSignatureIn(String sig,
+            ClassDoc targetClass) {
+        ExecutableMemberDoc targetMethod = null;
+        // find the matching method in the target class, check all methods
+        for (ExecutableMemberDoc mdoc : targetClass.methods()) {
+            if (equalsSignature(mdoc, sig)) {
+                return mdoc;
+            }
+        }
+        // check constructors, too
+        for (ExecutableMemberDoc mdoc : targetClass.constructors()) {
+            if (equalsSignature(mdoc, sig)) {
+                return mdoc;
+            }
+        }
+        return null;
+    }
+
+    private ExecutableMemberDoc findTargetMethodInSelfAndSupers(String sig,
+            ClassDoc targetClass) {
+        ExecutableMemberDoc mem = findMethodSignatureIn(sig, targetClass);
+        if (mem != null) {
+            return mem;
+        }
+
+        // else visit parent class or parent interface(s)
+        ClassDoc[] ifs = targetClass.interfaces();
+        for (int i = 0; i < ifs.length; i++) {
+            ClassDoc iface = ifs[i];
+            mem = findTargetMethodInSelfAndSupers(sig, iface);
+            if (mem != null) {
+                return mem;
+            }
+        }
+
+        ClassDoc superclass = targetClass.superclass();
+        if (superclass != null) {
+            mem = findTargetMethodInSelfAndSupers(sig, superclass);
+            if (mem != null) {
+                return mem;
+            }
+        }
+        return null;
+    }
+
+    private void parseTargetClassAndMethodSignature(AnnotationDesc targetAnnot,
+            ClassDoc targetClass) {
+        ElementValuePair[] pairs = targetAnnot.elementValues();
+        String methodName = null;
+        String args = "";
+        for (ElementValuePair kval : pairs) {
+            if (kval.element().name().equals("method")) {
+                methodName = (String)kval.value().value();
+            } else if (kval.element().name().equals("clazz")) {
+                // optional: a different target class than the test-class-level
+                // default.
+                Object obj = kval.value().value();
+                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() + " target "
+                            + "annotation = " + targetAnnot);
+                }
+            } else if (kval.element().name().equals("args")) {
+                AnnotationValue[] vals = (AnnotationValue[])kval.value()
+                        .value();
+                for (int i = 0; i < vals.length; i++) {
+                    AnnotationValue arg = vals[i];
+                    String argV;
+                    // TODO: we should be able to use Type.asClassDoc() here
+                    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;
+                }
+            } else if (kval.element().name().equals("level")) {
+                AnnotationValue lev = kval.value();
+                FieldDoc fd = (FieldDoc)lev.value();
+                String slevel = fd.name();
+
+                try {
+                    level = Enum.valueOf(Level.class, slevel);
+                } catch (IllegalArgumentException iae) {
+                    throw new RuntimeException("COMPILE ERROR!!! enum "
+                            + slevel + " used in targetMethod for class "
+                            + "\"+targetClass+\", "
+                            + "annotation was:\"+targetAnnot+\", "
+                            + "testMethod = \"+methodDoc.toString()");
+                }
+            } else if (kval.element().name().equals("notes")) {
+                notes = (String)kval.value().value();
+                if (notes.equals("")) {
+                    notes = null;
+                }
+            }
+        }
+
+        // String refSig = methodName + "(" + args + ")";
+        // both methodName and methodArgs != null because of Annotation
+        // definition
+        this.readTargetClass = targetClass;
+        this.readMethodSignature = "(" + args + ")";
+        this.readMethodName = methodName;
+    }
+
+    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();
+                }
+                String dim = ptype.dimension();
+                if (dim != null && dim.length() > 0) {
+                    ptname += dim;
+                }
+            } 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 methodName = mdoc.name();
+        int lastDot = methodName.lastIndexOf('.');
+        if (lastDot != -1) {
+            // we have a inner class constructor
+            // shrink the name to just name the constructor
+            methodName = methodName.substring(lastDot + 1);
+        }
+
+        String testSig = methodName + "(" + targs + ")";
+
+        // return testSig.equals(refSignature);
+        if (testSig.equals(refSignature)) {
+            // System.out.println("match!!!: ref = "+refSignature+",
+            // test = "+testSig);
+            return true;
+        } else {
+            // System.out.println("no match: ref = "+refSignature+",
+            // test = "+testSig);
+            return false;
+        }
+    }
+
+    public Level getLevel() {
+        return level;
+    }
+
+    public boolean isHavingProblems() {
+        return havingProblems;
+    }
+
+    public Originator getOriginator() {
+        return originator;
+    }
+
+    TestTargetNew cloneMe(String extraNote) {
+        TestTargetNew anew = new TestTargetNew(this.originator);
+        anew.level = this.level;
+        anew.notes = this.notes;
+        anew.targetMethod = this.targetMethod;
+        anew.readMethodSignature = this.readMethodSignature;
+        anew.readTargetClass = this.readTargetClass;
+
+        // mark indirectly tested method always as green, independent
+        // of the original status (to better estimate workload)
+        // anew.level = Level.COMPLETE;
+        anew.notes = extraNote + (notes != null ? ", " + notes : "");
+        return anew;
+    }
+
+    public ExecutableMemberDoc getTargetMethod() {
+        return targetMethod;
+    }
+
+    /**
+     * @return the class of the testtargetnew which method starts with "!", null
+     *         otherwise
+     */
+    public ClassDoc getTargetClass() {
+        return targetClass;
+    }
+
+    public String getNotes() {
+        return notes;
+    }
+}
diff --git a/tools/test-progress/Android.mk b/tools/test-progress/Android.mk
new file mode 100644
index 0000000..0cdab2e
--- /dev/null
+++ b/tools/test-progress/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := test-progress
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/test-progress | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+include $(LOCAL_PATH)/src/Android.mk
diff --git a/tools/test-progress/etc/test-progress b/tools/test-progress/etc/test-progress
new file mode 100644
index 0000000..5e42cc5
--- /dev/null
+++ b/tools/test-progress/etc/test-progress
@@ -0,0 +1,324 @@
+#!/bin/bash
+#
+# 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+    opt=`expr "$1" : '-J\(.*\)'`
+    javaOpts="${javaOpts} -${opt}"
+    shift
+done
+
+#exec java $javaOpts -jar $libdir/hat.jar "$@"
+
+#######################################################################
+# Original content of invocation script follows. Uses values cleverly
+# deduced by the above code. If you want to use this for a different
+# set of packages, adjust both the list of source directories and the
+# list of packages.
+#######################################################################
+export CLASSES=$progdir/../framework/test-progress.jar
+export INPUT=$ANDROID_BUILD_TOP
+export OUTPUT=$ANDROID_BUILD_TOP/out/target/common/cts/test-progress
+
+if [ "$1" != "" ]; then
+   export OUTPUT=$1
+fi
+
+# These two variables are defined for run the coverage tool for Dalvik or CTS
+DALVIK_SOURCE_PATH="\
+${INPUT}/dalvik/libcore/junit/src/main/java:\
+${INPUT}/dalvik/libcore/icu/src/main/java:\
+${INPUT}/dalvik/libcore/openssl/src/main/java:\
+${INPUT}/dalvik/libcore/android/src/main/java:\
+${INPUT}/dalvik/libcore/annotation/src/main/java:\
+${INPUT}/dalvik/libcore/archive/src/main/java:\
+${INPUT}/dalvik/libcore/auth/src/main/java:\
+${INPUT}/dalvik/libcore/awt/src/main/java:\
+${INPUT}/dalvik/libcore/beans/src/main/java:\
+${INPUT}/dalvik/libcore/concurrent/src/main/java:\
+${INPUT}/dalvik/libcore/crypto/src/main/java:\
+${INPUT}/dalvik/libcore/instrument/src/main/java:\
+${INPUT}/dalvik/libcore/logging/src/main/java:\
+${INPUT}/dalvik/libcore/luni/src/main/java:\
+${INPUT}/dalvik/libcore/luni-kernel/src/main/java:\
+${INPUT}/dalvik/libcore/math/src/main/java:\
+${INPUT}/dalvik/libcore/nio/src/main/java:\
+${INPUT}/dalvik/libcore/nio_char/src/main/java:\
+${INPUT}/dalvik/libcore/prefs/src/main/java:\
+${INPUT}/dalvik/libcore/regex/src/main/java:\
+${INPUT}/dalvik/libcore/security/src/main/java:\
+${INPUT}/dalvik/libcore/security-kernel/src/main/java:\
+${INPUT}/dalvik/libcore/sound/src/main/java:\
+${INPUT}/dalvik/libcore/sql/src/main/java:\
+${INPUT}/dalvik/libcore/text/src/main/java:\
+${INPUT}/dalvik/libcore/xml/src/main/java:\
+${INPUT}/dalvik/libcore/x-net/src/main/java:\
+${INPUT}/java/android:\
+${INPUT}/tests/framework-tests/src:\
+${INPUT}/apps/AndroidTests/src:\
+${INPUT}/apps/TestHarness/tests:\
+${INPUT}/apps/TestHarness/src:\
+${INPUT}/java/tests:\
+${INPUT}/dalvik/libcore/text/src/test/java:\
+${INPUT}/dalvik/libcore/support/src/test/java:\
+${INPUT}/dalvik/libcore/nio_char/src/test/java:\
+${INPUT}/dalvik/libcore/nio/src/test/java:\
+${INPUT}/dalvik/libcore/regex/src/test/java:\
+${INPUT}/dalvik/libcore/xml/src/test/java:\
+${INPUT}/dalvik/libcore/prefs/src/test/java:\
+${INPUT}/dalvik/libcore/x-net/src/test/java:\
+${INPUT}/dalvik/libcore/luni-kernel/src/test/java:\
+${INPUT}/dalvik/libcore/sql/src/test/java:\
+${INPUT}/dalvik/libcore/security/src/test/java:\
+${INPUT}/dalvik/libcore/math/src/test/java:\
+${INPUT}/dalvik/libcore/logging/src/test/java:\
+${INPUT}/dalvik/libcore/luni/src/test/java:\
+${INPUT}/dalvik/libcore/archive/src/test/java:\
+${INPUT}/dalvik/libcore/annotation/src/test/java:\
+${INPUT}/dalvik/libcore/dalvik/src/main/java:\
+${INPUT}/tests/HttpClient:\
+${INPUT}/apps/Mms/tests:\
+ \
+SQLite \
+junit.framework \
+org.apache.harmony.logging.tests.java.util.logging \
+org.apache.harmony.luni.tests.java.io \
+org.apache.harmony.luni.tests.java.lang \
+org.apache.harmony.luni.tests.java.lang.ref \
+org.apache.harmony.luni.tests.java.net \
+org.apache.harmony.luni.tests.java.util \
+org.apache.harmony.nio.tests.java.nio \
+org.apache.harmony.nio.tests.java.nio.channels \
+org.apache.harmony.nio.tests.java.nio.channels.spi \
+org.apache.harmony.nio_char.tests.java.nio.charset \
+org.apache.harmony.nio_char.tests.java.nio.charset.spi \
+org.apache.harmony.prefs.tests.java.util.prefs \
+org.apache.harmony.sql.tests.java.sql \
+org.apache.harmony.sql.tests.javax.sql \
+org.apache.harmony.tests.java.math \
+org.apache.harmony.tests.java.util.regex \
+org.apache.harmony.text.tests.java.text \
+tests.api.android.dalvik \
+tests.api.java.io \
+tests.api.java.lang \
+tests.api.java.lang.ref \
+tests.api.java.lang.reflect \
+tests.api.java.math \
+tests.api.java.net \
+tests.api.java.nio.charset \
+tests.api.java.util \
+tests.api.javax.net \
+tests.api.javax.net.ssl \
+tests.api.javax.xml.parsers \
+tests.java.lang.StrictMath \
+tests.java.sql \
+tests.sql \
+tests.xml \
+java.awt \
+java.awt.color \
+java.awt.event \
+java.awt.font \
+java.awt.geom \
+java.awt.im \
+java.awt.im.spi \
+java.awt.image \
+java.awt.image.renderable \
+javax.imageio \
+javax.imageio.event \
+javax.imageio.metadata \
+javax.imageio.plugins.bmp \
+javax.imageio.plugins.jpeg \
+javax.imageio.spi \
+javax.imageio.stream \
+java.io \
+java.lang \
+java.lang.annotation \
+java.lang.instrument \
+java.lang.ref \
+java.lang.reflect \
+java.math \ 
+java.net \
+java.nio \
+java.nio.channels \
+java.nio.channels.spi \
+java.nio.charset \
+java.nio.charset.spi \
+java.security \
+java.security.acl \
+java.security.cert \
+java.security.interfaces \
+java.security.spec \
+java.sql \
+java.text \
+java.util \
+java.util.concurrent \
+java.util.concurrent.atomic \
+java.util.concurrent.locks \
+java.util.jar \
+java.util.logging \
+java.util.prefs \
+java.util.regex \
+java.util.zip \
+javax.crypto \
+javax.crypto.interfaces \
+javax.crypto.spec \
+javax.net \
+javax.net.ssl \
+javax.security.auth \
+javax.security.auth.callback \
+javax.security.auth.login \
+javax.security.auth.x500 \
+javax.security.cert \
+javax.sound.midi \
+javax.sound.midi.spi \
+javax.sound.sampled \
+javax.sound.sampled.spi \
+javax.sql \
+javax.xml.parsers \
+org.w3c.dom \
+org.xml.sax \
+org.xml.sax.ext \
+org.xml.sax.helpers"
+
+CTS_SOURCE_PATH="\
+${INPUT}/frameworks/base/core/java:\
+${INPUT}/frameworks/base/graphics/java:\
+${INPUT}/frameworks/base/location/java:\
+${INPUT}/frameworks/base/media/java:\
+${INPUT}/frameworks/base/opengl/java:\
+${INPUT}/frameworks/base/sax/java:\
+${INPUT}/frameworks/base/telephony/java:\
+${INPUT}/frameworks/base/wifi/java:\
+${INPUT}/cts/tests/tests/app/src:\
+${INPUT}/cts/tests/tests/content/src:\
+${INPUT}/cts/tests/tests/database/src:\
+${INPUT}/cts/tests/tests/graphics/src:\
+${INPUT}/cts/tests/tests/hardware/src:\
+${INPUT}/cts/tests/tests/location/src:\
+${INPUT}/cts/tests/tests/media/src:\
+${INPUT}/cts/tests/tests/net/src:\
+${INPUT}/cts/tests/tests/os/src:\
+${INPUT}/cts/tests/tests/provider/src:\
+${INPUT}/cts/tests/tests/preference/src:\
+${INPUT}/cts/tests/tests/telephony/src:\
+${INPUT}/cts/tests/tests/text/src:\
+${INPUT}/cts/tests/tests/util/src:\
+${INPUT}/cts/tests/tests/view/src:\
+${INPUT}/cts/tests/tests/widget/src:\
+${INPUT}/cts/tests/src:\
+${INPUT}/frameworks/base/test-runner:\
+${INPUT}/dalvik/libcore/dalvik/src/main/java:\
+${INPUT}/dalvik/libcore/xml/src/main/java:\
+${INPUT}/dalvik/libcore/junit/src/main/java:\
+ \
+android.app \
+android.app.cts \
+android.content \
+android.content.cts \
+android.content.pm \
+android.content.pm.cts \
+android.content.res \
+android.content.res.cts \
+android.location \
+android.location.cts \
+android.os \
+android.os.cts \
+android.text \
+android.text.cts \
+android.text.method \
+android.text.method.cts \
+android.text.style \
+android.text.style.cts \
+android.text.util \
+android.text.util.cts \
+android.util \
+android.util.cts \
+android.view \
+android.view.cts \
+android.view.animation \
+android.view.animation.cts \
+android.view.inputmethod \
+android.view.inputmethod.cts \
+android.widget \
+android.widget.cts \
+android.hardware \
+android.hardware.cts \
+android.net \
+android.net.cts \
+android.net.http \
+android.net.http.cts \
+android.net.wifi \
+android.net.wifi.cts \
+android.provider \
+android.provider.cts \
+android.bluetooth \
+android.bluetooth.cts \
+android.database \
+android.database.cts \
+android.database.sqlite \
+android.database.sqlite.cts \
+android.graphics \
+android.graphics.cts \
+android.graphics.drawable \
+android.graphics.drawable.cts \
+android.graphics.drawable.shapes \
+android.graphics.drawable.shapes.cts \
+android.media \
+android.media.cts \
+android.telephony \
+android.telephony.cts \
+android.telephony.gsm \
+android.telephony.gsm.cts \
+android.accounts \
+android.accounts.cts \
+android.opengl \
+android.opengl.cts \
+android.preference \
+android.preference.cts \
+android.sax \
+android.sax.cts \
+android.security \
+android.security.cts \
+android.speech.srec \
+android.speech.srec.cts \
+android.webkit \
+android.webkit.cts \
+android.webkit.gears \
+android.webkit.gears.cts"
+
+javadoc -J-Xmx512m -docletpath $CLASSES -doclet TestCoverageDoclet -d $OUTPUT -sourcepath ${CTS_SOURCE_PATH}
diff --git a/tools/test-progress/src/Android.mk b/tools/test-progress/src/Android.mk
new file mode 100644
index 0000000..0428e48
--- /dev/null
+++ b/tools/test-progress/src/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+#LOCAL_MODULE_TAGS := cts
+
+LOCAL_SRC_FILES := \
+	TestCoverageDoclet.java
+
+LOCAL_CLASSPATH := \
+	$(HOST_JDK_TOOLS_JAR)
+
+LOCAL_MODULE:= test-progress
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/test-progress/src/TestCoverageDoclet.java b/tools/test-progress/src/TestCoverageDoclet.java
new file mode 100644
index 0000000..c6bb7d0
--- /dev/null
+++ b/tools/test-progress/src/TestCoverageDoclet.java
@@ -0,0 +1,823 @@
+/*
+ * 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;
+    }
+
+}