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