2 * Copyright (C) 2008 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 import com.sun.javadoc.*;
19 import org.clearsilver.HDF;
23 import java.lang.reflect.Proxy;
24 import java.lang.reflect.Array;
25 import java.lang.reflect.InvocationHandler;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
31 private static final String SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant";
32 private static final String SDK_CONSTANT_TYPE_ACTIVITY_ACTION = "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION";
33 private static final String SDK_CONSTANT_TYPE_BROADCAST_ACTION = "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION";
34 private static final String SDK_CONSTANT_TYPE_SERVICE_ACTION = "android.annotation.SdkConstant.SdkConstantType.SERVICE_INTENT_ACTION";
35 private static final String SDK_CONSTANT_TYPE_CATEGORY = "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY";
36 private static final String SDK_CONSTANT_TYPE_FEATURE = "android.annotation.SdkConstant.SdkConstantType.FEATURE";
37 private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget";
38 private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout";
40 private static final int TYPE_NONE = 0;
41 private static final int TYPE_WIDGET = 1;
42 private static final int TYPE_LAYOUT = 2;
43 private static final int TYPE_LAYOUT_PARAM = 3;
45 public static final int SHOW_PUBLIC = 0x00000001;
46 public static final int SHOW_PROTECTED = 0x00000003;
47 public static final int SHOW_PACKAGE = 0x00000007;
48 public static final int SHOW_PRIVATE = 0x0000000f;
49 public static final int SHOW_HIDDEN = 0x0000001f;
51 public static int showLevel = SHOW_PROTECTED;
53 public static final String javadocDir = "reference/";
54 public static String htmlExtension;
56 public static RootDoc root;
57 public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
58 public static Map<Character,String> escapeChars = new HashMap<Character,String>();
59 public static String title = "";
60 public static SinceTagger sinceTagger = new SinceTagger();
61 public static HashSet<String> knownTags = new HashSet<String>();
63 private static boolean parseComments = false;
64 private static boolean generateDocs = true;
67 * Returns true if we should parse javadoc comments,
68 * reporting errors in the process.
70 public static boolean parseComments() {
71 return generateDocs || parseComments;
74 public static boolean checkLevel(int level)
76 return (showLevel & level) == level;
79 public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp,
80 boolean priv, boolean hidden)
83 if (hidden && !checkLevel(SHOW_HIDDEN)) {
86 if (pub && checkLevel(SHOW_PUBLIC)) {
89 if (prot && checkLevel(SHOW_PROTECTED)) {
92 if (pkgp && checkLevel(SHOW_PACKAGE)) {
95 if (priv && checkLevel(SHOW_PRIVATE)) {
101 public static boolean start(RootDoc r)
103 String keepListFile = null;
104 String proofreadFile = null;
105 String todoFile = null;
106 String sdkValuePath = null;
107 ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>();
108 String stubsDir = null;
109 //Create the dependency graph for the stubs directory
110 boolean apiXML = false;
111 boolean offlineMode = false;
112 String apiFile = null;
113 String debugStubsFile = "";
114 HashSet<String> stubPackages = null;
115 ArrayList<String> knownTagsFiles = new ArrayList<String>();
119 String[][] options = r.options();
120 for (String[] a: options) {
121 if (a[0].equals("-d")) {
122 ClearPage.outputDir = a[1];
124 else if (a[0].equals("-templatedir")) {
125 ClearPage.addTemplateDir(a[1]);
127 else if (a[0].equals("-hdf")) {
128 mHDFData.add(new String[] {a[1], a[2]});
130 else if (a[0].equals("-knowntags")) {
131 knownTagsFiles.add(a[1]);
133 else if (a[0].equals("-toroot")) {
134 ClearPage.toroot = a[1];
136 else if (a[0].equals("-samplecode")) {
137 sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
139 else if (a[0].equals("-htmldir")) {
140 ClearPage.htmlDirs.add(a[1]);
142 else if (a[0].equals("-title")) {
143 DroidDoc.title = a[1];
145 else if (a[0].equals("-werror")) {
146 Errors.setWarningsAreErrors(true);
148 else if (a[0].equals("-error") || a[0].equals("-warning")
149 || a[0].equals("-hide")) {
152 if (a[0].equals("-error")) {
153 level = Errors.ERROR;
155 else if (a[0].equals("-warning")) {
156 level = Errors.WARNING;
158 else if (a[0].equals("-hide")) {
159 level = Errors.HIDDEN;
161 Errors.setErrorLevel(Integer.parseInt(a[1]), level);
163 catch (NumberFormatException e) {
164 // already printed below
168 else if (a[0].equals("-keeplist")) {
171 else if (a[0].equals("-proofread")) {
172 proofreadFile = a[1];
174 else if (a[0].equals("-todo")) {
177 else if (a[0].equals("-public")) {
178 showLevel = SHOW_PUBLIC;
180 else if (a[0].equals("-protected")) {
181 showLevel = SHOW_PROTECTED;
183 else if (a[0].equals("-package")) {
184 showLevel = SHOW_PACKAGE;
186 else if (a[0].equals("-private")) {
187 showLevel = SHOW_PRIVATE;
189 else if (a[0].equals("-hidden")) {
190 showLevel = SHOW_HIDDEN;
192 else if (a[0].equals("-stubs")) {
195 else if (a[0].equals("-stubpackages")) {
196 stubPackages = new HashSet();
197 for (String pkg: a[1].split(":")) {
198 stubPackages.add(pkg);
201 else if (a[0].equals("-sdkvalues")) {
204 else if (a[0].equals("-apixml")) {
208 else if (a[0].equals("-nodocs")) {
209 generateDocs = false;
211 else if (a[0].equals("-parsecomments")) {
212 parseComments = true;
214 else if (a[0].equals("-since")) {
215 sinceTagger.addVersion(a[1], a[2]);
217 else if (a[0].equals("-offlinemode")) {
222 if (!readKnownTagsFiles(knownTags, knownTagsFiles)) {
226 // read some prefs from the template
227 if (!readTemplateSettings()) {
231 // Set up the data structures
232 Converter.makeInfo(r);
235 long startTime = System.nanoTime();
237 // Apply @since tags from the XML file
238 sinceTagger.tagAll(Converter.rootClasses());
240 // Files for proofreading
241 if (proofreadFile != null) {
242 Proofread.initProofread(proofreadFile);
244 if (todoFile != null) {
245 TodoFile.writeTodoFile(todoFile);
249 if (!ClearPage.htmlDirs.isEmpty()) {
254 NavTree.writeNavTree(javadocDir);
257 writePackages(javadocDir
258 + (!ClearPage.htmlDirs.isEmpty()
259 ? "packages" + htmlExtension
260 : "index" + htmlExtension));
268 // Lists for JavaScript
270 if (keepListFile != null) {
271 writeKeepList(keepListFile);
275 for (SampleCode sc: sampleCodes) {
276 sc.write(offlineMode);
282 Proofread.finishProofread(proofreadFile);
284 if (sdkValuePath != null) {
285 writeSdkValues(sdkValuePath);
288 long time = System.nanoTime() - startTime;
289 System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to "
290 + ClearPage.outputDir);
294 if (stubsDir != null) {
295 Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
298 Errors.printErrors();
299 return !Errors.hadError;
302 private static void writeIndex() {
303 HDF data = makeHDF();
304 ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
307 private static boolean readTemplateSettings()
309 HDF data = makeHDF();
310 htmlExtension = data.getValue("template.extension", ".html");
313 String k = data.getValue("template.escape." + i + ".key", "");
314 String v = data.getValue("template.escape." + i + ".value", "");
318 if (k.length() != 1) {
319 System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
322 escapeChars.put(k.charAt(0), v);
328 private static boolean readKnownTagsFiles(HashSet<String> knownTags,
329 ArrayList<String> knownTagsFiles) {
330 for (String fn: knownTagsFiles) {
331 BufferedReader in = null;
333 in = new BufferedReader(new FileReader(fn));
335 boolean fail = false;
338 String line = in.readLine();
343 if (line.length() == 0) {
345 } else if (line.charAt(0) == '#') {
348 String[] words = line.split("\\s+", 2);
349 if (words.length == 2) {
350 if (words[1].charAt(0) != '#') {
351 System.err.println(fn + ":" + lineno
352 + ": Only one tag allowed per line: " + line);
357 knownTags.add(words[0]);
362 } catch (IOException ex) {
363 System.err.println("Error reading file: " + fn + " (" + ex.getMessage() + ")");
369 } catch (IOException e) {
377 public static String escape(String s) {
378 if (escapeChars.size() == 0) {
381 StringBuffer b = null;
383 final int N = s.length();
384 for (int i=0; i<N; i++) {
385 char c = s.charAt(i);
386 String mapped = escapeChars.get(c);
387 if (mapped != null) {
389 b = new StringBuffer(s.length() + mapped.length());
392 b.append(s.substring(begin, i));
400 b.append(s.substring(begin, N));
407 public static void setPageTitle(HDF data, String title)
410 if (DroidDoc.title.length() > 0) {
411 s += " - " + DroidDoc.title;
413 data.setValue("page.title", s);
416 public static LanguageVersion languageVersion()
418 return LanguageVersion.JAVA_1_5;
421 public static int optionLength(String option)
423 if (option.equals("-d")) {
426 if (option.equals("-templatedir")) {
429 if (option.equals("-hdf")) {
432 if (option.equals("-knowntags")) {
435 if (option.equals("-toroot")) {
438 if (option.equals("-samplecode")) {
441 if (option.equals("-htmldir")) {
444 if (option.equals("-title")) {
447 if (option.equals("-werror")) {
450 if (option.equals("-hide")) {
453 if (option.equals("-warning")) {
456 if (option.equals("-error")) {
459 if (option.equals("-keeplist")) {
462 if (option.equals("-proofread")) {
465 if (option.equals("-todo")) {
468 if (option.equals("-public")) {
471 if (option.equals("-protected")) {
474 if (option.equals("-package")) {
477 if (option.equals("-private")) {
480 if (option.equals("-hidden")) {
483 if (option.equals("-stubs")) {
486 if (option.equals("-stubpackages")) {
489 if (option.equals("-sdkvalues")) {
492 if (option.equals("-apixml")) {
495 if (option.equals("-nodocs")) {
498 if (option.equals("-parsecomments")) {
501 if (option.equals("-since")) {
504 if (option.equals("-offlinemode")) {
510 public static boolean validOptions(String[][] options, DocErrorReporter r)
512 for (String[] a: options) {
513 if (a[0].equals("-error") || a[0].equals("-warning")
514 || a[0].equals("-hide")) {
516 Integer.parseInt(a[1]);
518 catch (NumberFormatException e) {
519 r.printError("bad -" + a[0] + " value must be a number: "
529 public static HDF makeHDF()
531 HDF data = new HDF();
533 for (String[] p: mHDFData) {
534 data.setValue(p[0], p[1]);
538 for (String p: ClearPage.hdfFiles) {
542 catch (IOException e) {
543 throw new RuntimeException(e);
549 public static HDF makePackageHDF()
551 HDF data = makeHDF();
552 ClassInfo[] classes = Converter.rootClasses();
554 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
555 for (ClassInfo cl: classes) {
556 PackageInfo pkg = cl.containingPackage();
563 sorted.put(name, pkg);
567 for (String s: sorted.keySet()) {
568 PackageInfo pkg = sorted.get(s);
570 if (pkg.isHidden()) {
573 Boolean allHidden = true;
575 ClassInfo[] classesToCheck = null;
579 classesToCheck = pkg.ordinaryClasses();
582 classesToCheck = pkg.enums();
585 classesToCheck = pkg.errors();
588 classesToCheck = pkg.exceptions();
591 classesToCheck = pkg.interfaces();
594 System.err.println("Error reading package: " + pkg.name());
597 for (ClassInfo cl : classesToCheck) {
598 if (!cl.isHidden()) {
612 data.setValue("reference", "true");
613 data.setValue("docs.packages." + i + ".name", s);
614 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
615 data.setValue("docs.packages." + i + ".since", pkg.getSince());
616 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
617 pkg.firstSentenceTags());
621 sinceTagger.writeVersionNames(data);
625 public static void writeDirectory(File dir, String relative)
627 File[] files = dir.listFiles();
628 int i, count = files.length;
629 for (i=0; i<count; i++) {
632 String templ = relative + f.getName();
633 int len = templ.length();
634 if (len > 3 && ".cs".equals(templ.substring(len-3))) {
635 HDF data = makeHDF();
636 String filename = templ.substring(0,len-3) + htmlExtension;
637 ClearPage.write(data, templ, filename);
639 else if (len > 3 && ".jd".equals(templ.substring(len-3))) {
640 String filename = templ.substring(0,len-3) + htmlExtension;
641 DocFile.writePage(f.getAbsolutePath(), relative, filename);
644 ClearPage.copyFile(f, templ);
647 else if (f.isDirectory()) {
648 writeDirectory(f, relative + f.getName() + "/");
653 public static void writeHTMLPages()
655 for (String htmlDir : ClearPage.htmlDirs) {
656 File f = new File(htmlDir);
657 if (!f.isDirectory()) {
658 System.err.println("htmlDir not a directory: " + htmlDir);
661 writeDirectory(f, "");
665 public static void writeLists()
667 HDF data = makeHDF();
669 ClassInfo[] classes = Converter.rootClasses();
671 SortedMap<String, Object> sorted = new TreeMap<String, Object>();
672 for (ClassInfo cl: classes) {
676 sorted.put(cl.qualifiedName(), cl);
677 PackageInfo pkg = cl.containingPackage();
684 sorted.put(name, pkg);
688 for (String s: sorted.keySet()) {
689 data.setValue("docs.pages." + i + ".id" , ""+i);
690 data.setValue("docs.pages." + i + ".label" , s);
692 Object o = sorted.get(s);
693 if (o instanceof PackageInfo) {
694 PackageInfo pkg = (PackageInfo)o;
695 data.setValue("docs.pages." + i + ".link" , pkg.htmlPage());
696 data.setValue("docs.pages." + i + ".type" , "package");
698 else if (o instanceof ClassInfo) {
699 ClassInfo cl = (ClassInfo)o;
700 data.setValue("docs.pages." + i + ".link" , cl.htmlPage());
701 data.setValue("docs.pages." + i + ".type" , "class");
706 ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
709 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
710 if (!notStrippable.add(cl)) {
711 // slight optimization: if it already contains cl, it already contains
712 // all of cl's parents
715 ClassInfo supr = cl.superclass();
717 cantStripThis(supr, notStrippable);
719 for (ClassInfo iface: cl.interfaces()) {
720 cantStripThis(iface, notStrippable);
724 private static String getPrintableName(ClassInfo cl) {
725 ClassInfo containingClass = cl.containingClass();
726 if (containingClass != null) {
727 // This is an inner class.
728 String baseName = cl.name();
729 baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
730 return getPrintableName(containingClass) + '$' + baseName;
732 return cl.qualifiedName();
736 * Writes the list of classes that must be present in order to
737 * provide the non-hidden APIs known to javadoc.
739 * @param filename the path to the file to write the list to
741 public static void writeKeepList(String filename) {
742 HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
743 ClassInfo[] all = Converter.allClasses();
744 Arrays.sort(all); // just to make the file a little more readable
746 // If a class is public and not hidden, then it and everything it derives
747 // from cannot be stripped. Otherwise we can strip it.
748 for (ClassInfo cl: all) {
749 if (cl.isPublic() && !cl.isHidden()) {
750 cantStripThis(cl, notStrippable);
753 PrintStream stream = null;
755 stream = new PrintStream(filename);
756 for (ClassInfo cl: notStrippable) {
757 stream.println(getPrintableName(cl));
760 catch (FileNotFoundException e) {
761 System.err.println("error writing file: " + filename);
764 if (stream != null) {
770 private static PackageInfo[] sVisiblePackages = null;
771 public static PackageInfo[] choosePackages() {
772 if (sVisiblePackages != null) {
773 return sVisiblePackages;
776 ClassInfo[] classes = Converter.rootClasses();
777 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
778 for (ClassInfo cl: classes) {
779 PackageInfo pkg = cl.containingPackage();
786 sorted.put(name, pkg);
789 ArrayList<PackageInfo> result = new ArrayList();
791 for (String s: sorted.keySet()) {
792 PackageInfo pkg = sorted.get(s);
794 if (pkg.isHidden()) {
797 Boolean allHidden = true;
799 ClassInfo[] classesToCheck = null;
803 classesToCheck = pkg.ordinaryClasses();
806 classesToCheck = pkg.enums();
809 classesToCheck = pkg.errors();
812 classesToCheck = pkg.exceptions();
815 classesToCheck = pkg.interfaces();
818 System.err.println("Error reading package: " + pkg.name());
821 for (ClassInfo cl : classesToCheck) {
822 if (!cl.isHidden()) {
839 sVisiblePackages = result.toArray(new PackageInfo[result.size()]);
840 return sVisiblePackages;
843 public static void writePackages(String filename)
845 HDF data = makePackageHDF();
848 for (PackageInfo pkg: choosePackages()) {
851 data.setValue("docs.packages." + i + ".name", pkg.name());
852 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
853 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
854 pkg.firstSentenceTags());
859 setPageTitle(data, "Package Index");
861 TagInfo.makeHDF(data, "root.descr",
862 Converter.convertTags(root.inlineTags(), null));
864 ClearPage.write(data, "packages.cs", filename);
865 ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
867 Proofread.writePackages(filename,
868 Converter.convertTags(root.inlineTags(), null));
871 public static void writePackage(PackageInfo pkg)
873 // these this and the description are in the same directory,
875 HDF data = makePackageHDF();
877 String name = pkg.name();
879 data.setValue("package.name", name);
880 data.setValue("package.since", pkg.getSince());
881 data.setValue("package.descr", "...description...");
883 makeClassListHDF(data, "package.interfaces",
884 ClassInfo.sortByName(pkg.interfaces()));
885 makeClassListHDF(data, "package.classes",
886 ClassInfo.sortByName(pkg.ordinaryClasses()));
887 makeClassListHDF(data, "package.enums",
888 ClassInfo.sortByName(pkg.enums()));
889 makeClassListHDF(data, "package.exceptions",
890 ClassInfo.sortByName(pkg.exceptions()));
891 makeClassListHDF(data, "package.errors",
892 ClassInfo.sortByName(pkg.errors()));
893 TagInfo.makeHDF(data, "package.shortDescr",
894 pkg.firstSentenceTags());
895 TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
897 String filename = pkg.htmlPage();
898 setPageTitle(data, name);
899 ClearPage.write(data, "package.cs", filename);
901 filename = pkg.fullDescriptionHtmlPage();
902 setPageTitle(data, name + " Details");
903 ClearPage.write(data, "package-descr.cs", filename);
905 Proofread.writePackage(filename, pkg.inlineTags());
908 public static void writeClassLists()
911 HDF data = makePackageHDF();
913 ClassInfo[] classes = PackageInfo.filterHidden(
914 Converter.convertClasses(root.classes()));
915 if (classes.length == 0) {
919 Sorter[] sorted = new Sorter[classes.length];
920 for (i=0; i<sorted.length; i++) {
921 ClassInfo cl = classes[i];
922 String name = cl.name();
923 sorted[i] = new Sorter(name, cl);
928 // make a pass and resolve ones that have the same name
930 String lastName = sorted[0].label;
931 for (i=1; i<sorted.length; i++) {
932 String s = sorted[i].label;
933 if (!lastName.equals(s)) {
934 if (firstMatch != i-1) {
935 // there were duplicates
936 for (int j=firstMatch; j<i; j++) {
937 PackageInfo pkg = ((ClassInfo)sorted[j].data).containingPackage();
939 sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
951 for (i=0; i<sorted.length; i++) {
952 String s = sorted[i].label;
953 ClassInfo cl = (ClassInfo)sorted[i].data;
954 char first = Character.toUpperCase(s.charAt(0));
955 cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
958 setPageTitle(data, "Class Index");
959 ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
962 // we use the word keywords because "index" means something else in html land
963 // the user only ever sees the word index
964 /* public static void writeKeywords()
966 ArrayList<KeywordEntry> keywords = new ArrayList<KeywordEntry>();
968 ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes()));
970 for (ClassInfo cl: classes) {
971 cl.makeKeywordEntries(keywords);
974 HDF data = makeHDF();
976 Collections.sort(keywords);
979 for (KeywordEntry entry: keywords) {
980 String base = "keywords." + entry.firstChar() + "." + i;
981 entry.makeHDF(data, base);
985 setPageTitle(data, "Index");
986 ClearPage.write(data, "keywords.cs", javadocDir + "keywords" + htmlExtension);
989 public static void writeHierarchy()
991 ClassInfo[] classes = Converter.rootClasses();
992 ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
993 for (ClassInfo cl: classes) {
994 if (!cl.isHidden()) {
998 HDF data = makePackageHDF();
999 Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
1000 setPageTitle(data, "Class Hierarchy");
1001 ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
1004 public static void writeClasses()
1006 ClassInfo[] classes = Converter.rootClasses();
1008 for (ClassInfo cl: classes) {
1009 HDF data = makePackageHDF();
1010 if (!cl.isHidden()) {
1011 writeClass(cl, data);
1016 public static void writeClass(ClassInfo cl, HDF data)
1020 setPageTitle(data, cl.name());
1021 ClearPage.write(data, "class.cs", cl.htmlPage());
1023 Proofread.writeClass(cl.htmlPage(), cl);
1026 public static void makeClassListHDF(HDF data, String base,
1027 ClassInfo[] classes)
1029 for (int i=0; i<classes.length; i++) {
1030 ClassInfo cl = classes[i];
1031 if (!cl.isHidden()) {
1032 cl.makeShortDescrHDF(data, base + "." + i);
1037 public static String linkTarget(String source, String target)
1039 String[] src = source.split("/");
1040 String[] tgt = target.split("/");
1042 int srclen = src.length;
1043 int tgtlen = tgt.length;
1046 while (same < (srclen-1)
1047 && same < (tgtlen-1)
1048 && (src[same].equals(tgt[same]))) {
1054 int up = srclen-same-1;
1055 for (int i=0; i<up; i++) {
1061 for (int i=same; i<N; i++) {
1070 * Returns true if the given element has an @hide or @pending annotation.
1072 private static boolean hasHideAnnotation(Doc doc) {
1073 String comment = doc.getRawCommentText();
1074 return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1;
1078 * Returns true if the given element is hidden.
1080 private static boolean isHidden(Doc doc) {
1081 // Methods, fields, constructors.
1082 if (doc instanceof MemberDoc) {
1083 return hasHideAnnotation(doc);
1086 // Classes, interfaces, enums, annotation types.
1087 if (doc instanceof ClassDoc) {
1088 ClassDoc classDoc = (ClassDoc) doc;
1090 // Check the containing package.
1091 if (hasHideAnnotation(classDoc.containingPackage())) {
1095 // Check the class doc and containing class docs if this is a
1097 ClassDoc current = classDoc;
1099 if (hasHideAnnotation(current)) {
1103 current = current.containingClass();
1104 } while (current != null);
1111 * Filters out hidden elements.
1113 private static Object filterHidden(Object o, Class<?> expected) {
1118 Class type = o.getClass();
1119 if (type.getName().startsWith("com.sun.")) {
1120 // TODO: Implement interfaces from superclasses, too.
1121 return Proxy.newProxyInstance(type.getClassLoader(),
1122 type.getInterfaces(), new HideHandler(o));
1123 } else if (o instanceof Object[]) {
1124 Class<?> componentType = expected.getComponentType();
1125 Object[] array = (Object[]) o;
1126 List<Object> list = new ArrayList<Object>(array.length);
1127 for (Object entry : array) {
1128 if ((entry instanceof Doc) && isHidden((Doc) entry)) {
1131 list.add(filterHidden(entry, componentType));
1133 return list.toArray(
1134 (Object[]) Array.newInstance(componentType, list.size()));
1141 * Filters hidden elements out of method return values.
1143 private static class HideHandler implements InvocationHandler {
1145 private final Object target;
1147 public HideHandler(Object target) {
1148 this.target = target;
1151 public Object invoke(Object proxy, Method method, Object[] args)
1153 String methodName = method.getName();
1155 if (methodName.equals("compareTo") ||
1156 methodName.equals("equals") ||
1157 methodName.equals("overrides") ||
1158 methodName.equals("subclassOf")) {
1159 args[0] = unwrap(args[0]);
1163 if (methodName.equals("getRawCommentText")) {
1164 return filterComment((String) method.invoke(target, args));
1167 // escape "&" in disjunctive types.
1168 if (proxy instanceof Type && methodName.equals("toString")) {
1169 return ((String) method.invoke(target, args))
1170 .replace("&", "&");
1174 return filterHidden(method.invoke(target, args),
1175 method.getReturnType());
1176 } catch (InvocationTargetException e) {
1177 throw e.getTargetException();
1181 private String filterComment(String s) {
1188 // Work around off by one error
1189 while (s.length() >= 5
1190 && s.charAt(s.length() - 5) == '{') {
1197 private static Object unwrap(Object proxy) {
1198 if (proxy instanceof Proxy)
1199 return ((HideHandler)Proxy.getInvocationHandler(proxy)).target;
1204 public static String scope(Scoped scoped) {
1205 if (scoped.isPublic()) {
1208 else if (scoped.isProtected()) {
1211 else if (scoped.isPackagePrivate()) {
1214 else if (scoped.isPrivate()) {
1218 throw new RuntimeException("invalid scope for object " + scoped);
1223 * Collect the values used by the Dev tools and write them in files packaged with the SDK
1224 * @param output the ouput directory for the files.
1226 private static void writeSdkValues(String output) {
1227 ArrayList<String> activityActions = new ArrayList<String>();
1228 ArrayList<String> broadcastActions = new ArrayList<String>();
1229 ArrayList<String> serviceActions = new ArrayList<String>();
1230 ArrayList<String> categories = new ArrayList<String>();
1231 ArrayList<String> features = new ArrayList<String>();
1233 ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
1234 ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
1235 ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
1237 ClassInfo[] classes = Converter.allClasses();
1239 // Go through all the fields of all the classes, looking SDK stuff.
1240 for (ClassInfo clazz : classes) {
1242 // first check constant fields for the SdkConstant annotation.
1243 FieldInfo[] fields = clazz.allSelfFields();
1244 for (FieldInfo field : fields) {
1245 Object cValue = field.constantValue();
1246 if (cValue != null) {
1247 AnnotationInstanceInfo[] annotations = field.annotations();
1248 if (annotations.length > 0) {
1249 for (AnnotationInstanceInfo annotation : annotations) {
1250 if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1251 AnnotationValueInfo[] values = annotation.elementValues();
1252 if (values.length > 0) {
1253 String type = values[0].valueString();
1254 if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
1255 activityActions.add(cValue.toString());
1256 } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
1257 broadcastActions.add(cValue.toString());
1258 } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
1259 serviceActions.add(cValue.toString());
1260 } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
1261 categories.add(cValue.toString());
1262 } else if (SDK_CONSTANT_TYPE_FEATURE.equals(type)) {
1263 features.add(cValue.toString());
1273 // Now check the class for @Widget or if its in the android.widget package
1274 // (unless the class is hidden or abstract, or non public)
1275 if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) {
1276 boolean annotated = false;
1277 AnnotationInstanceInfo[] annotations = clazz.annotations();
1278 if (annotations.length > 0) {
1279 for (AnnotationInstanceInfo annotation : annotations) {
1280 if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
1284 } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1292 if (annotated == false) {
1293 // lets check if this is inside android.widget
1294 PackageInfo pckg = clazz.containingPackage();
1295 String packageName = pckg.name();
1296 if ("android.widget".equals(packageName) ||
1297 "android.view".equals(packageName)) {
1298 // now we check what this class inherits either from android.view.ViewGroup
1299 // or android.view.View, or android.view.ViewGroup.LayoutParams
1300 int type = checkInheritance(clazz);
1308 case TYPE_LAYOUT_PARAM:
1309 layoutParams.add(clazz);
1317 // now write the files, whether or not the list are empty.
1318 // the SDK built requires those files to be present.
1320 Collections.sort(activityActions);
1321 writeValues(output + "/activity_actions.txt", activityActions);
1323 Collections.sort(broadcastActions);
1324 writeValues(output + "/broadcast_actions.txt", broadcastActions);
1326 Collections.sort(serviceActions);
1327 writeValues(output + "/service_actions.txt", serviceActions);
1329 Collections.sort(categories);
1330 writeValues(output + "/categories.txt", categories);
1332 Collections.sort(features);
1333 writeValues(output + "/features.txt", features);
1335 // before writing the list of classes, we do some checks, to make sure the layout params
1336 // are enclosed by a layout class (and not one that has been declared as a widget)
1337 for (int i = 0 ; i < layoutParams.size();) {
1338 ClassInfo layoutParamClass = layoutParams.get(i);
1339 ClassInfo containingClass = layoutParamClass.containingClass();
1340 if (containingClass == null || layouts.indexOf(containingClass) == -1) {
1341 layoutParams.remove(i);
1347 writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
1351 * Writes a list of values into a text files.
1352 * @param pathname the absolute os path of the output file.
1353 * @param values the list of values to write.
1355 private static void writeValues(String pathname, ArrayList<String> values) {
1356 FileWriter fw = null;
1357 BufferedWriter bw = null;
1359 fw = new FileWriter(pathname, false);
1360 bw = new BufferedWriter(fw);
1362 for (String value : values) {
1363 bw.append(value).append('\n');
1365 } catch (IOException e) {
1369 if (bw != null) bw.close();
1370 } catch (IOException e) {
1374 if (fw != null) fw.close();
1375 } catch (IOException e) {
1382 * Writes the widget/layout/layout param classes into a text files.
1383 * @param pathname the absolute os path of the output file.
1384 * @param widgets the list of widget classes to write.
1385 * @param layouts the list of layout classes to write.
1386 * @param layoutParams the list of layout param classes to write.
1388 private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
1389 ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
1390 FileWriter fw = null;
1391 BufferedWriter bw = null;
1393 fw = new FileWriter(pathname, false);
1394 bw = new BufferedWriter(fw);
1396 // write the 3 types of classes.
1397 for (ClassInfo clazz : widgets) {
1398 writeClass(bw, clazz, 'W');
1400 for (ClassInfo clazz : layoutParams) {
1401 writeClass(bw, clazz, 'P');
1403 for (ClassInfo clazz : layouts) {
1404 writeClass(bw, clazz, 'L');
1406 } catch (IOException e) {
1410 if (bw != null) bw.close();
1411 } catch (IOException e) {
1415 if (fw != null) fw.close();
1416 } catch (IOException e) {
1423 * Writes a class name and its super class names into a {@link BufferedWriter}.
1424 * @param writer the BufferedWriter to write into
1425 * @param clazz the class to write
1426 * @param prefix the prefix to put at the beginning of the line.
1427 * @throws IOException
1429 private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
1430 throws IOException {
1431 writer.append(prefix).append(clazz.qualifiedName());
1432 ClassInfo superClass = clazz;
1433 while ((superClass = superClass.superclass()) != null) {
1434 writer.append(' ').append(superClass.qualifiedName());
1436 writer.append('\n');
1440 * Checks the inheritance of {@link ClassInfo} objects. This method return
1442 * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
1443 * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
1444 * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li>
1445 * <li>{@link #TYPE_NONE}: in all other cases</li>
1447 * @param clazz the {@link ClassInfo} to check.
1449 private static int checkInheritance(ClassInfo clazz) {
1450 if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
1452 } else if ("android.view.View".equals(clazz.qualifiedName())) {
1454 } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
1455 return TYPE_LAYOUT_PARAM;
1458 ClassInfo parent = clazz.superclass();
1459 if (parent != null) {
1460 return checkInheritance(parent);