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