import org.xml.sax.helpers.*;
import java.io.*;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Stack;
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<ClassInfo> mClassScope = new Stack<ClassInfo>();
-
-
+
+
public MakeHandler() {
super();
- mOriginalApi = new ApiInfo();
- mNewApi = new ApiInfo();
- mOldApi = true;
-
+ mApi = new ApiInfo();
}
public void startElement(String uri, String localName, String qName,
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;
}
+ }
}
}
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 {
}
public void addField(FieldInfo fInfo) {
- mFields.put(fInfo.qualifiedName(), fInfo);
+ mFields.put(fInfo.name(), fInfo);
}
return mExistsInBoth;
}
+ public Map<String, ConstructorInfo> allConstructors() {
+ return mConstructors;
+ }
+
+ public Map<String, FieldInfo> allFields() {
+ return mFields;
+ }
+
+ public Map<String, MethodInfo> allMethods() {
+ return mMethods;
+ }
+
+ /**
+ * Returns the class hierarchy for this class, starting with this class.
+ */
+ public Iterable<ClassInfo> hierarchy() {
+ List<ClassInfo> result = new ArrayList<ClassInfo>(4);
+ for (ClassInfo c = this; c != null; c = c.mSuperClass) {
+ result.add(c);
+ }
+ return result;
+ }
}
}
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() {
}
public String getHashableName() {
- return qualifiedName() + getParameterHash();
+ return name() + getParameterHash();
}
public String getSignature() {
SampleTagInfo.java \
Scoped.java \
SeeTagInfo.java \
+ SinceTagger.java \
Sorter.java \
SourcePositionInfo.java \
Stubs.java \
TypeInfo.java
LOCAL_JAVA_LIBRARIES := \
+ apicheck \
clearsilver
LOCAL_CLASSPATH := \
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
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;
}
String apiFile = null;
String debugStubsFile = "";
HashSet<String> stubPackages = null;
+ SinceTagger sinceTagger = new SinceTagger();
root = r;
else if (a[0].equals("-nodocs")) {
noDocs = true;
}
+ else if (a[0].equals("-since")) {
+ sinceTagger.addVersion(a[1], a[2]);
+ }
}
// read some prefs from the template
if (!noDocs) {
long startTime = System.nanoTime();
+ // Apply @since tags from the XML file
+ sinceTagger.tagAll(Converter.rootClasses());
+
// Files for proofreading
if (proofreadFile != null) {
Proofread.initProofread(proofreadFile);
if (stubsDir != null) {
Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
}
-
+
Errors.printErrors();
return !Errors.hadError;
}
if (option.equals("-nodocs")) {
return 1;
}
+ if (option.equals("-since")) {
+ return 3;
+ }
return 0;
}
-
+
public static boolean validOptions(String[][] options, DocErrorReporter r)
{
for (String[] a: options) {
String name = pkg.name();
data.setValue("package.name", name);
+ data.setValue("package.since", pkg.getSince());
data.setValue("package.descr", "...description...");
makeClassListHDF(data, "package.interfaces",
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,
HIDDEN_SUPERCLASS,
DEPRECATED,
IO_ERROR,
+ NO_SINCE_DATA,
};
public static boolean setErrorLevel(int code, int level) {
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()) {
*/
import org.clearsilver.HDF;
-import org.clearsilver.CS;
+
import java.util.*;
-import java.io.*;
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;
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());
--- /dev/null
+// 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());
+ }
+ }
+ }
+ }
+}
<?cs /if ?>
<?cs call:see_also_tags(class.seeAlso) ?>
+<?cs call:since_tags(class) ?>
</div><!-- jd-descr -->
/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
</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
<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 -->
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 \
-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 \
# 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