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