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