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