• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Google Inc.
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 
17 package com.google.doclava;
18 
19 import com.google.clearsilver.jsilver.JSilver;
20 import com.google.clearsilver.jsilver.data.Data;
21 import com.google.clearsilver.jsilver.resourceloader.ClassResourceLoader;
22 import com.google.clearsilver.jsilver.resourceloader.CompositeResourceLoader;
23 import com.google.clearsilver.jsilver.resourceloader.FileSystemResourceLoader;
24 import com.google.clearsilver.jsilver.resourceloader.ResourceLoader;
25 
26 import com.sun.javadoc.*;
27 
28 import java.util.*;
29 import java.util.jar.JarFile;
30 import java.util.regex.Matcher;
31 import java.io.*;
32 import java.lang.reflect.Proxy;
33 import java.lang.reflect.Array;
34 import java.lang.reflect.InvocationHandler;
35 import java.lang.reflect.InvocationTargetException;
36 import java.lang.reflect.Method;
37 import java.net.MalformedURLException;
38 import java.net.URL;
39 
40 public class Doclava {
41   private static final String SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant";
42   private static final String SDK_CONSTANT_TYPE_ACTIVITY_ACTION =
43       "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION";
44   private static final String SDK_CONSTANT_TYPE_BROADCAST_ACTION =
45       "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION";
46   private static final String SDK_CONSTANT_TYPE_SERVICE_ACTION =
47       "android.annotation.SdkConstant.SdkConstantType.SERVICE_INTENT_ACTION";
48   private static final String SDK_CONSTANT_TYPE_CATEGORY =
49       "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY";
50   private static final String SDK_CONSTANT_TYPE_FEATURE =
51       "android.annotation.SdkConstant.SdkConstantType.FEATURE";
52   private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget";
53   private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout";
54 
55   private static final int TYPE_NONE = 0;
56   private static final int TYPE_WIDGET = 1;
57   private static final int TYPE_LAYOUT = 2;
58   private static final int TYPE_LAYOUT_PARAM = 3;
59 
60   public static final int SHOW_PUBLIC = 0x00000001;
61   public static final int SHOW_PROTECTED = 0x00000003;
62   public static final int SHOW_PACKAGE = 0x00000007;
63   public static final int SHOW_PRIVATE = 0x0000000f;
64   public static final int SHOW_HIDDEN = 0x0000001f;
65 
66   public static int showLevel = SHOW_PROTECTED;
67 
68   public static final boolean SORT_BY_NAV_GROUPS = true;
69   /* Debug output for PageMetadata, format urls from site root */
70   public static boolean META_DBG=false;
71 
72   public static String outputPathBase = "/";
73   public static ArrayList<String> inputPathHtmlDirs = new ArrayList<String>();
74   public static ArrayList<String> inputPathHtmlDir2 = new ArrayList<String>();
75   public static String outputPathHtmlDirs;
76   public static String outputPathHtmlDir2;
77   public static final String devsiteRoot = "en/";
78   public static String javadocDir = "reference/";
79   public static String htmlExtension;
80 
81   public static RootDoc root;
82   public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
83   public static List<PageMetadata.Node> sTaglist = new ArrayList<PageMetadata.Node>();
84   public static ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>();
85   public static ArrayList<SampleCode> sampleCodeGroups = new ArrayList<SampleCode>();
86   public static Data samplesNavTree;
87   public static Map<Character, String> escapeChars = new HashMap<Character, String>();
88   public static String title = "";
89   public static SinceTagger sinceTagger = new SinceTagger();
90   public static HashSet<String> knownTags = new HashSet<String>();
91   public static FederationTagger federationTagger = new FederationTagger();
92   public static Set<String> showAnnotations = new HashSet<String>();
93   public static Set<String> hiddenPackages = new HashSet<String>();
94   public static boolean includeDefaultAssets = true;
95   private static boolean generateDocs = true;
96   private static boolean parseComments = false;
97   private static String yamlNavFile = null;
98 
99   public static JSilver jSilver = null;
100 
101   private static boolean gmsRef = false;
102   private static boolean gcmRef = false;
103   private static boolean samplesRef = false;
104   private static boolean sac = false;
105 
checkLevel(int level)106   public static boolean checkLevel(int level) {
107     return (showLevel & level) == level;
108   }
109 
110   /**
111    * Returns true if we should parse javadoc comments,
112    * reporting errors in the process.
113    */
parseComments()114   public static boolean parseComments() {
115     return generateDocs || parseComments;
116   }
117 
checkLevel(boolean pub, boolean prot, boolean pkgp, boolean priv, boolean hidden)118   public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp, boolean priv,
119       boolean hidden) {
120     if (hidden && !checkLevel(SHOW_HIDDEN)) {
121       return false;
122     }
123     if (pub && checkLevel(SHOW_PUBLIC)) {
124       return true;
125     }
126     if (prot && checkLevel(SHOW_PROTECTED)) {
127       return true;
128     }
129     if (pkgp && checkLevel(SHOW_PACKAGE)) {
130       return true;
131     }
132     if (priv && checkLevel(SHOW_PRIVATE)) {
133       return true;
134     }
135     return false;
136   }
137 
main(String[] args)138   public static void main(String[] args) {
139     com.sun.tools.javadoc.Main.execute(args);
140   }
141 
start(RootDoc r)142   public static boolean start(RootDoc r) {
143     long startTime = System.nanoTime();
144     String keepListFile = null;
145     String proguardFile = null;
146     String proofreadFile = null;
147     String todoFile = null;
148     String sdkValuePath = null;
149     String stubsDir = null;
150     // Create the dependency graph for the stubs  directory
151     boolean offlineMode = false;
152     String apiFile = null;
153     String removedApiFile = null;
154     String debugStubsFile = "";
155     HashSet<String> stubPackages = null;
156     ArrayList<String> knownTagsFiles = new ArrayList<String>();
157 
158     root = r;
159 
160     String[][] options = r.options();
161     for (String[] a : options) {
162       if (a[0].equals("-d")) {
163         outputPathBase = outputPathHtmlDirs = ClearPage.outputDir = a[1];
164       } else if (a[0].equals("-templatedir")) {
165         ClearPage.addTemplateDir(a[1]);
166       } else if (a[0].equals("-hdf")) {
167         mHDFData.add(new String[] {a[1], a[2]});
168       } else if (a[0].equals("-knowntags")) {
169         knownTagsFiles.add(a[1]);
170       } else if (a[0].equals("-toroot")) {
171         ClearPage.toroot = a[1];
172       } else if (a[0].equals("-samplecode")) {
173         sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
174       } else if (a[0].equals("-samplegroup")) {
175         sampleCodeGroups.add(new SampleCode(null, null, a[1]));
176       } else if (a[0].equals("-samplesdir")) {
177         getSampleProjects(new File(a[1]));
178       //the destination output path for main htmldir
179       } else if (a[0].equals("-htmldir")) {
180         inputPathHtmlDirs.add(a[1]);
181         ClearPage.htmlDirs = inputPathHtmlDirs;
182       //the destination output path for additional htmldir
183       } else if (a[0].equals("-htmldir2")) {
184           if (a[2].equals("default")) {
185           inputPathHtmlDirs.add(a[1]);
186         } else {
187           inputPathHtmlDir2.add(a[1]);
188           outputPathHtmlDir2 = a[2];
189         }
190       } else if (a[0].equals("-title")) {
191         Doclava.title = a[1];
192       } else if (a[0].equals("-werror")) {
193         Errors.setWarningsAreErrors(true);
194       } else if (a[0].equals("-error") || a[0].equals("-warning") || a[0].equals("-hide")) {
195         try {
196           int level = -1;
197           if (a[0].equals("-error")) {
198             level = Errors.ERROR;
199           } else if (a[0].equals("-warning")) {
200             level = Errors.WARNING;
201           } else if (a[0].equals("-hide")) {
202             level = Errors.HIDDEN;
203           }
204           Errors.setErrorLevel(Integer.parseInt(a[1]), level);
205         } catch (NumberFormatException e) {
206           // already printed below
207           return false;
208         }
209       } else if (a[0].equals("-keeplist")) {
210         keepListFile = a[1];
211       } else if (a[0].equals("-showAnnotation")) {
212         showAnnotations.add(a[1]);
213       } else if (a[0].equals("-hidePackage")) {
214         hiddenPackages.add(a[1]);
215       } else if (a[0].equals("-proguard")) {
216         proguardFile = a[1];
217       } else if (a[0].equals("-proofread")) {
218         proofreadFile = a[1];
219       } else if (a[0].equals("-todo")) {
220         todoFile = a[1];
221       } else if (a[0].equals("-public")) {
222         showLevel = SHOW_PUBLIC;
223       } else if (a[0].equals("-protected")) {
224         showLevel = SHOW_PROTECTED;
225       } else if (a[0].equals("-package")) {
226         showLevel = SHOW_PACKAGE;
227       } else if (a[0].equals("-private")) {
228         showLevel = SHOW_PRIVATE;
229       } else if (a[0].equals("-hidden")) {
230         showLevel = SHOW_HIDDEN;
231       } else if (a[0].equals("-stubs")) {
232         stubsDir = a[1];
233       } else if (a[0].equals("-stubpackages")) {
234         stubPackages = new HashSet<String>();
235         for (String pkg : a[1].split(":")) {
236           stubPackages.add(pkg);
237         }
238       } else if (a[0].equals("-sdkvalues")) {
239         sdkValuePath = a[1];
240       } else if (a[0].equals("-api")) {
241         apiFile = a[1];
242       } else if (a[0].equals("-removedApi")) {
243         removedApiFile = a[1];
244       }
245       else if (a[0].equals("-nodocs")) {
246         generateDocs = false;
247       } else if (a[0].equals("-nodefaultassets")) {
248         includeDefaultAssets = false;
249       } else if (a[0].equals("-parsecomments")) {
250         parseComments = true;
251       } else if (a[0].equals("-since")) {
252         sinceTagger.addVersion(a[1], a[2]);
253       } else if (a[0].equals("-offlinemode")) {
254         offlineMode = true;
255       } else if (a[0].equals("-metadataDebug")) {
256         META_DBG = true;
257       } else if (a[0].equals("-federate")) {
258         try {
259           String name = a[1];
260           URL federationURL = new URL(a[2]);
261           federationTagger.addSiteUrl(name, federationURL);
262         } catch (MalformedURLException e) {
263           System.err.println("Could not parse URL for federation: " + a[1]);
264           return false;
265         }
266       } else if (a[0].equals("-federationapi")) {
267         String name = a[1];
268         String file = a[2];
269         federationTagger.addSiteApi(name, file);
270       } else if (a[0].equals("-yaml")) {
271         yamlNavFile = a[1];
272       } else if (a[0].equals("-devsite")) {
273         // Don't copy the doclava assets to devsite output (ie use proj assets only)
274         includeDefaultAssets = false;
275         outputPathHtmlDirs = outputPathHtmlDirs + "/" + devsiteRoot;
276       }
277     }
278 
279     if (!readKnownTagsFiles(knownTags, knownTagsFiles)) {
280       return false;
281     }
282 
283     // Set up the data structures
284     Converter.makeInfo(r);
285 
286     if (generateDocs) {
287       ClearPage.addBundledTemplateDir("assets/customizations");
288       ClearPage.addBundledTemplateDir("assets/templates");
289 
290       List<ResourceLoader> resourceLoaders = new ArrayList<ResourceLoader>();
291       List<String> templates = ClearPage.getTemplateDirs();
292       for (String tmpl : templates) {
293         resourceLoaders.add(new FileSystemResourceLoader(tmpl));
294       }
295 
296       templates = ClearPage.getBundledTemplateDirs();
297       for (String tmpl : templates) {
298           // TODO - remove commented line - it's here for debugging purposes
299         //  resourceLoaders.add(new FileSystemResourceLoader("/Volumes/Android/master/external/doclava/res/" + tmpl));
300         resourceLoaders.add(new ClassResourceLoader(Doclava.class, '/'+tmpl));
301       }
302 
303       ResourceLoader compositeResourceLoader = new CompositeResourceLoader(resourceLoaders);
304       jSilver = new JSilver(compositeResourceLoader);
305 
306       if (!Doclava.readTemplateSettings()) {
307         return false;
308       }
309 
310       //startTime = System.nanoTime();
311 
312       // Apply @since tags from the XML file
313       sinceTagger.tagAll(Converter.rootClasses());
314 
315       // Apply details of federated documentation
316       federationTagger.tagAll(Converter.rootClasses());
317 
318       // Files for proofreading
319       if (proofreadFile != null) {
320         Proofread.initProofread(proofreadFile);
321       }
322       if (todoFile != null) {
323         TodoFile.writeTodoFile(todoFile);
324       }
325 
326   if (samplesRef) {
327         // always write samples without offlineMode behaviors
328   writeSamples(false, sampleCodes, SORT_BY_NAV_GROUPS);
329   }
330 
331       // HTML2 Pages -- Generate Pages from optional secondary dir
332       if (!inputPathHtmlDir2.isEmpty()) {
333         if (!outputPathHtmlDir2.isEmpty()) {
334           ClearPage.outputDir = outputPathBase + "/" + outputPathHtmlDir2;
335         }
336         ClearPage.htmlDirs = inputPathHtmlDir2;
337         writeHTMLPages();
338         ClearPage.htmlDirs = inputPathHtmlDirs;
339       }
340 
341       // HTML Pages
342       if (!ClearPage.htmlDirs.isEmpty()) {
343         ClearPage.htmlDirs = inputPathHtmlDirs;
344         ClearPage.outputDir = outputPathHtmlDirs;
345         writeHTMLPages();
346       }
347 
348       writeAssets();
349 
350       // Navigation tree
351       String refPrefix = new String();
352       if(gmsRef){
353         refPrefix = "gms-";
354       } else if(gcmRef){
355         refPrefix = "gcm-";
356       }
357       NavTree.writeNavTree(javadocDir, refPrefix);
358 
359       // Write yaml tree.
360       if (yamlNavFile != null){
361         NavTree.writeYamlTree(javadocDir, yamlNavFile);
362       }
363 
364       // Packages Pages
365       writePackages(javadocDir + refPrefix + "packages" + htmlExtension);
366 
367       // Classes
368   writeClassLists();
369   writeClasses();
370   writeHierarchy();
371       // writeKeywords();
372 
373       // Lists for JavaScript
374   writeLists();
375       if (keepListFile != null) {
376         writeKeepList(keepListFile);
377       }
378 
379       // Index page
380   writeIndex();
381 
382   Proofread.finishProofread(proofreadFile);
383 
384   if (sdkValuePath != null) {
385     writeSdkValues(sdkValuePath);
386   }
387       // Write metadata for all processed files to jd_lists_unified.js in out dir
388       if (!sTaglist.isEmpty()) {
389         PageMetadata.WriteList(sTaglist);
390       }
391     }
392 
393     // Stubs
394     if (stubsDir != null || apiFile != null || proguardFile != null || removedApiFile != null) {
395       Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, removedApiFile, stubPackages);
396     }
397 
398     Errors.printErrors();
399 
400     long time = System.nanoTime() - startTime;
401     System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to "
402         + outputPathBase );
403 
404     return !Errors.hadError;
405   }
406 
writeIndex()407   private static void writeIndex() {
408     Data data = makeHDF();
409     ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
410   }
411 
readTemplateSettings()412   private static boolean readTemplateSettings() {
413     Data data = makeHDF();
414 
415     // The .html extension is hard-coded in several .cs files,
416     // and so you cannot currently set it as a property.
417     htmlExtension = ".html";
418     // htmlExtension = data.getValue("template.extension", ".html");
419     int i = 0;
420     while (true) {
421       String k = data.getValue("template.escape." + i + ".key", "");
422       String v = data.getValue("template.escape." + i + ".value", "");
423       if ("".equals(k)) {
424         break;
425       }
426       if (k.length() != 1) {
427         System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
428         return false;
429       }
430       escapeChars.put(k.charAt(0), v);
431       i++;
432     }
433     return true;
434   }
435 
readKnownTagsFiles(HashSet<String> knownTags, ArrayList<String> knownTagsFiles)436     private static boolean readKnownTagsFiles(HashSet<String> knownTags,
437             ArrayList<String> knownTagsFiles) {
438         for (String fn: knownTagsFiles) {
439            BufferedReader in = null;
440            try {
441                in = new BufferedReader(new FileReader(fn));
442                int lineno = 0;
443                boolean fail = false;
444                while (true) {
445                    lineno++;
446                    String line = in.readLine();
447                    if (line == null) {
448                        break;
449                    }
450                    line = line.trim();
451                    if (line.length() == 0) {
452                        continue;
453                    } else if (line.charAt(0) == '#') {
454                        continue;
455                    }
456                    String[] words = line.split("\\s+", 2);
457                    if (words.length == 2) {
458                        if (words[1].charAt(0) != '#') {
459                            System.err.println(fn + ":" + lineno
460                                    + ": Only one tag allowed per line: " + line);
461                            fail = true;
462                            continue;
463                        }
464                    }
465                    knownTags.add(words[0]);
466                }
467                if (fail) {
468                    return false;
469                }
470            } catch (IOException ex) {
471                System.err.println("Error reading file: " + fn + " (" + ex.getMessage() + ")");
472                return false;
473            } finally {
474                if (in != null) {
475                    try {
476                        in.close();
477                    } catch (IOException e) {
478                    }
479                }
480            }
481         }
482         return true;
483     }
484 
escape(String s)485   public static String escape(String s) {
486     if (escapeChars.size() == 0) {
487       return s;
488     }
489     StringBuffer b = null;
490     int begin = 0;
491     final int N = s.length();
492     for (int i = 0; i < N; i++) {
493       char c = s.charAt(i);
494       String mapped = escapeChars.get(c);
495       if (mapped != null) {
496         if (b == null) {
497           b = new StringBuffer(s.length() + mapped.length());
498         }
499         if (begin != i) {
500           b.append(s.substring(begin, i));
501         }
502         b.append(mapped);
503         begin = i + 1;
504       }
505     }
506     if (b != null) {
507       if (begin != N) {
508         b.append(s.substring(begin, N));
509       }
510       return b.toString();
511     }
512     return s;
513   }
514 
setPageTitle(Data data, String title)515   public static void setPageTitle(Data data, String title) {
516     String s = title;
517     if (Doclava.title.length() > 0) {
518       s += " - " + Doclava.title;
519     }
520     data.setValue("page.title", s);
521   }
522 
523 
languageVersion()524   public static LanguageVersion languageVersion() {
525     return LanguageVersion.JAVA_1_5;
526   }
527 
528 
optionLength(String option)529   public static int optionLength(String option) {
530     if (option.equals("-d")) {
531       return 2;
532     }
533     if (option.equals("-templatedir")) {
534       return 2;
535     }
536     if (option.equals("-hdf")) {
537       return 3;
538     }
539     if (option.equals("-knowntags")) {
540       return 2;
541     }
542     if (option.equals("-toroot")) {
543       return 2;
544     }
545     if (option.equals("-samplecode")) {
546       samplesRef = true;
547       return 4;
548     }
549     if (option.equals("-samplegroup")) {
550       return 2;
551     }
552     if (option.equals("-samplesdir")) {
553       samplesRef = true;
554       return 2;
555     }
556     if (option.equals("-devsite")) {
557       return 1;
558     }
559     if (option.equals("-htmldir")) {
560       return 2;
561     }
562     if (option.equals("-htmldir2")) {
563       return 3;
564     }
565     if (option.equals("-title")) {
566       return 2;
567     }
568     if (option.equals("-werror")) {
569       return 1;
570     }
571     if (option.equals("-hide")) {
572       return 2;
573     }
574     if (option.equals("-warning")) {
575       return 2;
576     }
577     if (option.equals("-error")) {
578       return 2;
579     }
580     if (option.equals("-keeplist")) {
581       return 2;
582     }
583     if (option.equals("-showAnnotation")) {
584       return 2;
585     }
586     if (option.equals("-hidePackage")) {
587       return 2;
588     }
589     if (option.equals("-proguard")) {
590       return 2;
591     }
592     if (option.equals("-proofread")) {
593       return 2;
594     }
595     if (option.equals("-todo")) {
596       return 2;
597     }
598     if (option.equals("-public")) {
599       return 1;
600     }
601     if (option.equals("-protected")) {
602       return 1;
603     }
604     if (option.equals("-package")) {
605       return 1;
606     }
607     if (option.equals("-private")) {
608       return 1;
609     }
610     if (option.equals("-hidden")) {
611       return 1;
612     }
613     if (option.equals("-stubs")) {
614       return 2;
615     }
616     if (option.equals("-stubpackages")) {
617       return 2;
618     }
619     if (option.equals("-sdkvalues")) {
620       return 2;
621     }
622     if (option.equals("-api")) {
623       return 2;
624     }
625     if (option.equals("-removedApi")) {
626       return 2;
627     }
628     if (option.equals("-nodocs")) {
629       return 1;
630     }
631     if (option.equals("-nodefaultassets")) {
632       return 1;
633     }
634     if (option.equals("-parsecomments")) {
635       return 1;
636     }
637     if (option.equals("-since")) {
638       return 3;
639     }
640     if (option.equals("-offlinemode")) {
641       return 1;
642     }
643     if (option.equals("-federate")) {
644       return 3;
645     }
646     if (option.equals("-federationapi")) {
647       return 3;
648     }
649     if (option.equals("-yaml")) {
650       return 2;
651     }
652     if (option.equals("-devsite")) {
653       return 1;
654     }
655     if (option.equals("-gmsref")) {
656       gmsRef = true;
657       return 1;
658     }
659     if (option.equals("-gcmref")) {
660       gcmRef = true;
661       return 1;
662     }
663     if (option.equals("-metadataDebug")) {
664       return 1;
665     }
666     return 0;
667   }
validOptions(String[][] options, DocErrorReporter r)668   public static boolean validOptions(String[][] options, DocErrorReporter r) {
669     for (String[] a : options) {
670       if (a[0].equals("-error") || a[0].equals("-warning") || a[0].equals("-hide")) {
671         try {
672           Integer.parseInt(a[1]);
673         } catch (NumberFormatException e) {
674           r.printError("bad -" + a[0] + " value must be a number: " + a[1]);
675           return false;
676         }
677       }
678     }
679 
680     return true;
681   }
682 
makeHDF()683   public static Data makeHDF() {
684     Data data = jSilver.createData();
685 
686     for (String[] p : mHDFData) {
687       data.setValue(p[0], p[1]);
688     }
689 
690     return data;
691   }
692 
693 
694 
makePackageHDF()695   public static Data makePackageHDF() {
696     Data data = makeHDF();
697     ClassInfo[] classes = Converter.rootClasses();
698 
699     SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
700     for (ClassInfo cl : classes) {
701       PackageInfo pkg = cl.containingPackage();
702       String name;
703       if (pkg == null) {
704         name = "";
705       } else {
706         name = pkg.name();
707       }
708       sorted.put(name, pkg);
709     }
710 
711     int i = 0;
712     for (String s : sorted.keySet()) {
713       PackageInfo pkg = sorted.get(s);
714 
715       if (pkg.isHiddenOrRemoved()) {
716         continue;
717       }
718       boolean allHiddenOrRemoved = true;
719       int pass = 0;
720       ClassInfo[] classesToCheck = null;
721       while (pass < 6) {
722         switch (pass) {
723           case 0:
724             classesToCheck = pkg.ordinaryClasses();
725             break;
726           case 1:
727             classesToCheck = pkg.enums();
728             break;
729           case 2:
730             classesToCheck = pkg.errors();
731             break;
732           case 3:
733             classesToCheck = pkg.exceptions();
734             break;
735           case 4:
736             classesToCheck = pkg.interfaces();
737             break;
738           case 5:
739             classesToCheck = pkg.annotations();
740             break;
741           default:
742             System.err.println("Error reading package: " + pkg.name());
743             break;
744         }
745         for (ClassInfo cl : classesToCheck) {
746           if (!cl.isHiddenOrRemoved()) {
747             allHiddenOrRemoved = false;
748             break;
749           }
750         }
751         if (!allHiddenOrRemoved) {
752           break;
753         }
754         pass++;
755       }
756       if (allHiddenOrRemoved) {
757         continue;
758       }
759       if(gmsRef){
760           data.setValue("reference.gms", "true");
761       } else if(gcmRef){
762           data.setValue("reference.gcm", "true");
763       }
764       data.setValue("reference", "1");
765       data.setValue("reference.apilevels", sinceTagger.hasVersions() ? "1" : "0");
766       data.setValue("docs.packages." + i + ".name", s);
767       data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
768       data.setValue("docs.packages." + i + ".since", pkg.getSince());
769       TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", pkg.firstSentenceTags());
770       i++;
771     }
772 
773     sinceTagger.writeVersionNames(data);
774     return data;
775   }
776 
writeDirectory(File dir, String relative, JSilver js)777   private static void writeDirectory(File dir, String relative, JSilver js) {
778     File[] files = dir.listFiles();
779     int i, count = files.length;
780     for (i = 0; i < count; i++) {
781       File f = files[i];
782       if (f.isFile()) {
783         String templ = relative + f.getName();
784         int len = templ.length();
785         if (len > 3 && ".cs".equals(templ.substring(len - 3))) {
786           Data data = makeHDF();
787           String filename = templ.substring(0, len - 3) + htmlExtension;
788           ClearPage.write(data, templ, filename, js);
789         } else if (len > 3 && ".jd".equals(templ.substring(len - 3))) {
790           Data data = makeHDF();
791           String filename = templ.substring(0, len - 3) + htmlExtension;
792           DocFile.writePage(f.getAbsolutePath(), relative, filename, data);
793         } else if(!f.getName().equals(".DS_Store")){
794               Data data = makeHDF();
795               String hdfValue = data.getValue("sac") == null ? "" : data.getValue("sac");
796               boolean allowExcepted = hdfValue.equals("true") ? true : false;
797               ClearPage.copyFile(allowExcepted, f, templ);
798         }
799       } else if (f.isDirectory()) {
800         writeDirectory(f, relative + f.getName() + "/", js);
801       }
802     }
803   }
804 
writeHTMLPages()805   public static void writeHTMLPages() {
806     for (String htmlDir : ClearPage.htmlDirs) {
807       File f = new File(htmlDir);
808       if (!f.isDirectory()) {
809         System.err.println("htmlDir not a directory: " + htmlDir);
810         continue;
811       }
812 
813       ResourceLoader loader = new FileSystemResourceLoader(f);
814       JSilver js = new JSilver(loader);
815       writeDirectory(f, "", js);
816     }
817   }
818 
writeAssets()819   public static void writeAssets() {
820     JarFile thisJar = JarUtils.jarForClass(Doclava.class, null);
821     if ((thisJar != null) && (includeDefaultAssets)) {
822       try {
823         List<String> templateDirs = ClearPage.getBundledTemplateDirs();
824         for (String templateDir : templateDirs) {
825           String assetsDir = templateDir + "/assets";
826           JarUtils.copyResourcesToDirectory(thisJar, assetsDir, ClearPage.outputDir + "/assets");
827         }
828       } catch (IOException e) {
829         System.err.println("Error copying assets directory.");
830         e.printStackTrace();
831         return;
832       }
833     }
834 
835     //write the project-specific assets
836     List<String> templateDirs = ClearPage.getTemplateDirs();
837     for (String templateDir : templateDirs) {
838       File assets = new File(templateDir + "/assets");
839       if (assets.isDirectory()) {
840         writeDirectory(assets, "assets/", null);
841       }
842     }
843 
844     // Create the timestamp.js file based on .cs file
845     Data timedata = Doclava.makeHDF();
846     ClearPage.write(timedata, "timestamp.cs", "timestamp.js");
847   }
848 
849   /** Go through the docs and generate meta-data about each
850       page to use in search suggestions */
writeLists()851   public static void writeLists() {
852 
853     // Write the lists for API references
854     Data data = makeHDF();
855 
856     ClassInfo[] classes = Converter.rootClasses();
857 
858     SortedMap<String, Object> sorted = new TreeMap<String, Object>();
859     for (ClassInfo cl : classes) {
860       if (cl.isHiddenOrRemoved()) {
861         continue;
862       }
863       sorted.put(cl.qualifiedName(), cl);
864       PackageInfo pkg = cl.containingPackage();
865       String name;
866       if (pkg == null) {
867         name = "";
868       } else {
869         name = pkg.name();
870       }
871       sorted.put(name, pkg);
872     }
873 
874     int i = 0;
875     for (String s : sorted.keySet()) {
876       data.setValue("docs.pages." + i + ".id", "" + i);
877       data.setValue("docs.pages." + i + ".label", s);
878 
879       Object o = sorted.get(s);
880       if (o instanceof PackageInfo) {
881         PackageInfo pkg = (PackageInfo) o;
882         data.setValue("docs.pages." + i + ".link", pkg.htmlPage());
883         data.setValue("docs.pages." + i + ".type", "package");
884         data.setValue("docs.pages." + i + ".deprecated", pkg.isDeprecated() ? "true" : "false");
885       } else if (o instanceof ClassInfo) {
886         ClassInfo cl = (ClassInfo) o;
887         data.setValue("docs.pages." + i + ".link", cl.htmlPage());
888         data.setValue("docs.pages." + i + ".type", "class");
889         data.setValue("docs.pages." + i + ".deprecated", cl.isDeprecated() ? "true" : "false");
890       }
891       i++;
892     }
893     ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
894 
895 
896     // Write the lists for JD documents (if there are HTML directories to process)
897     if (inputPathHtmlDirs.size() > 0) {
898       Data jddata = makeHDF();
899       Iterator counter = new Iterator();
900       for (String htmlDir : inputPathHtmlDirs) {
901         File dir = new File(htmlDir);
902         if (!dir.isDirectory()) {
903           continue;
904         }
905         writeJdDirList(dir, jddata, counter);
906       }
907       ClearPage.write(jddata, "jd_lists.cs", javadocDir + "jd_lists.js");
908     }
909   }
910 
911   private static class Iterator {
912     int i = 0;
913   }
914 
915   /** Write meta-data for a JD file, used for search suggestions */
writeJdDirList(File dir, Data data, Iterator counter)916   private static void writeJdDirList(File dir, Data data, Iterator counter) {
917     File[] files = dir.listFiles();
918     int i, count = files.length;
919     // Loop all files in given directory
920     for (i = 0; i < count; i++) {
921       File f = files[i];
922       if (f.isFile()) {
923         String filePath = f.getAbsolutePath();
924         String templ = f.getName();
925         int len = templ.length();
926         // If it's a .jd file we want to process
927         if (len > 3 && ".jd".equals(templ.substring(len - 3))) {
928           // remove the directories below the site root
929           String webPath = filePath.substring(filePath.indexOf("docs/html/") + 10,
930               filePath.length());
931           // replace .jd with .html
932           webPath = webPath.substring(0, webPath.length() - 3) + htmlExtension;
933           // Parse the .jd file for properties data at top of page
934           Data hdf = Doclava.makeHDF();
935           String filedata = DocFile.readFile(filePath);
936           Matcher lines = DocFile.LINE.matcher(filedata);
937           String line = null;
938           // Get each line to add the key-value to hdf
939           while (lines.find()) {
940             line = lines.group(1);
941             if (line.length() > 0) {
942               // Stop when we hit the body
943               if (line.equals("@jd:body")) {
944                 break;
945               }
946               Matcher prop = DocFile.PROP.matcher(line);
947               if (prop.matches()) {
948                 String key = prop.group(1);
949                 String value = prop.group(2);
950                 hdf.setValue(key, value);
951               } else {
952                 break;
953               }
954             }
955           } // done gathering page properties
956 
957           // Insert the goods into HDF data (title, link, tags, type)
958           String title = hdf.getValue("page.title", "");
959           title = title.replaceAll("\"", "'");
960           // if there's a <span> in the title, get rid of it
961           if (title.indexOf("<span") != -1) {
962             String[] splitTitle = title.split("<span(.*?)</span>");
963             title = splitTitle[0];
964             for (int j = 1; j < splitTitle.length; j++) {
965               title.concat(splitTitle[j]);
966             }
967           }
968 
969           StringBuilder tags =  new StringBuilder();
970           String tagsList = hdf.getValue("page.tags", "");
971           if (!tagsList.equals("")) {
972             tagsList = tagsList.replaceAll("\"", "");
973             String[] tagParts = tagsList.split(",");
974             for (int iter = 0; iter < tagParts.length; iter++) {
975               tags.append("\"");
976               tags.append(tagParts[iter].trim());
977               tags.append("\"");
978               if (iter < tagParts.length - 1) {
979                 tags.append(",");
980               }
981             }
982           }
983 
984           String dirName = (webPath.indexOf("/") != -1)
985                   ? webPath.substring(0, webPath.indexOf("/")) : "";
986 
987           if (!"".equals(title) &&
988               !"intl".equals(dirName) &&
989               !hdf.getBooleanValue("excludeFromSuggestions")) {
990             data.setValue("docs.pages." + counter.i + ".label", title);
991             data.setValue("docs.pages." + counter.i + ".link", webPath);
992             data.setValue("docs.pages." + counter.i + ".tags", tags.toString());
993             data.setValue("docs.pages." + counter.i + ".type", dirName);
994             counter.i++;
995           }
996         }
997       } else if (f.isDirectory()) {
998         writeJdDirList(f, data, counter);
999       }
1000     }
1001   }
1002 
cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable)1003   public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
1004     if (!notStrippable.add(cl)) {
1005       // slight optimization: if it already contains cl, it already contains
1006       // all of cl's parents
1007       return;
1008     }
1009     ClassInfo supr = cl.superclass();
1010     if (supr != null) {
1011       cantStripThis(supr, notStrippable);
1012     }
1013     for (ClassInfo iface : cl.interfaces()) {
1014       cantStripThis(iface, notStrippable);
1015     }
1016   }
1017 
getPrintableName(ClassInfo cl)1018   private static String getPrintableName(ClassInfo cl) {
1019     ClassInfo containingClass = cl.containingClass();
1020     if (containingClass != null) {
1021       // This is an inner class.
1022       String baseName = cl.name();
1023       baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
1024       return getPrintableName(containingClass) + '$' + baseName;
1025     }
1026     return cl.qualifiedName();
1027   }
1028 
1029   /**
1030    * Writes the list of classes that must be present in order to provide the non-hidden APIs known
1031    * to javadoc.
1032    *
1033    * @param filename the path to the file to write the list to
1034    */
writeKeepList(String filename)1035   public static void writeKeepList(String filename) {
1036     HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
1037     ClassInfo[] all = Converter.allClasses();
1038     Arrays.sort(all); // just to make the file a little more readable
1039 
1040     // If a class is public and not hidden, then it and everything it derives
1041     // from cannot be stripped. Otherwise we can strip it.
1042     for (ClassInfo cl : all) {
1043       if (cl.isPublic() && !cl.isHiddenOrRemoved()) {
1044         cantStripThis(cl, notStrippable);
1045       }
1046     }
1047     PrintStream stream = null;
1048     try {
1049       stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(filename)));
1050       for (ClassInfo cl : notStrippable) {
1051         stream.println(getPrintableName(cl));
1052       }
1053     } catch (FileNotFoundException e) {
1054       System.err.println("error writing file: " + filename);
1055     } finally {
1056       if (stream != null) {
1057         stream.close();
1058       }
1059     }
1060   }
1061 
1062   private static PackageInfo[] sVisiblePackages = null;
1063 
choosePackages()1064   public static PackageInfo[] choosePackages() {
1065     if (sVisiblePackages != null) {
1066       return sVisiblePackages;
1067     }
1068 
1069     ClassInfo[] classes = Converter.rootClasses();
1070     SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
1071     for (ClassInfo cl : classes) {
1072       PackageInfo pkg = cl.containingPackage();
1073       String name;
1074       if (pkg == null) {
1075         name = "";
1076       } else {
1077         name = pkg.name();
1078       }
1079       sorted.put(name, pkg);
1080     }
1081 
1082     ArrayList<PackageInfo> result = new ArrayList<PackageInfo>();
1083 
1084     for (String s : sorted.keySet()) {
1085       PackageInfo pkg = sorted.get(s);
1086 
1087       if (pkg.isHiddenOrRemoved()) {
1088         continue;
1089       }
1090 
1091       boolean allHiddenOrRemoved = true;
1092       int pass = 0;
1093       ClassInfo[] classesToCheck = null;
1094       while (pass < 6) {
1095         switch (pass) {
1096           case 0:
1097             classesToCheck = pkg.ordinaryClasses();
1098             break;
1099           case 1:
1100             classesToCheck = pkg.enums();
1101             break;
1102           case 2:
1103             classesToCheck = pkg.errors();
1104             break;
1105           case 3:
1106             classesToCheck = pkg.exceptions();
1107             break;
1108           case 4:
1109             classesToCheck = pkg.interfaces();
1110             break;
1111           case 5:
1112             classesToCheck = pkg.annotations();
1113             break;
1114           default:
1115             System.err.println("Error reading package: " + pkg.name());
1116             break;
1117         }
1118         for (ClassInfo cl : classesToCheck) {
1119           if (!cl.isHiddenOrRemoved()) {
1120             allHiddenOrRemoved = false;
1121             break;
1122           }
1123         }
1124         if (!allHiddenOrRemoved) {
1125           break;
1126         }
1127         pass++;
1128       }
1129       if (allHiddenOrRemoved) {
1130         continue;
1131       }
1132 
1133       result.add(pkg);
1134     }
1135 
1136     sVisiblePackages = result.toArray(new PackageInfo[result.size()]);
1137     return sVisiblePackages;
1138   }
1139 
writePackages(String filename)1140   public static void writePackages(String filename) {
1141     Data data = makePackageHDF();
1142 
1143     int i = 0;
1144     for (PackageInfo pkg : choosePackages()) {
1145       writePackage(pkg);
1146 
1147       data.setValue("docs.packages." + i + ".name", pkg.name());
1148       data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
1149       TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", pkg.firstSentenceTags());
1150 
1151       i++;
1152     }
1153 
1154     setPageTitle(data, "Package Index");
1155 
1156     TagInfo.makeHDF(data, "root.descr", Converter.convertTags(root.inlineTags(), null));
1157 
1158     ClearPage.write(data, "packages.cs", filename);
1159     ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
1160 
1161     Proofread.writePackages(filename, Converter.convertTags(root.inlineTags(), null));
1162   }
1163 
writePackage(PackageInfo pkg)1164   public static void writePackage(PackageInfo pkg) {
1165     // these this and the description are in the same directory,
1166     // so it's okay
1167     Data data = makePackageHDF();
1168 
1169     String name = pkg.name();
1170 
1171     data.setValue("package.name", name);
1172     data.setValue("package.since", pkg.getSince());
1173     data.setValue("package.descr", "...description...");
1174     pkg.setFederatedReferences(data, "package");
1175 
1176     makeClassListHDF(data, "package.annotations", ClassInfo.sortByName(pkg.annotations()));
1177     makeClassListHDF(data, "package.interfaces", ClassInfo.sortByName(pkg.interfaces()));
1178     makeClassListHDF(data, "package.classes", ClassInfo.sortByName(pkg.ordinaryClasses()));
1179     makeClassListHDF(data, "package.enums", ClassInfo.sortByName(pkg.enums()));
1180     makeClassListHDF(data, "package.exceptions", ClassInfo.sortByName(pkg.exceptions()));
1181     makeClassListHDF(data, "package.errors", ClassInfo.sortByName(pkg.errors()));
1182     TagInfo.makeHDF(data, "package.shortDescr", pkg.firstSentenceTags());
1183     TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
1184 
1185     String filename = pkg.htmlPage();
1186     setPageTitle(data, name);
1187     ClearPage.write(data, "package.cs", filename);
1188 
1189     Proofread.writePackage(filename, pkg.inlineTags());
1190   }
1191 
writeClassLists()1192   public static void writeClassLists() {
1193     int i;
1194     Data data = makePackageHDF();
1195 
1196     ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved(
1197         Converter.convertClasses(root.classes()));
1198     if (classes.length == 0) {
1199       return;
1200     }
1201 
1202     Sorter[] sorted = new Sorter[classes.length];
1203     for (i = 0; i < sorted.length; i++) {
1204       ClassInfo cl = classes[i];
1205       String name = cl.name();
1206       sorted[i] = new Sorter(name, cl);
1207     }
1208 
1209     Arrays.sort(sorted);
1210 
1211     // make a pass and resolve ones that have the same name
1212     int firstMatch = 0;
1213     String lastName = sorted[0].label;
1214     for (i = 1; i < sorted.length; i++) {
1215       String s = sorted[i].label;
1216       if (!lastName.equals(s)) {
1217         if (firstMatch != i - 1) {
1218           // there were duplicates
1219           for (int j = firstMatch; j < i; j++) {
1220             PackageInfo pkg = ((ClassInfo) sorted[j].data).containingPackage();
1221             if (pkg != null) {
1222               sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
1223             }
1224           }
1225         }
1226         firstMatch = i;
1227         lastName = s;
1228       }
1229     }
1230 
1231     // and sort again
1232     Arrays.sort(sorted);
1233 
1234     for (i = 0; i < sorted.length; i++) {
1235       String s = sorted[i].label;
1236       ClassInfo cl = (ClassInfo) sorted[i].data;
1237       char first = Character.toUpperCase(s.charAt(0));
1238       cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
1239     }
1240 
1241     setPageTitle(data, "Class Index");
1242     ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
1243   }
1244 
1245   // we use the word keywords because "index" means something else in html land
1246   // the user only ever sees the word index
1247   /*
1248    * public static void writeKeywords() { ArrayList<KeywordEntry> keywords = new
1249    * ArrayList<KeywordEntry>();
1250    *
1251    * ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved(Converter.convertClasses(root.classes()));
1252    *
1253    * for (ClassInfo cl: classes) { cl.makeKeywordEntries(keywords); }
1254    *
1255    * HDF data = makeHDF();
1256    *
1257    * Collections.sort(keywords);
1258    *
1259    * int i=0; for (KeywordEntry entry: keywords) { String base = "keywords." + entry.firstChar() +
1260    * "." + i; entry.makeHDF(data, base); i++; }
1261    *
1262    * setPageTitle(data, "Index"); ClearPage.write(data, "keywords.cs", javadocDir + "keywords" +
1263    * htmlExtension); }
1264    */
1265 
writeHierarchy()1266   public static void writeHierarchy() {
1267     ClassInfo[] classes = Converter.rootClasses();
1268     ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
1269     for (ClassInfo cl : classes) {
1270       if (!cl.isHiddenOrRemoved()) {
1271         info.add(cl);
1272       }
1273     }
1274     Data data = makePackageHDF();
1275     Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
1276     setPageTitle(data, "Class Hierarchy");
1277     ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
1278   }
1279 
writeClasses()1280   public static void writeClasses() {
1281     ClassInfo[] classes = Converter.rootClasses();
1282 
1283     for (ClassInfo cl : classes) {
1284       Data data = makePackageHDF();
1285       if (!cl.isHiddenOrRemoved()) {
1286         writeClass(cl, data);
1287       }
1288     }
1289   }
1290 
writeClass(ClassInfo cl, Data data)1291   public static void writeClass(ClassInfo cl, Data data) {
1292     cl.makeHDF(data);
1293     setPageTitle(data, cl.name());
1294     String outfile = cl.htmlPage();
1295     ClearPage.write(data, "class.cs", outfile);
1296     Proofread.writeClass(cl.htmlPage(), cl);
1297   }
1298 
makeClassListHDF(Data data, String base, ClassInfo[] classes)1299   public static void makeClassListHDF(Data data, String base, ClassInfo[] classes) {
1300     for (int i = 0; i < classes.length; i++) {
1301       ClassInfo cl = classes[i];
1302       if (!cl.isHiddenOrRemoved()) {
1303         cl.makeShortDescrHDF(data, base + "." + i);
1304       }
1305     }
1306   }
1307 
linkTarget(String source, String target)1308   public static String linkTarget(String source, String target) {
1309     String[] src = source.split("/");
1310     String[] tgt = target.split("/");
1311 
1312     int srclen = src.length;
1313     int tgtlen = tgt.length;
1314 
1315     int same = 0;
1316     while (same < (srclen - 1) && same < (tgtlen - 1) && (src[same].equals(tgt[same]))) {
1317       same++;
1318     }
1319 
1320     String s = "";
1321 
1322     int up = srclen - same - 1;
1323     for (int i = 0; i < up; i++) {
1324       s += "../";
1325     }
1326 
1327 
1328     int N = tgtlen - 1;
1329     for (int i = same; i < N; i++) {
1330       s += tgt[i] + '/';
1331     }
1332     s += tgt[tgtlen - 1];
1333 
1334     return s;
1335   }
1336 
1337   /**
1338    * Returns true if the given element has an @hide, @removed or @pending annotation.
1339    */
hasHideOrRemovedAnnotation(Doc doc)1340   private static boolean hasHideOrRemovedAnnotation(Doc doc) {
1341     String comment = doc.getRawCommentText();
1342     return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1 ||
1343         comment.indexOf("@removed") != -1;
1344   }
1345 
1346   /**
1347    * Returns true if the given element is hidden.
1348    */
isHiddenOrRemoved(Doc doc)1349   private static boolean isHiddenOrRemoved(Doc doc) {
1350     // Methods, fields, constructors.
1351     if (doc instanceof MemberDoc) {
1352       return hasHideOrRemovedAnnotation(doc);
1353     }
1354 
1355     // Classes, interfaces, enums, annotation types.
1356     if (doc instanceof ClassDoc) {
1357       ClassDoc classDoc = (ClassDoc) doc;
1358 
1359       // Check the containing package.
1360       if (hasHideOrRemovedAnnotation(classDoc.containingPackage())) {
1361         return true;
1362       }
1363 
1364       // Check the class doc and containing class docs if this is a
1365       // nested class.
1366       ClassDoc current = classDoc;
1367       do {
1368         if (hasHideOrRemovedAnnotation(current)) {
1369           return true;
1370         }
1371 
1372         current = current.containingClass();
1373       } while (current != null);
1374     }
1375 
1376     return false;
1377   }
1378 
1379   /**
1380    * Filters out hidden and removed elements.
1381    */
filterHiddenAndRemoved(Object o, Class<?> expected)1382   private static Object filterHiddenAndRemoved(Object o, Class<?> expected) {
1383     if (o == null) {
1384       return null;
1385     }
1386 
1387     Class type = o.getClass();
1388     if (type.getName().startsWith("com.sun.")) {
1389       // TODO: Implement interfaces from superclasses, too.
1390       return Proxy
1391           .newProxyInstance(type.getClassLoader(), type.getInterfaces(), new HideHandler(o));
1392     } else if (o instanceof Object[]) {
1393       Class<?> componentType = expected.getComponentType();
1394       Object[] array = (Object[]) o;
1395       List<Object> list = new ArrayList<Object>(array.length);
1396       for (Object entry : array) {
1397         if ((entry instanceof Doc) && isHiddenOrRemoved((Doc) entry)) {
1398           continue;
1399         }
1400         list.add(filterHiddenAndRemoved(entry, componentType));
1401       }
1402       return list.toArray((Object[]) Array.newInstance(componentType, list.size()));
1403     } else {
1404       return o;
1405     }
1406   }
1407 
1408   /**
1409    * Filters hidden elements out of method return values.
1410    */
1411   private static class HideHandler implements InvocationHandler {
1412 
1413     private final Object target;
1414 
HideHandler(Object target)1415     public HideHandler(Object target) {
1416       this.target = target;
1417     }
1418 
invoke(Object proxy, Method method, Object[] args)1419     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
1420       String methodName = method.getName();
1421       if (args != null) {
1422         if (methodName.equals("compareTo") || methodName.equals("equals")
1423             || methodName.equals("overrides") || methodName.equals("subclassOf")) {
1424           args[0] = unwrap(args[0]);
1425         }
1426       }
1427 
1428       if (methodName.equals("getRawCommentText")) {
1429         return filterComment((String) method.invoke(target, args));
1430       }
1431 
1432       // escape "&" in disjunctive types.
1433       if (proxy instanceof Type && methodName.equals("toString")) {
1434         return ((String) method.invoke(target, args)).replace("&", "&amp;");
1435       }
1436 
1437       try {
1438         return filterHiddenAndRemoved(method.invoke(target, args), method.getReturnType());
1439       } catch (InvocationTargetException e) {
1440         throw e.getTargetException();
1441       }
1442     }
1443 
filterComment(String s)1444     private String filterComment(String s) {
1445       if (s == null) {
1446         return null;
1447       }
1448 
1449       s = s.trim();
1450 
1451       // Work around off by one error
1452       while (s.length() >= 5 && s.charAt(s.length() - 5) == '{') {
1453         s += "&nbsp;";
1454       }
1455 
1456       return s;
1457     }
1458 
unwrap(Object proxy)1459     private static Object unwrap(Object proxy) {
1460       if (proxy instanceof Proxy) return ((HideHandler) Proxy.getInvocationHandler(proxy)).target;
1461       return proxy;
1462     }
1463   }
1464 
1465   /**
1466    * Collect the values used by the Dev tools and write them in files packaged with the SDK
1467    *
1468    * @param output the ouput directory for the files.
1469    */
writeSdkValues(String output)1470   private static void writeSdkValues(String output) {
1471     ArrayList<String> activityActions = new ArrayList<String>();
1472     ArrayList<String> broadcastActions = new ArrayList<String>();
1473     ArrayList<String> serviceActions = new ArrayList<String>();
1474     ArrayList<String> categories = new ArrayList<String>();
1475     ArrayList<String> features = new ArrayList<String>();
1476 
1477     ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
1478     ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
1479     ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
1480 
1481     ClassInfo[] classes = Converter.allClasses();
1482 
1483     // The topmost LayoutParams class - android.view.ViewGroup.LayoutParams
1484     ClassInfo topLayoutParams = null;
1485 
1486     // Go through all the fields of all the classes, looking SDK stuff.
1487     for (ClassInfo clazz : classes) {
1488 
1489       // first check constant fields for the SdkConstant annotation.
1490       ArrayList<FieldInfo> fields = clazz.allSelfFields();
1491       for (FieldInfo field : fields) {
1492         Object cValue = field.constantValue();
1493         if (cValue != null) {
1494             ArrayList<AnnotationInstanceInfo> annotations = field.annotations();
1495           if (!annotations.isEmpty()) {
1496             for (AnnotationInstanceInfo annotation : annotations) {
1497               if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1498                 if (!annotation.elementValues().isEmpty()) {
1499                   String type = annotation.elementValues().get(0).valueString();
1500                   if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
1501                     activityActions.add(cValue.toString());
1502                   } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
1503                     broadcastActions.add(cValue.toString());
1504                   } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
1505                     serviceActions.add(cValue.toString());
1506                   } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
1507                     categories.add(cValue.toString());
1508                   } else if (SDK_CONSTANT_TYPE_FEATURE.equals(type)) {
1509                     features.add(cValue.toString());
1510                   }
1511                 }
1512                 break;
1513               }
1514             }
1515           }
1516         }
1517       }
1518 
1519       // Now check the class for @Widget or if its in the android.widget package
1520       // (unless the class is hidden or abstract, or non public)
1521       if (clazz.isHiddenOrRemoved() == false && clazz.isPublic() && clazz.isAbstract() == false) {
1522         boolean annotated = false;
1523         ArrayList<AnnotationInstanceInfo> annotations = clazz.annotations();
1524         if (!annotations.isEmpty()) {
1525           for (AnnotationInstanceInfo annotation : annotations) {
1526             if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
1527               widgets.add(clazz);
1528               annotated = true;
1529               break;
1530             } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1531               layouts.add(clazz);
1532               annotated = true;
1533               break;
1534             }
1535           }
1536         }
1537 
1538         if (annotated == false) {
1539           if (topLayoutParams == null
1540               && "android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
1541             topLayoutParams = clazz;
1542           }
1543           // let's check if this is inside android.widget or android.view
1544           if (isIncludedPackage(clazz)) {
1545             // now we check what this class inherits either from android.view.ViewGroup
1546             // or android.view.View, or android.view.ViewGroup.LayoutParams
1547             int type = checkInheritance(clazz);
1548             switch (type) {
1549               case TYPE_WIDGET:
1550                 widgets.add(clazz);
1551                 break;
1552               case TYPE_LAYOUT:
1553                 layouts.add(clazz);
1554                 break;
1555               case TYPE_LAYOUT_PARAM:
1556                 layoutParams.add(clazz);
1557                 break;
1558             }
1559           }
1560         }
1561       }
1562     }
1563 
1564     // now write the files, whether or not the list are empty.
1565     // the SDK built requires those files to be present.
1566 
1567     Collections.sort(activityActions);
1568     writeValues(output + "/activity_actions.txt", activityActions);
1569 
1570     Collections.sort(broadcastActions);
1571     writeValues(output + "/broadcast_actions.txt", broadcastActions);
1572 
1573     Collections.sort(serviceActions);
1574     writeValues(output + "/service_actions.txt", serviceActions);
1575 
1576     Collections.sort(categories);
1577     writeValues(output + "/categories.txt", categories);
1578 
1579     Collections.sort(features);
1580     writeValues(output + "/features.txt", features);
1581 
1582     // before writing the list of classes, we do some checks, to make sure the layout params
1583     // are enclosed by a layout class (and not one that has been declared as a widget)
1584     for (int i = 0; i < layoutParams.size();) {
1585       ClassInfo clazz = layoutParams.get(i);
1586       ClassInfo containingClass = clazz.containingClass();
1587       boolean remove = containingClass == null || layouts.indexOf(containingClass) == -1;
1588       // Also ensure that super classes of the layout params are in android.widget or android.view.
1589       while (!remove && (clazz = clazz.superclass()) != null && !clazz.equals(topLayoutParams)) {
1590         remove = !isIncludedPackage(clazz);
1591       }
1592       if (remove) {
1593         layoutParams.remove(i);
1594       } else {
1595         i++;
1596       }
1597     }
1598 
1599     writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
1600   }
1601 
1602   /**
1603    * Check if the clazz is in package android.view or android.widget
1604    */
isIncludedPackage(ClassInfo clazz)1605   private static boolean isIncludedPackage(ClassInfo clazz) {
1606     String pckg = clazz.containingPackage().name();
1607     return "android.widget".equals(pckg) || "android.view".equals(pckg);
1608   }
1609 
1610   /**
1611    * Writes a list of values into a text files.
1612    *
1613    * @param pathname the absolute os path of the output file.
1614    * @param values the list of values to write.
1615    */
writeValues(String pathname, ArrayList<String> values)1616   private static void writeValues(String pathname, ArrayList<String> values) {
1617     FileWriter fw = null;
1618     BufferedWriter bw = null;
1619     try {
1620       fw = new FileWriter(pathname, false);
1621       bw = new BufferedWriter(fw);
1622 
1623       for (String value : values) {
1624         bw.append(value).append('\n');
1625       }
1626     } catch (IOException e) {
1627       // pass for now
1628     } finally {
1629       try {
1630         if (bw != null) bw.close();
1631       } catch (IOException e) {
1632         // pass for now
1633       }
1634       try {
1635         if (fw != null) fw.close();
1636       } catch (IOException e) {
1637         // pass for now
1638       }
1639     }
1640   }
1641 
1642   /**
1643    * Writes the widget/layout/layout param classes into a text files.
1644    *
1645    * @param pathname the absolute os path of the output file.
1646    * @param widgets the list of widget classes to write.
1647    * @param layouts the list of layout classes to write.
1648    * @param layoutParams the list of layout param classes to write.
1649    */
writeClasses(String pathname, ArrayList<ClassInfo> widgets, ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams)1650   private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
1651       ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
1652     FileWriter fw = null;
1653     BufferedWriter bw = null;
1654     try {
1655       fw = new FileWriter(pathname, false);
1656       bw = new BufferedWriter(fw);
1657 
1658       // write the 3 types of classes.
1659       for (ClassInfo clazz : widgets) {
1660         writeClass(bw, clazz, 'W');
1661       }
1662       for (ClassInfo clazz : layoutParams) {
1663         writeClass(bw, clazz, 'P');
1664       }
1665       for (ClassInfo clazz : layouts) {
1666         writeClass(bw, clazz, 'L');
1667       }
1668     } catch (IOException e) {
1669       // pass for now
1670     } finally {
1671       try {
1672         if (bw != null) bw.close();
1673       } catch (IOException e) {
1674         // pass for now
1675       }
1676       try {
1677         if (fw != null) fw.close();
1678       } catch (IOException e) {
1679         // pass for now
1680       }
1681     }
1682   }
1683 
1684   /**
1685    * Writes a class name and its super class names into a {@link BufferedWriter}.
1686    *
1687    * @param writer the BufferedWriter to write into
1688    * @param clazz the class to write
1689    * @param prefix the prefix to put at the beginning of the line.
1690    * @throws IOException
1691    */
writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)1692   private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
1693       throws IOException {
1694     writer.append(prefix).append(clazz.qualifiedName());
1695     ClassInfo superClass = clazz;
1696     while ((superClass = superClass.superclass()) != null) {
1697       writer.append(' ').append(superClass.qualifiedName());
1698     }
1699     writer.append('\n');
1700   }
1701 
1702   /**
1703    * Checks the inheritance of {@link ClassInfo} objects. This method return
1704    * <ul>
1705    * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
1706    * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
1707    * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends
1708    * <code>android.view.ViewGroup$LayoutParams</code></li>
1709    * <li>{@link #TYPE_NONE}: in all other cases</li>
1710    * </ul>
1711    *
1712    * @param clazz the {@link ClassInfo} to check.
1713    */
checkInheritance(ClassInfo clazz)1714   private static int checkInheritance(ClassInfo clazz) {
1715     if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
1716       return TYPE_LAYOUT;
1717     } else if ("android.view.View".equals(clazz.qualifiedName())) {
1718       return TYPE_WIDGET;
1719     } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
1720       return TYPE_LAYOUT_PARAM;
1721     }
1722 
1723     ClassInfo parent = clazz.superclass();
1724     if (parent != null) {
1725       return checkInheritance(parent);
1726     }
1727 
1728     return TYPE_NONE;
1729   }
1730 
1731   /**
1732    * Ensures a trailing '/' at the end of a string.
1733    */
ensureSlash(String path)1734   static String ensureSlash(String path) {
1735     return path.endsWith("/") ? path : path + "/";
1736   }
1737 
1738   /**
1739   * Process sample projects. Generate the TOC for the samples groups and project
1740   * and write it to a cs var, which is then written to files during templating to
1741   * html output. Collect metadata from sample project _index.jd files. Copy html
1742   * and specific source file types to the output directory.
1743   */
writeSamples(boolean offlineMode, ArrayList<SampleCode> sampleCodes, boolean sortNavByGroups)1744   public static void writeSamples(boolean offlineMode, ArrayList<SampleCode> sampleCodes,
1745       boolean sortNavByGroups) {
1746     samplesNavTree = makeHDF();
1747 
1748     // Go through samples processing files. Create a root list for SC nodes,
1749     // pass it to SCs for their NavTree children and append them.
1750     List<SampleCode.Node> samplesList = new ArrayList<SampleCode.Node>();
1751     List<SampleCode.Node> sampleGroupsRootNodes = null;
1752     for (SampleCode sc : sampleCodes) {
1753       samplesList.add(sc.setSamplesTOC(offlineMode));
1754      }
1755     if (sortNavByGroups) {
1756       sampleGroupsRootNodes = new ArrayList<SampleCode.Node>();
1757       for (SampleCode gsc : sampleCodeGroups) {
1758         String link =  ClearPage.toroot + "samples/" + gsc.mTitle.replaceAll(" ", "").trim().toLowerCase() + ".html";
1759         sampleGroupsRootNodes.add(new SampleCode.Node.Builder().setLabel(gsc.mTitle).setLink(link).setType("groupholder").build());
1760       }
1761     }
1762     // Pass full samplesList to SC to render the samples TOC to sampleNavTree hdf
1763     if (!offlineMode) {
1764       SampleCode.writeSamplesNavTree(samplesList, sampleGroupsRootNodes);
1765     }
1766     // Iterate the samplecode projects writing the files to out
1767     for (SampleCode sc : sampleCodes) {
1768       sc.writeSamplesFiles(offlineMode);
1769     }
1770   }
1771 
1772   /**
1773   * Given an initial samples directory root, walk through the directory collecting
1774   * sample code project roots and adding them to an array of SampleCodes.
1775   * @param rootDir Root directory holding all browseable sample code projects,
1776   *        defined in frameworks/base/Android.mk as "-sampleDir path".
1777   */
getSampleProjects(File rootDir)1778   public static void getSampleProjects(File rootDir) {
1779     for (File f : rootDir.listFiles()) {
1780       String name = f.getName();
1781       if (f.isDirectory()) {
1782         if (isValidSampleProjectRoot(f)) {
1783           sampleCodes.add(new SampleCode(f.getAbsolutePath(), "samples/" + name, name));
1784         } else {
1785           getSampleProjects(f);
1786         }
1787       }
1788     }
1789   }
1790 
1791   /**
1792   * Test whether a given directory is the root directory for a sample code project.
1793   * Root directories must contain a valid _index.jd file and a src/ directory
1794   * or a module directory that contains a src/ directory.
1795   */
isValidSampleProjectRoot(File dir)1796   public static boolean isValidSampleProjectRoot(File dir) {
1797     File indexJd = new File(dir, "_index.jd");
1798     if (!indexJd.exists()) {
1799       return false;
1800     }
1801     File srcDir = new File(dir, "src");
1802     if (srcDir.exists()) {
1803       return true;
1804     } else {
1805       // Look for a src/ directory one level below the root directory, so
1806       // modules are supported.
1807       for (File childDir : dir.listFiles()) {
1808         if (childDir.isDirectory()) {
1809           srcDir = new File(childDir, "src");
1810           if (srcDir.exists()) {
1811             return true;
1812           }
1813         }
1814       }
1815       return false;
1816     }
1817   }
1818 
1819 }
1820