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