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