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