diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:42 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:42 -0800 |
commit | 88b607994a148f4af5bffee163e39ce8296750c6 (patch) | |
tree | fa249ff843e976cf034f2029437d3362a8396321 /tools/droiddoc/src/Stubs.java | |
parent | 05806d7af62e07c6225b2e7103a1b115ecf6c9ad (diff) | |
download | build-88b607994a148f4af5bffee163e39ce8296750c6.tar.gz build-88b607994a148f4af5bffee163e39ce8296750c6.tar.bz2 build-88b607994a148f4af5bffee163e39ce8296750c6.zip |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'tools/droiddoc/src/Stubs.java')
-rw-r--r-- | tools/droiddoc/src/Stubs.java | 988 |
1 files changed, 988 insertions, 0 deletions
diff --git a/tools/droiddoc/src/Stubs.java b/tools/droiddoc/src/Stubs.java new file mode 100644 index 000000000..e1ec76a30 --- /dev/null +++ b/tools/droiddoc/src/Stubs.java @@ -0,0 +1,988 @@ +/* + * 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 java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; +import java.util.Comparator; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; + +public class Stubs { + private static HashSet<ClassInfo> notStrippable; + public static void writeStubs(String stubsDir, Boolean writeXML, String xmlFile, + HashSet<String> stubPackages) { + // figure out which classes we need + notStrippable = new HashSet(); + ClassInfo[] all = Converter.allClasses(); + File xml = new File(xmlFile); + xml.getParentFile().mkdirs(); + PrintStream xmlWriter = null; + if (writeXML) { + try { + xmlWriter = new PrintStream(xml); + } catch (FileNotFoundException e) { + Errors.error(Errors.IO_ERROR, new SourcePositionInfo(xmlFile, 0, 0), + "Cannot open file for write."); + } + } + // If a class is public or protected, not hidden, and marked as included, + // then we can't strip it + for (ClassInfo cl: all) { + if (cl.checkLevel() && cl.isIncluded()) { + cantStripThis(cl, notStrippable, "0:0"); + } + } + + // complain about anything that looks includeable but is not supposed to + // be written, e.g. hidden things + for (ClassInfo cl: notStrippable) { + if (!cl.isHidden()) { + MethodInfo[] methods = cl.selfMethods(); + for (MethodInfo m: methods) { + if (m.isHidden()) { + Errors.error(Errors.UNAVAILABLE_SYMBOL, + m.position(), "Reference to hidden method " + + m.name()); + } else if (m.isDeprecated()) { + // don't bother reporting deprecated methods + // unless they are public + Errors.error(Errors.DEPRECATED, + m.position(), "Method " + + cl.qualifiedName() + "." + m.name() + + " is deprecated"); + } + + ClassInfo returnClass = m.returnType().asClassInfo(); + if (returnClass != null && returnClass.isHidden()) { + Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), + "Method " + cl.qualifiedName() + "." + m.name() + + " returns unavailable type " + returnClass.name()); + } + + ParameterInfo[] params = m.parameters(); + for (ParameterInfo p: params) { + TypeInfo t = p.type(); + if (!t.isPrimitive()) { + if (t.asClassInfo().isHidden()) { + Errors.error(Errors.UNAVAILABLE_SYMBOL, + m.position(), "Parameter of hidden type " + + t.fullName() + " in " + + cl.qualifiedName() + "." + m.name() + "()"); + } + } + } + } + + // annotations are handled like methods + methods = cl.annotationElements(); + for (MethodInfo m: methods) { + if (m.isHidden()) { + Errors.error(Errors.UNAVAILABLE_SYMBOL, + m.position(), "Reference to hidden annotation " + + m.name()); + } + + ClassInfo returnClass = m.returnType().asClassInfo(); + if (returnClass != null && returnClass.isHidden()) { + Errors.error(Errors.UNAVAILABLE_SYMBOL, + m.position(), "Annotation '" + m.name() + + "' returns unavailable type " + returnClass.name()); + } + + ParameterInfo[] params = m.parameters(); + for (ParameterInfo p: params) { + TypeInfo t = p.type(); + if (!t.isPrimitive()) { + if (t.asClassInfo().isHidden()) { + Errors.error(Errors.UNAVAILABLE_SYMBOL, + p.position(), "Reference to unavailable annotation class " + + t.fullName()); + } + } + } + } + } else if (cl.isDeprecated()) { + // not hidden, but deprecated + Errors.error(Errors.DEPRECATED, + cl.position(), "Class " + cl.qualifiedName() + + " is deprecated"); + } + } + + // write out the stubs + HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>(); + for (ClassInfo cl: notStrippable) { + if (!cl.isDocOnly()) { + if (stubPackages == null || stubPackages.contains(cl.containingPackage().name())) { + writeClassFile(stubsDir, cl); + if (packages.containsKey(cl.containingPackage())) { + packages.get(cl.containingPackage()).add(cl); + } else { + ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>(); + classes.add(cl); + packages.put(cl.containingPackage(), classes); + } + } + } + } + + // write out the XML + if (writeXML && xmlWriter != null) { + writeXML(xmlWriter, packages, notStrippable); + } + + if (xmlWriter != null) { + xmlWriter.close(); + } + + } + + public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why) { + + if (!notStrippable.add(cl)) { + // slight optimization: if it already contains cl, it already contains + // all of cl's parents + return; + } + cl.setReasonIncluded(why); + + // cant strip annotations + /*if (cl.annotations() != null){ + for (AnnotationInstanceInfo ai : cl.annotations()){ + if (ai.type() != null){ + cantStripThis(ai.type(), notStrippable, "1:" + cl.qualifiedName()); + } + } + }*/ + // cant strip any public fields or their generics + if (cl.allSelfFields() != null){ + for (FieldInfo fInfo : cl.allSelfFields()){ + if (fInfo.type() != null){ + if (fInfo.type().asClassInfo() != null){ + cantStripThis(fInfo.type().asClassInfo(), notStrippable, + "2:" + cl.qualifiedName()); + } + if (fInfo.type().typeArguments() != null){ + for (TypeInfo tTypeInfo : fInfo.type().typeArguments()){ + if (tTypeInfo.asClassInfo() != null){ + cantStripThis(tTypeInfo.asClassInfo(), notStrippable, + "3:" + cl.qualifiedName()); + } + } + } + } + } + } + //cant strip any of the type's generics + if (cl.asTypeInfo() != null){ + if (cl.asTypeInfo().typeArguments() != null){ + for (TypeInfo tInfo : cl.asTypeInfo().typeArguments()){ + if (tInfo.asClassInfo() != null){ + cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName()); + } + } + } + } + //cant strip any of the annotation elements + //cantStripThis(cl.annotationElements(), notStrippable); + // take care of methods + cantStripThis(cl.allSelfMethods(), notStrippable); + cantStripThis(cl.allConstructors(), notStrippable); + // blow the outer class open if this is an inner class + if(cl.containingClass() != null){ + cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName()); + } + // blow open super class and interfaces + ClassInfo supr = cl.realSuperclass(); + if (supr != null) { + if (supr.isHidden()) { + // cl is a public class declared as extending a hidden superclass. + // this is not a desired practice but it's happened, so we deal + // with it by stripping off the superclass relation for purposes of + // generating the doc & stub information, and proceeding normally. + cl.init(cl.asTypeInfo(), cl.realInterfaces(), cl.realInterfaceTypes(), + cl.innerClasses(), cl.allConstructors(), cl.allSelfMethods(), + cl.annotationElements(), cl.allSelfFields(), cl.enumConstants(), + cl.containingPackage(), cl.containingClass(), + null, null, cl.annotations()); + Errors.error(Errors.HIDDEN_SUPERCLASS, + cl.position(), "Public class " + cl.qualifiedName() + + " stripped of unavailable superclass " + + supr.qualifiedName()); + } else { + cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name() + + cl.qualifiedName()); + } + } + } + + private static void cantStripThis(MethodInfo[] mInfos , HashSet<ClassInfo> notStrippable) { + //for each method, blow open the parameters, throws and return types. also blow open their generics + if (mInfos != null){ + for (MethodInfo mInfo : mInfos){ + if (mInfo.getTypeParameters() != null){ + for (TypeInfo tInfo : mInfo.getTypeParameters()){ + if (tInfo.asClassInfo() != null){ + cantStripThis(tInfo.asClassInfo(), notStrippable, "8:" + + mInfo.realContainingClass().qualifiedName() + ":" + + mInfo.name()); + } + } + } + if (mInfo.parameters() != null){ + for (ParameterInfo pInfo : mInfo.parameters()){ + if (pInfo.type() != null && pInfo.type().asClassInfo() != null){ + cantStripThis(pInfo.type().asClassInfo(), notStrippable, + "9:"+ mInfo.realContainingClass().qualifiedName() + + ":" + mInfo.name()); + if (pInfo.type().typeArguments() != null){ + for (TypeInfo tInfoType : pInfo.type().typeArguments()){ + if (tInfoType.asClassInfo() != null){ + ClassInfo tcl = tInfoType.asClassInfo(); + if (tcl.isHidden()) { + Errors.error(Errors.UNAVAILABLE_SYMBOL, mInfo.position(), + "Parameter of hidden type " + + tInfoType.fullName() + " in " + + mInfo.containingClass().qualifiedName() + + '.' + mInfo.name() + "()"); + } else { + cantStripThis(tcl, notStrippable, + "10:" + + mInfo.realContainingClass().qualifiedName() + ":" + + mInfo.name()); + } + } + } + } + } + } + } + for (ClassInfo thrown : mInfo.thrownExceptions()){ + cantStripThis(thrown, notStrippable, "11:" + + mInfo.realContainingClass().qualifiedName() + +":" + mInfo.name()); + } + if (mInfo.returnType() != null && mInfo.returnType().asClassInfo() != null){ + cantStripThis(mInfo.returnType().asClassInfo(), notStrippable, + "12:" + mInfo.realContainingClass().qualifiedName() + + ":" + mInfo.name()); + if (mInfo.returnType().typeArguments() != null){ + for (TypeInfo tyInfo: mInfo.returnType().typeArguments() ){ + if (tyInfo.asClassInfo() != null){ + cantStripThis(tyInfo.asClassInfo(), notStrippable, + "13:" + + mInfo.realContainingClass().qualifiedName() + + ":" + mInfo.name()); + } + } + } + } + } + } + } + + static String javaFileName(ClassInfo cl) { + String dir = ""; + PackageInfo pkg = cl.containingPackage(); + if (pkg != null) { + dir = pkg.name(); + dir = dir.replace('.', '/') + '/'; + } + return dir + cl.name() + ".java"; + } + + static void writeClassFile(String stubsDir, ClassInfo cl) { + // inner classes are written by their containing class + if (cl.containingClass() != null) { + return; + } + + String filename = stubsDir + '/' + javaFileName(cl); + File file = new File(filename); + ClearPage.ensureDirectory(file); + + PrintStream stream = null; + try { + stream = new PrintStream(file); + writeClassFile(stream, cl); + } + catch (FileNotFoundException e) { + System.err.println("error writing file: " + filename); + } + finally { + if (stream != null) { + stream.close(); + } + } + } + + static void writeClassFile(PrintStream stream, ClassInfo cl) { + PackageInfo pkg = cl.containingPackage(); + if (pkg != null) { + stream.println("package " + pkg.name() + ";"); + } + writeClass(stream, cl); + } + + static void writeClass(PrintStream stream, ClassInfo cl) { + writeAnnotations(stream, cl.annotations()); + + stream.print(DroidDoc.scope(cl) + " "); + if (cl.isAbstract() && !cl.isAnnotation() && !cl.isInterface()) { + stream.print("abstract "); + } + if (cl.isStatic()){ + stream.print("static "); + } + if (cl.isFinal() && !cl.isEnum()) { + stream.print("final "); + } + if (false) { + stream.print("strictfp "); + } + + HashSet<String> classDeclTypeVars = new HashSet(); + String leafName = cl.asTypeInfo().fullName(classDeclTypeVars); + int bracket = leafName.indexOf('<'); + if (bracket < 0) bracket = leafName.length() - 1; + int period = leafName.lastIndexOf('.', bracket); + if (period < 0) period = -1; + leafName = leafName.substring(period+1); + + String kind = cl.kind(); + stream.println(kind + " " + leafName); + + TypeInfo base = cl.superclassType(); + + if (!"enum".equals(kind)) { + if (base != null && !"java.lang.Object".equals(base.qualifiedTypeName())) { + stream.println(" extends " + base.fullName(classDeclTypeVars)); + } + } + + TypeInfo[] interfaces = cl.realInterfaceTypes(); + List<TypeInfo> usedInterfaces = new ArrayList<TypeInfo>(); + for (TypeInfo iface : interfaces) { + if (notStrippable.contains(iface.asClassInfo()) + && !iface.asClassInfo().isDocOnly()) { + usedInterfaces.add(iface); + } + } + if (usedInterfaces.size() > 0 && !cl.isAnnotation()) { + // can java annotations extend other ones? + if (cl.isInterface() || cl.isAnnotation()) { + stream.print(" extends "); + } else { + stream.print(" implements "); + } + String comma = ""; + for (TypeInfo iface: usedInterfaces) { + stream.print(comma + iface.fullName(classDeclTypeVars)); + comma = ", "; + } + stream.println(); + } + + stream.println("{"); + + FieldInfo[] enumConstants = cl.enumConstants(); + int N = enumConstants.length; + for (int i=0; i<N; i++) { + FieldInfo field = enumConstants[i]; + if (!field.constantLiteralValue().equals("null")){ + stream.println(field.name() + "(" + field.constantLiteralValue() + + (i==N-1 ? ");" : "),")); + }else{ + stream.println(field.name() + "(" + (i==N-1 ? ");" : "),")); + } + } + + for (ClassInfo inner: cl.getRealInnerClasses()) { + if (notStrippable.contains(inner) + && !inner.isDocOnly()){ + writeClass(stream, inner); + } + } + + + for (MethodInfo method: cl.constructors()) { + if (!method.isDocOnly()) { + writeMethod(stream, method, true); + } + } + + boolean fieldNeedsInitialization = false; + boolean staticFieldNeedsInitialization = false; + for (FieldInfo field: cl.allSelfFields()) { + if (!field.isDocOnly()) { + if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) { + fieldNeedsInitialization = true; + } + if (field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) { + staticFieldNeedsInitialization = true; + } + } + } + + // The compiler includes a default public constructor that calls the super classes + // default constructor in the case where there are no written constructors. + // So, if we hide all the constructors, java may put in a constructor + // that calls a nonexistent super class constructor. So, if there are no constructors, + // and the super class doesn't have a default constructor, write in a private constructor + // that works. TODO -- we generate this as protected, but we really should generate + // it as private unless it also exists in the real code. + if ((cl.constructors().length == 0 && (cl.getNonWrittenConstructors().length != 0 + || fieldNeedsInitialization)) + && !cl.isAnnotation() + && !cl.isInterface() + && !cl.isEnum() ) { + //Errors.error(Errors.HIDDEN_CONSTRUCTOR, + // cl.position(), "No constructors " + + // "found and superclass has no parameterless constructor. A constructor " + + // "that calls an appropriate superclass constructor " + + // "was automatically written to stubs.\n"); + stream.println(cl.leafName() + + "() { " + superCtorCall(cl,null) + + "throw new" + " RuntimeException(\"Stub!\"); }"); + } + + for (MethodInfo method: cl.allSelfMethods()) { + if (cl.isEnum()) { + if (("values".equals(method.name()) + && "()".equals(method.signature())) + || ("valueOf".equals(method.name()) + && "(java.lang.String)".equals(method.signature()))) { + // skip these two methods on enums, because they're synthetic, + // although for some reason javadoc doesn't mark them as synthetic, + // maybe because they still want them documented + continue; + } + } + if (!method.isDocOnly()) { + writeMethod(stream, method, false); + } + } + //Write all methods that are hidden, but override abstract methods or interface methods. + //These can't be hidden. + for (MethodInfo method : cl.getHiddenMethods()){ + MethodInfo overriddenMethod = method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable); + ClassInfo classContainingMethod = method.findRealOverriddenClass(method.name(), + method.signature()); + if (overriddenMethod != null && !overriddenMethod.isHidden() + && !overriddenMethod.isDocOnly() && + (overriddenMethod.isAbstract() || + overriddenMethod.containingClass().isInterface())) { + method.setReason("1:" + classContainingMethod.qualifiedName()); + cl.addMethod(method); + writeMethod(stream, method, false); + } + } + + for (MethodInfo element: cl.annotationElements()) { + if (!element.isDocOnly()) { + writeAnnotationElement(stream, element); + } + } + + for (FieldInfo field: cl.allSelfFields()) { + if (!field.isDocOnly()) { + writeField(stream, field); + } + } + + if (staticFieldNeedsInitialization) { + stream.print("static { "); + for (FieldInfo field: cl.allSelfFields()) { + if (!field.isDocOnly() && field.isStatic() && field.isFinal() + && !fieldIsInitialized(field) && field.constantValue() == null) { + stream.print(field.name() + " = " + field.type().defaultValue() + + "; "); + } + } + stream.println("}"); + } + + stream.println("}"); + } + + + static void writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor) { + String comma; + + stream.print(DroidDoc.scope(method) + " "); + if (method.isStatic()) { + stream.print("static "); + } + if (method.isFinal()) { + stream.print("final "); + } + if (method.isAbstract()) { + stream.print("abstract "); + } + if (method.isSynchronized()) { + stream.print("synchronized "); + } + if (method.isNative()) { + stream.print("native "); + } + if (false /*method.isStictFP()*/) { + stream.print("strictfp "); + } + + stream.print(method.typeArgumentsName(new HashSet()) + " "); + + if (!isConstructor) { + stream.print(method.returnType().fullName(method.typeVariables()) + " "); + } + String n = method.name(); + int pos = n.lastIndexOf('.'); + if (pos >= 0) { + n = n.substring(pos + 1); + } + stream.print(n + "("); + comma = ""; + int count = 1; + int size = method.parameters().length; + for (ParameterInfo param: method.parameters()) { + stream.print(comma + fullParameterTypeName(method, param.type(), count == size) + + " " + param.name()); + comma = ", "; + count++; + } + stream.print(")"); + + comma = ""; + if (method.thrownExceptions().length > 0) { + stream.print(" throws "); + for (ClassInfo thrown: method.thrownExceptions()) { + stream.print(comma + thrown.qualifiedName()); + comma = ", "; + } + } + if (method.isAbstract() || method.isNative() || method.containingClass().isInterface()) { + stream.println(";"); + } else { + stream.print(" { "); + if (isConstructor) { + stream.print(superCtorCall(method.containingClass(), method.thrownExceptions())); + } + stream.println("throw new RuntimeException(\"Stub!\"); }"); + } + } + + static void writeField(PrintStream stream, FieldInfo field) { + stream.print(DroidDoc.scope(field) + " "); + if (field.isStatic()) { + stream.print("static "); + } + if (field.isFinal()) { + stream.print("final "); + } + if (field.isTransient()) { + stream.print("transient "); + } + if (field.isVolatile()) { + stream.print("volatile "); + } + + stream.print(field.type().fullName()); + stream.print(" "); + stream.print(field.name()); + + if (fieldIsInitialized(field)) { + stream.print(" = " + field.constantLiteralValue()); + } + + stream.println(";"); + } + + static boolean fieldIsInitialized(FieldInfo field) { + return (field.isFinal() && field.constantValue() != null) + || !field.type().dimension().equals("") + || field.containingClass().isInterface(); + } + + // Returns 'true' if the method is an @Override of a visible parent + // method implementation, and thus does not affect the API. + static boolean methodIsOverride(MethodInfo mi) { + // Abstract/static/final methods are always listed in the API description + if (mi.isAbstract() || mi.isStatic() || mi.isFinal()) { + return false; + } + + // Find any relevant ancestor declaration and inspect it + MethodInfo om = mi.findSuperclassImplementation(notStrippable); + if (om != null) { + // Visibility mismatch is an API change, so check for it + if (mi.mIsPrivate == om.mIsPrivate + && mi.mIsPublic == om.mIsPublic + && mi.mIsProtected == om.mIsProtected) { + // Look only for overrides of an ancestor class implementation, + // not of e.g. an abstract or interface method declaration + if (!om.isAbstract()) { + // If the parent is hidden, we can't rely on it to provide + // the API + if (!om.isHidden()) { + // If the only "override" turns out to be in our own class + // (which sometimes happens in concrete subclasses of + // abstract base classes), it's not really an override + if (!mi.mContainingClass.equals(om.mContainingClass)) { + return true; + } + } + } + } + } + return false; + } + + static boolean canCallMethod(ClassInfo from, MethodInfo m) { + if (m.isPublic() || m.isProtected()) { + return true; + } + if (m.isPackagePrivate()) { + String fromPkg = from.containingPackage().name(); + String pkg = m.containingClass().containingPackage().name(); + if (fromPkg.equals(pkg)) { + return true; + } + } + return false; + } + + // call a constructor, any constructor on this class's superclass. + static String superCtorCall(ClassInfo cl, ClassInfo[] thrownExceptions) { + ClassInfo base = cl.realSuperclass(); + if (base == null) { + return ""; + } + HashSet<String> exceptionNames = new HashSet<String>(); + if (thrownExceptions != null ){ + for (ClassInfo thrown : thrownExceptions){ + exceptionNames.add(thrown.name()); + } + } + MethodInfo[] ctors = base.constructors(); + MethodInfo ctor = null; + //bad exception indicates that the exceptions thrown by the super constructor + //are incompatible with the constructor we're using for the sub class. + Boolean badException = false; + for (MethodInfo m: ctors) { + if (canCallMethod(cl, m)) { + if (m.thrownExceptions() != null){ + for (ClassInfo thrown : m.thrownExceptions()){ + if (!exceptionNames.contains(thrown.name())){ + badException = true; + } + } + } + if (badException){ + badException = false; + continue; + } + // if it has no args, we're done + if (m.parameters().length == 0) { + return ""; + } + ctor = m; + } + } + if (ctor != null) { + String result = ""; + result+= "super("; + ParameterInfo[] params = ctor.parameters(); + int N = params.length; + for (int i=0; i<N; i++) { + TypeInfo t = params[i].type(); + if (t.isPrimitive() && t.dimension().equals("")) { + String n = t.simpleTypeName(); + if (("byte".equals(n) + || "short".equals(n) + || "int".equals(n) + || "long".equals(n) + || "float".equals(n) + || "double".equals(n)) && t.dimension().equals("")) { + result += "0"; + } + else if ("char".equals(n)) { + result += "'\\0'"; + } + else if ("boolean".equals(n)) { + result += "false"; + } + else { + result += "<<unknown-" + n + ">>"; + } + } else { + //put null in each super class method. Cast null to the correct type + //to avoid collisions with other constructors. If the type is generic + //don't cast it + result += (!t.isTypeVariable() ? "(" + t.qualifiedTypeName() + t.dimension() + + ")" : "") + "null"; + } + if (i != N-1) { + result += ","; + } + } + result += "); "; + return result; + } else { + return ""; + } + } + + static void writeAnnotations(PrintStream stream, AnnotationInstanceInfo[] annotations) { + for (AnnotationInstanceInfo ann: annotations) { + if (!ann.type().isHidden()) { + stream.println(ann.toString()); + } + } + } + + static void writeAnnotationElement(PrintStream stream, MethodInfo ann) { + stream.print(ann.returnType().fullName()); + stream.print(" "); + stream.print(ann.name()); + stream.print("()"); + AnnotationValueInfo def = ann.defaultAnnotationElementValue(); + if (def != null) { + stream.print(" default "); + stream.print(def.valueString()); + } + stream.println(";"); + } + + static void writeXML(PrintStream xmlWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses, + HashSet notStrippable) { + // extract the set of packages, sort them by name, and write them out in that order + Set<PackageInfo> allClassKeys = allClasses.keySet(); + PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]); + Arrays.sort(allPackages, PackageInfo.comparator); + + xmlWriter.println("<api>"); + for (PackageInfo pack : allPackages) { + writePackageXML(xmlWriter, pack, allClasses.get(pack), notStrippable); + } + xmlWriter.println("</api>"); + } + + static void writePackageXML(PrintStream xmlWriter, PackageInfo pack, List<ClassInfo> classList, + HashSet notStrippable) { + ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]); + Arrays.sort(classes, ClassInfo.comparator); + xmlWriter.println("<package name=\"" + pack.name() + "\"\n" + //+ " source=\"" + pack.position() + "\"\n" + + ">"); + for (ClassInfo cl : classes) { + writeClassXML(xmlWriter, cl, notStrippable); + } + xmlWriter.println("</package>"); + + + } + + static void writeClassXML(PrintStream xmlWriter, ClassInfo cl, HashSet notStrippable) { + String scope = DroidDoc.scope(cl); + String deprecatedString = ""; + String declString = (cl.isInterface()) ? "interface" : "class"; + if (cl.isDeprecated()) { + deprecatedString = "deprecated"; + } else { + deprecatedString = "not deprecated"; + } + xmlWriter.println("<" + declString + " name=\"" + cl.name() + "\""); + if (!cl.isInterface() && !cl.qualifiedName().equals("java.lang.Object")) { + xmlWriter.println(" extends=\"" + ((cl.realSuperclass() == null) + ? "java.lang.Object" + : cl.realSuperclass().qualifiedName()) + "\""); + } + xmlWriter.println(" abstract=\"" + cl.isAbstract() + "\"\n" + + " static=\"" + cl.isStatic() + "\"\n" + + " final=\"" + cl.isFinal() + "\"\n" + + " deprecated=\"" + deprecatedString + "\"\n" + + " visibility=\"" + scope + "\"\n" + //+ " source=\"" + cl.position() + "\"\n" + + ">"); + + ClassInfo[] interfaces = cl.realInterfaces(); + Arrays.sort(interfaces, ClassInfo.comparator); + for (ClassInfo iface : interfaces) { + if (notStrippable.contains(iface)) { + xmlWriter.println("<implements name=\"" + iface.qualifiedName() + "\">"); + xmlWriter.println("</implements>"); + } + } + + MethodInfo[] constructors = cl.constructors(); + Arrays.sort(constructors, MethodInfo.comparator); + for (MethodInfo mi : constructors) { + writeConstructorXML(xmlWriter, mi); + } + + MethodInfo[] methods = cl.allSelfMethods(); + Arrays.sort(methods, MethodInfo.comparator); + for (MethodInfo mi : methods) { + if (!methodIsOverride(mi)) { + writeMethodXML(xmlWriter, mi); + } + } + + FieldInfo[] fields = cl.allSelfFields(); + Arrays.sort(fields, FieldInfo.comparator); + for (FieldInfo fi : fields) { + writeFieldXML(xmlWriter, fi); + } + xmlWriter.println("</" + declString + ">"); + + } + + static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) { + String scope = DroidDoc.scope(mi); + + String deprecatedString = ""; + if (mi.isDeprecated()) { + deprecatedString = "deprecated"; + } else { + deprecatedString = "not deprecated"; + } + xmlWriter.println("<method name=\"" + mi.name() + "\"\n" + + ((mi.returnType() != null) + ? " return=\"" + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n" + : "") + + " abstract=\"" + mi.isAbstract() + "\"\n" + + " native=\"" + mi.isNative() + "\"\n" + + " synchronized=\"" + mi.isSynchronized() + "\"\n" + + " static=\"" + mi.isStatic() + "\"\n" + + " final=\"" + mi.isFinal() + "\"\n" + + " deprecated=\""+ deprecatedString + "\"\n" + + " visibility=\"" + scope + "\"\n" + //+ " source=\"" + mi.position() + "\"\n" + + ">"); + + // write parameters in declaration order + int numParameters = mi.parameters().length; + int count = 0; + for (ParameterInfo pi : mi.parameters()) { + count++; + writeParameterXML(xmlWriter, mi, pi, count == numParameters); + } + + // but write exceptions in canonicalized order + ClassInfo[] exceptions = mi.thrownExceptions(); + Arrays.sort(exceptions, ClassInfo.comparator); + for (ClassInfo pi : exceptions) { + xmlWriter.println("<exception name=\"" + pi.name() +"\" type=\"" + pi.qualifiedName() + + "\">"); + xmlWriter.println("</exception>"); + } + xmlWriter.println("</method>"); + } + + static void writeConstructorXML(PrintStream xmlWriter, MethodInfo mi) { + String scope = DroidDoc.scope(mi); + String deprecatedString = ""; + if (mi.isDeprecated()) { + deprecatedString = "deprecated"; + } else { + deprecatedString = "not deprecated"; + } + xmlWriter.println("<constructor name=\"" + mi.name() + "\"\n" + + " type=\"" + mi.containingClass().qualifiedName() + "\"\n" + + " static=\"" + mi.isStatic() + "\"\n" + + " final=\"" + mi.isFinal() + "\"\n" + + " deprecated=\"" + deprecatedString + "\"\n" + + " visibility=\"" + scope +"\"\n" + //+ " source=\"" + mi.position() + "\"\n" + + ">"); + + int numParameters = mi.parameters().length; + int count = 0; + for (ParameterInfo pi : mi.parameters()) { + count++; + writeParameterXML(xmlWriter, mi, pi, count == numParameters); + } + + ClassInfo[] exceptions = mi.thrownExceptions(); + Arrays.sort(exceptions, ClassInfo.comparator); + for (ClassInfo pi : exceptions) { + xmlWriter.println("<exception name=\"" + pi.name() +"\" type=\"" + pi.qualifiedName() + + "\">"); + xmlWriter.println("</exception>"); + } + xmlWriter.println("</constructor>"); + } + + static void writeParameterXML(PrintStream xmlWriter, MethodInfo method, + ParameterInfo pi, boolean isLast) { + xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\"" + + makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">"); + xmlWriter.println("</parameter>"); + } + + static void writeFieldXML(PrintStream xmlWriter, FieldInfo fi) { + String scope = DroidDoc.scope(fi); + String deprecatedString = ""; + if (fi.isDeprecated()) { + deprecatedString = "deprecated"; + } else { + deprecatedString = "not deprecated"; + } + //need to make sure value is valid XML + String value = makeXMLcompliant(fi.constantLiteralValue()); + + String fullTypeName = makeXMLcompliant(fi.type().qualifiedTypeName()) + + fi.type().dimension(); + + xmlWriter.println("<field name=\"" + fi.name() +"\"\n" + + " type=\"" + fullTypeName + "\"\n" + + " transient=\"" + fi.isTransient() + "\"\n" + + " volatile=\"" + fi.isVolatile() + "\"\n" + + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "") + + " static=\"" + fi.isStatic() + "\"\n" + + " final=\"" + fi.isFinal() + "\"\n" + + " deprecated=\"" + deprecatedString + "\"\n" + + " visibility=\"" + scope + "\"\n" + //+ " source=\"" + fi.position() + "\"\n" + + ">"); + xmlWriter.println("</field>"); + } + + static String makeXMLcompliant(String s) { + String returnString = ""; + returnString = s.replaceAll("&", "&"); + returnString = returnString.replaceAll("<", "<"); + returnString = returnString.replaceAll(">", ">"); + returnString = returnString.replaceAll("\"", """); + returnString = returnString.replaceAll("'", "&pos;"); + return returnString; + } + + static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) { + String fullTypeName = type.fullName(method.typeVariables()); + if (isLast && method.isVarArgs()) { + // TODO: note that this does not attempt to handle hypothetical + // vararg methods whose last parameter is a list of arrays, e.g. + // "Object[]...". + fullTypeName = type.fullNameNoDimension(method.typeVariables()) + "..."; + } + return fullTypeName; + } +} |