summaryrefslogtreecommitdiffstats
path: root/tools/droiddoc/src/DroidDoc.java
diff options
context:
space:
mode:
Diffstat (limited to 'tools/droiddoc/src/DroidDoc.java')
-rw-r--r--tools/droiddoc/src/DroidDoc.java1326
1 files changed, 1326 insertions, 0 deletions
diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java
new file mode 100644
index 0000000..b0412c9
--- /dev/null
+++ b/tools/droiddoc/src/DroidDoc.java
@@ -0,0 +1,1326 @@
+/*
+ * 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.*;
+
+import org.clearsilver.HDF;
+
+import java.util.*;
+import java.io.*;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class DroidDoc
+{
+ private static final String SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant";
+ private static final String SDK_CONSTANT_TYPE_ACTIVITY_ACTION = "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION";
+ private static final String SDK_CONSTANT_TYPE_BROADCAST_ACTION = "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION";
+ private static final String SDK_CONSTANT_TYPE_SERVICE_ACTION = "android.annotation.SdkConstant.SdkConstantType.SERVICE_INTENT_ACTION";
+ private static final String SDK_CONSTANT_TYPE_CATEGORY = "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY";
+ private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget";
+ private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout";
+
+ private static final int TYPE_NONE = 0;
+ private static final int TYPE_WIDGET = 1;
+ private static final int TYPE_LAYOUT = 2;
+ private static final int TYPE_LAYOUT_PARAM = 3;
+
+ public static final int SHOW_PUBLIC = 0x00000001;
+ public static final int SHOW_PROTECTED = 0x00000003;
+ public static final int SHOW_PACKAGE = 0x00000007;
+ public static final int SHOW_PRIVATE = 0x0000000f;
+ public static final int SHOW_HIDDEN = 0x0000001f;
+
+ public static int showLevel = SHOW_PROTECTED;
+
+ public static final String javadocDir = "reference/";
+ public static String htmlExtension;
+
+ public static RootDoc root;
+ public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
+ public static Map<Character,String> escapeChars = new HashMap<Character,String>();
+ public static String title = "";
+
+ public static boolean checkLevel(int level)
+ {
+ return (showLevel & level) == level;
+ }
+
+ public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp,
+ boolean priv, boolean hidden)
+ {
+ int level = 0;
+ if (hidden && !checkLevel(SHOW_HIDDEN)) {
+ return false;
+ }
+ if (pub && checkLevel(SHOW_PUBLIC)) {
+ return true;
+ }
+ if (prot && checkLevel(SHOW_PROTECTED)) {
+ return true;
+ }
+ if (pkgp && checkLevel(SHOW_PACKAGE)) {
+ return true;
+ }
+ if (priv && checkLevel(SHOW_PRIVATE)) {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean start(RootDoc r)
+ {
+ String keepListFile = null;
+ String proofreadFile = null;
+ String todoFile = null;
+ String sdkValuePath = null;
+ ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>();
+ String stubsDir = null;
+ //Create the dependency graph for the stubs directory
+ boolean apiXML = false;
+ String apiFile = null;
+ String debugStubsFile = "";
+ HashSet<String> stubPackages = null;
+
+ root = r;
+
+ String[][] options = r.options();
+ for (String[] a: options) {
+ if (a[0].equals("-d")) {
+ ClearPage.outputDir = a[1];
+ }
+ else if (a[0].equals("-templatedir")) {
+ ClearPage.addTemplateDir(a[1]);
+ }
+ else if (a[0].equals("-hdf")) {
+ mHDFData.add(new String[] {a[1], a[2]});
+ }
+ else if (a[0].equals("-toroot")) {
+ ClearPage.toroot = a[1];
+ }
+ else if (a[0].equals("-samplecode")) {
+ sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
+ }
+ else if (a[0].equals("-htmldir")) {
+ ClearPage.htmlDir = a[1];
+ }
+ else if (a[0].equals("-title")) {
+ DroidDoc.title = a[1];
+ }
+ else if (a[0].equals("-werror")) {
+ Errors.setWarningsAreErrors(true);
+ }
+ else if (a[0].equals("-error") || a[0].equals("-warning")
+ || a[0].equals("-hide")) {
+ try {
+ int level = -1;
+ if (a[0].equals("-error")) {
+ level = Errors.ERROR;
+ }
+ else if (a[0].equals("-warning")) {
+ level = Errors.WARNING;
+ }
+ else if (a[0].equals("-hide")) {
+ level = Errors.HIDDEN;
+ }
+ Errors.setErrorLevel(Integer.parseInt(a[1]), level);
+ }
+ catch (NumberFormatException e) {
+ // already printed below
+ return false;
+ }
+ }
+ else if (a[0].equals("-keeplist")) {
+ keepListFile = a[1];
+ }
+ else if (a[0].equals("-proofread")) {
+ proofreadFile = a[1];
+ }
+ else if (a[0].equals("-todo")) {
+ todoFile = a[1];
+ }
+ else if (a[0].equals("-public")) {
+ showLevel = SHOW_PUBLIC;
+ }
+ else if (a[0].equals("-protected")) {
+ showLevel = SHOW_PROTECTED;
+ }
+ else if (a[0].equals("-package")) {
+ showLevel = SHOW_PACKAGE;
+ }
+ else if (a[0].equals("-private")) {
+ showLevel = SHOW_PRIVATE;
+ }
+ else if (a[0].equals("-hidden")) {
+ showLevel = SHOW_HIDDEN;
+ }
+ else if (a[0].equals("-stubs")) {
+ stubsDir = a[1];
+ }
+ else if (a[0].equals("-stubpackages")) {
+ stubPackages = new HashSet();
+ for (String pkg: a[1].split(":")) {
+ stubPackages.add(pkg);
+ }
+ }
+ else if (a[0].equals("-sdkvalues")) {
+ sdkValuePath = a[1];
+ }
+ else if (a[0].equals("-apixml")) {
+ apiXML = true;
+ apiFile = a[1];
+ }
+ }
+
+ // read some prefs from the template
+ if (!readTemplateSettings()) {
+ return false;
+ }
+
+ // Set up the data structures
+ Converter.makeInfo(r);
+
+ // Files for proofreading
+ if (proofreadFile != null) {
+ Proofread.initProofread(proofreadFile);
+ }
+ if (todoFile != null) {
+ TodoFile.writeTodoFile(todoFile);
+ }
+
+ // HTML Pages
+ if (ClearPage.htmlDir != null) {
+ writeHTMLPages();
+ }
+
+ // Packages Pages
+ writePackages(javadocDir
+ + (ClearPage.htmlDir!=null
+ ? "packages" + htmlExtension
+ : "index" + htmlExtension));
+
+ // Classes
+ writeClassLists();
+ writeClasses();
+ writeHierarchy();
+ // writeKeywords();
+
+ // Lists for JavaScript
+ writeLists();
+ if (keepListFile != null) {
+ writeKeepList(keepListFile);
+ }
+
+ // Sample Code
+ for (SampleCode sc: sampleCodes) {
+ sc.write();
+ }
+
+ // Index page
+ writeIndex();
+
+ Proofread.finishProofread(proofreadFile);
+
+ // Stubs
+ if (stubsDir != null) {
+ Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
+ }
+
+ if (sdkValuePath != null) {
+ writeSdkValues(sdkValuePath);
+ }
+
+ Errors.printErrors();
+ return !Errors.hadError;
+ }
+
+ private static void writeIndex() {
+ HDF data = makeHDF();
+ ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
+ }
+
+ private static boolean readTemplateSettings()
+ {
+ HDF data = makeHDF();
+ htmlExtension = data.getValue("template.extension", ".html");
+ int i=0;
+ while (true) {
+ String k = data.getValue("template.escape." + i + ".key", "");
+ String v = data.getValue("template.escape." + i + ".value", "");
+ if ("".equals(k)) {
+ break;
+ }
+ if (k.length() != 1) {
+ System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
+ return false;
+ }
+ escapeChars.put(k.charAt(0), v);
+ i++;
+ }
+ return true;
+ }
+
+ public static String escape(String s) {
+ if (escapeChars.size() == 0) {
+ return s;
+ }
+ StringBuffer b = null;
+ int begin = 0;
+ final int N = s.length();
+ for (int i=0; i<N; i++) {
+ char c = s.charAt(i);
+ String mapped = escapeChars.get(c);
+ if (mapped != null) {
+ if (b == null) {
+ b = new StringBuffer(s.length() + mapped.length());
+ }
+ if (begin != i) {
+ b.append(s.substring(begin, i));
+ }
+ b.append(mapped);
+ begin = i+1;
+ }
+ }
+ if (b != null) {
+ if (begin != N) {
+ b.append(s.substring(begin, N));
+ }
+ return b.toString();
+ }
+ return s;
+ }
+
+ public static void setPageTitle(HDF data, String title)
+ {
+ String s = title;
+ if (DroidDoc.title.length() > 0) {
+ s += " - " + DroidDoc.title;
+ }
+ data.setValue("page.title", s);
+ }
+
+ public static LanguageVersion languageVersion()
+ {
+ return LanguageVersion.JAVA_1_5;
+ }
+
+ public static int optionLength(String option)
+ {
+ if (option.equals("-d")) {
+ return 2;
+ }
+ if (option.equals("-templatedir")) {
+ return 2;
+ }
+ if (option.equals("-hdf")) {
+ return 3;
+ }
+ if (option.equals("-toroot")) {
+ return 2;
+ }
+ if (option.equals("-samplecode")) {
+ return 4;
+ }
+ if (option.equals("-htmldir")) {
+ return 2;
+ }
+ if (option.equals("-title")) {
+ return 2;
+ }
+ if (option.equals("-werror")) {
+ return 1;
+ }
+ if (option.equals("-hide")) {
+ return 2;
+ }
+ if (option.equals("-warning")) {
+ return 2;
+ }
+ if (option.equals("-error")) {
+ return 2;
+ }
+ if (option.equals("-keeplist")) {
+ return 2;
+ }
+ if (option.equals("-proofread")) {
+ return 2;
+ }
+ if (option.equals("-todo")) {
+ return 2;
+ }
+ if (option.equals("-public")) {
+ return 1;
+ }
+ if (option.equals("-protected")) {
+ return 1;
+ }
+ if (option.equals("-package")) {
+ return 1;
+ }
+ if (option.equals("-private")) {
+ return 1;
+ }
+ if (option.equals("-hidden")) {
+ return 1;
+ }
+ if (option.equals("-stubs")) {
+ return 2;
+ }
+ if (option.equals("-stubpackages")) {
+ return 2;
+ }
+ if (option.equals("-sdkvalues")) {
+ return 2;
+ }
+ if (option.equals("-apixml")) {
+ return 2;
+ }
+ return 0;
+ }
+
+ public static boolean validOptions(String[][] options, DocErrorReporter r)
+ {
+ for (String[] a: options) {
+ if (a[0].equals("-error") || a[0].equals("-warning")
+ || a[0].equals("-hide")) {
+ try {
+ Integer.parseInt(a[1]);
+ }
+ catch (NumberFormatException e) {
+ r.printError("bad -" + a[0] + " value must be a number: "
+ + a[1]);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public static HDF makeHDF()
+ {
+ HDF data = new HDF();
+
+ for (String[] p: mHDFData) {
+ data.setValue(p[0], p[1]);
+ }
+
+ try {
+ for (String p: ClearPage.hdfFiles) {
+ data.readFile(p);
+ }
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return data;
+ }
+
+ public static HDF makePackageHDF()
+ {
+ HDF data = makeHDF();
+ ClassInfo[] classes = Converter.rootClasses();
+
+ SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
+ for (ClassInfo cl: classes) {
+ PackageInfo pkg = cl.containingPackage();
+ String name;
+ if (pkg == null) {
+ name = "";
+ } else {
+ name = pkg.name();
+ }
+ sorted.put(name, pkg);
+ }
+
+ int i = 0;
+ for (String s: sorted.keySet()) {
+ PackageInfo pkg = sorted.get(s);
+
+ if (pkg.isHidden()) {
+ continue;
+ }
+ Boolean allHidden = true;
+ int pass = 1;
+ ClassInfo[] classesToCheck = pkg.ordinaryClasses();
+ while (pass < 5 ) {
+ switch(pass) {
+ case 1:
+ classesToCheck = pkg.enums();
+ break;
+ case 2:
+ classesToCheck = pkg.errors();
+ break;
+ case 3:
+ classesToCheck = pkg.exceptions();
+ break;
+ case 4:
+ classesToCheck = pkg.interfaces();
+ break;
+ default:
+ System.out.println("Error reading package: " + pkg.name());
+ break;
+ }
+ for (ClassInfo cl : classesToCheck) {
+ if (!cl.isHidden()) {
+ allHidden = false;
+ break;
+ }
+ }
+ if (!allHidden) {
+ break;
+ }
+ pass++;
+ }
+ if (allHidden) {
+ continue;
+ }
+
+ data.setValue("reference", "true");
+ data.setValue("docs.packages." + i + ".name", s);
+ data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
+ TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
+ pkg.firstSentenceTags());
+ i++;
+ }
+
+ return data;
+ }
+
+ public static void writeDirectory(File dir, String relative)
+ {
+ File[] files = dir.listFiles();
+ int i, count = files.length;
+ for (i=0; i<count; i++) {
+ File f = files[i];
+ if (f.isFile()) {
+ String templ = relative + f.getName();
+ int len = templ.length();
+ if (len > 3 && ".cs".equals(templ.substring(len-3))) {
+ HDF data = makeHDF();
+ String filename = templ.substring(0,len-3) + htmlExtension;
+ System.out.println("Writing CS: " + filename);
+ ClearPage.write(data, templ, filename);
+ }
+ else if (len > 3 && ".jd".equals(templ.substring(len-3))) {
+ String filename = templ.substring(0,len-3) + htmlExtension;
+ System.out.println("Writing JD: " + filename);
+ DocFile.writePage(f.getAbsolutePath(), relative, filename);
+ }
+ else {
+// System.out.println("relative=" + relative
+// + " f.getAbsolutePath()=" + f.getAbsolutePath()
+// + " templ=" + templ);
+ System.out.println("Copying: " + templ);
+ ClearPage.copyFile(f, templ);
+ }
+ }
+ else if (f.isDirectory()) {
+ System.out.println("Writing dir: " + relative + f.getName() + "/");
+ writeDirectory(f, relative + f.getName() + "/");
+ }
+ }
+ }
+
+ public static void writeHTMLPages()
+ {
+ File f = new File(ClearPage.htmlDir);
+ if (!f.isDirectory()) {
+ System.out.println("htmlDir not a directory: " + ClearPage.htmlDir);
+ }
+ writeDirectory(f, "");
+ }
+
+ public static void writeLists()
+ {
+ HDF data = makeHDF();
+
+ ClassInfo[] classes = Converter.rootClasses();
+
+ SortedMap<String, Object> sorted = new TreeMap<String, Object>();
+ for (ClassInfo cl: classes) {
+ if (cl.isHidden()) {
+ continue;
+ }
+ sorted.put(cl.qualifiedName(), cl);
+ PackageInfo pkg = cl.containingPackage();
+ String name;
+ if (pkg == null) {
+ name = "";
+ } else {
+ name = pkg.name();
+ }
+ sorted.put(name, pkg);
+ }
+
+ int i = 0;
+ for (String s: sorted.keySet()) {
+ data.setValue("docs.pages." + i + ".id" , ""+i);
+ data.setValue("docs.pages." + i + ".label" , s);
+
+ Object o = sorted.get(s);
+ if (o instanceof PackageInfo) {
+ PackageInfo pkg = (PackageInfo)o;
+ data.setValue("docs.pages." + i + ".link" , pkg.htmlPage());
+ data.setValue("docs.pages." + i + ".type" , "package");
+ }
+ else if (o instanceof ClassInfo) {
+ ClassInfo cl = (ClassInfo)o;
+ data.setValue("docs.pages." + i + ".link" , cl.htmlPage());
+ data.setValue("docs.pages." + i + ".type" , "class");
+ }
+ i++;
+ }
+
+ ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
+ }
+
+ public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
+ if (!notStrippable.add(cl)) {
+ // slight optimization: if it already contains cl, it already contains
+ // all of cl's parents
+ return;
+ }
+ ClassInfo supr = cl.superclass();
+ if (supr != null) {
+ cantStripThis(supr, notStrippable);
+ }
+ for (ClassInfo iface: cl.interfaces()) {
+ cantStripThis(iface, notStrippable);
+ }
+ }
+
+ private static String getPrintableName(ClassInfo cl) {
+ ClassInfo containingClass = cl.containingClass();
+ if (containingClass != null) {
+ // This is an inner class.
+ String baseName = cl.name();
+ baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
+ return getPrintableName(containingClass) + '$' + baseName;
+ }
+ return cl.qualifiedName();
+ }
+
+ /**
+ * Writes the list of classes that must be present in order to
+ * provide the non-hidden APIs known to javadoc.
+ *
+ * @param filename the path to the file to write the list to
+ */
+ public static void writeKeepList(String filename) {
+ HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
+ ClassInfo[] all = Converter.allClasses();
+ Arrays.sort(all); // just to make the file a little more readable
+
+ // If a class is public and not hidden, then it and everything it derives
+ // from cannot be stripped. Otherwise we can strip it.
+ for (ClassInfo cl: all) {
+ if (cl.isPublic() && !cl.isHidden()) {
+ cantStripThis(cl, notStrippable);
+ }
+ }
+ PrintStream stream = null;
+ try {
+ stream = new PrintStream(filename);
+ for (ClassInfo cl: notStrippable) {
+ stream.println(getPrintableName(cl));
+ }
+ }
+ catch (FileNotFoundException e) {
+ System.out.println("error writing file: " + filename);
+ }
+ finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+ }
+
+ public static void writePackages(String filename)
+ {
+ System.out.println("Writing packages...");
+ HDF data = makePackageHDF();
+
+ ClassInfo[] classes = Converter.rootClasses();
+
+ SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
+ for (ClassInfo cl: classes) {
+ PackageInfo pkg = cl.containingPackage();
+ String name;
+ if (pkg == null) {
+ name = "";
+ } else {
+ name = pkg.name();
+ }
+ sorted.put(name, pkg);
+ }
+
+ int i = 0;
+ for (String s: sorted.keySet()) {
+ PackageInfo pkg = sorted.get(s);
+
+ if (pkg.isHidden()) {
+ continue;
+ }
+ Boolean allHidden = true;
+ int pass = 1;
+ ClassInfo[] classesToCheck = pkg.ordinaryClasses();
+ while (pass < 5 ) {
+ switch(pass) {
+ case 1:
+ classesToCheck = pkg.enums();
+ break;
+ case 2:
+ classesToCheck = pkg.errors();
+ break;
+ case 3:
+ classesToCheck = pkg.exceptions();
+ break;
+ case 4:
+ classesToCheck = pkg.interfaces();
+ break;
+ default:
+ System.out.println("Error reading package: " + pkg.name());
+ break;
+ }
+ for (ClassInfo cl : classesToCheck) {
+ if (!cl.isHidden()) {
+ allHidden = false;
+ break;
+ }
+ }
+ if (!allHidden) {
+ break;
+ }
+ pass++;
+ }
+ if (allHidden) {
+ continue;
+ }
+
+ writePackage(pkg);
+
+ i++;
+ }
+
+ setPageTitle(data, "Package Index");
+
+ TagInfo.makeHDF(data, "root.descr",
+ Converter.convertTags(root.inlineTags(), null));
+
+ ClearPage.write(data, "packages.cs", filename);
+ ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
+
+ Proofread.writePackages(filename,
+ Converter.convertTags(root.inlineTags(), null));
+ }
+
+ public static void writePackage(PackageInfo pkg)
+ {
+ // these this and the description are in the same directory,
+ // so it's okay
+ HDF data = makePackageHDF();
+
+ String name = pkg.name();
+ System.out.println("Writing " + name);
+
+ data.setValue("package.name", name);
+ data.setValue("package.descr", "...description...");
+
+ makeClassListHDF(data, "package.interfaces",
+ ClassInfo.sortByName(pkg.interfaces()));
+ makeClassListHDF(data, "package.classes",
+ ClassInfo.sortByName(pkg.ordinaryClasses()));
+ makeClassListHDF(data, "package.enums",
+ ClassInfo.sortByName(pkg.enums()));
+ makeClassListHDF(data, "package.exceptions",
+ ClassInfo.sortByName(pkg.exceptions()));
+ makeClassListHDF(data, "package.errors",
+ ClassInfo.sortByName(pkg.errors()));
+ TagInfo.makeHDF(data, "package.shortDescr",
+ pkg.firstSentenceTags());
+ TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
+
+ String filename = pkg.htmlPage();
+ setPageTitle(data, name);
+ ClearPage.write(data, "package.cs", filename);
+
+ filename = filename.substring(0, filename.lastIndexOf('/')+1)
+ + "package-descr" + htmlExtension;
+ setPageTitle(data, name + " Details");
+ ClearPage.write(data, "package-descr.cs", filename);
+
+ Proofread.writePackage(filename, pkg.inlineTags());
+ }
+
+ public static void writeClassLists()
+ {
+ int i;
+ HDF data = makePackageHDF();
+
+ ClassInfo[] classes = PackageInfo.filterHidden(
+ Converter.convertClasses(root.classes()));
+ if (classes.length == 0) {
+ return ;
+ }
+
+ Sorter[] sorted = new Sorter[classes.length];
+ for (i=0; i<sorted.length; i++) {
+ ClassInfo cl = classes[i];
+ String name = cl.name();
+ sorted[i] = new Sorter(name, cl);
+ }
+
+ Arrays.sort(sorted);
+
+ // make a pass and resolve ones that have the same name
+ int firstMatch = 0;
+ String lastName = sorted[0].label;
+ for (i=1; i<sorted.length; i++) {
+ String s = sorted[i].label;
+ if (!lastName.equals(s)) {
+ if (firstMatch != i-1) {
+ // there were duplicates
+ for (int j=firstMatch; j<i; j++) {
+ PackageInfo pkg = ((ClassInfo)sorted[j].data).containingPackage();
+ if (pkg != null) {
+ sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
+ }
+ }
+ } else {
+ //System.out.println("not duplicate: " + sorted[i].label);
+ }
+ firstMatch = i;
+ lastName = s;
+ }
+ }
+
+ // and sort again
+ Arrays.sort(sorted);
+
+ for (i=0; i<sorted.length; i++) {
+ String s = sorted[i].label;
+ ClassInfo cl = (ClassInfo)sorted[i].data;
+ char first = Character.toUpperCase(s.charAt(0));
+ cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
+ }
+
+ setPageTitle(data, "Class Index");
+ ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
+ }
+
+ // we use the word keywords because "index" means something else in html land
+ // the user only ever sees the word index
+/* public static void writeKeywords()
+ {
+ ArrayList<KeywordEntry> keywords = new ArrayList<KeywordEntry>();
+
+ ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes()));
+
+ for (ClassInfo cl: classes) {
+ cl.makeKeywordEntries(keywords);
+ }
+
+ HDF data = makePackageHDF();
+
+ Collections.sort(keywords);
+
+ int i=0;
+ for (KeywordEntry entry: keywords) {
+ String base = "keywords." + entry.firstChar() + "." + i;
+ entry.makeHDF(data, base);
+ i++;
+ }
+
+ setPageTitle(data, "Index");
+ ClearPage.write(data, "keywords.cs", javadocDir + "keywords" + htmlExtension);
+ } */
+
+ public static void writeHierarchy()
+ {
+ ClassInfo[] classes = Converter.rootClasses();
+ ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
+ for (ClassInfo cl: classes) {
+ if (!cl.isHidden()) {
+ info.add(cl);
+ }
+ }
+ HDF data = makePackageHDF();
+ Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
+ setPageTitle(data, "Class Hierarchy");
+ ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
+ }
+
+ public static void writeClasses()
+ {
+ ClassInfo[] classes = Converter.rootClasses();
+
+ for (ClassInfo cl: classes) {
+ HDF data = makePackageHDF();
+ if (!cl.isHidden()) {
+ writeClass(cl, data);
+ }
+ }
+ }
+
+ public static void writeClass(ClassInfo cl, HDF data)
+ {
+ cl.makeHDF(data);
+
+ System.out.println("Writing " + cl.name());
+ setPageTitle(data, cl.name());
+ ClearPage.write(data, "class.cs", cl.htmlPage());
+
+ Proofread.writeClass(cl.htmlPage(), cl);
+ }
+
+ public static void makeClassListHDF(HDF data, String base,
+ ClassInfo[] classes)
+ {
+ for (int i=0; i<classes.length; i++) {
+ ClassInfo cl = classes[i];
+ if (!cl.isHidden()) {
+ cl.makeShortDescrHDF(data, base + "." + i);
+ }
+ }
+ }
+
+ public static String linkTarget(String source, String target)
+ {
+ String[] src = source.split("/");
+ String[] tgt = target.split("/");
+
+ int srclen = src.length;
+ int tgtlen = tgt.length;
+
+ int same = 0;
+ while (same < (srclen-1)
+ && same < (tgtlen-1)
+ && (src[same].equals(tgt[same]))) {
+ same++;
+ }
+
+ String s = "";
+
+ int up = srclen-same-1;
+ for (int i=0; i<up; i++) {
+ s += "../";
+ }
+
+
+ int N = tgtlen-1;
+ for (int i=same; i<N; i++) {
+ s += tgt[i] + '/';
+ }
+ s += tgt[tgtlen-1];
+
+ return s;
+ }
+
+ /**
+ * Returns true if the given element has an @hide annotation.
+ */
+ private static boolean hasHideAnnotation(Doc doc) {
+ return doc.getRawCommentText().indexOf("@hide") != -1;
+ }
+
+ /**
+ * Returns true if the given element is hidden.
+ */
+ private static boolean isHidden(Doc doc) {
+ // Methods, fields, constructors.
+ if (doc instanceof MemberDoc) {
+ return hasHideAnnotation(doc);
+ }
+
+ // Classes, interfaces, enums, annotation types.
+ if (doc instanceof ClassDoc) {
+ ClassDoc classDoc = (ClassDoc) doc;
+
+ // Check the containing package.
+ if (hasHideAnnotation(classDoc.containingPackage())) {
+ return true;
+ }
+
+ // Check the class doc and containing class docs if this is a
+ // nested class.
+ ClassDoc current = classDoc;
+ do {
+ if (hasHideAnnotation(current)) {
+ return true;
+ }
+
+ current = current.containingClass();
+ } while (current != null);
+ }
+
+ return false;
+ }
+
+ /**
+ * Filters out hidden elements.
+ */
+ private static Object filterHidden(Object o, Class<?> expected) {
+ if (o == null) {
+ return null;
+ }
+
+ Class type = o.getClass();
+ if (type.getName().startsWith("com.sun.")) {
+ // TODO: Implement interfaces from superclasses, too.
+ return Proxy.newProxyInstance(type.getClassLoader(),
+ type.getInterfaces(), new HideHandler(o));
+ } else if (o instanceof Object[]) {
+ Class<?> componentType = expected.getComponentType();
+ Object[] array = (Object[]) o;
+ List<Object> list = new ArrayList<Object>(array.length);
+ for (Object entry : array) {
+ if ((entry instanceof Doc) && isHidden((Doc) entry)) {
+ continue;
+ }
+ list.add(filterHidden(entry, componentType));
+ }
+ return list.toArray(
+ (Object[]) Array.newInstance(componentType, list.size()));
+ } else {
+ return o;
+ }
+ }
+
+ /**
+ * Filters hidden elements out of method return values.
+ */
+ private static class HideHandler implements InvocationHandler {
+
+ private final Object target;
+
+ public HideHandler(Object target) {
+ this.target = target;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ String methodName = method.getName();
+ if (args != null) {
+ if (methodName.equals("compareTo") ||
+ methodName.equals("equals") ||
+ methodName.equals("overrides") ||
+ methodName.equals("subclassOf")) {
+ args[0] = unwrap(args[0]);
+ }
+ }
+
+ if (methodName.equals("getRawCommentText")) {
+ return filterComment((String) method.invoke(target, args));
+ }
+
+ // escape "&" in disjunctive types.
+ if (proxy instanceof Type && methodName.equals("toString")) {
+ return ((String) method.invoke(target, args))
+ .replace("&", "&amp;");
+ }
+
+ try {
+ return filterHidden(method.invoke(target, args),
+ method.getReturnType());
+ } catch (InvocationTargetException e) {
+ throw e.getTargetException();
+ }
+ }
+
+ private String filterComment(String s) {
+ if (s == null) {
+ return null;
+ }
+
+ s = s.trim();
+
+ // Work around off by one error
+ while (s.length() >= 5
+ && s.charAt(s.length() - 5) == '{') {
+ s += "&nbsp;";
+ }
+
+ return s;
+ }
+
+ private static Object unwrap(Object proxy) {
+ if (proxy instanceof Proxy)
+ return ((HideHandler)Proxy.getInvocationHandler(proxy)).target;
+ return proxy;
+ }
+ }
+
+ public static String scope(Scoped scoped) {
+ if (scoped.isPublic()) {
+ return "public";
+ }
+ else if (scoped.isProtected()) {
+ return "protected";
+ }
+ else if (scoped.isPackagePrivate()) {
+ return "";
+ }
+ else if (scoped.isPrivate()) {
+ return "private";
+ }
+ else {
+ throw new RuntimeException("invalid scope for object " + scoped);
+ }
+ }
+
+ /**
+ * Collect the values used by the Dev tools and write them in files packaged with the SDK
+ * @param output the ouput directory for the files.
+ */
+ private static void writeSdkValues(String output) {
+ ArrayList<String> activityActions = new ArrayList<String>();
+ ArrayList<String> broadcastActions = new ArrayList<String>();
+ ArrayList<String> serviceActions = new ArrayList<String>();
+ ArrayList<String> categories = new ArrayList<String>();
+
+ ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
+ ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
+ ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
+
+ ClassInfo[] classes = Converter.allClasses();
+
+ // Go through all the fields of all the classes, looking SDK stuff.
+ for (ClassInfo clazz : classes) {
+
+ // first check constant fields for the SdkConstant annotation.
+ FieldInfo[] fields = clazz.allSelfFields();
+ for (FieldInfo field : fields) {
+ Object cValue = field.constantValue();
+ if (cValue != null) {
+ AnnotationInstanceInfo[] annotations = field.annotations();
+ if (annotations.length > 0) {
+ for (AnnotationInstanceInfo annotation : annotations) {
+ if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
+ AnnotationValueInfo[] values = annotation.elementValues();
+ if (values.length > 0) {
+ String type = values[0].valueString();
+ if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
+ activityActions.add(cValue.toString());
+ } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
+ broadcastActions.add(cValue.toString());
+ } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
+ serviceActions.add(cValue.toString());
+ } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
+ categories.add(cValue.toString());
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Now check the class for @Widget or if its in the android.widget package
+ // (unless the class is hidden or abstract, or non public)
+ if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) {
+ boolean annotated = false;
+ AnnotationInstanceInfo[] annotations = clazz.annotations();
+ if (annotations.length > 0) {
+ for (AnnotationInstanceInfo annotation : annotations) {
+ if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
+ widgets.add(clazz);
+ annotated = true;
+ break;
+ } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
+ layouts.add(clazz);
+ annotated = true;
+ break;
+ }
+ }
+ }
+
+ if (annotated == false) {
+ // lets check if this is inside android.widget
+ PackageInfo pckg = clazz.containingPackage();
+ String packageName = pckg.name();
+ if ("android.widget".equals(packageName) ||
+ "android.view".equals(packageName)) {
+ // now we check what this class inherits either from android.view.ViewGroup
+ // or android.view.View, or android.view.ViewGroup.LayoutParams
+ int type = checkInheritance(clazz);
+ switch (type) {
+ case TYPE_WIDGET:
+ widgets.add(clazz);
+ break;
+ case TYPE_LAYOUT:
+ layouts.add(clazz);
+ break;
+ case TYPE_LAYOUT_PARAM:
+ layoutParams.add(clazz);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // now write the files, whether or not the list are empty.
+ // the SDK built requires those files to be present.
+
+ Collections.sort(activityActions);
+ writeValues(output + "/activity_actions.txt", activityActions);
+
+ Collections.sort(broadcastActions);
+ writeValues(output + "/broadcast_actions.txt", broadcastActions);
+
+ Collections.sort(serviceActions);
+ writeValues(output + "/service_actions.txt", serviceActions);
+
+ Collections.sort(categories);
+ writeValues(output + "/categories.txt", categories);
+
+ // before writing the list of classes, we do some checks, to make sure the layout params
+ // are enclosed by a layout class (and not one that has been declared as a widget)
+ for (int i = 0 ; i < layoutParams.size();) {
+ ClassInfo layoutParamClass = layoutParams.get(i);
+ ClassInfo containingClass = layoutParamClass.containingClass();
+ if (containingClass == null || layouts.indexOf(containingClass) == -1) {
+ layoutParams.remove(i);
+ } else {
+ i++;
+ }
+ }
+
+ writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
+ }
+
+ /**
+ * Writes a list of values into a text files.
+ * @param pathname the absolute os path of the output file.
+ * @param values the list of values to write.
+ */
+ private static void writeValues(String pathname, ArrayList<String> values) {
+ FileWriter fw = null;
+ BufferedWriter bw = null;
+ try {
+ fw = new FileWriter(pathname, false);
+ bw = new BufferedWriter(fw);
+
+ for (String value : values) {
+ bw.append(value).append('\n');
+ }
+ } catch (IOException e) {
+ // pass for now
+ } finally {
+ try {
+ if (bw != null) bw.close();
+ } catch (IOException e) {
+ // pass for now
+ }
+ try {
+ if (fw != null) fw.close();
+ } catch (IOException e) {
+ // pass for now
+ }
+ }
+ }
+
+ /**
+ * Writes the widget/layout/layout param classes into a text files.
+ * @param pathname the absolute os path of the output file.
+ * @param widgets the list of widget classes to write.
+ * @param layouts the list of layout classes to write.
+ * @param layoutParams the list of layout param classes to write.
+ */
+ private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
+ ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
+ FileWriter fw = null;
+ BufferedWriter bw = null;
+ try {
+ fw = new FileWriter(pathname, false);
+ bw = new BufferedWriter(fw);
+
+ // write the 3 types of classes.
+ for (ClassInfo clazz : widgets) {
+ writeClass(bw, clazz, 'W');
+ }
+ for (ClassInfo clazz : layoutParams) {
+ writeClass(bw, clazz, 'P');
+ }
+ for (ClassInfo clazz : layouts) {
+ writeClass(bw, clazz, 'L');
+ }
+ } catch (IOException e) {
+ // pass for now
+ } finally {
+ try {
+ if (bw != null) bw.close();
+ } catch (IOException e) {
+ // pass for now
+ }
+ try {
+ if (fw != null) fw.close();
+ } catch (IOException e) {
+ // pass for now
+ }
+ }
+ }
+
+ /**
+ * Writes a class name and its super class names into a {@link BufferedWriter}.
+ * @param writer the BufferedWriter to write into
+ * @param clazz the class to write
+ * @param prefix the prefix to put at the beginning of the line.
+ * @throws IOException
+ */
+ private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
+ throws IOException {
+ writer.append(prefix).append(clazz.qualifiedName());
+ ClassInfo superClass = clazz;
+ while ((superClass = superClass.superclass()) != null) {
+ writer.append(' ').append(superClass.qualifiedName());
+ }
+ writer.append('\n');
+ }
+
+ /**
+ * Checks the inheritance of {@link ClassInfo} objects. This method return
+ * <ul>
+ * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
+ * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
+ * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li>
+ * <li>{@link #TYPE_NONE}: in all other cases</li>
+ * </ul>
+ * @param clazz the {@link ClassInfo} to check.
+ */
+ private static int checkInheritance(ClassInfo clazz) {
+ if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
+ return TYPE_LAYOUT;
+ } else if ("android.view.View".equals(clazz.qualifiedName())) {
+ return TYPE_WIDGET;
+ } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
+ return TYPE_LAYOUT_PARAM;
+ }
+
+ ClassInfo parent = clazz.superclass();
+ if (parent != null) {
+ return checkInheritance(parent);
+ }
+
+ return TYPE_NONE;
+ }
+}