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("&", "&"); 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 += " "; 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