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