diff --git a/core/droiddoc.mk b/core/droiddoc.mk index 30bd918d11..03ffa55f83 100644 --- a/core/droiddoc.mk +++ b/core/droiddoc.mk @@ -127,7 +127,7 @@ droiddoc := \ $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX) \ $(HOST_OUT_SHARED_LIBRARIES)/libclearsilver-jni$(HOST_JNILIB_SUFFIX) -$(full_target): PRIVATE_DOCLETPATH := $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX) +$(full_target): PRIVATE_DOCLETPATH := $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/apicheck$(COMMON_JAVA_PACKAGE_SUFFIX) $(full_target): PRIVATE_CURRENT_BUILD := -hdf page.build $(BUILD_ID)-$(BUILD_NUMBER) $(full_target): PRIVATE_CURRENT_TIME := -hdf page.now "$(shell date "+%d %b %Y %k:%M")" $(full_target): PRIVATE_TEMPLATE_DIR := $(LOCAL_DROIDDOC_TEMPLATE_DIR) diff --git a/tools/apicheck/src/com/android/apicheck/ApiCheck.java b/tools/apicheck/src/com/android/apicheck/ApiCheck.java index f78117cd52..20a98ce5ed 100644 --- a/tools/apicheck/src/com/android/apicheck/ApiCheck.java +++ b/tools/apicheck/src/com/android/apicheck/ApiCheck.java @@ -20,7 +20,6 @@ import org.xml.sax.*; import org.xml.sax.helpers.*; import java.io.*; import java.util.ArrayList; -import java.util.Arrays; import java.util.Stack; public class ApiCheck { @@ -83,62 +82,62 @@ public class ApiCheck { } } - String xmlFileName = args.get(0); - String xmlFileNameNew = args.get(1); - XMLReader xmlreader = null; - try { - // parse the XML files into our data structures - xmlreader = XMLReaderFactory.createXMLReader(); - ApiCheck acheck = new ApiCheck(); - MakeHandler handler = acheck.new MakeHandler(); - xmlreader.setContentHandler(handler); - xmlreader.setErrorHandler(handler); - FileReader filereader = new FileReader(xmlFileName); - xmlreader.parse(new InputSource(filereader)); - FileReader filereaderNew = new FileReader(xmlFileNameNew); - xmlreader.parse(new InputSource(filereaderNew)); + ApiCheck acheck = new ApiCheck(); - // establish the superclass relationships - handler.getOldApi().resolveSuperclasses(); - handler.getNewApi().resolveSuperclasses(); - - // finally, run the consistency check - handler.getOldApi().isConsistent(handler.getNewApi()); + ApiInfo oldApi = acheck.parseApi(args.get(0)); + ApiInfo newApi = acheck.parseApi(args.get(1)); - } catch (SAXParseException e) { - Errors.error(Errors.PARSE_ERROR, - new SourcePositionInfo(xmlFileName, e.getLineNumber(), 0), - e.getMessage()); - } catch (Exception e) { - e.printStackTrace(); - Errors.error(Errors.PARSE_ERROR, - new SourcePositionInfo(xmlFileName, 0, 0), - e.getMessage()); - } + // only run the consistency check if we haven't had XML parse errors + if (!Errors.hadError) { + oldApi.isConsistent(newApi); + } Errors.printErrors(); System.exit(Errors.hadError ? 1 : 0); } - private class MakeHandler extends DefaultHandler { + public ApiInfo parseApi(String xmlFile) { + FileReader fileReader = null; + try { + XMLReader xmlreader = XMLReaderFactory.createXMLReader(); + MakeHandler handler = new MakeHandler(); + xmlreader.setContentHandler(handler); + xmlreader.setErrorHandler(handler); + fileReader = new FileReader(xmlFile); + xmlreader.parse(new InputSource(fileReader)); + ApiInfo apiInfo = handler.getApi(); + apiInfo.resolveSuperclasses(); + return apiInfo; + } catch (SAXParseException e) { + Errors.error(Errors.PARSE_ERROR, + new SourcePositionInfo(xmlFile, e.getLineNumber(), 0), + e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + Errors.error(Errors.PARSE_ERROR, + new SourcePositionInfo(xmlFile, 0, 0), e.getMessage()); + } finally { + if (fileReader != null) { + try { + fileReader.close(); + } catch (IOException ignored) {} + } + } + return null; + } + + private static class MakeHandler extends DefaultHandler { - private Integer mWarningCount; - private ApiInfo mOriginalApi; - private ApiInfo mNewApi; - private boolean mOldApi; + private ApiInfo mApi; private PackageInfo mCurrentPackage; private ClassInfo mCurrentClass; private AbstractMethodInfo mCurrentMethod; - private ConstructorInfo mCurrentConstructor; private Stack mClassScope = new Stack(); - - + + public MakeHandler() { super(); - mOriginalApi = new ApiInfo(); - mNewApi = new ApiInfo(); - mOldApi = true; - + mApi = new ApiInfo(); } public void startElement(String uri, String localName, String qName, @@ -229,25 +228,11 @@ public class ApiCheck { mCurrentPackage.addClass(mCurrentClass); mCurrentClass = mClassScope.pop(); } else if (qName.equals("package")){ - if (mOldApi) { - mOriginalApi.addPackage(mCurrentPackage); - } else { - mNewApi.addPackage(mCurrentPackage); - } + mApi.addPackage(mCurrentPackage); } } - public void endDocument() { - mOldApi = !mOldApi; - } - - public ApiInfo getOldApi() { - return mOriginalApi; - } - - public ApiInfo getNewApi() { - return mNewApi; - } - - + public ApiInfo getApi() { + return mApi; } + } } diff --git a/tools/apicheck/src/com/android/apicheck/ClassInfo.java b/tools/apicheck/src/com/android/apicheck/ClassInfo.java index 4bbf78b19a..5405ad2c07 100644 --- a/tools/apicheck/src/com/android/apicheck/ClassInfo.java +++ b/tools/apicheck/src/com/android/apicheck/ClassInfo.java @@ -187,8 +187,8 @@ public class ClassInfo { } for (FieldInfo mInfo : mFields.values()) { - if (cl.mFields.containsKey(mInfo.qualifiedName())) { - if (!mInfo.isConsistent(cl.mFields.get(mInfo.qualifiedName()))) { + if (cl.mFields.containsKey(mInfo.name())) { + if (!mInfo.isConsistent(cl.mFields.get(mInfo.name()))) { consistent = false; } } else { @@ -267,7 +267,7 @@ public class ClassInfo { } public void addField(FieldInfo fInfo) { - mFields.put(fInfo.qualifiedName(), fInfo); + mFields.put(fInfo.name(), fInfo); } @@ -279,4 +279,26 @@ public class ClassInfo { return mExistsInBoth; } + public Map allConstructors() { + return mConstructors; + } + + public Map allFields() { + return mFields; + } + + public Map allMethods() { + return mMethods; + } + + /** + * Returns the class hierarchy for this class, starting with this class. + */ + public Iterable hierarchy() { + List result = new ArrayList(4); + for (ClassInfo c = this; c != null; c = c.mSuperClass) { + result.add(c); + } + return result; + } } diff --git a/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java b/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java index 57d7617167..f36c7cd0be 100644 --- a/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java +++ b/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java @@ -55,11 +55,12 @@ public class ConstructorInfo implements AbstractMethodInfo { } public String getHashableName() { - String returnString = qualifiedName(); + StringBuilder result = new StringBuilder(); + result.append(name()); for (ParameterInfo pInfo : mParameters) { - returnString += ":" + pInfo.getType(); + result.append(":").append(pInfo.getType()); } - return returnString; + return result.toString(); } public boolean isInBoth() { diff --git a/tools/apicheck/src/com/android/apicheck/MethodInfo.java b/tools/apicheck/src/com/android/apicheck/MethodInfo.java index 86e20deed6..e4e45376ee 100644 --- a/tools/apicheck/src/com/android/apicheck/MethodInfo.java +++ b/tools/apicheck/src/com/android/apicheck/MethodInfo.java @@ -195,7 +195,7 @@ public class MethodInfo implements AbstractMethodInfo { } public String getHashableName() { - return qualifiedName() + getParameterHash(); + return name() + getParameterHash(); } public String getSignature() { diff --git a/tools/droiddoc/src/Android.mk b/tools/droiddoc/src/Android.mk index bf404b7beb..30270b566c 100644 --- a/tools/droiddoc/src/Android.mk +++ b/tools/droiddoc/src/Android.mk @@ -47,6 +47,7 @@ LOCAL_SRC_FILES := \ SampleTagInfo.java \ Scoped.java \ SeeTagInfo.java \ + SinceTagger.java \ Sorter.java \ SourcePositionInfo.java \ Stubs.java \ @@ -57,6 +58,7 @@ LOCAL_SRC_FILES := \ TypeInfo.java LOCAL_JAVA_LIBRARIES := \ + apicheck \ clearsilver LOCAL_CLASSPATH := \ diff --git a/tools/droiddoc/src/ClassInfo.java b/tools/droiddoc/src/ClassInfo.java index 2fd65e0c7f..094159583c 100644 --- a/tools/droiddoc/src/ClassInfo.java +++ b/tools/droiddoc/src/ClassInfo.java @@ -907,6 +907,7 @@ public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Sco 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 2530dc2497..3abb367419 100644 --- a/tools/droiddoc/src/DocInfo.java +++ b/tools/droiddoc/src/DocInfo.java @@ -51,8 +51,17 @@ public abstract class DocInfo 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 f664c416df..b487629a5e 100644 --- a/tools/droiddoc/src/DroidDoc.java +++ b/tools/droiddoc/src/DroidDoc.java @@ -97,6 +97,7 @@ public class DroidDoc String apiFile = null; String debugStubsFile = ""; HashSet stubPackages = null; + SinceTagger sinceTagger = new SinceTagger(); root = r; @@ -186,6 +187,9 @@ public class DroidDoc 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 @@ public class DroidDoc 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 @@ public class DroidDoc if (stubsDir != null) { Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages); } - + if (sdkValuePath != null) { writeSdkValues(sdkValuePath); } @@ -394,9 +401,12 @@ public class DroidDoc 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 @@ public class DroidDoc 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 dfeac8867c..95439f1c00 100644 --- a/tools/droiddoc/src/Errors.java +++ b/tools/droiddoc/src/Errors.java @@ -114,6 +114,7 @@ public class Errors 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 @@ public class Errors 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 536d798483..1c975e4da0 100644 --- a/tools/droiddoc/src/FieldInfo.java +++ b/tools/droiddoc/src/FieldInfo.java @@ -223,6 +223,7 @@ public class FieldInfo extends MemberInfo 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 ca306653b8..bded88baa4 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 @@ public class MethodInfo extends MemberInfo 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 @@ public class MethodInfo extends MemberInfo 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 0000000000..a34814c00e --- /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: + *
+ *   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(...);
+ * 
+ */ +public class SinceTagger { + + private final Map xmlToName + = new LinkedHashMap(); + + /** + * 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 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 10778860c4..41d34dde28 100644 --- a/tools/droiddoc/templates/class.cs +++ b/tools/droiddoc/templates/class.cs @@ -180,6 +180,7 @@ Summary: + diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs index a2306a0895..1ca2f8b1e8 100644 --- a/tools/droiddoc/templates/macros.cs +++ b/tools/droiddoc/templates/macros.cs @@ -115,9 +115,15 @@ def:see_also_tags(also) ?> + +
+
Since
+
+

+ diff --git a/tools/droiddoc/test/stubs/func.sh b/tools/droiddoc/test/stubs/func.sh index 1ad4bd5115..ea4fe75108 100644 --- a/tools/droiddoc/test/stubs/func.sh +++ b/tools/droiddoc/test/stubs/func.sh @@ -26,21 +26,22 @@ function build_stubs() 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 f237a7d924..2ea15a69ec 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