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