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