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