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