blob: 79d593c8f15246501ccd3512d1f40e95111b83bc [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";
Xavier Ducrohetf9b6d382009-12-14 17:55:05 -080036 private static final String SDK_CONSTANT_TYPE_FEATURE = "android.annotation.SdkConstant.SdkConstantType.FEATURE";
The Android Open Source Project88b60792009-03-03 19:28:42 -080037 private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget";
38 private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout";
39
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;
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -070044
The Android Open Source Project88b60792009-03-03 19:28:42 -080045 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;
50
51 public static int showLevel = SHOW_PROTECTED;
52
53 public static final String javadocDir = "reference/";
54 public static String htmlExtension;
55
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 = "";
Scott Main25fda192009-08-04 11:26:30 -070060 public static SinceTagger sinceTagger = new SinceTagger();
Joe Onorato63170ff2010-09-16 11:57:42 -040061 public static HashSet<String> knownTags = new HashSet<String>();
The Android Open Source Project88b60792009-03-03 19:28:42 -080062
Joe Onorato0ee89a72010-08-31 11:27:25 -070063 private static boolean parseComments = false;
64 private static boolean generateDocs = true;
65
66 /**
67 * Returns true if we should parse javadoc comments,
68 * reporting errors in the process.
69 */
70 public static boolean parseComments() {
71 return generateDocs || parseComments;
72 }
73
The Android Open Source Project88b60792009-03-03 19:28:42 -080074 public static boolean checkLevel(int level)
75 {
76 return (showLevel & level) == level;
77 }
78
79 public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp,
80 boolean priv, boolean hidden)
81 {
82 int level = 0;
83 if (hidden && !checkLevel(SHOW_HIDDEN)) {
84 return false;
85 }
86 if (pub && checkLevel(SHOW_PUBLIC)) {
87 return true;
88 }
89 if (prot && checkLevel(SHOW_PROTECTED)) {
90 return true;
91 }
92 if (pkgp && checkLevel(SHOW_PACKAGE)) {
93 return true;
94 }
95 if (priv && checkLevel(SHOW_PRIVATE)) {
96 return true;
97 }
98 return false;
99 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -0700100
The Android Open Source Project88b60792009-03-03 19:28:42 -0800101 public static boolean start(RootDoc r)
102 {
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;
Scott Main2fa99f12009-11-20 10:41:49 -0800111 boolean offlineMode = false;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800112 String apiFile = null;
113 String debugStubsFile = "";
114 HashSet<String> stubPackages = null;
Joe Onorato63170ff2010-09-16 11:57:42 -0400115 ArrayList<String> knownTagsFiles = new ArrayList<String>();
The Android Open Source Project88b60792009-03-03 19:28:42 -0800116
117 root = r;
118
119 String[][] options = r.options();
120 for (String[] a: options) {
121 if (a[0].equals("-d")) {
122 ClearPage.outputDir = a[1];
123 }
124 else if (a[0].equals("-templatedir")) {
125 ClearPage.addTemplateDir(a[1]);
126 }
127 else if (a[0].equals("-hdf")) {
128 mHDFData.add(new String[] {a[1], a[2]});
129 }
Joe Onorato63170ff2010-09-16 11:57:42 -0400130 else if (a[0].equals("-knowntags")) {
131 knownTagsFiles.add(a[1]);
132 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800133 else if (a[0].equals("-toroot")) {
134 ClearPage.toroot = a[1];
135 }
136 else if (a[0].equals("-samplecode")) {
137 sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
138 }
139 else if (a[0].equals("-htmldir")) {
Bill Napier0e143c02010-08-24 22:16:01 -0700140 ClearPage.htmlDirs.add(a[1]);
The Android Open Source Project88b60792009-03-03 19:28:42 -0800141 }
142 else if (a[0].equals("-title")) {
143 DroidDoc.title = a[1];
144 }
145 else if (a[0].equals("-werror")) {
146 Errors.setWarningsAreErrors(true);
147 }
148 else if (a[0].equals("-error") || a[0].equals("-warning")
149 || a[0].equals("-hide")) {
150 try {
151 int level = -1;
152 if (a[0].equals("-error")) {
153 level = Errors.ERROR;
154 }
155 else if (a[0].equals("-warning")) {
156 level = Errors.WARNING;
157 }
158 else if (a[0].equals("-hide")) {
159 level = Errors.HIDDEN;
160 }
161 Errors.setErrorLevel(Integer.parseInt(a[1]), level);
162 }
163 catch (NumberFormatException e) {
164 // already printed below
165 return false;
166 }
167 }
168 else if (a[0].equals("-keeplist")) {
169 keepListFile = a[1];
170 }
171 else if (a[0].equals("-proofread")) {
172 proofreadFile = a[1];
173 }
174 else if (a[0].equals("-todo")) {
175 todoFile = a[1];
176 }
177 else if (a[0].equals("-public")) {
178 showLevel = SHOW_PUBLIC;
179 }
180 else if (a[0].equals("-protected")) {
181 showLevel = SHOW_PROTECTED;
182 }
183 else if (a[0].equals("-package")) {
184 showLevel = SHOW_PACKAGE;
185 }
186 else if (a[0].equals("-private")) {
187 showLevel = SHOW_PRIVATE;
188 }
189 else if (a[0].equals("-hidden")) {
190 showLevel = SHOW_HIDDEN;
191 }
192 else if (a[0].equals("-stubs")) {
193 stubsDir = a[1];
194 }
195 else if (a[0].equals("-stubpackages")) {
196 stubPackages = new HashSet();
197 for (String pkg: a[1].split(":")) {
198 stubPackages.add(pkg);
199 }
200 }
201 else if (a[0].equals("-sdkvalues")) {
202 sdkValuePath = a[1];
203 }
204 else if (a[0].equals("-apixml")) {
205 apiXML = true;
206 apiFile = a[1];
207 }
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400208 else if (a[0].equals("-nodocs")) {
Joe Onorato0ee89a72010-08-31 11:27:25 -0700209 generateDocs = false;
210 }
211 else if (a[0].equals("-parsecomments")) {
212 parseComments = true;
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400213 }
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700214 else if (a[0].equals("-since")) {
215 sinceTagger.addVersion(a[1], a[2]);
216 }
Scott Main2fa99f12009-11-20 10:41:49 -0800217 else if (a[0].equals("-offlinemode")) {
218 offlineMode = true;
219 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800220 }
221
Joe Onorato63170ff2010-09-16 11:57:42 -0400222 if (!readKnownTagsFiles(knownTags, knownTagsFiles)) {
223 return false;
224 }
225
The Android Open Source Project88b60792009-03-03 19:28:42 -0800226 // read some prefs from the template
227 if (!readTemplateSettings()) {
228 return false;
229 }
230
231 // Set up the data structures
232 Converter.makeInfo(r);
233
Joe Onorato0ee89a72010-08-31 11:27:25 -0700234 if (generateDocs) {
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400235 long startTime = System.nanoTime();
236
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700237 // Apply @since tags from the XML file
238 sinceTagger.tagAll(Converter.rootClasses());
239
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400240 // Files for proofreading
241 if (proofreadFile != null) {
242 Proofread.initProofread(proofreadFile);
243 }
244 if (todoFile != null) {
245 TodoFile.writeTodoFile(todoFile);
246 }
247
248 // HTML Pages
Bill Napier0e143c02010-08-24 22:16:01 -0700249 if (!ClearPage.htmlDirs.isEmpty()) {
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400250 writeHTMLPages();
251 }
252
253 // Navigation tree
254 NavTree.writeNavTree(javadocDir);
255
256 // Packages Pages
257 writePackages(javadocDir
Bill Napier0e143c02010-08-24 22:16:01 -0700258 + (!ClearPage.htmlDirs.isEmpty()
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400259 ? "packages" + htmlExtension
260 : "index" + htmlExtension));
261
262 // Classes
263 writeClassLists();
264 writeClasses();
265 writeHierarchy();
266 // writeKeywords();
267
268 // Lists for JavaScript
269 writeLists();
270 if (keepListFile != null) {
271 writeKeepList(keepListFile);
272 }
273
274 // Sample Code
275 for (SampleCode sc: sampleCodes) {
Scott Main2fa99f12009-11-20 10:41:49 -0800276 sc.write(offlineMode);
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400277 }
278
279 // Index page
280 writeIndex();
281
282 Proofread.finishProofread(proofreadFile);
283
284 if (sdkValuePath != null) {
285 writeSdkValues(sdkValuePath);
286 }
287
288 long time = System.nanoTime() - startTime;
289 System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to "
290 + ClearPage.outputDir);
The Android Open Source Project88b60792009-03-03 19:28:42 -0800291 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800292
293 // Stubs
294 if (stubsDir != null) {
295 Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
296 }
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700297
The Android Open Source Project88b60792009-03-03 19:28:42 -0800298 Errors.printErrors();
299 return !Errors.hadError;
300 }
301
302 private static void writeIndex() {
303 HDF data = makeHDF();
304 ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
305 }
306
307 private static boolean readTemplateSettings()
308 {
309 HDF data = makeHDF();
310 htmlExtension = data.getValue("template.extension", ".html");
311 int i=0;
312 while (true) {
313 String k = data.getValue("template.escape." + i + ".key", "");
314 String v = data.getValue("template.escape." + i + ".value", "");
315 if ("".equals(k)) {
316 break;
317 }
318 if (k.length() != 1) {
319 System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
320 return false;
321 }
322 escapeChars.put(k.charAt(0), v);
323 i++;
324 }
325 return true;
326 }
327
Joe Onorato63170ff2010-09-16 11:57:42 -0400328 private static boolean readKnownTagsFiles(HashSet<String> knownTags,
329 ArrayList<String> knownTagsFiles) {
330 for (String fn: knownTagsFiles) {
331 BufferedReader in = null;
332 try {
333 in = new BufferedReader(new FileReader(fn));
334 int lineno = 0;
335 boolean fail = false;
336 while (true) {
337 lineno++;
338 String line = in.readLine();
339 if (line == null) {
340 break;
341 }
342 line = line.trim();
343 if (line.length() == 0) {
344 continue;
345 } else if (line.charAt(0) == '#') {
346 continue;
347 }
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);
353 fail = true;
354 continue;
355 }
356 }
357 knownTags.add(words[0]);
358 System.out.println("Known tag: " + words[0]);
359 }
360 if (fail) {
361 return false;
362 }
363 } catch (IOException ex) {
364 System.err.println("Error reading file: " + fn + " (" + ex.getMessage() + ")");
365 return false;
366 } finally {
367 if (in != null) {
368 try {
369 in.close();
370 } catch (IOException e) {
371 }
372 }
373 }
374 }
375 return true;
376 }
377
The Android Open Source Project88b60792009-03-03 19:28:42 -0800378 public static String escape(String s) {
379 if (escapeChars.size() == 0) {
380 return s;
381 }
382 StringBuffer b = null;
383 int begin = 0;
384 final int N = s.length();
385 for (int i=0; i<N; i++) {
386 char c = s.charAt(i);
387 String mapped = escapeChars.get(c);
388 if (mapped != null) {
389 if (b == null) {
390 b = new StringBuffer(s.length() + mapped.length());
391 }
392 if (begin != i) {
393 b.append(s.substring(begin, i));
394 }
395 b.append(mapped);
396 begin = i+1;
397 }
398 }
399 if (b != null) {
400 if (begin != N) {
401 b.append(s.substring(begin, N));
402 }
403 return b.toString();
404 }
405 return s;
406 }
407
408 public static void setPageTitle(HDF data, String title)
409 {
410 String s = title;
411 if (DroidDoc.title.length() > 0) {
412 s += " - " + DroidDoc.title;
413 }
414 data.setValue("page.title", s);
415 }
416
417 public static LanguageVersion languageVersion()
418 {
419 return LanguageVersion.JAVA_1_5;
420 }
421
422 public static int optionLength(String option)
423 {
424 if (option.equals("-d")) {
425 return 2;
426 }
427 if (option.equals("-templatedir")) {
428 return 2;
429 }
430 if (option.equals("-hdf")) {
431 return 3;
432 }
Joe Onorato63170ff2010-09-16 11:57:42 -0400433 if (option.equals("-knowntags")) {
434 return 2;
435 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800436 if (option.equals("-toroot")) {
437 return 2;
438 }
439 if (option.equals("-samplecode")) {
440 return 4;
441 }
442 if (option.equals("-htmldir")) {
443 return 2;
444 }
445 if (option.equals("-title")) {
446 return 2;
447 }
448 if (option.equals("-werror")) {
449 return 1;
450 }
451 if (option.equals("-hide")) {
452 return 2;
453 }
454 if (option.equals("-warning")) {
455 return 2;
456 }
457 if (option.equals("-error")) {
458 return 2;
459 }
460 if (option.equals("-keeplist")) {
461 return 2;
462 }
463 if (option.equals("-proofread")) {
464 return 2;
465 }
466 if (option.equals("-todo")) {
467 return 2;
468 }
469 if (option.equals("-public")) {
470 return 1;
471 }
472 if (option.equals("-protected")) {
473 return 1;
474 }
475 if (option.equals("-package")) {
476 return 1;
477 }
478 if (option.equals("-private")) {
479 return 1;
480 }
481 if (option.equals("-hidden")) {
482 return 1;
483 }
484 if (option.equals("-stubs")) {
485 return 2;
486 }
487 if (option.equals("-stubpackages")) {
488 return 2;
489 }
490 if (option.equals("-sdkvalues")) {
491 return 2;
492 }
493 if (option.equals("-apixml")) {
494 return 2;
495 }
Joe Onoratob7c41aa2009-07-20 11:57:59 -0400496 if (option.equals("-nodocs")) {
497 return 1;
498 }
Joe Onoratocd7c7752010-08-31 12:56:49 -0700499 if (option.equals("-parsecomments")) {
500 return 1;
501 }
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700502 if (option.equals("-since")) {
503 return 3;
504 }
Scott Main2fa99f12009-11-20 10:41:49 -0800505 if (option.equals("-offlinemode")) {
506 return 1;
507 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800508 return 0;
509 }
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700510
The Android Open Source Project88b60792009-03-03 19:28:42 -0800511 public static boolean validOptions(String[][] options, DocErrorReporter r)
512 {
513 for (String[] a: options) {
514 if (a[0].equals("-error") || a[0].equals("-warning")
515 || a[0].equals("-hide")) {
516 try {
517 Integer.parseInt(a[1]);
518 }
519 catch (NumberFormatException e) {
520 r.printError("bad -" + a[0] + " value must be a number: "
521 + a[1]);
522 return false;
523 }
524 }
525 }
526
527 return true;
528 }
529
530 public static HDF makeHDF()
531 {
532 HDF data = new HDF();
533
534 for (String[] p: mHDFData) {
535 data.setValue(p[0], p[1]);
536 }
537
538 try {
539 for (String p: ClearPage.hdfFiles) {
540 data.readFile(p);
541 }
542 }
543 catch (IOException e) {
544 throw new RuntimeException(e);
545 }
546
547 return data;
548 }
549
550 public static HDF makePackageHDF()
551 {
552 HDF data = makeHDF();
553 ClassInfo[] classes = Converter.rootClasses();
554
555 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
556 for (ClassInfo cl: classes) {
557 PackageInfo pkg = cl.containingPackage();
558 String name;
559 if (pkg == null) {
560 name = "";
561 } else {
562 name = pkg.name();
563 }
564 sorted.put(name, pkg);
565 }
566
567 int i = 0;
568 for (String s: sorted.keySet()) {
569 PackageInfo pkg = sorted.get(s);
570
571 if (pkg.isHidden()) {
572 continue;
573 }
574 Boolean allHidden = true;
575 int pass = 0;
576 ClassInfo[] classesToCheck = null;
577 while (pass < 5 ) {
578 switch(pass) {
579 case 0:
580 classesToCheck = pkg.ordinaryClasses();
581 break;
582 case 1:
583 classesToCheck = pkg.enums();
584 break;
585 case 2:
586 classesToCheck = pkg.errors();
587 break;
588 case 3:
589 classesToCheck = pkg.exceptions();
590 break;
591 case 4:
592 classesToCheck = pkg.interfaces();
593 break;
594 default:
595 System.err.println("Error reading package: " + pkg.name());
596 break;
597 }
598 for (ClassInfo cl : classesToCheck) {
599 if (!cl.isHidden()) {
600 allHidden = false;
601 break;
602 }
603 }
604 if (!allHidden) {
605 break;
606 }
607 pass++;
608 }
609 if (allHidden) {
610 continue;
611 }
612
613 data.setValue("reference", "true");
614 data.setValue("docs.packages." + i + ".name", s);
615 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
Scott Maindf094242009-07-27 09:47:11 -0700616 data.setValue("docs.packages." + i + ".since", pkg.getSince());
The Android Open Source Project88b60792009-03-03 19:28:42 -0800617 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
618 pkg.firstSentenceTags());
619 i++;
620 }
621
Scott Main25fda192009-08-04 11:26:30 -0700622 sinceTagger.writeVersionNames(data);
The Android Open Source Project88b60792009-03-03 19:28:42 -0800623 return data;
624 }
625
626 public static void writeDirectory(File dir, String relative)
627 {
628 File[] files = dir.listFiles();
629 int i, count = files.length;
630 for (i=0; i<count; i++) {
631 File f = files[i];
632 if (f.isFile()) {
633 String templ = relative + f.getName();
634 int len = templ.length();
635 if (len > 3 && ".cs".equals(templ.substring(len-3))) {
636 HDF data = makeHDF();
637 String filename = templ.substring(0,len-3) + htmlExtension;
638 ClearPage.write(data, templ, filename);
639 }
640 else if (len > 3 && ".jd".equals(templ.substring(len-3))) {
641 String filename = templ.substring(0,len-3) + htmlExtension;
642 DocFile.writePage(f.getAbsolutePath(), relative, filename);
643 }
644 else {
645 ClearPage.copyFile(f, templ);
646 }
647 }
648 else if (f.isDirectory()) {
649 writeDirectory(f, relative + f.getName() + "/");
650 }
651 }
652 }
653
654 public static void writeHTMLPages()
655 {
Bill Napier0e143c02010-08-24 22:16:01 -0700656 for (String htmlDir : ClearPage.htmlDirs) {
657 File f = new File(htmlDir);
658 if (!f.isDirectory()) {
659 System.err.println("htmlDir not a directory: " + htmlDir);
Bill Napier10cd7f92010-08-25 09:59:14 -0700660 continue;
Bill Napier0e143c02010-08-24 22:16:01 -0700661 }
662 writeDirectory(f, "");
The Android Open Source Project88b60792009-03-03 19:28:42 -0800663 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800664 }
665
666 public static void writeLists()
667 {
668 HDF data = makeHDF();
669
670 ClassInfo[] classes = Converter.rootClasses();
671
672 SortedMap<String, Object> sorted = new TreeMap<String, Object>();
673 for (ClassInfo cl: classes) {
674 if (cl.isHidden()) {
675 continue;
676 }
677 sorted.put(cl.qualifiedName(), cl);
678 PackageInfo pkg = cl.containingPackage();
679 String name;
680 if (pkg == null) {
681 name = "";
682 } else {
683 name = pkg.name();
684 }
685 sorted.put(name, pkg);
686 }
687
688 int i = 0;
689 for (String s: sorted.keySet()) {
690 data.setValue("docs.pages." + i + ".id" , ""+i);
691 data.setValue("docs.pages." + i + ".label" , s);
692
693 Object o = sorted.get(s);
694 if (o instanceof PackageInfo) {
695 PackageInfo pkg = (PackageInfo)o;
696 data.setValue("docs.pages." + i + ".link" , pkg.htmlPage());
697 data.setValue("docs.pages." + i + ".type" , "package");
698 }
699 else if (o instanceof ClassInfo) {
700 ClassInfo cl = (ClassInfo)o;
701 data.setValue("docs.pages." + i + ".link" , cl.htmlPage());
702 data.setValue("docs.pages." + i + ".type" , "class");
703 }
704 i++;
705 }
706
707 ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
708 }
709
710 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
711 if (!notStrippable.add(cl)) {
712 // slight optimization: if it already contains cl, it already contains
713 // all of cl's parents
714 return;
715 }
716 ClassInfo supr = cl.superclass();
717 if (supr != null) {
718 cantStripThis(supr, notStrippable);
719 }
720 for (ClassInfo iface: cl.interfaces()) {
721 cantStripThis(iface, notStrippable);
722 }
723 }
724
725 private static String getPrintableName(ClassInfo cl) {
726 ClassInfo containingClass = cl.containingClass();
727 if (containingClass != null) {
728 // This is an inner class.
729 String baseName = cl.name();
730 baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
731 return getPrintableName(containingClass) + '$' + baseName;
732 }
733 return cl.qualifiedName();
734 }
735
736 /**
737 * Writes the list of classes that must be present in order to
738 * provide the non-hidden APIs known to javadoc.
739 *
740 * @param filename the path to the file to write the list to
741 */
742 public static void writeKeepList(String filename) {
743 HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
744 ClassInfo[] all = Converter.allClasses();
745 Arrays.sort(all); // just to make the file a little more readable
746
747 // If a class is public and not hidden, then it and everything it derives
748 // from cannot be stripped. Otherwise we can strip it.
749 for (ClassInfo cl: all) {
750 if (cl.isPublic() && !cl.isHidden()) {
751 cantStripThis(cl, notStrippable);
752 }
753 }
754 PrintStream stream = null;
755 try {
756 stream = new PrintStream(filename);
757 for (ClassInfo cl: notStrippable) {
758 stream.println(getPrintableName(cl));
759 }
760 }
761 catch (FileNotFoundException e) {
762 System.err.println("error writing file: " + filename);
763 }
764 finally {
765 if (stream != null) {
766 stream.close();
767 }
768 }
769 }
770
771 private static PackageInfo[] sVisiblePackages = null;
772 public static PackageInfo[] choosePackages() {
773 if (sVisiblePackages != null) {
774 return sVisiblePackages;
775 }
776
777 ClassInfo[] classes = Converter.rootClasses();
778 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
779 for (ClassInfo cl: classes) {
780 PackageInfo pkg = cl.containingPackage();
781 String name;
782 if (pkg == null) {
783 name = "";
784 } else {
785 name = pkg.name();
786 }
787 sorted.put(name, pkg);
788 }
789
790 ArrayList<PackageInfo> result = new ArrayList();
791
792 for (String s: sorted.keySet()) {
793 PackageInfo pkg = sorted.get(s);
794
795 if (pkg.isHidden()) {
796 continue;
797 }
798 Boolean allHidden = true;
799 int pass = 0;
800 ClassInfo[] classesToCheck = null;
801 while (pass < 5 ) {
802 switch(pass) {
803 case 0:
804 classesToCheck = pkg.ordinaryClasses();
805 break;
806 case 1:
807 classesToCheck = pkg.enums();
808 break;
809 case 2:
810 classesToCheck = pkg.errors();
811 break;
812 case 3:
813 classesToCheck = pkg.exceptions();
814 break;
815 case 4:
816 classesToCheck = pkg.interfaces();
817 break;
818 default:
819 System.err.println("Error reading package: " + pkg.name());
820 break;
821 }
822 for (ClassInfo cl : classesToCheck) {
823 if (!cl.isHidden()) {
824 allHidden = false;
825 break;
826 }
827 }
828 if (!allHidden) {
829 break;
830 }
831 pass++;
832 }
833 if (allHidden) {
834 continue;
835 }
836
837 result.add(pkg);
838 }
839
840 sVisiblePackages = result.toArray(new PackageInfo[result.size()]);
841 return sVisiblePackages;
842 }
843
844 public static void writePackages(String filename)
845 {
846 HDF data = makePackageHDF();
847
848 int i = 0;
849 for (PackageInfo pkg: choosePackages()) {
850 writePackage(pkg);
851
852 data.setValue("docs.packages." + i + ".name", pkg.name());
853 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
854 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
855 pkg.firstSentenceTags());
856
857 i++;
858 }
859
860 setPageTitle(data, "Package Index");
861
862 TagInfo.makeHDF(data, "root.descr",
863 Converter.convertTags(root.inlineTags(), null));
864
865 ClearPage.write(data, "packages.cs", filename);
866 ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
867
868 Proofread.writePackages(filename,
869 Converter.convertTags(root.inlineTags(), null));
870 }
871
872 public static void writePackage(PackageInfo pkg)
873 {
874 // these this and the description are in the same directory,
875 // so it's okay
876 HDF data = makePackageHDF();
877
878 String name = pkg.name();
879
880 data.setValue("package.name", name);
Jesse Wilson5e0dd412009-06-01 17:59:44 -0700881 data.setValue("package.since", pkg.getSince());
The Android Open Source Project88b60792009-03-03 19:28:42 -0800882 data.setValue("package.descr", "...description...");
883
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -0700884 makeClassListHDF(data, "package.interfaces",
The Android Open Source Project88b60792009-03-03 19:28:42 -0800885 ClassInfo.sortByName(pkg.interfaces()));
886 makeClassListHDF(data, "package.classes",
887 ClassInfo.sortByName(pkg.ordinaryClasses()));
888 makeClassListHDF(data, "package.enums",
889 ClassInfo.sortByName(pkg.enums()));
890 makeClassListHDF(data, "package.exceptions",
891 ClassInfo.sortByName(pkg.exceptions()));
892 makeClassListHDF(data, "package.errors",
893 ClassInfo.sortByName(pkg.errors()));
894 TagInfo.makeHDF(data, "package.shortDescr",
895 pkg.firstSentenceTags());
896 TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
897
898 String filename = pkg.htmlPage();
899 setPageTitle(data, name);
900 ClearPage.write(data, "package.cs", filename);
901
902 filename = pkg.fullDescriptionHtmlPage();
903 setPageTitle(data, name + " Details");
904 ClearPage.write(data, "package-descr.cs", filename);
905
906 Proofread.writePackage(filename, pkg.inlineTags());
907 }
908
909 public static void writeClassLists()
910 {
911 int i;
912 HDF data = makePackageHDF();
913
914 ClassInfo[] classes = PackageInfo.filterHidden(
915 Converter.convertClasses(root.classes()));
916 if (classes.length == 0) {
917 return ;
918 }
919
920 Sorter[] sorted = new Sorter[classes.length];
921 for (i=0; i<sorted.length; i++) {
922 ClassInfo cl = classes[i];
923 String name = cl.name();
924 sorted[i] = new Sorter(name, cl);
925 }
926
927 Arrays.sort(sorted);
928
929 // make a pass and resolve ones that have the same name
930 int firstMatch = 0;
931 String lastName = sorted[0].label;
932 for (i=1; i<sorted.length; i++) {
933 String s = sorted[i].label;
934 if (!lastName.equals(s)) {
935 if (firstMatch != i-1) {
936 // there were duplicates
937 for (int j=firstMatch; j<i; j++) {
938 PackageInfo pkg = ((ClassInfo)sorted[j].data).containingPackage();
939 if (pkg != null) {
940 sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
941 }
942 }
943 }
944 firstMatch = i;
945 lastName = s;
946 }
947 }
948
949 // and sort again
950 Arrays.sort(sorted);
951
952 for (i=0; i<sorted.length; i++) {
953 String s = sorted[i].label;
954 ClassInfo cl = (ClassInfo)sorted[i].data;
955 char first = Character.toUpperCase(s.charAt(0));
956 cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
957 }
958
959 setPageTitle(data, "Class Index");
960 ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
961 }
962
963 // we use the word keywords because "index" means something else in html land
964 // the user only ever sees the word index
965/* public static void writeKeywords()
966 {
967 ArrayList<KeywordEntry> keywords = new ArrayList<KeywordEntry>();
968
969 ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes()));
970
971 for (ClassInfo cl: classes) {
972 cl.makeKeywordEntries(keywords);
973 }
974
975 HDF data = makeHDF();
976
977 Collections.sort(keywords);
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -0700978
The Android Open Source Project88b60792009-03-03 19:28:42 -0800979 int i=0;
980 for (KeywordEntry entry: keywords) {
981 String base = "keywords." + entry.firstChar() + "." + i;
982 entry.makeHDF(data, base);
983 i++;
984 }
985
986 setPageTitle(data, "Index");
987 ClearPage.write(data, "keywords.cs", javadocDir + "keywords" + htmlExtension);
988 } */
989
990 public static void writeHierarchy()
991 {
992 ClassInfo[] classes = Converter.rootClasses();
993 ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
994 for (ClassInfo cl: classes) {
995 if (!cl.isHidden()) {
996 info.add(cl);
997 }
998 }
999 HDF data = makePackageHDF();
1000 Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
1001 setPageTitle(data, "Class Hierarchy");
1002 ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
1003 }
1004
1005 public static void writeClasses()
1006 {
1007 ClassInfo[] classes = Converter.rootClasses();
1008
1009 for (ClassInfo cl: classes) {
1010 HDF data = makePackageHDF();
1011 if (!cl.isHidden()) {
1012 writeClass(cl, data);
1013 }
1014 }
1015 }
1016
1017 public static void writeClass(ClassInfo cl, HDF data)
1018 {
1019 cl.makeHDF(data);
1020
1021 setPageTitle(data, cl.name());
1022 ClearPage.write(data, "class.cs", cl.htmlPage());
1023
1024 Proofread.writeClass(cl.htmlPage(), cl);
1025 }
1026
1027 public static void makeClassListHDF(HDF data, String base,
1028 ClassInfo[] classes)
1029 {
1030 for (int i=0; i<classes.length; i++) {
1031 ClassInfo cl = classes[i];
1032 if (!cl.isHidden()) {
1033 cl.makeShortDescrHDF(data, base + "." + i);
1034 }
1035 }
1036 }
1037
1038 public static String linkTarget(String source, String target)
1039 {
1040 String[] src = source.split("/");
1041 String[] tgt = target.split("/");
1042
1043 int srclen = src.length;
1044 int tgtlen = tgt.length;
1045
1046 int same = 0;
1047 while (same < (srclen-1)
1048 && same < (tgtlen-1)
1049 && (src[same].equals(tgt[same]))) {
1050 same++;
1051 }
1052
1053 String s = "";
1054
1055 int up = srclen-same-1;
1056 for (int i=0; i<up; i++) {
1057 s += "../";
1058 }
1059
1060
1061 int N = tgtlen-1;
1062 for (int i=same; i<N; i++) {
1063 s += tgt[i] + '/';
1064 }
1065 s += tgt[tgtlen-1];
1066
1067 return s;
1068 }
1069
1070 /**
Xavier Ducrohet02e14df2009-09-10 14:50:12 -07001071 * Returns true if the given element has an @hide or @pending annotation.
The Android Open Source Project88b60792009-03-03 19:28:42 -08001072 */
1073 private static boolean hasHideAnnotation(Doc doc) {
Xavier Ducrohet02e14df2009-09-10 14:50:12 -07001074 String comment = doc.getRawCommentText();
1075 return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1;
The Android Open Source Project88b60792009-03-03 19:28:42 -08001076 }
1077
1078 /**
1079 * Returns true if the given element is hidden.
1080 */
1081 private static boolean isHidden(Doc doc) {
1082 // Methods, fields, constructors.
1083 if (doc instanceof MemberDoc) {
1084 return hasHideAnnotation(doc);
1085 }
1086
1087 // Classes, interfaces, enums, annotation types.
1088 if (doc instanceof ClassDoc) {
1089 ClassDoc classDoc = (ClassDoc) doc;
1090
1091 // Check the containing package.
1092 if (hasHideAnnotation(classDoc.containingPackage())) {
1093 return true;
1094 }
1095
1096 // Check the class doc and containing class docs if this is a
1097 // nested class.
1098 ClassDoc current = classDoc;
1099 do {
1100 if (hasHideAnnotation(current)) {
1101 return true;
1102 }
1103
1104 current = current.containingClass();
1105 } while (current != null);
1106 }
1107
1108 return false;
1109 }
1110
1111 /**
1112 * Filters out hidden elements.
1113 */
1114 private static Object filterHidden(Object o, Class<?> expected) {
1115 if (o == null) {
1116 return null;
1117 }
1118
1119 Class type = o.getClass();
1120 if (type.getName().startsWith("com.sun.")) {
1121 // TODO: Implement interfaces from superclasses, too.
1122 return Proxy.newProxyInstance(type.getClassLoader(),
1123 type.getInterfaces(), new HideHandler(o));
1124 } else if (o instanceof Object[]) {
1125 Class<?> componentType = expected.getComponentType();
1126 Object[] array = (Object[]) o;
1127 List<Object> list = new ArrayList<Object>(array.length);
1128 for (Object entry : array) {
1129 if ((entry instanceof Doc) && isHidden((Doc) entry)) {
1130 continue;
1131 }
1132 list.add(filterHidden(entry, componentType));
1133 }
1134 return list.toArray(
1135 (Object[]) Array.newInstance(componentType, list.size()));
1136 } else {
1137 return o;
1138 }
1139 }
1140
1141 /**
1142 * Filters hidden elements out of method return values.
1143 */
1144 private static class HideHandler implements InvocationHandler {
1145
1146 private final Object target;
1147
1148 public HideHandler(Object target) {
1149 this.target = target;
1150 }
1151
1152 public Object invoke(Object proxy, Method method, Object[] args)
1153 throws Throwable {
1154 String methodName = method.getName();
1155 if (args != null) {
1156 if (methodName.equals("compareTo") ||
1157 methodName.equals("equals") ||
1158 methodName.equals("overrides") ||
1159 methodName.equals("subclassOf")) {
1160 args[0] = unwrap(args[0]);
1161 }
1162 }
1163
1164 if (methodName.equals("getRawCommentText")) {
1165 return filterComment((String) method.invoke(target, args));
1166 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001167
The Android Open Source Project88b60792009-03-03 19:28:42 -08001168 // escape "&" in disjunctive types.
1169 if (proxy instanceof Type && methodName.equals("toString")) {
1170 return ((String) method.invoke(target, args))
1171 .replace("&", "&amp;");
1172 }
1173
1174 try {
1175 return filterHidden(method.invoke(target, args),
1176 method.getReturnType());
1177 } catch (InvocationTargetException e) {
1178 throw e.getTargetException();
1179 }
1180 }
1181
1182 private String filterComment(String s) {
1183 if (s == null) {
1184 return null;
1185 }
1186
1187 s = s.trim();
1188
1189 // Work around off by one error
1190 while (s.length() >= 5
1191 && s.charAt(s.length() - 5) == '{') {
1192 s += "&nbsp;";
1193 }
1194
1195 return s;
1196 }
1197
1198 private static Object unwrap(Object proxy) {
1199 if (proxy instanceof Proxy)
1200 return ((HideHandler)Proxy.getInvocationHandler(proxy)).target;
1201 return proxy;
1202 }
1203 }
1204
1205 public static String scope(Scoped scoped) {
1206 if (scoped.isPublic()) {
1207 return "public";
1208 }
1209 else if (scoped.isProtected()) {
1210 return "protected";
1211 }
1212 else if (scoped.isPackagePrivate()) {
1213 return "";
1214 }
1215 else if (scoped.isPrivate()) {
1216 return "private";
1217 }
1218 else {
1219 throw new RuntimeException("invalid scope for object " + scoped);
1220 }
1221 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001222
The Android Open Source Project88b60792009-03-03 19:28:42 -08001223 /**
1224 * Collect the values used by the Dev tools and write them in files packaged with the SDK
1225 * @param output the ouput directory for the files.
1226 */
1227 private static void writeSdkValues(String output) {
1228 ArrayList<String> activityActions = new ArrayList<String>();
1229 ArrayList<String> broadcastActions = new ArrayList<String>();
1230 ArrayList<String> serviceActions = new ArrayList<String>();
1231 ArrayList<String> categories = new ArrayList<String>();
Xavier Ducrohetf9b6d382009-12-14 17:55:05 -08001232 ArrayList<String> features = new ArrayList<String>();
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001233
The Android Open Source Project88b60792009-03-03 19:28:42 -08001234 ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
1235 ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
1236 ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001237
The Android Open Source Project88b60792009-03-03 19:28:42 -08001238 ClassInfo[] classes = Converter.allClasses();
1239
1240 // Go through all the fields of all the classes, looking SDK stuff.
1241 for (ClassInfo clazz : classes) {
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001242
The Android Open Source Project88b60792009-03-03 19:28:42 -08001243 // first check constant fields for the SdkConstant annotation.
1244 FieldInfo[] fields = clazz.allSelfFields();
1245 for (FieldInfo field : fields) {
1246 Object cValue = field.constantValue();
1247 if (cValue != null) {
1248 AnnotationInstanceInfo[] annotations = field.annotations();
1249 if (annotations.length > 0) {
1250 for (AnnotationInstanceInfo annotation : annotations) {
1251 if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1252 AnnotationValueInfo[] values = annotation.elementValues();
1253 if (values.length > 0) {
1254 String type = values[0].valueString();
1255 if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
1256 activityActions.add(cValue.toString());
1257 } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
1258 broadcastActions.add(cValue.toString());
1259 } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
1260 serviceActions.add(cValue.toString());
1261 } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
1262 categories.add(cValue.toString());
Xavier Ducrohetf9b6d382009-12-14 17:55:05 -08001263 } else if (SDK_CONSTANT_TYPE_FEATURE.equals(type)) {
1264 features.add(cValue.toString());
The Android Open Source Project88b60792009-03-03 19:28:42 -08001265 }
1266 }
1267 break;
1268 }
1269 }
1270 }
1271 }
1272 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001273
The Android Open Source Project88b60792009-03-03 19:28:42 -08001274 // Now check the class for @Widget or if its in the android.widget package
1275 // (unless the class is hidden or abstract, or non public)
1276 if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) {
1277 boolean annotated = false;
1278 AnnotationInstanceInfo[] annotations = clazz.annotations();
1279 if (annotations.length > 0) {
1280 for (AnnotationInstanceInfo annotation : annotations) {
1281 if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
1282 widgets.add(clazz);
1283 annotated = true;
1284 break;
1285 } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1286 layouts.add(clazz);
1287 annotated = true;
1288 break;
1289 }
1290 }
1291 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001292
The Android Open Source Project88b60792009-03-03 19:28:42 -08001293 if (annotated == false) {
1294 // lets check if this is inside android.widget
1295 PackageInfo pckg = clazz.containingPackage();
1296 String packageName = pckg.name();
1297 if ("android.widget".equals(packageName) ||
1298 "android.view".equals(packageName)) {
1299 // now we check what this class inherits either from android.view.ViewGroup
1300 // or android.view.View, or android.view.ViewGroup.LayoutParams
1301 int type = checkInheritance(clazz);
1302 switch (type) {
1303 case TYPE_WIDGET:
1304 widgets.add(clazz);
1305 break;
1306 case TYPE_LAYOUT:
1307 layouts.add(clazz);
1308 break;
1309 case TYPE_LAYOUT_PARAM:
1310 layoutParams.add(clazz);
1311 break;
1312 }
1313 }
1314 }
1315 }
1316 }
1317
1318 // now write the files, whether or not the list are empty.
1319 // the SDK built requires those files to be present.
1320
1321 Collections.sort(activityActions);
1322 writeValues(output + "/activity_actions.txt", activityActions);
1323
1324 Collections.sort(broadcastActions);
1325 writeValues(output + "/broadcast_actions.txt", broadcastActions);
1326
1327 Collections.sort(serviceActions);
1328 writeValues(output + "/service_actions.txt", serviceActions);
1329
1330 Collections.sort(categories);
1331 writeValues(output + "/categories.txt", categories);
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001332
Xavier Ducrohetf9b6d382009-12-14 17:55:05 -08001333 Collections.sort(features);
1334 writeValues(output + "/features.txt", features);
1335
The Android Open Source Project88b60792009-03-03 19:28:42 -08001336 // before writing the list of classes, we do some checks, to make sure the layout params
1337 // are enclosed by a layout class (and not one that has been declared as a widget)
1338 for (int i = 0 ; i < layoutParams.size();) {
1339 ClassInfo layoutParamClass = layoutParams.get(i);
1340 ClassInfo containingClass = layoutParamClass.containingClass();
1341 if (containingClass == null || layouts.indexOf(containingClass) == -1) {
1342 layoutParams.remove(i);
1343 } else {
1344 i++;
1345 }
1346 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001347
The Android Open Source Project88b60792009-03-03 19:28:42 -08001348 writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
1349 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001350
The Android Open Source Project88b60792009-03-03 19:28:42 -08001351 /**
1352 * Writes a list of values into a text files.
1353 * @param pathname the absolute os path of the output file.
1354 * @param values the list of values to write.
1355 */
1356 private static void writeValues(String pathname, ArrayList<String> values) {
1357 FileWriter fw = null;
1358 BufferedWriter bw = null;
1359 try {
1360 fw = new FileWriter(pathname, false);
1361 bw = new BufferedWriter(fw);
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001362
The Android Open Source Project88b60792009-03-03 19:28:42 -08001363 for (String value : values) {
1364 bw.append(value).append('\n');
1365 }
1366 } catch (IOException e) {
1367 // pass for now
1368 } finally {
1369 try {
1370 if (bw != null) bw.close();
1371 } catch (IOException e) {
1372 // pass for now
1373 }
1374 try {
1375 if (fw != null) fw.close();
1376 } catch (IOException e) {
1377 // pass for now
1378 }
1379 }
1380 }
1381
1382 /**
1383 * Writes the widget/layout/layout param classes into a text files.
1384 * @param pathname the absolute os path of the output file.
1385 * @param widgets the list of widget classes to write.
1386 * @param layouts the list of layout classes to write.
1387 * @param layoutParams the list of layout param classes to write.
1388 */
1389 private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
1390 ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
1391 FileWriter fw = null;
1392 BufferedWriter bw = null;
1393 try {
1394 fw = new FileWriter(pathname, false);
1395 bw = new BufferedWriter(fw);
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001396
The Android Open Source Project88b60792009-03-03 19:28:42 -08001397 // write the 3 types of classes.
1398 for (ClassInfo clazz : widgets) {
1399 writeClass(bw, clazz, 'W');
1400 }
1401 for (ClassInfo clazz : layoutParams) {
1402 writeClass(bw, clazz, 'P');
1403 }
1404 for (ClassInfo clazz : layouts) {
1405 writeClass(bw, clazz, 'L');
1406 }
1407 } catch (IOException e) {
1408 // pass for now
1409 } finally {
1410 try {
1411 if (bw != null) bw.close();
1412 } catch (IOException e) {
1413 // pass for now
1414 }
1415 try {
1416 if (fw != null) fw.close();
1417 } catch (IOException e) {
1418 // pass for now
1419 }
1420 }
1421 }
1422
1423 /**
1424 * Writes a class name and its super class names into a {@link BufferedWriter}.
1425 * @param writer the BufferedWriter to write into
1426 * @param clazz the class to write
1427 * @param prefix the prefix to put at the beginning of the line.
1428 * @throws IOException
1429 */
1430 private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
1431 throws IOException {
1432 writer.append(prefix).append(clazz.qualifiedName());
1433 ClassInfo superClass = clazz;
1434 while ((superClass = superClass.superclass()) != null) {
1435 writer.append(' ').append(superClass.qualifiedName());
1436 }
1437 writer.append('\n');
1438 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001439
The Android Open Source Project88b60792009-03-03 19:28:42 -08001440 /**
1441 * Checks the inheritance of {@link ClassInfo} objects. This method return
1442 * <ul>
1443 * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
1444 * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
1445 * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li>
1446 * <li>{@link #TYPE_NONE}: in all other cases</li>
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001447 * </ul>
The Android Open Source Project88b60792009-03-03 19:28:42 -08001448 * @param clazz the {@link ClassInfo} to check.
1449 */
1450 private static int checkInheritance(ClassInfo clazz) {
1451 if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
1452 return TYPE_LAYOUT;
1453 } else if ("android.view.View".equals(clazz.qualifiedName())) {
1454 return TYPE_WIDGET;
1455 } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
1456 return TYPE_LAYOUT_PARAM;
1457 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001458
The Android Open Source Project88b60792009-03-03 19:28:42 -08001459 ClassInfo parent = clazz.superclass();
1460 if (parent != null) {
1461 return checkInheritance(parent);
1462 }
Xavier Ducrohet5ee390d2009-09-10 13:08:27 -07001463
The Android Open Source Project88b60792009-03-03 19:28:42 -08001464 return TYPE_NONE;
1465 }
1466}