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