blob: f48b56c721e9d320eb4a2c9c50c4bbdc3ffadecd [file] [log] [blame]
The Android Open Source Project88b60792009-03-03 19:28:42 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17import com.sun.javadoc.*;
18
19import org.clearsilver.HDF;
20
21import java.util.*;
22import java.io.*;
23import java.lang.reflect.Proxy;
24import java.lang.reflect.Array;
25import java.lang.reflect.InvocationHandler;
26import java.lang.reflect.InvocationTargetException;
27import java.lang.reflect.Method;
28
29public class DroidDoc
30{
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_WIDGET_ANNOTATION = "android.annotation.Widget";
37 private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout";
38
39 private static final int TYPE_NONE = 0;
40 private static final int TYPE_WIDGET = 1;
41 private static final int TYPE_LAYOUT = 2;
42 private static final int TYPE_LAYOUT_PARAM = 3;
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -070043
The Android Open Source Project88b60792009-03-03 19:28:42 -080044 public static final int SHOW_PUBLIC = 0x00000001;
45 public static final int SHOW_PROTECTED = 0x00000003;
46 public static final int SHOW_PACKAGE = 0x00000007;
47 public static final int SHOW_PRIVATE = 0x0000000f;
48 public static final int SHOW_HIDDEN = 0x0000001f;
49
50 public static int showLevel = SHOW_PROTECTED;
51
52 public static final String javadocDir = "reference/";
53 public static String htmlExtension;
54
55 public static RootDoc root;
56 public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
57 public static Map<Character,String> escapeChars = new HashMap<Character,String>();
58 public static String title = "";
Scott Main25fda192009-08-04 11:26:30 -070059 public static SinceTagger sinceTagger = new SinceTagger();
The Android Open Source Project88b60792009-03-03 19:28:42 -080060
61 public static boolean checkLevel(int level)
62 {
63 return (showLevel & level) == level;
64 }
65
66 public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp,
67 boolean priv, boolean hidden)
68 {
69 int level = 0;
70 if (hidden && !checkLevel(SHOW_HIDDEN)) {
71 return false;
72 }
73 if (pub && checkLevel(SHOW_PUBLIC)) {
74 return true;
75 }
76 if (prot && checkLevel(SHOW_PROTECTED)) {
77 return true;
78 }
79 if (pkgp && checkLevel(SHOW_PACKAGE)) {
80 return true;
81 }
82 if (priv && checkLevel(SHOW_PRIVATE)) {
83 return true;
84 }
85 return false;
86 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -070087
The Android Open Source Project88b60792009-03-03 19:28:42 -080088 public static boolean start(RootDoc r)
89 {
90 String keepListFile = null;
91 String proofreadFile = null;
92 String todoFile = null;
93 String sdkValuePath = null;
94 ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>();
95 String stubsDir = null;
96 //Create the dependency graph for the stubs directory
97 boolean apiXML = false;
Joe Onoratob7c41aa2009-07-20 11:57:59 -040098 boolean noDocs = false;
The Android Open Source Project88b60792009-03-03 19:28:42 -080099 String apiFile = null;
100 String debugStubsFile = "";
101 HashSet<String> stubPackages = null;
102
103 root = r;
104
105 String[][] options = r.options();
106 for (String[] a: options) {
107 if (a[0].equals("-d")) {
108 ClearPage.outputDir = a[1];
109 }
110 else if (a[0].equals("-templatedir")) {
111 ClearPage.addTemplateDir(a[1]);
112 }
113 else if (a[0].equals("-hdf")) {
114 mHDFData.add(new String[] {a[1], a[2]});
115 }
116 else if (a[0].equals("-toroot")) {
117 ClearPage.toroot = a[1];
118 }
119 else if (a[0].equals("-samplecode")) {
120 sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
121 }
122 else if (a[0].equals("-htmldir")) {
123 ClearPage.htmlDir = a[1];
124 }
125 else if (a[0].equals("-title")) {
126 DroidDoc.title = a[1];
127 }
128 else if (a[0].equals("-werror")) {
129 Errors.setWarningsAreErrors(true);
130 }
131 else if (a[0].equals("-error") || a[0].equals("-warning")
132 || a[0].equals("-hide")) {
133 try {
134 int level = -1;
135 if (a[0].equals("-error")) {
136 level = Errors.ERROR;
137 }
138 else if (a[0].equals("-warning")) {
139 level = Errors.WARNING;
140 }
141 else if (a[0].equals("-hide")) {
142 level = Errors.HIDDEN;
143 }
144 Errors.setErrorLevel(Integer.parseInt(a[1]), level);
145 }
146 catch (NumberFormatException e) {
147 // already printed below
148 return false;
149 }
150 }
151 else if (a[0].equals("-keeplist")) {
152 keepListFile = a[1];
153 }
154 else if (a[0].equals("-proofread")) {
155 proofreadFile = a[1];
156 }
157 else if (a[0].equals("-todo")) {
158 todoFile = a[1];
159 }
160 else if (a[0].equals("-public")) {
161 showLevel = SHOW_PUBLIC;
162 }
163 else if (a[0].equals("-protected")) {
164 showLevel = SHOW_PROTECTED;
165 }
166 else if (a[0].equals("-package")) {
167 showLevel = SHOW_PACKAGE;
168 }
169 else if (a[0].equals("-private")) {
170 showLevel = SHOW_PRIVATE;
171 }
172 else if (a[0].equals("-hidden")) {
173 showLevel = SHOW_HIDDEN;
174 }
175 else if (a[0].equals("-stubs")) {
176 stubsDir = a[1];
177 }
178 else if (a[0].equals("-stubpackages")) {
179 stubPackages = new HashSet();
180 for (String pkg: a[1].split(":")) {
181 stubPackages.add(pkg);
182 }
183 }
184 else if (a[0].equals("-sdkvalues")) {
185 sdkValuePath = a[1];
186 }
187 else if (a[0].equals("-apixml")) {
188 apiXML = true;
189 apiFile = a[1];
190 }
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400191 else if (a[0].equals("-nodocs")) {
192 noDocs = true;
193 }
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700194 else if (a[0].equals("-since")) {
195 sinceTagger.addVersion(a[1], a[2]);
196 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800197 }
198
199 // read some prefs from the template
200 if (!readTemplateSettings()) {
201 return false;
202 }
203
204 // Set up the data structures
205 Converter.makeInfo(r);
206
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400207 if (!noDocs) {
208 long startTime = System.nanoTime();
209
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700210 // Apply @since tags from the XML file
211 sinceTagger.tagAll(Converter.rootClasses());
212
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400213 // Files for proofreading
214 if (proofreadFile != null) {
215 Proofread.initProofread(proofreadFile);
216 }
217 if (todoFile != null) {
218 TodoFile.writeTodoFile(todoFile);
219 }
220
221 // HTML Pages
222 if (ClearPage.htmlDir != null) {
223 writeHTMLPages();
224 }
225
226 // Navigation tree
227 NavTree.writeNavTree(javadocDir);
228
229 // Packages Pages
230 writePackages(javadocDir
231 + (ClearPage.htmlDir!=null
232 ? "packages" + htmlExtension
233 : "index" + htmlExtension));
234
235 // Classes
236 writeClassLists();
237 writeClasses();
238 writeHierarchy();
239 // writeKeywords();
240
241 // Lists for JavaScript
242 writeLists();
243 if (keepListFile != null) {
244 writeKeepList(keepListFile);
245 }
246
247 // Sample Code
248 for (SampleCode sc: sampleCodes) {
249 sc.write();
250 }
251
252 // Index page
253 writeIndex();
254
255 Proofread.finishProofread(proofreadFile);
256
257 if (sdkValuePath != null) {
258 writeSdkValues(sdkValuePath);
259 }
260
261 long time = System.nanoTime() - startTime;
262 System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to "
263 + ClearPage.outputDir);
The Android Open Source Project88b60792009-03-03 19:28:42 -0800264 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800265
266 // Stubs
267 if (stubsDir != null) {
268 Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
269 }
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700270
The Android Open Source Project88b60792009-03-03 19:28:42 -0800271 Errors.printErrors();
272 return !Errors.hadError;
273 }
274
275 private static void writeIndex() {
276 HDF data = makeHDF();
277 ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
278 }
279
280 private static boolean readTemplateSettings()
281 {
282 HDF data = makeHDF();
283 htmlExtension = data.getValue("template.extension", ".html");
284 int i=0;
285 while (true) {
286 String k = data.getValue("template.escape." + i + ".key", "");
287 String v = data.getValue("template.escape." + i + ".value", "");
288 if ("".equals(k)) {
289 break;
290 }
291 if (k.length() != 1) {
292 System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
293 return false;
294 }
295 escapeChars.put(k.charAt(0), v);
296 i++;
297 }
298 return true;
299 }
300
301 public static String escape(String s) {
302 if (escapeChars.size() == 0) {
303 return s;
304 }
305 StringBuffer b = null;
306 int begin = 0;
307 final int N = s.length();
308 for (int i=0; i<N; i++) {
309 char c = s.charAt(i);
310 String mapped = escapeChars.get(c);
311 if (mapped != null) {
312 if (b == null) {
313 b = new StringBuffer(s.length() + mapped.length());
314 }
315 if (begin != i) {
316 b.append(s.substring(begin, i));
317 }
318 b.append(mapped);
319 begin = i+1;
320 }
321 }
322 if (b != null) {
323 if (begin != N) {
324 b.append(s.substring(begin, N));
325 }
326 return b.toString();
327 }
328 return s;
329 }
330
331 public static void setPageTitle(HDF data, String title)
332 {
333 String s = title;
334 if (DroidDoc.title.length() > 0) {
335 s += " - " + DroidDoc.title;
336 }
337 data.setValue("page.title", s);
338 }
339
340 public static LanguageVersion languageVersion()
341 {
342 return LanguageVersion.JAVA_1_5;
343 }
344
345 public static int optionLength(String option)
346 {
347 if (option.equals("-d")) {
348 return 2;
349 }
350 if (option.equals("-templatedir")) {
351 return 2;
352 }
353 if (option.equals("-hdf")) {
354 return 3;
355 }
356 if (option.equals("-toroot")) {
357 return 2;
358 }
359 if (option.equals("-samplecode")) {
360 return 4;
361 }
362 if (option.equals("-htmldir")) {
363 return 2;
364 }
365 if (option.equals("-title")) {
366 return 2;
367 }
368 if (option.equals("-werror")) {
369 return 1;
370 }
371 if (option.equals("-hide")) {
372 return 2;
373 }
374 if (option.equals("-warning")) {
375 return 2;
376 }
377 if (option.equals("-error")) {
378 return 2;
379 }
380 if (option.equals("-keeplist")) {
381 return 2;
382 }
383 if (option.equals("-proofread")) {
384 return 2;
385 }
386 if (option.equals("-todo")) {
387 return 2;
388 }
389 if (option.equals("-public")) {
390 return 1;
391 }
392 if (option.equals("-protected")) {
393 return 1;
394 }
395 if (option.equals("-package")) {
396 return 1;
397 }
398 if (option.equals("-private")) {
399 return 1;
400 }
401 if (option.equals("-hidden")) {
402 return 1;
403 }
404 if (option.equals("-stubs")) {
405 return 2;
406 }
407 if (option.equals("-stubpackages")) {
408 return 2;
409 }
410 if (option.equals("-sdkvalues")) {
411 return 2;
412 }
413 if (option.equals("-apixml")) {
414 return 2;
415 }
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400416 if (option.equals("-nodocs")) {
417 return 1;
418 }
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700419 if (option.equals("-since")) {
420 return 3;
421 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800422 return 0;
423 }
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700424
The Android Open Source Project88b60792009-03-03 19:28:42 -0800425 public static boolean validOptions(String[][] options, DocErrorReporter r)
426 {
427 for (String[] a: options) {
428 if (a[0].equals("-error") || a[0].equals("-warning")
429 || a[0].equals("-hide")) {
430 try {
431 Integer.parseInt(a[1]);
432 }
433 catch (NumberFormatException e) {
434 r.printError("bad -" + a[0] + " value must be a number: "
435 + a[1]);
436 return false;
437 }
438 }
439 }
440
441 return true;
442 }
443
444 public static HDF makeHDF()
445 {
446 HDF data = new HDF();
447
448 for (String[] p: mHDFData) {
449 data.setValue(p[0], p[1]);
450 }
451
452 try {
453 for (String p: ClearPage.hdfFiles) {
454 data.readFile(p);
455 }
456 }
457 catch (IOException e) {
458 throw new RuntimeException(e);
459 }
460
461 return data;
462 }
463
464 public static HDF makePackageHDF()
465 {
466 HDF data = makeHDF();
467 ClassInfo[] classes = Converter.rootClasses();
468
469 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
470 for (ClassInfo cl: classes) {
471 PackageInfo pkg = cl.containingPackage();
472 String name;
473 if (pkg == null) {
474 name = "";
475 } else {
476 name = pkg.name();
477 }
478 sorted.put(name, pkg);
479 }
480
481 int i = 0;
482 for (String s: sorted.keySet()) {
483 PackageInfo pkg = sorted.get(s);
484
485 if (pkg.isHidden()) {
486 continue;
487 }
488 Boolean allHidden = true;
489 int pass = 0;
490 ClassInfo[] classesToCheck = null;
491 while (pass < 5 ) {
492 switch(pass) {
493 case 0:
494 classesToCheck = pkg.ordinaryClasses();
495 break;
496 case 1:
497 classesToCheck = pkg.enums();
498 break;
499 case 2:
500 classesToCheck = pkg.errors();
501 break;
502 case 3:
503 classesToCheck = pkg.exceptions();
504 break;
505 case 4:
506 classesToCheck = pkg.interfaces();
507 break;
508 default:
509 System.err.println("Error reading package: " + pkg.name());
510 break;
511 }
512 for (ClassInfo cl : classesToCheck) {
513 if (!cl.isHidden()) {
514 allHidden = false;
515 break;
516 }
517 }
518 if (!allHidden) {
519 break;
520 }
521 pass++;
522 }
523 if (allHidden) {
524 continue;
525 }
526
527 data.setValue("reference", "true");
528 data.setValue("docs.packages." + i + ".name", s);
529 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
Scott Maindf094242009-07-27 09:47:11 -0700530 data.setValue("docs.packages." + i + ".since", pkg.getSince());
The Android Open Source Project88b60792009-03-03 19:28:42 -0800531 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
532 pkg.firstSentenceTags());
533 i++;
534 }
535
Scott Main25fda192009-08-04 11:26:30 -0700536 sinceTagger.writeVersionNames(data);
The Android Open Source Project88b60792009-03-03 19:28:42 -0800537 return data;
538 }
539
540 public static void writeDirectory(File dir, String relative)
541 {
542 File[] files = dir.listFiles();
543 int i, count = files.length;
544 for (i=0; i<count; i++) {
545 File f = files[i];
546 if (f.isFile()) {
547 String templ = relative + f.getName();
548 int len = templ.length();
549 if (len > 3 && ".cs".equals(templ.substring(len-3))) {
550 HDF data = makeHDF();
551 String filename = templ.substring(0,len-3) + htmlExtension;
552 ClearPage.write(data, templ, filename);
553 }
554 else if (len > 3 && ".jd".equals(templ.substring(len-3))) {
555 String filename = templ.substring(0,len-3) + htmlExtension;
556 DocFile.writePage(f.getAbsolutePath(), relative, filename);
557 }
558 else {
559 ClearPage.copyFile(f, templ);
560 }
561 }
562 else if (f.isDirectory()) {
563 writeDirectory(f, relative + f.getName() + "/");
564 }
565 }
566 }
567
568 public static void writeHTMLPages()
569 {
570 File f = new File(ClearPage.htmlDir);
571 if (!f.isDirectory()) {
572 System.err.println("htmlDir not a directory: " + ClearPage.htmlDir);
573 }
574 writeDirectory(f, "");
575 }
576
577 public static void writeLists()
578 {
579 HDF data = makeHDF();
580
581 ClassInfo[] classes = Converter.rootClasses();
582
583 SortedMap<String, Object> sorted = new TreeMap<String, Object>();
584 for (ClassInfo cl: classes) {
585 if (cl.isHidden()) {
586 continue;
587 }
588 sorted.put(cl.qualifiedName(), cl);
589 PackageInfo pkg = cl.containingPackage();
590 String name;
591 if (pkg == null) {
592 name = "";
593 } else {
594 name = pkg.name();
595 }
596 sorted.put(name, pkg);
597 }
598
599 int i = 0;
600 for (String s: sorted.keySet()) {
601 data.setValue("docs.pages." + i + ".id" , ""+i);
602 data.setValue("docs.pages." + i + ".label" , s);
603
604 Object o = sorted.get(s);
605 if (o instanceof PackageInfo) {
606 PackageInfo pkg = (PackageInfo)o;
607 data.setValue("docs.pages." + i + ".link" , pkg.htmlPage());
608 data.setValue("docs.pages." + i + ".type" , "package");
609 }
610 else if (o instanceof ClassInfo) {
611 ClassInfo cl = (ClassInfo)o;
612 data.setValue("docs.pages." + i + ".link" , cl.htmlPage());
613 data.setValue("docs.pages." + i + ".type" , "class");
614 }
615 i++;
616 }
617
618 ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
619 }
620
621 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
622 if (!notStrippable.add(cl)) {
623 // slight optimization: if it already contains cl, it already contains
624 // all of cl's parents
625 return;
626 }
627 ClassInfo supr = cl.superclass();
628 if (supr != null) {
629 cantStripThis(supr, notStrippable);
630 }
631 for (ClassInfo iface: cl.interfaces()) {
632 cantStripThis(iface, notStrippable);
633 }
634 }
635
636 private static String getPrintableName(ClassInfo cl) {
637 ClassInfo containingClass = cl.containingClass();
638 if (containingClass != null) {
639 // This is an inner class.
640 String baseName = cl.name();
641 baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
642 return getPrintableName(containingClass) + '$' + baseName;
643 }
644 return cl.qualifiedName();
645 }
646
647 /**
648 * Writes the list of classes that must be present in order to
649 * provide the non-hidden APIs known to javadoc.
650 *
651 * @param filename the path to the file to write the list to
652 */
653 public static void writeKeepList(String filename) {
654 HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
655 ClassInfo[] all = Converter.allClasses();
656 Arrays.sort(all); // just to make the file a little more readable
657
658 // If a class is public and not hidden, then it and everything it derives
659 // from cannot be stripped. Otherwise we can strip it.
660 for (ClassInfo cl: all) {
661 if (cl.isPublic() && !cl.isHidden()) {
662 cantStripThis(cl, notStrippable);
663 }
664 }
665 PrintStream stream = null;
666 try {
667 stream = new PrintStream(filename);
668 for (ClassInfo cl: notStrippable) {
669 stream.println(getPrintableName(cl));
670 }
671 }
672 catch (FileNotFoundException e) {
673 System.err.println("error writing file: " + filename);
674 }
675 finally {
676 if (stream != null) {
677 stream.close();
678 }
679 }
680 }
681
682 private static PackageInfo[] sVisiblePackages = null;
683 public static PackageInfo[] choosePackages() {
684 if (sVisiblePackages != null) {
685 return sVisiblePackages;
686 }
687
688 ClassInfo[] classes = Converter.rootClasses();
689 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
690 for (ClassInfo cl: classes) {
691 PackageInfo pkg = cl.containingPackage();
692 String name;
693 if (pkg == null) {
694 name = "";
695 } else {
696 name = pkg.name();
697 }
698 sorted.put(name, pkg);
699 }
700
701 ArrayList<PackageInfo> result = new ArrayList();
702
703 for (String s: sorted.keySet()) {
704 PackageInfo pkg = sorted.get(s);
705
706 if (pkg.isHidden()) {
707 continue;
708 }
709 Boolean allHidden = true;
710 int pass = 0;
711 ClassInfo[] classesToCheck = null;
712 while (pass < 5 ) {
713 switch(pass) {
714 case 0:
715 classesToCheck = pkg.ordinaryClasses();
716 break;
717 case 1:
718 classesToCheck = pkg.enums();
719 break;
720 case 2:
721 classesToCheck = pkg.errors();
722 break;
723 case 3:
724 classesToCheck = pkg.exceptions();
725 break;
726 case 4:
727 classesToCheck = pkg.interfaces();
728 break;
729 default:
730 System.err.println("Error reading package: " + pkg.name());
731 break;
732 }
733 for (ClassInfo cl : classesToCheck) {
734 if (!cl.isHidden()) {
735 allHidden = false;
736 break;
737 }
738 }
739 if (!allHidden) {
740 break;
741 }
742 pass++;
743 }
744 if (allHidden) {
745 continue;
746 }
747
748 result.add(pkg);
749 }
750
751 sVisiblePackages = result.toArray(new PackageInfo[result.size()]);
752 return sVisiblePackages;
753 }
754
755 public static void writePackages(String filename)
756 {
757 HDF data = makePackageHDF();
758
759 int i = 0;
760 for (PackageInfo pkg: choosePackages()) {
761 writePackage(pkg);
762
763 data.setValue("docs.packages." + i + ".name", pkg.name());
764 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
765 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
766 pkg.firstSentenceTags());
767
768 i++;
769 }
770
771 setPageTitle(data, "Package Index");
772
773 TagInfo.makeHDF(data, "root.descr",
774 Converter.convertTags(root.inlineTags(), null));
775
776 ClearPage.write(data, "packages.cs", filename);
777 ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
778
779 Proofread.writePackages(filename,
780 Converter.convertTags(root.inlineTags(), null));
781 }
782
783 public static void writePackage(PackageInfo pkg)
784 {
785 // these this and the description are in the same directory,
786 // so it's okay
787 HDF data = makePackageHDF();
788
789 String name = pkg.name();
790
791 data.setValue("package.name", name);
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700792 data.setValue("package.since", pkg.getSince());
The Android Open Source Project88b60792009-03-03 19:28:42 -0800793 data.setValue("package.descr", "...description...");
794
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -0700795 makeClassListHDF(data, "package.interfaces",
The Android Open Source Project88b60792009-03-03 19:28:42 -0800796 ClassInfo.sortByName(pkg.interfaces()));
797 makeClassListHDF(data, "package.classes",
798 ClassInfo.sortByName(pkg.ordinaryClasses()));
799 makeClassListHDF(data, "package.enums",
800 ClassInfo.sortByName(pkg.enums()));
801 makeClassListHDF(data, "package.exceptions",
802 ClassInfo.sortByName(pkg.exceptions()));
803 makeClassListHDF(data, "package.errors",
804 ClassInfo.sortByName(pkg.errors()));
805 TagInfo.makeHDF(data, "package.shortDescr",
806 pkg.firstSentenceTags());
807 TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
808
809 String filename = pkg.htmlPage();
810 setPageTitle(data, name);
811 ClearPage.write(data, "package.cs", filename);
812
813 filename = pkg.fullDescriptionHtmlPage();
814 setPageTitle(data, name + " Details");
815 ClearPage.write(data, "package-descr.cs", filename);
816
817 Proofread.writePackage(filename, pkg.inlineTags());
818 }
819
820 public static void writeClassLists()
821 {
822 int i;
823 HDF data = makePackageHDF();
824
825 ClassInfo[] classes = PackageInfo.filterHidden(
826 Converter.convertClasses(root.classes()));
827 if (classes.length == 0) {
828 return ;
829 }
830
831 Sorter[] sorted = new Sorter[classes.length];
832 for (i=0; i<sorted.length; i++) {
833 ClassInfo cl = classes[i];
834 String name = cl.name();
835 sorted[i] = new Sorter(name, cl);
836 }
837
838 Arrays.sort(sorted);
839
840 // make a pass and resolve ones that have the same name
841 int firstMatch = 0;
842 String lastName = sorted[0].label;
843 for (i=1; i<sorted.length; i++) {
844 String s = sorted[i].label;
845 if (!lastName.equals(s)) {
846 if (firstMatch != i-1) {
847 // there were duplicates
848 for (int j=firstMatch; j<i; j++) {
849 PackageInfo pkg = ((ClassInfo)sorted[j].data).containingPackage();
850 if (pkg != null) {
851 sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
852 }
853 }
854 }
855 firstMatch = i;
856 lastName = s;
857 }
858 }
859
860 // and sort again
861 Arrays.sort(sorted);
862
863 for (i=0; i<sorted.length; i++) {
864 String s = sorted[i].label;
865 ClassInfo cl = (ClassInfo)sorted[i].data;
866 char first = Character.toUpperCase(s.charAt(0));
867 cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
868 }
869
870 setPageTitle(data, "Class Index");
871 ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
872 }
873
874 // we use the word keywords because "index" means something else in html land
875 // the user only ever sees the word index
876/* public static void writeKeywords()
877 {
878 ArrayList<KeywordEntry> keywords = new ArrayList<KeywordEntry>();
879
880 ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes()));
881
882 for (ClassInfo cl: classes) {
883 cl.makeKeywordEntries(keywords);
884 }
885
886 HDF data = makeHDF();
887
888 Collections.sort(keywords);
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -0700889
The Android Open Source Project88b60792009-03-03 19:28:42 -0800890 int i=0;
891 for (KeywordEntry entry: keywords) {
892 String base = "keywords." + entry.firstChar() + "." + i;
893 entry.makeHDF(data, base);
894 i++;
895 }
896
897 setPageTitle(data, "Index");
898 ClearPage.write(data, "keywords.cs", javadocDir + "keywords" + htmlExtension);
899 } */
900
901 public static void writeHierarchy()
902 {
903 ClassInfo[] classes = Converter.rootClasses();
904 ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
905 for (ClassInfo cl: classes) {
906 if (!cl.isHidden()) {
907 info.add(cl);
908 }
909 }
910 HDF data = makePackageHDF();
911 Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
912 setPageTitle(data, "Class Hierarchy");
913 ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
914 }
915
916 public static void writeClasses()
917 {
918 ClassInfo[] classes = Converter.rootClasses();
919
920 for (ClassInfo cl: classes) {
921 HDF data = makePackageHDF();
922 if (!cl.isHidden()) {
923 writeClass(cl, data);
924 }
925 }
926 }
927
928 public static void writeClass(ClassInfo cl, HDF data)
929 {
930 cl.makeHDF(data);
931
932 setPageTitle(data, cl.name());
933 ClearPage.write(data, "class.cs", cl.htmlPage());
934
935 Proofread.writeClass(cl.htmlPage(), cl);
936 }
937
938 public static void makeClassListHDF(HDF data, String base,
939 ClassInfo[] classes)
940 {
941 for (int i=0; i<classes.length; i++) {
942 ClassInfo cl = classes[i];
943 if (!cl.isHidden()) {
944 cl.makeShortDescrHDF(data, base + "." + i);
945 }
946 }
947 }
948
949 public static String linkTarget(String source, String target)
950 {
951 String[] src = source.split("/");
952 String[] tgt = target.split("/");
953
954 int srclen = src.length;
955 int tgtlen = tgt.length;
956
957 int same = 0;
958 while (same < (srclen-1)
959 && same < (tgtlen-1)
960 && (src[same].equals(tgt[same]))) {
961 same++;
962 }
963
964 String s = "";
965
966 int up = srclen-same-1;
967 for (int i=0; i<up; i++) {
968 s += "../";
969 }
970
971
972 int N = tgtlen-1;
973 for (int i=same; i<N; i++) {
974 s += tgt[i] + '/';
975 }
976 s += tgt[tgtlen-1];
977
978 return s;
979 }
980
981 /**
Xavier Ducrohet02e14df2009-09-10 14:50:12 -0700982 * Returns true if the given element has an @hide or @pending annotation.
The Android Open Source Project88b60792009-03-03 19:28:42 -0800983 */
984 private static boolean hasHideAnnotation(Doc doc) {
Xavier Ducrohet02e14df2009-09-10 14:50:12 -0700985 String comment = doc.getRawCommentText();
986 return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800987 }
988
989 /**
990 * Returns true if the given element is hidden.
991 */
992 private static boolean isHidden(Doc doc) {
993 // Methods, fields, constructors.
994 if (doc instanceof MemberDoc) {
995 return hasHideAnnotation(doc);
996 }
997
998 // Classes, interfaces, enums, annotation types.
999 if (doc instanceof ClassDoc) {
1000 ClassDoc classDoc = (ClassDoc) doc;
1001
1002 // Check the containing package.
1003 if (hasHideAnnotation(classDoc.containingPackage())) {
1004 return true;
1005 }
1006
1007 // Check the class doc and containing class docs if this is a
1008 // nested class.
1009 ClassDoc current = classDoc;
1010 do {
1011 if (hasHideAnnotation(current)) {
1012 return true;
1013 }
1014
1015 current = current.containingClass();
1016 } while (current != null);
1017 }
1018
1019 return false;
1020 }
1021
1022 /**
1023 * Filters out hidden elements.
1024 */
1025 private static Object filterHidden(Object o, Class<?> expected) {
1026 if (o == null) {
1027 return null;
1028 }
1029
1030 Class type = o.getClass();
1031 if (type.getName().startsWith("com.sun.")) {
1032 // TODO: Implement interfaces from superclasses, too.
1033 return Proxy.newProxyInstance(type.getClassLoader(),
1034 type.getInterfaces(), new HideHandler(o));
1035 } else if (o instanceof Object[]) {
1036 Class<?> componentType = expected.getComponentType();
1037 Object[] array = (Object[]) o;
1038 List<Object> list = new ArrayList<Object>(array.length);
1039 for (Object entry : array) {
1040 if ((entry instanceof Doc) && isHidden((Doc) entry)) {
1041 continue;
1042 }
1043 list.add(filterHidden(entry, componentType));
1044 }
1045 return list.toArray(
1046 (Object[]) Array.newInstance(componentType, list.size()));
1047 } else {
1048 return o;
1049 }
1050 }
1051
1052 /**
1053 * Filters hidden elements out of method return values.
1054 */
1055 private static class HideHandler implements InvocationHandler {
1056
1057 private final Object target;
1058
1059 public HideHandler(Object target) {
1060 this.target = target;
1061 }
1062
1063 public Object invoke(Object proxy, Method method, Object[] args)
1064 throws Throwable {
1065 String methodName = method.getName();
1066 if (args != null) {
1067 if (methodName.equals("compareTo") ||
1068 methodName.equals("equals") ||
1069 methodName.equals("overrides") ||
1070 methodName.equals("subclassOf")) {
1071 args[0] = unwrap(args[0]);
1072 }
1073 }
1074
1075 if (methodName.equals("getRawCommentText")) {
1076 return filterComment((String) method.invoke(target, args));
1077 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001078
The Android Open Source Project88b60792009-03-03 19:28:42 -08001079 // escape "&" in disjunctive types.
1080 if (proxy instanceof Type && methodName.equals("toString")) {
1081 return ((String) method.invoke(target, args))
1082 .replace("&", "&amp;");
1083 }
1084
1085 try {
1086 return filterHidden(method.invoke(target, args),
1087 method.getReturnType());
1088 } catch (InvocationTargetException e) {
1089 throw e.getTargetException();
1090 }
1091 }
1092
1093 private String filterComment(String s) {
1094 if (s == null) {
1095 return null;
1096 }
1097
1098 s = s.trim();
1099
1100 // Work around off by one error
1101 while (s.length() >= 5
1102 && s.charAt(s.length() - 5) == '{') {
1103 s += "&nbsp;";
1104 }
1105
1106 return s;
1107 }
1108
1109 private static Object unwrap(Object proxy) {
1110 if (proxy instanceof Proxy)
1111 return ((HideHandler)Proxy.getInvocationHandler(proxy)).target;
1112 return proxy;
1113 }
1114 }
1115
1116 public static String scope(Scoped scoped) {
1117 if (scoped.isPublic()) {
1118 return "public";
1119 }
1120 else if (scoped.isProtected()) {
1121 return "protected";
1122 }
1123 else if (scoped.isPackagePrivate()) {
1124 return "";
1125 }
1126 else if (scoped.isPrivate()) {
1127 return "private";
1128 }
1129 else {
1130 throw new RuntimeException("invalid scope for object " + scoped);
1131 }
1132 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001133
The Android Open Source Project88b60792009-03-03 19:28:42 -08001134 /**
1135 * Collect the values used by the Dev tools and write them in files packaged with the SDK
1136 * @param output the ouput directory for the files.
1137 */
1138 private static void writeSdkValues(String output) {
1139 ArrayList<String> activityActions = new ArrayList<String>();
1140 ArrayList<String> broadcastActions = new ArrayList<String>();
1141 ArrayList<String> serviceActions = new ArrayList<String>();
1142 ArrayList<String> categories = new ArrayList<String>();
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001143
The Android Open Source Project88b60792009-03-03 19:28:42 -08001144 ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
1145 ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
1146 ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001147
The Android Open Source Project88b60792009-03-03 19:28:42 -08001148 ClassInfo[] classes = Converter.allClasses();
1149
1150 // Go through all the fields of all the classes, looking SDK stuff.
1151 for (ClassInfo clazz : classes) {
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001152
The Android Open Source Project88b60792009-03-03 19:28:42 -08001153 // first check constant fields for the SdkConstant annotation.
1154 FieldInfo[] fields = clazz.allSelfFields();
1155 for (FieldInfo field : fields) {
1156 Object cValue = field.constantValue();
1157 if (cValue != null) {
1158 AnnotationInstanceInfo[] annotations = field.annotations();
1159 if (annotations.length > 0) {
1160 for (AnnotationInstanceInfo annotation : annotations) {
1161 if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1162 AnnotationValueInfo[] values = annotation.elementValues();
1163 if (values.length > 0) {
1164 String type = values[0].valueString();
1165 if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
1166 activityActions.add(cValue.toString());
1167 } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
1168 broadcastActions.add(cValue.toString());
1169 } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
1170 serviceActions.add(cValue.toString());
1171 } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
1172 categories.add(cValue.toString());
1173 }
1174 }
1175 break;
1176 }
1177 }
1178 }
1179 }
1180 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001181
The Android Open Source Project88b60792009-03-03 19:28:42 -08001182 // Now check the class for @Widget or if its in the android.widget package
1183 // (unless the class is hidden or abstract, or non public)
1184 if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) {
1185 boolean annotated = false;
1186 AnnotationInstanceInfo[] annotations = clazz.annotations();
1187 if (annotations.length > 0) {
1188 for (AnnotationInstanceInfo annotation : annotations) {
1189 if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
1190 widgets.add(clazz);
1191 annotated = true;
1192 break;
1193 } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1194 layouts.add(clazz);
1195 annotated = true;
1196 break;
1197 }
1198 }
1199 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001200
The Android Open Source Project88b60792009-03-03 19:28:42 -08001201 if (annotated == false) {
1202 // lets check if this is inside android.widget
1203 PackageInfo pckg = clazz.containingPackage();
1204 String packageName = pckg.name();
1205 if ("android.widget".equals(packageName) ||
1206 "android.view".equals(packageName)) {
1207 // now we check what this class inherits either from android.view.ViewGroup
1208 // or android.view.View, or android.view.ViewGroup.LayoutParams
1209 int type = checkInheritance(clazz);
1210 switch (type) {
1211 case TYPE_WIDGET:
1212 widgets.add(clazz);
1213 break;
1214 case TYPE_LAYOUT:
1215 layouts.add(clazz);
1216 break;
1217 case TYPE_LAYOUT_PARAM:
1218 layoutParams.add(clazz);
1219 break;
1220 }
1221 }
1222 }
1223 }
1224 }
1225
1226 // now write the files, whether or not the list are empty.
1227 // the SDK built requires those files to be present.
1228
1229 Collections.sort(activityActions);
1230 writeValues(output + "/activity_actions.txt", activityActions);
1231
1232 Collections.sort(broadcastActions);
1233 writeValues(output + "/broadcast_actions.txt", broadcastActions);
1234
1235 Collections.sort(serviceActions);
1236 writeValues(output + "/service_actions.txt", serviceActions);
1237
1238 Collections.sort(categories);
1239 writeValues(output + "/categories.txt", categories);
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001240
The Android Open Source Project88b60792009-03-03 19:28:42 -08001241 // before writing the list of classes, we do some checks, to make sure the layout params
1242 // are enclosed by a layout class (and not one that has been declared as a widget)
1243 for (int i = 0 ; i < layoutParams.size();) {
1244 ClassInfo layoutParamClass = layoutParams.get(i);
1245 ClassInfo containingClass = layoutParamClass.containingClass();
1246 if (containingClass == null || layouts.indexOf(containingClass) == -1) {
1247 layoutParams.remove(i);
1248 } else {
1249 i++;
1250 }
1251 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001252
The Android Open Source Project88b60792009-03-03 19:28:42 -08001253 writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
1254 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001255
The Android Open Source Project88b60792009-03-03 19:28:42 -08001256 /**
1257 * Writes a list of values into a text files.
1258 * @param pathname the absolute os path of the output file.
1259 * @param values the list of values to write.
1260 */
1261 private static void writeValues(String pathname, ArrayList<String> values) {
1262 FileWriter fw = null;
1263 BufferedWriter bw = null;
1264 try {
1265 fw = new FileWriter(pathname, false);
1266 bw = new BufferedWriter(fw);
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001267
The Android Open Source Project88b60792009-03-03 19:28:42 -08001268 for (String value : values) {
1269 bw.append(value).append('\n');
1270 }
1271 } catch (IOException e) {
1272 // pass for now
1273 } finally {
1274 try {
1275 if (bw != null) bw.close();
1276 } catch (IOException e) {
1277 // pass for now
1278 }
1279 try {
1280 if (fw != null) fw.close();
1281 } catch (IOException e) {
1282 // pass for now
1283 }
1284 }
1285 }
1286
1287 /**
1288 * Writes the widget/layout/layout param classes into a text files.
1289 * @param pathname the absolute os path of the output file.
1290 * @param widgets the list of widget classes to write.
1291 * @param layouts the list of layout classes to write.
1292 * @param layoutParams the list of layout param classes to write.
1293 */
1294 private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
1295 ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
1296 FileWriter fw = null;
1297 BufferedWriter bw = null;
1298 try {
1299 fw = new FileWriter(pathname, false);
1300 bw = new BufferedWriter(fw);
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001301
The Android Open Source Project88b60792009-03-03 19:28:42 -08001302 // write the 3 types of classes.
1303 for (ClassInfo clazz : widgets) {
1304 writeClass(bw, clazz, 'W');
1305 }
1306 for (ClassInfo clazz : layoutParams) {
1307 writeClass(bw, clazz, 'P');
1308 }
1309 for (ClassInfo clazz : layouts) {
1310 writeClass(bw, clazz, 'L');
1311 }
1312 } catch (IOException e) {
1313 // pass for now
1314 } finally {
1315 try {
1316 if (bw != null) bw.close();
1317 } catch (IOException e) {
1318 // pass for now
1319 }
1320 try {
1321 if (fw != null) fw.close();
1322 } catch (IOException e) {
1323 // pass for now
1324 }
1325 }
1326 }
1327
1328 /**
1329 * Writes a class name and its super class names into a {@link BufferedWriter}.
1330 * @param writer the BufferedWriter to write into
1331 * @param clazz the class to write
1332 * @param prefix the prefix to put at the beginning of the line.
1333 * @throws IOException
1334 */
1335 private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
1336 throws IOException {
1337 writer.append(prefix).append(clazz.qualifiedName());
1338 ClassInfo superClass = clazz;
1339 while ((superClass = superClass.superclass()) != null) {
1340 writer.append(' ').append(superClass.qualifiedName());
1341 }
1342 writer.append('\n');
1343 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001344
The Android Open Source Project88b60792009-03-03 19:28:42 -08001345 /**
1346 * Checks the inheritance of {@link ClassInfo} objects. This method return
1347 * <ul>
1348 * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
1349 * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
1350 * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li>
1351 * <li>{@link #TYPE_NONE}: in all other cases</li>
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001352 * </ul>
The Android Open Source Project88b60792009-03-03 19:28:42 -08001353 * @param clazz the {@link ClassInfo} to check.
1354 */
1355 private static int checkInheritance(ClassInfo clazz) {
1356 if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
1357 return TYPE_LAYOUT;
1358 } else if ("android.view.View".equals(clazz.qualifiedName())) {
1359 return TYPE_WIDGET;
1360 } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
1361 return TYPE_LAYOUT_PARAM;
1362 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001363
The Android Open Source Project88b60792009-03-03 19:28:42 -08001364 ClassInfo parent = clazz.superclass();
1365 if (parent != null) {
1366 return checkInheritance(parent);
1367 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001368
The Android Open Source Project88b60792009-03-03 19:28:42 -08001369 return TYPE_NONE;
1370 }
1371}