Adding version information to DroidDoc. Rather than pulling the version from
@since tags in the code, it's pulled from the API XML files also used by
apicheck.

The code now reads the apicheck XML, and applies it's versions to the DroidDoc
class models. The models output the version to HDF, and that's picked up by
the CS templates.

The clearsilver templates will be changed to be pretty in a follow up change.

Conflicts:
	tools/droiddoc/src/DroidDoc.java
diff --git a/tools/droiddoc/src/Android.mk b/tools/droiddoc/src/Android.mk
index bf404b7..30270b5 100644
--- a/tools/droiddoc/src/Android.mk
+++ b/tools/droiddoc/src/Android.mk
@@ -47,6 +47,7 @@
 	SampleTagInfo.java \
     Scoped.java \
 	SeeTagInfo.java \
+	SinceTagger.java \
 	Sorter.java \
 	SourcePositionInfo.java \
     Stubs.java \
@@ -57,6 +58,7 @@
 	TypeInfo.java
 
 LOCAL_JAVA_LIBRARIES := \
+	apicheck \
 	clearsilver
 
 LOCAL_CLASSPATH := \
diff --git a/tools/droiddoc/src/ClassInfo.java b/tools/droiddoc/src/ClassInfo.java
index 2fd65e0..0941595 100644
--- a/tools/droiddoc/src/ClassInfo.java
+++ b/tools/droiddoc/src/ClassInfo.java
@@ -907,6 +907,7 @@
         if (kind != null) {
             data.setValue("class.kind", kind);
         }
+        data.setValue("class.since", getSince());
 
         // the containing package -- note that this can be passed to type_link,
         // but it also contains the list of all of the packages
diff --git a/tools/droiddoc/src/DocInfo.java b/tools/droiddoc/src/DocInfo.java
index 2530dc2..3abb367 100644
--- a/tools/droiddoc/src/DocInfo.java
+++ b/tools/droiddoc/src/DocInfo.java
@@ -51,8 +51,17 @@
 
     public abstract ContainerInfo parent();
 
+    public void setSince(String since) {
+        mSince = since;
+    }
+
+    public String getSince() {
+        return mSince;
+    }
+
     private String mRawCommentText;
     Comment mComment;
     SourcePositionInfo mPosition;
+    private String mSince;
 }
 
diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java
index f664c41..b487629 100644
--- a/tools/droiddoc/src/DroidDoc.java
+++ b/tools/droiddoc/src/DroidDoc.java
@@ -97,6 +97,7 @@
         String apiFile = null;
         String debugStubsFile = "";
         HashSet<String> stubPackages = null;
+        SinceTagger sinceTagger = new SinceTagger();
 
         root = r;
 
@@ -186,6 +187,9 @@
                 apiXML = true;
                 apiFile = a[1];
             }
+            else if (a[0].equals("-since")) {
+                sinceTagger.addVersion(a[1], a[2]);
+            }
         }
 
         // read some prefs from the template
@@ -204,6 +208,9 @@
             TodoFile.writeTodoFile(todoFile);
         }
 
+        // Apply @since tags from the XML file
+        sinceTagger.tagAll(Converter.rootClasses());
+
         // HTML Pages
         if (ClearPage.htmlDir != null) {
             writeHTMLPages();
@@ -244,7 +251,7 @@
         if (stubsDir != null) {
             Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
         }
-        
+
         if (sdkValuePath != null) {
             writeSdkValues(sdkValuePath);
         }
@@ -394,9 +401,12 @@
         if (option.equals("-apixml")) {
             return 2;
         }
+        if (option.equals("-since")) {
+            return 3;
+        }
         return 0;
     }
-    
+
     public static boolean validOptions(String[][] options, DocErrorReporter r)
     {
         for (String[] a: options) {
@@ -762,6 +772,7 @@
         String name = pkg.name();
 
         data.setValue("package.name", name);
+        data.setValue("package.since", pkg.getSince());
         data.setValue("package.descr", "...description...");
 
         makeClassListHDF(data, "package.interfaces", 
diff --git a/tools/droiddoc/src/Errors.java b/tools/droiddoc/src/Errors.java
index dfeac88..95439f1 100644
--- a/tools/droiddoc/src/Errors.java
+++ b/tools/droiddoc/src/Errors.java
@@ -114,6 +114,7 @@
     public static Error DEPRECATION_MISMATCH = new Error(13, WARNING);
     public static Error MISSING_COMMENT = new Error(14, WARNING);
     public static Error IO_ERROR = new Error(15, HIDDEN);
+    public static Error NO_SINCE_DATA = new Error(16, WARNING);
 
     public static Error[] ERRORS = {
             UNRESOLVED_LINK,
@@ -129,6 +130,7 @@
             HIDDEN_SUPERCLASS,
             DEPRECATED,
             IO_ERROR,
+            NO_SINCE_DATA,
         };
 
     public static boolean setErrorLevel(int code, int level) {
diff --git a/tools/droiddoc/src/FieldInfo.java b/tools/droiddoc/src/FieldInfo.java
index 536d798..1c975e4 100644
--- a/tools/droiddoc/src/FieldInfo.java
+++ b/tools/droiddoc/src/FieldInfo.java
@@ -223,6 +223,7 @@
         TagInfo.makeHDF(data, base + ".descr", inlineTags());
         TagInfo.makeHDF(data, base + ".deprecated", comment().deprecatedTags());
         TagInfo.makeHDF(data, base + ".seeAlso", comment().seeTags());
+        data.setValue(base + ".since", getSince());
         data.setValue(base + ".final", isFinal() ? "final" : "");
         data.setValue(base + ".static", isStatic() ? "static" : "");
         if (isPublic()) {
diff --git a/tools/droiddoc/src/MethodInfo.java b/tools/droiddoc/src/MethodInfo.java
index ca30665..bded88b 100644
--- a/tools/droiddoc/src/MethodInfo.java
+++ b/tools/droiddoc/src/MethodInfo.java
@@ -15,9 +15,8 @@
  */
 
 import org.clearsilver.HDF;
-import org.clearsilver.CS;
+
 import java.util.*;
-import java.io.*;
 
 public class MethodInfo extends MemberInfo
 {
@@ -357,6 +356,19 @@
         return s;
     }
 
+    /**
+     * Returns a name consistent with the {@link
+     * com.android.apicheck.MethodInfo#getHashableName()}.
+     */
+    public String getHashableName() {
+        StringBuilder result = new StringBuilder();
+        result.append(name());
+        for (ParameterInfo pInfo : mParameters) {
+            result.append(":").append(pInfo.type().fullName());
+        }
+        return result.toString();
+    }
+
     private boolean inList(ClassInfo item, ThrowsTagInfo[] list)
     {
         int len = list.length;
@@ -545,6 +557,7 @@
         TagInfo.makeHDF(data, base + ".descr", inlineTags());
         TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
         TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
+        data.setValue(base + ".since", getSince());
         ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
         AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
         ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
diff --git a/tools/droiddoc/src/SinceTagger.java b/tools/droiddoc/src/SinceTagger.java
new file mode 100644
index 0000000..a34814c
--- /dev/null
+++ b/tools/droiddoc/src/SinceTagger.java
@@ -0,0 +1,182 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+import com.android.apicheck.*;
+
+import java.util.*;
+
+/**
+ * Applies version information to the DroidDoc class model from apicheck XML
+ * files. Sample usage:
+ * <pre>
+ *   ClassInfo[] classInfos = ...
+ *
+ *   SinceTagger sinceTagger = new SinceTagger()
+ *   sinceTagger.addVersion("frameworks/base/api/1.xml", "Android 1.0")
+ *   sinceTagger.addVersion("frameworks/base/api/2.xml", "Android 1.5")
+ *   sinceTagger.tagAll(...);
+ * </pre>
+ */
+public class SinceTagger {
+
+    private final Map<String, String> xmlToName
+            = new LinkedHashMap<String, String>();
+
+    /**
+     * Specifies the apicheck XML file and the API version it holds. Calls to
+     * this method should be called in order from oldest version to newest.
+     */
+    public void addVersion(String file, String name) {
+        xmlToName.put(file, name);
+    }
+
+    public void tagAll(ClassInfo[] classDocs) {
+        // read through the XML files in order, applying their since information
+        // to the Javadoc models
+        for (Map.Entry<String, String> versionSpec : xmlToName.entrySet()) {
+            String xmlFile = versionSpec.getKey();
+            String versionName = versionSpec.getValue();
+            ApiInfo specApi = new ApiCheck().parseApi(xmlFile);
+
+            applyVersionsFromSpec(versionName, specApi, classDocs);
+        }
+
+        if (!xmlToName.isEmpty()) {
+            warnForMissingVersions(classDocs);
+        }
+    }
+
+    /**
+     * Applies the version information to {@code classDocs} where not already
+     * present.
+     *
+     * @param versionName the version name
+     * @param specApi the spec for this version. If a symbol is in this spec, it
+     *      was present in the named version
+     * @param classDocs the doc model to update
+     */
+    private void applyVersionsFromSpec(String versionName,
+            ApiInfo specApi, ClassInfo[] classDocs) {
+        for (ClassInfo classDoc : classDocs) {
+            com.android.apicheck.PackageInfo packageSpec
+                    = specApi.getPackages().get(classDoc.containingPackage().name());
+
+            if (packageSpec == null) {
+                continue;
+            }
+
+            com.android.apicheck.ClassInfo classSpec
+                    = packageSpec.allClasses().get(classDoc.name());
+
+            if (classSpec == null) {
+                continue;
+            }
+
+            versionPackage(versionName, classDoc.containingPackage());
+            versionClass(versionName, classDoc);
+            versionConstructors(versionName, classSpec, classDoc);
+            versionFields(versionName, classSpec, classDoc);
+            versionMethods(versionName, classSpec, classDoc);
+        }
+    }
+
+    /**
+     * Applies version information to {@code doc} where not already present.
+     */
+    private void versionPackage(String versionName, PackageInfo doc) {
+        if (doc.getSince() == null) {
+            doc.setSince(versionName);
+        }
+    }
+
+    /**
+     * Applies version information to {@code doc} where not already present.
+     */
+    private void versionClass(String versionName, ClassInfo doc) {
+        if (doc.getSince() == null) {
+            doc.setSince(versionName);
+        }
+    }
+
+    /**
+     * Applies version information from {@code spec} to {@code doc} where not
+     * already present.
+     */
+    private void versionConstructors(String versionName,
+            com.android.apicheck.ClassInfo spec, ClassInfo doc) {
+        for (MethodInfo constructor : doc.constructors()) {
+            if (constructor.getSince() == null
+                    && spec.allConstructors().containsKey(constructor.getHashableName())) {
+                constructor.setSince(versionName);
+            }
+        }
+    }
+
+    /**
+     * Applies version information from {@code spec} to {@code doc} where not
+     * already present.
+     */
+    private void versionFields(String versionName,
+            com.android.apicheck.ClassInfo spec, ClassInfo doc) {
+        for (FieldInfo field : doc.fields()) {
+            if (field.getSince() == null
+                    && spec.allFields().containsKey(field.name())) {
+                field.setSince(versionName);
+            }
+        }
+    }
+
+    /**
+     * Applies version information from {@code spec} to {@code doc} where not
+     * already present.
+     */
+    private void versionMethods(String versionName,
+            com.android.apicheck.ClassInfo spec, ClassInfo doc) {
+        for (MethodInfo method : doc.methods()) {
+            if (method.getSince() != null) {
+                continue;
+            }
+
+            for (com.android.apicheck.ClassInfo superclass : spec.hierarchy()) {
+                if (superclass.allMethods().containsKey(method.getHashableName())) {
+                    method.setSince(versionName);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Warns if any symbols are missing version information. When configured
+     * properly, this will yield zero warnings because {@code apicheck}
+     * guarantees that all symbols are present in the most recent API.
+     */
+    private void warnForMissingVersions(ClassInfo[] classDocs) {
+        for (ClassInfo claz : classDocs) {
+            if (claz.getSince() == null) {
+                Errors.error(Errors.NO_SINCE_DATA, claz.position(),
+                        "XML missing class " + claz.qualifiedName());
+            }
+            for (FieldInfo field : claz.fields()) {
+                if (field.getSince() == null) {
+                    Errors.error(Errors.NO_SINCE_DATA, field.position(),
+                            "XML missing field "
+                                    + claz.qualifiedName() + "#" + field .name());
+                }
+            }
+            for (MethodInfo constructor : claz.constructors()) {
+                if (constructor.getSince() == null) {
+                    Errors.error(Errors.NO_SINCE_DATA, constructor.position(),
+                            "XML missing constructor "
+                                    + claz.qualifiedName() + "#" + constructor.getHashableName());
+                }
+            }
+            for (MethodInfo method : claz.methods()) {
+                if (method.getSince() == null) {
+                    Errors.error(Errors.NO_SINCE_DATA, method.position(),
+                            "XML missing method "
+                                    + claz.qualifiedName() + "#" + method .getHashableName());
+                }
+            }
+        }
+    }
+}
diff --git a/tools/droiddoc/templates/class.cs b/tools/droiddoc/templates/class.cs
index 1077886..41d34dd 100644
--- a/tools/droiddoc/templates/class.cs
+++ b/tools/droiddoc/templates/class.cs
@@ -180,6 +180,7 @@
 <?cs /if ?>
 
 <?cs call:see_also_tags(class.seeAlso) ?>
+<?cs call:since_tags(class) ?>
 
 </div><!-- jd-descr -->
 
diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs
index a2306a0..1ca2f8b 100644
--- a/tools/droiddoc/templates/macros.cs
+++ b/tools/droiddoc/templates/macros.cs
@@ -115,9 +115,15 @@
   /if ?>
 <?cs /def ?>
 
+<?cs # print the Since: section ?><?cs
+def:since_tags(obj) ?>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Since <?cs var:obj.since ?></h5>
+  </div>
+<?cs /def ?>
 
 <?cs # Print the long-form description for something.
-       Uses the following fields: deprecated descr seeAlso ?><?cs 
+       Uses the following fields: deprecated descr seeAlso since ?><?cs
 def:description(obj) ?><?cs 
   call:deprecated_warning(obj) ?>
   <div class="jd-tagdata jd-tagdescr"><p><?cs call:tag_list(obj.descr) ?></p></div><?cs 
@@ -165,6 +171,7 @@
   </div><?cs 
   /if ?><?cs 
   call:see_also_tags(obj.seeAlso) ?><?cs 
+  call:since_tags(obj) ?><?cs
 /def ?>
 
 <?cs # A table of links to classes with descriptions, as in a package file or the nested classes ?><?cs
diff --git a/tools/droiddoc/templates/package-descr.cs b/tools/droiddoc/templates/package-descr.cs
index 385ce23..112db4b 100644
--- a/tools/droiddoc/templates/package-descr.cs
+++ b/tools/droiddoc/templates/package-descr.cs
@@ -21,6 +21,7 @@
 <div class="jd-descr">
 <p><?cs call:tag_list(package.descr) ?></p>
 </div>
+<?cs call:since_tags(package) ?>
 
 <?cs include:"footer.cs" ?>
 </div><!-- end jd-content -->
diff --git a/tools/droiddoc/test/stubs/func.sh b/tools/droiddoc/test/stubs/func.sh
index 1ad4bd5..ea4fe75 100644
--- a/tools/droiddoc/test/stubs/func.sh
+++ b/tools/droiddoc/test/stubs/func.sh
@@ -26,21 +26,22 @@
     STUBS_DIR=$3
 
     OBJ_DIR=out/stubs/$ID
+    PLATFORM=${HOST_OS}-${HOST_ARCH}
 
     rm -rf $OBJ_DIR &> /dev/null
     mkdir -p $OBJ_DIR
 
     find $SRC_DIR -name '*.java' > $OBJ_DIR/javadoc-src-list
     ( \
-        LD_LIBRARY_PATH=out/host/darwin-x86/lib \
+        LD_LIBRARY_PATH=out/host/$PLATFORM/lib \
         javadoc \
             \@$OBJ_DIR/javadoc-src-list \
             -J-Xmx512m \
-            -J-Djava.library.path=out/host/darwin-x86/lib \
+            -J-Djava.library.path=out/host/$PLATFORM/lib \
              \
             -quiet \
             -doclet DroidDoc \
-            -docletpath out/host/darwin-x86/framework/clearsilver.jar:out/host/darwin-x86/framework/droiddoc.jar \
+            -docletpath out/host/$PLATFORM/framework/clearsilver.jar:out/host/$PLATFORM/framework/droiddoc.jar:out/host/$PLATFORM/framework/apicheck.jar \
             -templatedir tools/droiddoc/templates \
             -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
             -sourcepath $SRC_DIR:out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
diff --git a/tools/droiddoc/test/stubs/run.sh b/tools/droiddoc/test/stubs/run.sh
index f237a7d..2ea15a6 100755
--- a/tools/droiddoc/test/stubs/run.sh
+++ b/tools/droiddoc/test/stubs/run.sh
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-DIR=tools/droiddoc/test/stubs
+DIR=build/tools/droiddoc/test/stubs
 
 pushd $TOP