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