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