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 import com.google.doclava.javadoc.RootDocImpl; 26 import com.sun.javadoc.ClassDoc; 27 import com.sun.javadoc.Doc; 28 import com.sun.javadoc.MemberDoc; 29 import com.sun.javadoc.RootDoc; 30 import com.sun.javadoc.Type; 31 import java.io.BufferedInputStream; 32 import java.io.BufferedOutputStream; 33 import java.io.BufferedReader; 34 import java.io.BufferedWriter; 35 import java.io.ByteArrayOutputStream; 36 import java.io.File; 37 import java.io.FileInputStream; 38 import java.io.FileNotFoundException; 39 import java.io.FileOutputStream; 40 import java.io.FileReader; 41 import java.io.FileWriter; 42 import java.io.IOException; 43 import java.io.PrintStream; 44 import java.lang.reflect.Array; 45 import java.lang.reflect.InvocationHandler; 46 import java.lang.reflect.InvocationTargetException; 47 import java.lang.reflect.Method; 48 import java.lang.reflect.Proxy; 49 import java.net.MalformedURLException; 50 import java.net.URL; 51 import java.util.ArrayList; 52 import java.util.Arrays; 53 import java.util.Collection; 54 import java.util.Collections; 55 import java.util.HashMap; 56 import java.util.HashSet; 57 import java.util.List; 58 import java.util.Locale; 59 import java.util.Map; 60 import java.util.Set; 61 import java.util.SortedMap; 62 import java.util.TreeMap; 63 import java.util.jar.JarFile; 64 import java.util.regex.Matcher; 65 import java.util.regex.Pattern; 66 import java.util.stream.Collectors; 67 import javax.lang.model.SourceVersion; 68 import jdk.javadoc.doclet.Doclet; 69 import jdk.javadoc.doclet.DocletEnvironment; 70 import jdk.javadoc.doclet.Reporter; 71 72 public class Doclava implements Doclet { 73 74 private static final String SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant"; 75 private static final String SDK_CONSTANT_TYPE_ACTIVITY_ACTION = 76 "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION"; 77 private static final String SDK_CONSTANT_TYPE_BROADCAST_ACTION = 78 "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION"; 79 private static final String SDK_CONSTANT_TYPE_SERVICE_ACTION = 80 "android.annotation.SdkConstant.SdkConstantType.SERVICE_ACTION"; 81 private static final String SDK_CONSTANT_TYPE_CATEGORY = 82 "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY"; 83 private static final String SDK_CONSTANT_TYPE_FEATURE = 84 "android.annotation.SdkConstant.SdkConstantType.FEATURE"; 85 private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget"; 86 private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout"; 87 88 private static final int TYPE_NONE = 0; 89 private static final int TYPE_WIDGET = 1; 90 private static final int TYPE_LAYOUT = 2; 91 private static final int TYPE_LAYOUT_PARAM = 3; 92 93 public static final int SHOW_PUBLIC = 0x00000001; 94 public static final int SHOW_PROTECTED = 0x00000003; 95 public static final int SHOW_PACKAGE = 0x00000007; 96 public static final int SHOW_PRIVATE = 0x0000000f; 97 public static final int SHOW_HIDDEN = 0x0000001f; 98 99 public static int showLevel = SHOW_PROTECTED; 100 101 public static final boolean SORT_BY_NAV_GROUPS = true; 102 /* Debug output for PageMetadata, format urls from site root */ 103 public static boolean META_DBG=false; 104 /* Generate the static html docs with devsite tempating only */ 105 public static boolean DEVSITE_STATIC_ONLY = false; 106 /* Don't resolve @link refs found in devsite pages */ 107 public static boolean DEVSITE_IGNORE_JDLINKS = false; 108 /* Show Preview navigation and process preview docs */ 109 public static boolean INCLUDE_PREVIEW = false; 110 /* output en, es, ja without parent intl/ container */ 111 public static boolean USE_DEVSITE_LOCALE_OUTPUT_PATHS = false; 112 /* generate navtree.js without other docs */ 113 public static boolean NAVTREE_ONLY = false; 114 /* Generate reference navtree.js with all inherited members */ 115 public static boolean AT_LINKS_NAVTREE = false; 116 public static boolean METALAVA_API_SINCE = false; 117 public static String outputPathBase = "/"; 118 public static ArrayList<String> inputPathHtmlDirs = new ArrayList<String>(); 119 public static ArrayList<String> inputPathHtmlDir2 = new ArrayList<String>(); 120 public static String inputPathResourcesDir; 121 public static String outputPathResourcesDir; 122 public static String outputPathHtmlDirs; 123 public static String outputPathHtmlDir2; 124 /* Javadoc output directory and included in url path */ 125 public static String javadocDir = "reference/"; 126 public static String htmlExtension; 127 128 public static RootDoc root; 129 public static ArrayList<String[]> mHDFData = new ArrayList<String[]>(); 130 public static List<PageMetadata.Node> sTaglist = new ArrayList<PageMetadata.Node>(); 131 public static ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>(); 132 public static ArrayList<SampleCode> sampleCodeGroups = new ArrayList<SampleCode>(); 133 public static Data samplesNavTree; 134 public static Map<Character, String> escapeChars = new HashMap<Character, String>(); 135 public static String title = ""; 136 public static SinceTagger sinceTagger = new SinceTagger(); 137 public static ArtifactTagger artifactTagger = new ArtifactTagger(); 138 public static HashSet<String> knownTags = new HashSet<String>(); 139 public static FederationTagger federationTagger = new FederationTagger(); 140 public static boolean showUnannotated = false; 141 public static Set<String> showAnnotations = new HashSet<String>(); 142 public static Set<String> hideAnnotations = new HashSet<String>(); 143 public static boolean showAnnotationOverridesVisibility = false; 144 public static Set<String> hiddenPackages = new HashSet<String>(); 145 public static boolean includeAssets = true; 146 public static boolean includeDefaultAssets = true; 147 private static boolean generateDocs = true; 148 private static boolean parseComments = false; 149 private static String yamlNavFile = null; 150 public static boolean documentAnnotations = false; 151 public static String documentAnnotationsPath = null; 152 public static Map<String, String> annotationDocumentationMap = null; 153 public static boolean referenceOnly = false; 154 public static boolean staticOnly = false; 155 public static boolean yamlV2 = false; /* whether to build the new version of the yaml file */ 156 public static boolean devsite = false; /* whether to build docs for devsite */ 157 public static AuxSource auxSource = new EmptyAuxSource(); 158 public static Linter linter = new EmptyLinter(); 159 public static boolean android = false; 160 public static String manifestFile = null; 161 public static String compatConfig = null; 162 public static Map<String, String> manifestPermissions = new HashMap<>(); 163 164 public static JSilver jSilver = null; 165 166 //API reference extensions 167 private static boolean gmsRef = false; 168 private static boolean gcmRef = false; 169 public static String libraryRoot = null; 170 private static boolean samplesRef = false; 171 private static boolean sac = false; 172 173 private static ArrayList<String> knownTagsFiles = new ArrayList<>(); 174 private static String keepListFile; 175 private static String proguardFile; 176 private static String proofreadFile; 177 private static String todoFile; 178 private static String stubsDir; 179 private static HashSet<String> stubPackages; 180 private static HashSet<String> stubImportPackages; 181 private static boolean stubSourceOnly; 182 private static boolean keepStubComments; 183 private static String sdkValuePath; 184 private static String apiFile; 185 private static String dexApiFile; 186 private static String removedApiFile; 187 private static String removedDexApiFile; 188 private static String exactApiFile; 189 private static String privateApiFile; 190 private static String privateDexApiFile; 191 private static String apiMappingFile; 192 private static boolean offlineMode; 193 194 @Override init(Locale locale, Reporter reporter)195 public void init(Locale locale, Reporter reporter) { 196 keepListFile = null; 197 proguardFile = null; 198 proofreadFile = null; 199 todoFile = null; 200 sdkValuePath = null; 201 stubsDir = null; 202 // Create the dependency graph for the stubs directory 203 offlineMode = false; 204 apiFile = null; 205 dexApiFile = null; 206 removedApiFile = null; 207 removedDexApiFile = null; 208 exactApiFile = null; 209 privateApiFile = null; 210 privateDexApiFile = null; 211 apiMappingFile = null; 212 stubPackages = null; 213 stubImportPackages = null; 214 stubSourceOnly = false; 215 keepStubComments = false; 216 } 217 218 @Override getName()219 public String getName() { 220 return "Doclava"; 221 } 222 223 /** 224 * @implNote 225 * {@code -overview} option used to be a built-in parameter in javadoc 226 * tool, and with new Doclet APIs it was moved to 227 * {@link jdk.javadoc.doclet.StandardDoclet}, so we have to implement this 228 * functionality by ourselves. 229 */ 230 @Override getSupportedOptions()231 public Set<? extends Option> getSupportedOptions() { 232 Set<Doclet.Option> options = new HashSet<>(); 233 234 options.add( 235 new Option() { 236 private final List<String> names = List.of("-overview"); 237 @Override public int getArgumentCount() { return 1; } 238 @Override public String getDescription() { 239 return "Pick overview documentation from HTML file"; 240 } 241 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 242 @Override public List<String> getNames() { return names; } 243 @Override public String getParameters() { return "<file>"; } 244 @Override public boolean process(String opt, List<String> arguments) { 245 // TODO(nikitai): implement "overview" file inclusion. 246 // This used to be built in javadoc tool but in new Doclet APIs it was 247 // removed from default functionality and moved to StandardDoclet 248 // implementation. In our case we need to implement this on our own. 249 return true; 250 } 251 } 252 ); 253 254 options.add( 255 new Option() { 256 private final List<String> names = List.of("-d"); 257 @Override public int getArgumentCount() { return 1; } 258 @Override public String getDescription() { 259 return "Destination directory for output files"; 260 } 261 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 262 @Override public List<String> getNames() { return names; } 263 @Override public String getParameters() { return "<directory>"; } 264 @Override public boolean process(String opt, List<String> arguments) { 265 outputPathBase = outputPathHtmlDirs = ClearPage.outputDir 266 = arguments.get(0); 267 return true; 268 } 269 } 270 ); 271 272 options.add( 273 new Option() { 274 private final List<String> names = List.of("-templatedir"); 275 @Override public int getArgumentCount() { return 1; } 276 @Override public String getDescription() { 277 return "Templates for jSilver template engine used to generate docs"; 278 } 279 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 280 @Override public List<String> getNames() { return names; } 281 @Override public String getParameters() { return "<directory>"; } 282 @Override public boolean process(String opt, List<String> arguments) { 283 ClearPage.addTemplateDir(arguments.get(0)); 284 return true; 285 } 286 } 287 ); 288 289 options.add( 290 new Option() { 291 private final List<String> names = List.of("-hdf"); 292 @Override public int getArgumentCount() { return 2; } 293 @Override public String getDescription() { 294 return """ 295 Doclava uses the jSilver template engine to render docs. This 296 option adds a key-value pair to the global data holder object which 297 is passed to all render calls. Think of it as a list of default 298 parameters for jSilver."""; 299 } 300 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 301 @Override public List<String> getNames() { return names; } 302 @Override public String getParameters() { return "<key> <value>"; } 303 @Override public boolean process(String opt, List<String> arguments) { 304 mHDFData.add(new String[] { arguments.get(0), arguments.get(1) }); 305 return true; 306 } 307 } 308 ); 309 310 options.add( 311 new Option() { 312 private final List<String> names = List.of("-knowntags"); 313 @Override public int getArgumentCount() { return 1; } 314 @Override public String getDescription() { 315 return """ 316 List of non-standard tags used in sources. 317 Example: ${ANDROID_BUILD_TOP}/libcore/known_oj_tags.txt"""; 318 } 319 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 320 @Override public List<String> getNames() { return names; } 321 @Override public String getParameters() { return "<file>"; } 322 @Override public boolean process(String opt, List<String> arguments) { 323 knownTagsFiles.add(arguments.get(0)); 324 return true; } 325 } 326 ); 327 328 options.add( 329 new Option() { 330 private final List<String> names = List.of("-apidocsdir"); 331 @Override public int getArgumentCount() { return 1; } 332 @Override public String getDescription() { 333 return """ 334 Javadoc output directory path relative to root, which is specified \ 335 with '-d root' 336 337 Default value: 'reference/'"""; 338 } 339 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 340 @Override public List<String> getNames() { return names; } 341 @Override public String getParameters() { return "<path>"; } 342 @Override public boolean process(String opt, List<String> arguments) { 343 javadocDir = arguments.get(0); 344 return true; 345 } 346 } 347 ); 348 349 options.add( 350 new Option() { 351 private final List<String> names = List.of("-toroot"); 352 @Override public int getArgumentCount() { return 1; } 353 @Override public String getDescription() { 354 return """ 355 Relative path to documentation root. 356 If set, use <path> as a (relative or absolute) link to \ 357 documentation root in .html pages. 358 359 If not set, an auto-generated path traversal links will be used, \ 360 e.g. “../../../”. 361 """; 362 } 363 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 364 @Override public List<String> getNames() { return names; } 365 @Override public String getParameters() { return "<path>"; } 366 @Override public boolean process(String opt, List<String> arguments) { 367 ClearPage.toroot = arguments.get(0); 368 return true; } 369 } 370 ); 371 372 options.add( 373 new Option() { 374 private final List<String> names = List.of("-samplecode"); 375 @Override public int getArgumentCount() { return 3; } 376 @Override public String getDescription() { 377 return """ 378 Adds a browsable sample code project from <source> directory under \ 379 <dest> path relative to root (specified with '-d' <directory>) and \ 380 named <title>. 381 """; 382 } 383 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 384 @Override public List<String> getNames() { return names; } 385 @Override public String getParameters() { 386 return "<source> <dest> <title>"; 387 } 388 @Override public boolean process(String opt, List<String> arguments) { 389 sampleCodes.add(new SampleCode(arguments.get(0), arguments.get(1), arguments.get(2))); 390 samplesRef = true; 391 return true; 392 } 393 } 394 ); 395 396 options.add( 397 new Option() { 398 private final List<String> names = List.of("-samplegroup"); 399 @Override public int getArgumentCount() { return 1; } 400 @Override public String getDescription() { 401 return "Add a sample code project group"; 402 } 403 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 404 @Override public List<String> getNames() { return names; } 405 @Override public String getParameters() { return "<group>"; } 406 @Override public boolean process(String opt, List<String> arguments) { 407 sampleCodeGroups.add(new SampleCode(null, null, arguments.get(0))); 408 return true; 409 } 410 } 411 ); 412 413 options.add( 414 new Option() { 415 private final List<String> names = List.of("-samplesdir"); 416 @Override public int getArgumentCount() { return 1; } 417 @Override public String getDescription() { 418 return """ 419 Directory where to look for samples. Android uses \ 420 ${ANDROID_BUILD_TOP}/development/samples/browseable. 421 """; 422 } 423 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 424 @Override public List<String> getNames() { return names; } 425 @Override public String getParameters() { return "<directory>"; } 426 @Override public boolean process(String opt, List<String> arguments) { 427 samplesRef = true; 428 getSampleProjects(new File(arguments.get(0))); 429 return true; 430 } 431 } 432 ); 433 434 options.add( 435 new Option() { 436 private final List<String> names = List.of("-htmldir"); 437 @Override public int getArgumentCount() { return 1; } 438 @Override public String getDescription() { return ""; } 439 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 440 @Override public List<String> getNames() { return names; } 441 @Override public String getParameters() { return "<path>"; } 442 @Override public boolean process(String opt, List<String> arguments) { 443 inputPathHtmlDirs.add(arguments.get(0)); 444 ClearPage.htmlDirs = inputPathHtmlDirs; 445 return true; 446 } 447 } 448 ); 449 450 options.add( 451 new Option() { 452 private final List<String> names = List.of("-htmldir2"); 453 @Override public int getArgumentCount() { return 2; } 454 @Override public String getDescription() { return ""; } 455 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 456 @Override public List<String> getNames() { return names; } 457 @Override public String getParameters() { 458 return "<input_path> <output_path>"; 459 } 460 @Override public boolean process(String opt, List<String> arguments) { 461 if (arguments.get(1).equals("default")) { 462 inputPathHtmlDir2.add(arguments.get(0)); 463 } else { 464 inputPathHtmlDir2.add(arguments.get(0)); 465 outputPathHtmlDir2 = arguments.get(1); 466 } 467 return true; 468 } 469 } 470 ); 471 472 options.add( 473 new Option() { 474 private final List<String> names = List.of("-resourcesdir"); 475 @Override public int getArgumentCount() { return 1; } 476 @Override public String getDescription() { return ""; } 477 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 478 @Override public List<String> getNames() { return names; } 479 @Override public String getParameters() { return "<path>"; } 480 @Override public boolean process(String opt, List<String> arguments) { 481 inputPathResourcesDir = arguments.get(0); 482 return true; 483 } 484 } 485 ); 486 487 options.add( 488 new Option() { 489 private final List<String> names = List.of("-resourcesoutdir"); 490 @Override public int getArgumentCount() { return 1; } 491 @Override public String getDescription() { return ""; } 492 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 493 @Override public List<String> getNames() { return names; } 494 @Override public String getParameters() { return "<path>"; } 495 @Override public boolean process(String opt, List<String> arguments) { 496 outputPathResourcesDir = arguments.get(0); 497 return true; 498 } 499 } 500 ); 501 502 options.add( 503 new Option() { 504 private final List<String> names = List.of("-title"); 505 @Override public int getArgumentCount() { return 1; } 506 @Override public String getDescription() { return ""; } 507 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 508 @Override public List<String> getNames() { return names; } 509 @Override public String getParameters() { return "<title>"; } 510 @Override public boolean process(String opt, List<String> arguments) { 511 Doclava.title = arguments.get(0); 512 return true; 513 } 514 } 515 ); 516 517 options.add( 518 new Option() { 519 private final List<String> names = List.of("-werror"); 520 @Override public int getArgumentCount() { return 0; } 521 @Override public String getDescription() { return ""; } 522 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 523 @Override public List<String> getNames() { return names; } 524 @Override public String getParameters() { return ""; } 525 @Override public boolean process(String opt, List<String> arguments) { 526 // b/270335911: disable warnings as errors until new findings are addressed. 527 // Errors.setWarningsAreErrors(true); 528 return true; 529 } 530 } 531 ); 532 533 options.add( 534 new Option() { 535 private final List<String> names = List.of("-lerror"); 536 @Override public int getArgumentCount() { return 0; } 537 @Override public String getDescription() { return ""; } 538 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 539 @Override public List<String> getNames() { return names; } 540 @Override public String getParameters() { return ""; } 541 @Override public boolean process(String opt, List<String> arguments) { 542 // b/270335653: disable lint warnings as errors until new findings are addressed. 543 // Errors.setLintsAreErrors(true); 544 return true; 545 } 546 } 547 ); 548 549 options.add( 550 new Option() { 551 private final List<String> names = List.of("-error"); 552 @Override public int getArgumentCount() { return 1; } 553 @Override public String getDescription() { return ""; } 554 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 555 @Override public List<String> getNames() { return names; } 556 @Override public String getParameters() { return "<code_value>"; } 557 @Override public boolean process(String opt, List<String> arguments) { 558 try { 559 int level = Integer.parseInt(arguments.get(0)); 560 Errors.setErrorLevel(level, Errors.ERROR); 561 return true; 562 } catch (NumberFormatException e) { 563 return false; 564 } 565 } 566 } 567 ); 568 569 options.add( 570 new Option() { 571 private final List<String> names = List.of("-warning"); 572 @Override public int getArgumentCount() { return 1; } 573 @Override public String getDescription() { return ""; } 574 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 575 @Override public List<String> getNames() { return names; } 576 @Override public String getParameters() { return "<code_value>"; } 577 @Override public boolean process(String opt, List<String> arguments) { 578 try { 579 int level = Integer.parseInt(arguments.get(0)); 580 Errors.setErrorLevel(level, Errors.WARNING); 581 return true; 582 } catch (NumberFormatException e) { 583 return false; 584 } 585 } 586 } 587 ); 588 589 options.add( 590 new Option() { 591 private final List<String> names = List.of("-lint"); 592 @Override public int getArgumentCount() { return 1; } 593 @Override public String getDescription() { return ""; } 594 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 595 @Override public List<String> getNames() { return names; } 596 @Override public String getParameters() { return "<code_value>"; } 597 @Override public boolean process(String opt, List<String> arguments) { 598 try { 599 int level = Integer.parseInt(arguments.get(0)); 600 Errors.setErrorLevel(level, Errors.LINT); 601 return true; 602 } catch (NumberFormatException e) { 603 return false; 604 } 605 } 606 } 607 ); 608 609 options.add( 610 new Option() { 611 private final List<String> names = List.of("-hide"); 612 @Override public int getArgumentCount() { return 1; } 613 @Override public String getDescription() { return ""; } 614 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 615 @Override public List<String> getNames() { return names; } 616 @Override public String getParameters() { return "<code_value>"; } 617 @Override public boolean process(String opt, List<String> arguments) { 618 try { 619 int level = Integer.parseInt(arguments.get(0)); 620 Errors.setErrorLevel(level, Errors.HIDDEN); 621 return true; 622 } catch (NumberFormatException e) { 623 return false; 624 } 625 } 626 } 627 ); 628 629 options.add( 630 new Option() { 631 private final List<String> names = List.of("-keeplist"); 632 @Override public int getArgumentCount() { return 1; } 633 @Override public String getDescription() { return ""; } 634 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 635 @Override public List<String> getNames() { return names; } 636 @Override public String getParameters() { return "<list>"; } 637 @Override public boolean process(String opt, List<String> arguments) { 638 keepListFile = arguments.get(0); 639 return true; 640 } 641 } 642 ); 643 644 options.add( 645 new Option() { 646 private final List<String> names = List.of("-showUnannotated"); 647 @Override public int getArgumentCount() { return 0; } 648 @Override public String getDescription() { return ""; } 649 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 650 @Override public List<String> getNames() { return names; } 651 @Override public String getParameters() { return ""; } 652 @Override public boolean process(String opt, List<String> arguments) { 653 showUnannotated = true; 654 return true; 655 } 656 } 657 ); 658 659 options.add( 660 new Option() { 661 private final List<String> names = List.of("-showAnnotation"); 662 @Override public int getArgumentCount() { return 1; } 663 @Override public String getDescription() { return ""; } 664 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 665 @Override public List<String> getNames() { return names; } 666 @Override public String getParameters() { return "<annotation>"; } 667 @Override public boolean process(String opt, List<String> arguments) { 668 showAnnotations.add(arguments.get(0)); 669 return true; 670 } 671 } 672 ); 673 674 options.add( 675 new Option() { 676 private final List<String> names = List.of("-hideAnnotation"); 677 @Override public int getArgumentCount() { return 1; } 678 @Override public String getDescription() { return ""; } 679 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 680 @Override public List<String> getNames() { return names; } 681 @Override public String getParameters() { return "<annotation>"; } 682 @Override public boolean process(String opt, List<String> arguments) { 683 hideAnnotations.add(arguments.get(0)); 684 return true; 685 } 686 } 687 ); 688 689 options.add( 690 new Option() { 691 private final List<String> names = List.of("-showAnnotationOverridesVisibility"); 692 @Override public int getArgumentCount() { return 0; } 693 @Override public String getDescription() { return ""; } 694 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 695 @Override public List<String> getNames() { return names; } 696 @Override public String getParameters() { return ""; } 697 @Override public boolean process(String opt, List<String> arguments) { 698 showAnnotationOverridesVisibility = true; 699 return true; 700 } 701 } 702 ); 703 704 options.add( 705 new Option() { 706 private final List<String> names = List.of("-hidePackage"); 707 @Override public int getArgumentCount() { return 1; } 708 @Override public String getDescription() { return ""; } 709 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 710 @Override public List<String> getNames() { return names; } 711 @Override public String getParameters() { return "<package>"; } 712 @Override public boolean process(String opt, List<String> arguments) { 713 hiddenPackages.add(arguments.get(0)); 714 return true; 715 } 716 } 717 ); 718 719 options.add( 720 new Option() { 721 private final List<String> names = List.of("-proguard"); 722 @Override public int getArgumentCount() { return 1; } 723 @Override public String getDescription() { return ""; } 724 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 725 @Override public List<String> getNames() { return names; } 726 @Override public String getParameters() { return "<arg>"; } 727 @Override public boolean process(String opt, List<String> arguments) { 728 proguardFile = arguments.get(0); 729 return true; 730 } 731 } 732 ); 733 734 options.add( 735 new Option() { 736 private final List<String> names = List.of("-proofread"); 737 @Override public int getArgumentCount() { return 1; } 738 @Override public String getDescription() { return ""; } 739 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 740 @Override public List<String> getNames() { return names; } 741 @Override public String getParameters() { return "<arg>"; } 742 @Override public boolean process(String opt, List<String> arguments) { 743 proofreadFile = arguments.get(0); 744 return true; 745 } 746 } 747 ); 748 749 options.add( 750 new Option() { 751 private final List<String> names = List.of("-todo"); 752 @Override public int getArgumentCount() { return 1; } 753 @Override public String getDescription() { return ""; } 754 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 755 @Override public List<String> getNames() { return names; } 756 @Override public String getParameters() { return "<file>"; } 757 @Override public boolean process(String opt, List<String> arguments) { 758 todoFile = arguments.get(0); 759 return true; 760 } 761 } 762 ); 763 764 options.add( 765 new Option() { 766 private final List<String> names = List.of("-public"); 767 @Override public int getArgumentCount() { return 0; } 768 @Override public String getDescription() { return ""; } 769 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 770 @Override public List<String> getNames() { return names; } 771 @Override public String getParameters() { return ""; } 772 @Override public boolean process(String opt, List<String> arguments) { 773 showLevel = SHOW_PUBLIC; 774 return true; 775 } 776 } 777 ); 778 779 options.add( 780 new Option() { 781 private final List<String> names = List.of("-protected"); 782 @Override public int getArgumentCount() { return 0; } 783 @Override public String getDescription() { return ""; } 784 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 785 @Override public List<String> getNames() { return names; } 786 @Override public String getParameters() { return ""; } 787 @Override public boolean process(String opt, List<String> arguments) { 788 showLevel = SHOW_PROTECTED; 789 return true; 790 } 791 } 792 ); 793 794 options.add( 795 new Option() { 796 private final List<String> names = List.of("-package"); 797 @Override public int getArgumentCount() { return 0; } 798 @Override public String getDescription() { return ""; } 799 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 800 @Override public List<String> getNames() { return names; } 801 @Override public String getParameters() { return ""; } 802 @Override public boolean process(String opt, List<String> arguments) { 803 showLevel = SHOW_PACKAGE; 804 return true; 805 } 806 } 807 ); 808 809 options.add( 810 new Option() { 811 private final List<String> names = List.of("-private"); 812 @Override public int getArgumentCount() { return 0; } 813 @Override public String getDescription() { return ""; } 814 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 815 @Override public List<String> getNames() { return names; } 816 @Override public String getParameters() { return ""; } 817 @Override public boolean process(String opt, List<String> arguments) { 818 showLevel = SHOW_PRIVATE; 819 return true; 820 } 821 } 822 ); 823 824 options.add( 825 new Option() { 826 private final List<String> names = List.of("-hidden"); 827 @Override public int getArgumentCount() { return 0; } 828 @Override public String getDescription() { return ""; } 829 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 830 @Override public List<String> getNames() { return names; } 831 @Override public String getParameters() { return ""; } 832 @Override public boolean process(String opt, List<String> arguments) { 833 showLevel = SHOW_HIDDEN; 834 return true; 835 } 836 } 837 ); 838 839 options.add( 840 new Option() { 841 private final List<String> names = List.of("-stubs"); 842 @Override public int getArgumentCount() { return 1; } 843 @Override public String getDescription() { return ""; } 844 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 845 @Override public List<String> getNames() { return names; } 846 @Override public String getParameters() { return "<stubs>"; } 847 @Override public boolean process(String opt, List<String> arguments) { 848 stubsDir = arguments.get(0); 849 return true; 850 } 851 } 852 ); 853 854 options.add( 855 new Option() { 856 private final List<String> names = List.of("-stubpackages"); 857 @Override public int getArgumentCount() { return 1; } 858 @Override public String getDescription() { return ""; } 859 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 860 @Override public List<String> getNames() { return names; } 861 @Override public String getParameters() { return "<packages>"; } 862 @Override public boolean process(String opt, List<String> arguments) { 863 stubPackages = new HashSet<>(); 864 stubPackages.addAll(Arrays.asList(arguments.get(0).split(":"))); 865 return true; 866 } 867 } 868 ); 869 870 options.add( 871 new Option() { 872 private final List<String> names = List.of("-stubimportpackages"); 873 @Override public int getArgumentCount() { return 1; } 874 @Override public String getDescription() { return ""; } 875 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 876 @Override public List<String> getNames() { return names; } 877 @Override public String getParameters() { return "<packages>"; } 878 @Override public boolean process(String opt, List<String> arguments) { 879 stubImportPackages = new HashSet<>(); 880 for (String pkg : arguments.get(0).split(":")) { 881 stubImportPackages.add(pkg); 882 hiddenPackages.add(pkg); 883 } 884 return true; 885 } 886 } 887 ); 888 889 options.add( 890 new Option() { 891 private final List<String> names = List.of("-stubsourceonly"); 892 @Override public int getArgumentCount() { return 0; } 893 @Override public String getDescription() { return ""; } 894 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 895 @Override public List<String> getNames() { return names; } 896 @Override public String getParameters() { return ""; } 897 @Override public boolean process(String opt, List<String> arguments) { 898 stubSourceOnly = true; 899 return true; 900 } 901 } 902 ); 903 904 options.add( 905 new Option() { 906 private final List<String> names = List.of("-keepstubcomments"); 907 @Override public int getArgumentCount() { return 0; } 908 @Override public String getDescription() { return ""; } 909 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 910 @Override public List<String> getNames() { return names; } 911 @Override public String getParameters() { return ""; } 912 @Override public boolean process(String opt, List<String> arguments) { 913 keepStubComments = true; 914 return true; 915 } 916 } 917 ); 918 919 options.add( 920 new Option() { 921 private final List<String> names = List.of("-sdkvalues"); 922 @Override public int getArgumentCount() { return 1; } 923 @Override public String getDescription() { return ""; } 924 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 925 @Override public List<String> getNames() { return names; } 926 @Override public String getParameters() { return "<path>"; } 927 @Override public boolean process(String opt, List<String> arguments) { 928 sdkValuePath = arguments.get(0); 929 return true; 930 } 931 } 932 ); 933 934 options.add( 935 new Option() { 936 private final List<String> names = List.of("-api"); 937 @Override public int getArgumentCount() { return 1; } 938 @Override public String getDescription() { return ""; } 939 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 940 @Override public List<String> getNames() { return names; } 941 @Override public String getParameters() { return "<file>"; } 942 @Override public boolean process(String opt, List<String> arguments) { 943 apiFile = arguments.get(0); 944 return true; 945 } 946 } 947 ); 948 949 options.add( 950 new Option() { 951 private final List<String> names = List.of("-dexApi"); 952 @Override public int getArgumentCount() { return 1; } 953 @Override public String getDescription() { return ""; } 954 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 955 @Override public List<String> getNames() { return names; } 956 @Override public String getParameters() { return "<file>"; } 957 @Override public boolean process(String opt, List<String> arguments) { 958 dexApiFile = arguments.get(0); 959 return true; 960 } 961 } 962 ); 963 964 options.add( 965 new Option() { 966 private final List<String> names = List.of("-removedApi"); 967 @Override public int getArgumentCount() { return 1; } 968 @Override public String getDescription() { return ""; } 969 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 970 @Override public List<String> getNames() { return names; } 971 @Override public String getParameters() { return "<file>"; } 972 @Override public boolean process(String opt, List<String> arguments) { 973 removedApiFile = arguments.get(0); 974 return true; 975 } 976 } 977 ); 978 979 options.add( 980 new Option() { 981 private final List<String> names = List.of("-removedDexApi"); 982 @Override public int getArgumentCount() { return 1; } 983 @Override public String getDescription() { return ""; } 984 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 985 @Override public List<String> getNames() { return names; } 986 @Override public String getParameters() { return "<file>"; } 987 @Override public boolean process(String opt, List<String> arguments) { 988 removedDexApiFile = arguments.get(0); 989 return true; 990 } 991 } 992 ); 993 994 options.add( 995 new Option() { 996 private final List<String> names = List.of("-exactApi"); 997 @Override public int getArgumentCount() { return 1; } 998 @Override public String getDescription() { return ""; } 999 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1000 @Override public List<String> getNames() { return names; } 1001 @Override public String getParameters() { return "<file>"; } 1002 @Override public boolean process(String opt, List<String> arguments) { 1003 exactApiFile = arguments.get(0); 1004 return true; 1005 } 1006 } 1007 ); 1008 1009 options.add( 1010 new Option() { 1011 private final List<String> names = List.of("-privateApi"); 1012 @Override public int getArgumentCount() { return 1; } 1013 @Override public String getDescription() { return ""; } 1014 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1015 @Override public List<String> getNames() { return names; } 1016 @Override public String getParameters() { return "<file>"; } 1017 @Override public boolean process(String opt, List<String> arguments) { 1018 privateApiFile = arguments.get(0); 1019 return true; 1020 } 1021 } 1022 ); 1023 1024 options.add( 1025 new Option() { 1026 private final List<String> names = List.of("-privateDexApi"); 1027 @Override public int getArgumentCount() { return 1; } 1028 @Override public String getDescription() { return ""; } 1029 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1030 @Override public List<String> getNames() { return names; } 1031 @Override public String getParameters() { return "<file>"; } 1032 @Override public boolean process(String opt, List<String> arguments) { 1033 privateDexApiFile = arguments.get(0); 1034 return true; 1035 } 1036 } 1037 ); 1038 1039 options.add( 1040 new Option() { 1041 private final List<String> names = List.of("-apiMapping"); 1042 @Override public int getArgumentCount() { return 1; } 1043 @Override public String getDescription() { return ""; } 1044 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1045 @Override public List<String> getNames() { return names; } 1046 @Override public String getParameters() { return "<file>"; } 1047 @Override public boolean process(String opt, List<String> arguments) { 1048 apiMappingFile = arguments.get(0); 1049 return true; 1050 } 1051 } 1052 ); 1053 1054 options.add( 1055 new Option() { 1056 private final List<String> names = List.of("-nodocs"); 1057 @Override public int getArgumentCount() { return 0; } 1058 @Override public String getDescription() { return ""; } 1059 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1060 @Override public List<String> getNames() { return names; } 1061 @Override public String getParameters() { return ""; } 1062 @Override public boolean process(String opt, List<String> arguments) { 1063 generateDocs = false; 1064 return true; 1065 } 1066 } 1067 ); 1068 1069 options.add( 1070 new Option() { 1071 private final List<String> names = List.of("-noassets"); 1072 @Override public int getArgumentCount() { return 0; } 1073 @Override public String getDescription() { return ""; } 1074 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1075 @Override public List<String> getNames() { return names; } 1076 @Override public String getParameters() { return ""; } 1077 @Override public boolean process(String opt, List<String> arguments) { 1078 includeAssets = false; 1079 return true; 1080 } 1081 } 1082 ); 1083 1084 options.add( 1085 new Option() { 1086 private final List<String> names = List.of("-nodefaultassets"); 1087 @Override public int getArgumentCount() { return 0; } 1088 @Override public String getDescription() { return ""; } 1089 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1090 @Override public List<String> getNames() { return names; } 1091 @Override public String getParameters() { return ""; } 1092 @Override public boolean process(String opt, List<String> arguments) { 1093 includeDefaultAssets = false; 1094 return true; 1095 } 1096 } 1097 ); 1098 1099 options.add( 1100 new Option() { 1101 private final List<String> names = List.of("-parsecomments"); 1102 @Override public int getArgumentCount() { return 0; } 1103 @Override public String getDescription() { return ""; } 1104 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1105 @Override public List<String> getNames() { return names; } 1106 @Override public String getParameters() { return ""; } 1107 @Override public boolean process(String opt, List<String> arguments) { 1108 parseComments = true; 1109 return true; 1110 } 1111 } 1112 ); 1113 1114 options.add( 1115 new Option() { 1116 private final List<String> names = List.of("-metalavaApiSince"); 1117 @Override public int getArgumentCount() { return 0; } 1118 @Override public String getDescription() { return ""; } 1119 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1120 @Override public List<String> getNames() { return names; } 1121 @Override public String getParameters() { return ""; } 1122 @Override public boolean process(String opt, List<String> arguments) { 1123 METALAVA_API_SINCE = true; 1124 return true; 1125 } 1126 } 1127 ); 1128 1129 options.add( 1130 new Option() { 1131 private final List<String> names = List.of("-since"); 1132 @Override public int getArgumentCount() { return 2; } 1133 @Override public String getDescription() { return ""; } 1134 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1135 @Override public List<String> getNames() { return names; } 1136 @Override public String getParameters() { return "<major> <minor>"; } 1137 @Override public boolean process(String opt, List<String> arguments) { 1138 sinceTagger.addVersion(arguments.get(0), arguments.get(1)); 1139 return true; 1140 } 1141 } 1142 ); 1143 1144 options.add( 1145 new Option() { 1146 private final List<String> names = List.of("-artifact"); 1147 @Override public int getArgumentCount() { return 2; } 1148 @Override public String getDescription() { return ""; } 1149 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1150 @Override public List<String> getNames() { return names; } 1151 @Override public String getParameters() { return "<arg1> <arg2>"; } 1152 @Override public boolean process(String opt, List<String> arguments) { 1153 artifactTagger.addArtifact(arguments.get(0), arguments.get(1)); 1154 return true; 1155 } 1156 } 1157 ); 1158 1159 options.add( 1160 new Option() { 1161 private final List<String> names = List.of("-offlinemode"); 1162 @Override public int getArgumentCount() { return 0; } 1163 @Override public String getDescription() { return ""; } 1164 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1165 @Override public List<String> getNames() { return names; } 1166 @Override public String getParameters() { return ""; } 1167 @Override public boolean process(String opt, List<String> arguments) { 1168 // TODO(nikitai): This option is not used anywhere, consider removing. 1169 offlineMode = true; 1170 return true; 1171 } 1172 } 1173 ); 1174 1175 options.add( 1176 new Option() { 1177 private final List<String> names = List.of("-metadataDebug"); 1178 @Override public int getArgumentCount() { return 0; } 1179 @Override public String getDescription() { return ""; } 1180 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1181 @Override public List<String> getNames() { return names; } 1182 @Override public String getParameters() { return ""; } 1183 @Override public boolean process(String opt, List<String> arguments) { 1184 META_DBG = true; 1185 return true; 1186 } 1187 } 1188 ); 1189 1190 options.add( 1191 new Option() { 1192 private final List<String> names = List.of("-includePreview"); 1193 @Override public int getArgumentCount() { return 0; } 1194 @Override public String getDescription() { return ""; } 1195 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1196 @Override public List<String> getNames() { return names; } 1197 @Override public String getParameters() { return ""; } 1198 @Override public boolean process(String opt, List<String> arguments) { 1199 INCLUDE_PREVIEW = true; 1200 return true; 1201 } 1202 } 1203 ); 1204 1205 options.add( 1206 new Option() { 1207 private final List<String> names = List.of("-ignoreJdLinks"); 1208 @Override public int getArgumentCount() { return 0; } 1209 @Override public String getDescription() { return ""; } 1210 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1211 @Override public List<String> getNames() { return names; } 1212 @Override public String getParameters() { return ""; } 1213 @Override public boolean process(String opt, List<String> arguments) { 1214 if (DEVSITE_STATIC_ONLY) { 1215 DEVSITE_IGNORE_JDLINKS = true; 1216 } 1217 return true; 1218 } 1219 } 1220 ); 1221 1222 options.add( 1223 new Option() { 1224 private final List<String> names = List.of("-federate"); 1225 @Override public int getArgumentCount() { return 2; } 1226 @Override public String getDescription() { return ""; } 1227 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1228 @Override public List<String> getNames() { return names; } 1229 @Override public String getParameters() { return "<name> <URL>"; } 1230 @Override public boolean process(String opt, List<String> arguments) { 1231 try { 1232 String name = arguments.get(0); 1233 URL federationURL = new URL(arguments.get(1)); 1234 federationTagger.addSiteUrl(name, federationURL); 1235 } catch (MalformedURLException e) { 1236 System.err.println("Could not parse URL for federation: " + arguments.get(0)); 1237 return false; 1238 } 1239 return true; 1240 } 1241 } 1242 ); 1243 1244 options.add( 1245 new Option() { 1246 private final List<String> names = List.of("-federationapi"); 1247 @Override public int getArgumentCount() { return 2; } 1248 @Override public String getDescription() { return ""; } 1249 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1250 @Override public List<String> getNames() { return names; } 1251 @Override public String getParameters() { return "<name> <file>"; } 1252 @Override public boolean process(String opt, List<String> arguments) { 1253 String name = arguments.get(0); 1254 String file = arguments.get(1); 1255 federationTagger.addSiteApi(name, file); 1256 return true; 1257 } 1258 } 1259 ); 1260 1261 options.add( 1262 new Option() { 1263 private final List<String> names = List.of("-gmsref"); 1264 @Override public int getArgumentCount() { return 0; } 1265 @Override public String getDescription() { return ""; } 1266 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1267 @Override public List<String> getNames() { return names; } 1268 @Override public String getParameters() { return ""; } 1269 @Override public boolean process(String opt, List<String> arguments) { 1270 gmsRef = true; 1271 return true; 1272 } 1273 } 1274 ); 1275 1276 options.add( 1277 new Option() { 1278 private final List<String> names = List.of("-gcmref"); 1279 @Override public int getArgumentCount() { return 0; } 1280 @Override public String getDescription() { return ""; } 1281 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1282 @Override public List<String> getNames() { return names; } 1283 @Override public String getParameters() { return ""; } 1284 @Override public boolean process(String opt, List<String> arguments) { 1285 gcmRef = true; 1286 return true; 1287 } 1288 } 1289 ); 1290 1291 options.add( 1292 new Option() { 1293 private final List<String> names = List.of("-yaml"); 1294 @Override public int getArgumentCount() { return 1; } 1295 @Override public String getDescription() { return ""; } 1296 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1297 @Override public List<String> getNames() { return names; } 1298 @Override public String getParameters() { return "<file>"; } 1299 @Override public boolean process(String opt, List<String> arguments) { 1300 yamlNavFile = arguments.get(0); 1301 return true; 1302 } 1303 } 1304 ); 1305 1306 options.add( 1307 new Option() { 1308 private final List<String> names = List.of("-dac_libraryroot"); 1309 @Override public int getArgumentCount() { return 1; } 1310 @Override public String getDescription() { return ""; } 1311 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1312 @Override public List<String> getNames() { return names; } 1313 @Override public String getParameters() { return "<library_root>"; } 1314 @Override public boolean process(String opt, List<String> arguments) { 1315 libraryRoot = ensureSlash(arguments.get(0)); 1316 mHDFData.add(new String[] {"library.root", arguments.get(0)}); 1317 return true; 1318 } 1319 } 1320 ); 1321 1322 options.add( 1323 new Option() { 1324 private final List<String> names = List.of("-dac_dataname"); 1325 @Override public int getArgumentCount() { return 1; } 1326 @Override public String getDescription() { return ""; } 1327 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1328 @Override public List<String> getNames() { return names; } 1329 @Override public String getParameters() { return "<data_name>"; } 1330 @Override public boolean process(String opt, List<String> arguments) { 1331 mHDFData.add(new String[] {"dac_dataname", arguments.get(0)}); 1332 return true; 1333 } 1334 } 1335 ); 1336 1337 options.add( 1338 new Option() { 1339 private final List<String> names = List.of("-documentannotations"); 1340 @Override public int getArgumentCount() { return 1; } 1341 @Override public String getDescription() { return ""; } 1342 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1343 @Override public List<String> getNames() { return names; } 1344 @Override public String getParameters() { return "<path>"; } 1345 @Override public boolean process(String opt, List<String> arguments) { 1346 documentAnnotations = true; 1347 documentAnnotationsPath = arguments.get(0); 1348 return true; 1349 } 1350 } 1351 ); 1352 1353 options.add( 1354 new Option() { 1355 private final List<String> names = List.of("-referenceonly"); 1356 @Override public int getArgumentCount() { return 0; } 1357 @Override public String getDescription() { return ""; } 1358 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1359 @Override public List<String> getNames() { return names; } 1360 @Override public String getParameters() { return ""; } 1361 @Override public boolean process(String opt, List<String> arguments) { 1362 referenceOnly = true; 1363 mHDFData.add(new String[] {"referenceonly", "1"}); 1364 return true; 1365 } 1366 } 1367 ); 1368 1369 options.add( 1370 new Option() { 1371 private final List<String> names = List.of("-staticonly"); 1372 @Override public int getArgumentCount() { return 0; } 1373 @Override public String getDescription() { return ""; } 1374 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1375 @Override public List<String> getNames() { return names; } 1376 @Override public String getParameters() { return ""; } 1377 @Override public boolean process(String opt, List<String> arguments) { 1378 staticOnly = true; 1379 mHDFData.add(new String[] {"staticonly", "1"}); 1380 return true; 1381 } 1382 } 1383 ); 1384 1385 options.add( 1386 new Option() { 1387 private final List<String> names = List.of("-navtreeonly"); 1388 @Override public int getArgumentCount() { return 0; } 1389 @Override public String getDescription() { return ""; } 1390 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1391 @Override public List<String> getNames() { return names; } 1392 @Override public String getParameters() { return ""; } 1393 @Override public boolean process(String opt, List<String> arguments) { 1394 NAVTREE_ONLY = true; 1395 return true; 1396 } 1397 } 1398 ); 1399 1400 options.add( 1401 new Option() { 1402 private final List<String> names = List.of("-atLinksNavtree"); 1403 @Override public int getArgumentCount() { return 0; } 1404 @Override public String getDescription() { return ""; } 1405 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1406 @Override public List<String> getNames() { return names; } 1407 @Override public String getParameters() { return ""; } 1408 @Override public boolean process(String opt, List<String> arguments) { 1409 AT_LINKS_NAVTREE = true; 1410 return true; 1411 } 1412 } 1413 ); 1414 1415 options.add( 1416 new Option() { 1417 private final List<String> names = List.of("-yamlV2"); 1418 @Override public int getArgumentCount() { return 0; } 1419 @Override public String getDescription() { return ""; } 1420 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1421 @Override public List<String> getNames() { return names; } 1422 @Override public String getParameters() { return ""; } 1423 @Override public boolean process(String opt, List<String> arguments) { 1424 yamlV2 = true; 1425 return true; 1426 } 1427 } 1428 ); 1429 1430 options.add( 1431 new Option() { 1432 private final List<String> names = List.of("-devsite"); 1433 @Override public int getArgumentCount() { return 0; } 1434 @Override public String getDescription() { return ""; } 1435 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1436 @Override public List<String> getNames() { return names; } 1437 @Override public String getParameters() { return ""; } 1438 @Override public boolean process(String opt, List<String> arguments) { 1439 devsite = true; 1440 // Don't copy any assets to devsite output 1441 includeAssets = false; 1442 USE_DEVSITE_LOCALE_OUTPUT_PATHS = true; 1443 mHDFData.add(new String[] {"devsite", "1"}); 1444 if (staticOnly) { 1445 DEVSITE_STATIC_ONLY = true; 1446 System.out.println(" ... Generating static html only for devsite"); 1447 } 1448 if (yamlNavFile == null) { 1449 // Use _toc.yaml as default to avoid clobbering possible manual _book.yaml files 1450 yamlNavFile = "_toc.yaml"; 1451 } 1452 return true; 1453 } 1454 } 1455 ); 1456 1457 options.add( 1458 new Option() { 1459 private final List<String> names = List.of("-android"); 1460 @Override public int getArgumentCount() { return 0; } 1461 @Override public String getDescription() { return ""; } 1462 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1463 @Override public List<String> getNames() { return names; } 1464 @Override public String getParameters() { return ""; } 1465 @Override public boolean process(String opt, List<String> arguments) { 1466 auxSource = new AndroidAuxSource(); 1467 linter = new AndroidLinter(); 1468 android = true; 1469 return true; 1470 } 1471 } 1472 ); 1473 1474 options.add( 1475 new Option() { 1476 private final List<String> names = List.of("-manifest"); 1477 @Override public int getArgumentCount() { return 1; } 1478 @Override public String getDescription() { return ""; } 1479 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1480 @Override public List<String> getNames() { return names; } 1481 @Override public String getParameters() { return "<file>"; } 1482 @Override public boolean process(String opt, List<String> arguments) { 1483 manifestFile = arguments.get(0); 1484 return true; 1485 } 1486 } 1487 ); 1488 1489 options.add( 1490 new Option() { 1491 private final List<String> names = List.of("-compatconfig"); 1492 @Override public int getArgumentCount() { return 1; } 1493 @Override public String getDescription() { return ""; } 1494 @Override public Option.Kind getKind() { return Option.Kind.STANDARD; } 1495 @Override public List<String> getNames() { return names; } 1496 @Override public String getParameters() { return "<config>"; } 1497 @Override public boolean process(String opt, List<String> arguments) { 1498 compatConfig = arguments.get(0); 1499 return true; 1500 } 1501 } 1502 ); 1503 1504 return options; 1505 } 1506 1507 @Override getSupportedSourceVersion()1508 public SourceVersion getSupportedSourceVersion() { 1509 return SourceVersion.latest(); 1510 } 1511 1512 @Override run(DocletEnvironment environment)1513 public boolean run(DocletEnvironment environment) { 1514 return start(environment); 1515 } 1516 checkLevel(int level)1517 public static boolean checkLevel(int level) { 1518 return (showLevel & level) == level; 1519 } 1520 1521 /** 1522 * Returns true if we should parse javadoc comments, 1523 * reporting errors in the process. 1524 */ parseComments()1525 public static boolean parseComments() { 1526 return generateDocs || parseComments; 1527 } 1528 checkLevel(boolean pub, boolean prot, boolean pkgp, boolean priv, boolean hidden)1529 public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp, boolean priv, 1530 boolean hidden) { 1531 if (hidden && !checkLevel(SHOW_HIDDEN)) { 1532 return false; 1533 } 1534 if (pub && checkLevel(SHOW_PUBLIC)) { 1535 return true; 1536 } 1537 if (prot && checkLevel(SHOW_PROTECTED)) { 1538 return true; 1539 } 1540 if (pkgp && checkLevel(SHOW_PACKAGE)) { 1541 return true; 1542 } 1543 if (priv && checkLevel(SHOW_PRIVATE)) { 1544 return true; 1545 } 1546 return false; 1547 } 1548 main(String[] args)1549 public static void main(String[] args) { 1550 throw new UnsupportedOperationException("Not yet implemented"); 1551 } 1552 start(DocletEnvironment environment)1553 public static boolean start(DocletEnvironment environment) { 1554 root = new RootDocImpl(environment); 1555 1556 // If the caller has not explicitly requested that unannotated classes and members should be 1557 // shown in the output then only show them if no annotations were provided. 1558 if (!showUnannotated && showAnnotations.isEmpty()) { 1559 showUnannotated = true; 1560 } 1561 1562 if (!readKnownTagsFiles(knownTags, knownTagsFiles)) { 1563 return false; 1564 } 1565 if (!readManifest()) { 1566 return false; 1567 } 1568 1569 // Set up the data structures 1570 Converter.makeInfo(root); 1571 1572 if (generateDocs) { 1573 ClearPage.addBundledTemplateDir("assets/customizations"); 1574 ClearPage.addBundledTemplateDir("assets/templates"); 1575 1576 List<ResourceLoader> resourceLoaders = new ArrayList<ResourceLoader>(); 1577 List<String> templates = ClearPage.getTemplateDirs(); 1578 for (String tmpl : templates) { 1579 resourceLoaders.add(new FileSystemResourceLoader(tmpl)); 1580 } 1581 // If no custom template path is provided, and this is a devsite build, 1582 // then use the bundled templates-sdk/ files by default 1583 if (templates.isEmpty() && devsite) { 1584 resourceLoaders.add(new ClassResourceLoader(Doclava.class, "/assets/templates-sdk")); 1585 } 1586 1587 templates = ClearPage.getBundledTemplateDirs(); 1588 for (String tmpl : templates) { 1589 // TODO - remove commented line - it's here for debugging purposes 1590 // resourceLoaders.add(new FileSystemResourceLoader("/Volumes/Android/master/external/doclava/res/" + tmpl)); 1591 resourceLoaders.add(new ClassResourceLoader(Doclava.class, '/'+tmpl)); 1592 } 1593 1594 ResourceLoader compositeResourceLoader = new CompositeResourceLoader(resourceLoaders); 1595 jSilver = new JSilver(compositeResourceLoader); 1596 1597 if (!Doclava.readTemplateSettings()) { 1598 return false; 1599 } 1600 1601 // if requested, only generate the navtree for ds use-case 1602 if (NAVTREE_ONLY) { 1603 if (AT_LINKS_NAVTREE) { 1604 AtLinksNavTree.writeAtLinksNavTree(javadocDir); 1605 } else if (yamlV2) { 1606 // Generate DAC-formatted left-nav for devsite 1607 NavTree.writeYamlTree2(javadocDir, yamlNavFile); 1608 } else { 1609 // This shouldn't happen; this is the legacy DAC left nav file 1610 NavTree.writeNavTree(javadocDir, ""); 1611 } 1612 return true; 1613 } 1614 1615 // don't do ref doc tasks in devsite static-only builds 1616 if (!DEVSITE_STATIC_ONLY) { 1617 // Load additional data structures from federated sites. 1618 for(FederatedSite site : federationTagger.getSites()) { 1619 Converter.addApiInfo(site.apiInfo()); 1620 } 1621 1622 // Apply @since tags from the XML file 1623 sinceTagger.tagAll(Converter.rootClasses()); 1624 1625 // Apply @artifact tags from the XML file 1626 artifactTagger.tagAll(Converter.rootClasses()); 1627 1628 // Apply details of federated documentation 1629 federationTagger.tagAll(Converter.rootClasses()); 1630 1631 // Files for proofreading 1632 if (proofreadFile != null) { 1633 Proofread.initProofread(proofreadFile); 1634 } 1635 if (todoFile != null) { 1636 TodoFile.writeTodoFile(todoFile); 1637 } 1638 1639 if (samplesRef) { 1640 // always write samples without offlineMode behaviors 1641 writeSamples(false, sampleCodes, SORT_BY_NAV_GROUPS); 1642 } 1643 } 1644 if (!referenceOnly) { 1645 // HTML2 Pages -- Generate Pages from optional secondary dir 1646 if (!inputPathHtmlDir2.isEmpty()) { 1647 if (!outputPathHtmlDir2.isEmpty()) { 1648 ClearPage.outputDir = outputPathBase + "/" + outputPathHtmlDir2; 1649 } 1650 ClearPage.htmlDirs = inputPathHtmlDir2; 1651 writeHTMLPages(); 1652 ClearPage.htmlDirs = inputPathHtmlDirs; 1653 } 1654 1655 // HTML Pages 1656 if (!ClearPage.htmlDirs.isEmpty()) { 1657 ClearPage.htmlDirs = inputPathHtmlDirs; 1658 if (USE_DEVSITE_LOCALE_OUTPUT_PATHS) { 1659 ClearPage.outputDir = outputPathHtmlDirs + "/en/"; 1660 } else { 1661 ClearPage.outputDir = outputPathHtmlDirs; 1662 } 1663 writeHTMLPages(); 1664 } 1665 } 1666 1667 writeResources(); 1668 1669 writeAssets(); 1670 1671 // don't do ref doc tasks in devsite static-only builds 1672 if (!DEVSITE_STATIC_ONLY) { 1673 // Navigation tree 1674 String refPrefix = new String(); 1675 if(gmsRef){ 1676 refPrefix = "gms-"; 1677 } else if(gcmRef){ 1678 refPrefix = "gcm-"; 1679 } 1680 1681 // Packages Pages 1682 writePackages(refPrefix + "packages" + htmlExtension); 1683 1684 // Classes 1685 writeClassLists(); 1686 writeClasses(); 1687 writeHierarchy(); 1688 writeCompatConfig(); 1689 // writeKeywords(); 1690 1691 // Write yaml tree. 1692 if (yamlNavFile != null) { 1693 if (yamlV2) { 1694 // Generate DAC-formatted left-nav for devsite 1695 NavTree.writeYamlTree2(javadocDir, yamlNavFile); 1696 } else { 1697 // Generate legacy devsite left-nav (shows sub-classes nested under parent class) 1698 NavTree.writeYamlTree(javadocDir, yamlNavFile); 1699 } 1700 } else { 1701 // This shouldn't happen; this is the legacy DAC left nav file 1702 NavTree.writeNavTree(javadocDir, refPrefix); 1703 } 1704 1705 // Lists for JavaScript 1706 writeLists(); 1707 if (keepListFile != null) { 1708 writeKeepList(keepListFile); 1709 } 1710 1711 Proofread.finishProofread(proofreadFile); 1712 1713 if (sdkValuePath != null) { 1714 writeSdkValues(sdkValuePath); 1715 } 1716 } 1717 // Write metadata for all processed files to jd_lists_unified in out dir 1718 if (!sTaglist.isEmpty()) { 1719 PageMetadata.WriteListByLang(sTaglist); 1720 // For devsite (ds) reference only, write samples_metadata to out dir 1721 if ((devsite) && (!DEVSITE_STATIC_ONLY)) { 1722 PageMetadata.WriteSamplesListByLang(sTaglist); 1723 } 1724 } 1725 } 1726 1727 // Stubs 1728 if (stubsDir != null || apiFile != null || dexApiFile != null || proguardFile != null 1729 || removedApiFile != null || removedDexApiFile != null || exactApiFile != null 1730 || privateApiFile != null || privateDexApiFile != null || apiMappingFile != null) { 1731 Stubs.writeStubsAndApi(stubsDir, apiFile, dexApiFile, proguardFile, removedApiFile, 1732 removedDexApiFile, exactApiFile, privateApiFile, privateDexApiFile, apiMappingFile, 1733 stubPackages, stubImportPackages, stubSourceOnly, keepStubComments); 1734 } 1735 1736 Errors.printErrors(); 1737 1738 return !Errors.hadError; 1739 } 1740 writeIndex(String dir)1741 private static void writeIndex(String dir) { 1742 Data data = makeHDF(); 1743 ClearPage.write(data, "index.cs", dir + "index" + htmlExtension); 1744 } 1745 readTemplateSettings()1746 private static boolean readTemplateSettings() { 1747 Data data = makeHDF(); 1748 1749 // The .html extension is hard-coded in several .cs files, 1750 // and so you cannot currently set it as a property. 1751 htmlExtension = ".html"; 1752 // htmlExtension = data.getValue("template.extension", ".html"); 1753 int i = 0; 1754 while (true) { 1755 String k = data.getValue("template.escape." + i + ".key", ""); 1756 String v = data.getValue("template.escape." + i + ".value", ""); 1757 if ("".equals(k)) { 1758 break; 1759 } 1760 if (k.length() != 1) { 1761 System.err.println("template.escape." + i + ".key must have a length of 1: " + k); 1762 return false; 1763 } 1764 escapeChars.put(k.charAt(0), v); 1765 i++; 1766 } 1767 return true; 1768 } 1769 readKnownTagsFiles(HashSet<String> knownTags, ArrayList<String> knownTagsFiles)1770 private static boolean readKnownTagsFiles(HashSet<String> knownTags, 1771 ArrayList<String> knownTagsFiles) { 1772 for (String fn: knownTagsFiles) { 1773 BufferedReader in = null; 1774 try { 1775 in = new BufferedReader(new FileReader(fn)); 1776 int lineno = 0; 1777 boolean fail = false; 1778 while (true) { 1779 lineno++; 1780 String line = in.readLine(); 1781 if (line == null) { 1782 break; 1783 } 1784 line = line.trim(); 1785 if (line.length() == 0) { 1786 continue; 1787 } else if (line.charAt(0) == '#') { 1788 continue; 1789 } 1790 String[] words = line.split("\\s+", 2); 1791 if (words.length == 2) { 1792 if (words[1].charAt(0) != '#') { 1793 System.err.println(fn + ":" + lineno 1794 + ": Only one tag allowed per line: " + line); 1795 fail = true; 1796 continue; 1797 } 1798 } 1799 knownTags.add(words[0]); 1800 } 1801 if (fail) { 1802 return false; 1803 } 1804 } catch (IOException ex) { 1805 System.err.println("Error reading file: " + fn + " (" + ex.getMessage() + ")"); 1806 return false; 1807 } finally { 1808 if (in != null) { 1809 try { 1810 in.close(); 1811 } catch (IOException e) { 1812 } 1813 } 1814 } 1815 } 1816 return true; 1817 } 1818 readManifest()1819 private static boolean readManifest() { 1820 manifestPermissions.clear(); 1821 if (manifestFile == null) { 1822 return true; 1823 } 1824 try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(manifestFile)); 1825 ByteArrayOutputStream out = new ByteArrayOutputStream()) { 1826 byte[] buffer = new byte[1024]; 1827 int count; 1828 while ((count = in.read(buffer)) != -1) { 1829 out.write(buffer, 0, count); 1830 } 1831 final Matcher m = Pattern.compile("(?s)<permission " 1832 + "[^>]*android:name=\"([^\">]+)\"" 1833 + "[^>]*android:protectionLevel=\"([^\">]+)\"").matcher(out.toString()); 1834 while (m.find()) { 1835 manifestPermissions.put(m.group(1), m.group(2)); 1836 } 1837 } catch (IOException e) { 1838 Errors.error(Errors.PARSE_ERROR, (SourcePositionInfo) null, 1839 "Failed to parse " + manifestFile + ": " + e); 1840 return false; 1841 } 1842 return true; 1843 } 1844 escape(String s)1845 public static String escape(String s) { 1846 if (escapeChars.size() == 0) { 1847 return s; 1848 } 1849 StringBuffer b = null; 1850 int begin = 0; 1851 final int N = s.length(); 1852 for (int i = 0; i < N; i++) { 1853 char c = s.charAt(i); 1854 String mapped = escapeChars.get(c); 1855 if (mapped != null) { 1856 if (b == null) { 1857 b = new StringBuffer(s.length() + mapped.length()); 1858 } 1859 if (begin != i) { 1860 b.append(s.substring(begin, i)); 1861 } 1862 b.append(mapped); 1863 begin = i + 1; 1864 } 1865 } 1866 if (b != null) { 1867 if (begin != N) { 1868 b.append(s.substring(begin, N)); 1869 } 1870 return b.toString(); 1871 } 1872 return s; 1873 } 1874 setPageTitle(Data data, String title)1875 public static void setPageTitle(Data data, String title) { 1876 String s = title; 1877 if (Doclava.title.length() > 0) { 1878 s += " - " + Doclava.title; 1879 } 1880 data.setValue("page.title", s); 1881 } 1882 makeHDF()1883 public static Data makeHDF() { 1884 Data data = jSilver.createData(); 1885 1886 for (String[] p : mHDFData) { 1887 data.setValue(p[0], p[1]); 1888 } 1889 1890 return data; 1891 } 1892 makePackageHDF()1893 public static Data makePackageHDF() { 1894 Data data = makeHDF(); 1895 Collection<ClassInfo> classes = Converter.rootClasses(); 1896 1897 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>(); 1898 for (ClassInfo cl : classes) { 1899 PackageInfo pkg = cl.containingPackage(); 1900 String name; 1901 if (pkg == null) { 1902 name = ""; 1903 } else { 1904 name = pkg.name(); 1905 } 1906 sorted.put(name, pkg); 1907 } 1908 1909 int i = 0; 1910 for (Map.Entry<String, PackageInfo> entry : sorted.entrySet()) { 1911 String s = entry.getKey(); 1912 PackageInfo pkg = entry.getValue(); 1913 1914 if (pkg.isHiddenOrRemoved()) { 1915 continue; 1916 } 1917 boolean allHiddenOrRemoved = true; 1918 int pass = 0; 1919 ClassInfo[] classesToCheck = null; 1920 while (pass < 6) { 1921 switch (pass) { 1922 case 0: 1923 classesToCheck = pkg.ordinaryClasses(); 1924 break; 1925 case 1: 1926 classesToCheck = pkg.enums(); 1927 break; 1928 case 2: 1929 classesToCheck = pkg.errors(); 1930 break; 1931 case 3: 1932 classesToCheck = pkg.exceptions(); 1933 break; 1934 case 4: 1935 classesToCheck = pkg.interfaces(); 1936 break; 1937 case 5: 1938 classesToCheck = pkg.annotations(); 1939 break; 1940 default: 1941 System.err.println("Error reading package: " + pkg.name()); 1942 break; 1943 } 1944 for (ClassInfo cl : classesToCheck) { 1945 if (!cl.isHiddenOrRemoved()) { 1946 allHiddenOrRemoved = false; 1947 break; 1948 } 1949 } 1950 if (!allHiddenOrRemoved) { 1951 break; 1952 } 1953 pass++; 1954 } 1955 if (allHiddenOrRemoved) { 1956 continue; 1957 } 1958 if(gmsRef){ 1959 data.setValue("reference.gms", "true"); 1960 } else if(gcmRef){ 1961 data.setValue("reference.gcm", "true"); 1962 } 1963 data.setValue("reference", "1"); 1964 if (METALAVA_API_SINCE) { 1965 data.setValue("reference.apilevels", (pkg.getSince() != null) ? "1" : "0"); 1966 } else { 1967 data.setValue("reference.apilevels", sinceTagger.hasVersions() ? "1" : "0"); 1968 } 1969 data.setValue("reference.artifacts", artifactTagger.hasArtifacts() ? "1" : "0"); 1970 data.setValue("docs.packages." + i + ".name", s); 1971 data.setValue("docs.packages." + i + ".link", pkg.htmlPage()); 1972 data.setValue("docs.packages." + i + ".since", pkg.getSince()); 1973 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", pkg.firstSentenceTags()); 1974 i++; 1975 } 1976 1977 sinceTagger.writeVersionNames(data); 1978 return data; 1979 } 1980 writeDirectory(File dir, String relative, JSilver js)1981 private static void writeDirectory(File dir, String relative, JSilver js) { 1982 File[] files = dir.listFiles(); 1983 int i, count = files.length; 1984 for (i = 0; i < count; i++) { 1985 File f = files[i]; 1986 if (f.isFile()) { 1987 String templ = relative + f.getName(); 1988 int len = templ.length(); 1989 if (len > 3 && ".cs".equals(templ.substring(len - 3))) { 1990 Data data = makePackageHDF(); 1991 String filename = templ.substring(0, len - 3) + htmlExtension; 1992 ClearPage.write(data, templ, filename, js); 1993 } else if (len > 3 && ".jd".equals(templ.substring(len - 3))) { 1994 Data data = makePackageHDF(); 1995 String filename = templ.substring(0, len - 3) + htmlExtension; 1996 DocFile.writePage(f.getAbsolutePath(), relative, filename, data); 1997 } else if(!f.getName().equals(".DS_Store")){ 1998 Data data = makeHDF(); 1999 String hdfValue = data.getValue("sac") == null ? "" : data.getValue("sac"); 2000 boolean allowExcepted = hdfValue.equals("true") ? true : false; 2001 boolean append = false; 2002 ClearPage.copyFile(allowExcepted, f, templ, append); 2003 } 2004 } else if (f.isDirectory()) { 2005 writeDirectory(f, relative + f.getName() + "/", js); 2006 } 2007 } 2008 } 2009 writeHTMLPages()2010 public static void writeHTMLPages() { 2011 for (String htmlDir : ClearPage.htmlDirs) { 2012 File f = new File(htmlDir); 2013 if (!f.isDirectory()) { 2014 System.err.println("htmlDir not a directory: " + htmlDir); 2015 continue; 2016 } 2017 2018 ResourceLoader loader = new FileSystemResourceLoader(f); 2019 JSilver js = new JSilver(loader); 2020 writeDirectory(f, "", js); 2021 } 2022 } 2023 2024 /* copy files supplied by the -resourcesdir flag */ writeResources()2025 public static void writeResources() { 2026 if (inputPathResourcesDir != null && !inputPathResourcesDir.isEmpty()) { 2027 try { 2028 File f = new File(inputPathResourcesDir); 2029 if (!f.isDirectory()) { 2030 System.err.println("resourcesdir is not a directory: " + inputPathResourcesDir); 2031 return; 2032 } 2033 2034 ResourceLoader loader = new FileSystemResourceLoader(f); 2035 JSilver js = new JSilver(loader); 2036 writeDirectory(f, outputPathResourcesDir, js); 2037 } catch(Exception e) { 2038 System.err.println("Could not copy resourcesdir: " + e); 2039 } 2040 } 2041 } 2042 writeAssets()2043 public static void writeAssets() { 2044 if (!includeAssets) return; 2045 JarFile thisJar = JarUtils.jarForClass(Doclava.class, null); 2046 if ((thisJar != null) && (includeDefaultAssets)) { 2047 try { 2048 List<String> templateDirs = ClearPage.getBundledTemplateDirs(); 2049 for (String templateDir : templateDirs) { 2050 String assetsDir = templateDir + "/assets"; 2051 JarUtils.copyResourcesToDirectory(thisJar, assetsDir, ClearPage.outputDir + "/assets"); 2052 } 2053 } catch (IOException e) { 2054 System.err.println("Error copying assets directory."); 2055 e.printStackTrace(); 2056 return; 2057 } 2058 } 2059 2060 //write the project-specific assets 2061 List<String> templateDirs = ClearPage.getTemplateDirs(); 2062 for (String templateDir : templateDirs) { 2063 File assets = new File(templateDir + "/assets"); 2064 if (assets.isDirectory()) { 2065 writeDirectory(assets, "assets/", null); 2066 } 2067 } 2068 2069 // Create the timestamp.js file based on .cs file 2070 Data timedata = Doclava.makeHDF(); 2071 ClearPage.write(timedata, "timestamp.cs", "timestamp.js"); 2072 } 2073 2074 /** Go through the docs and generate meta-data about each 2075 page to use in search suggestions */ writeLists()2076 public static void writeLists() { 2077 2078 // Write the lists for API references 2079 Data data = makeHDF(); 2080 2081 Collection<ClassInfo> classes = Converter.rootClasses(); 2082 2083 SortedMap<String, Object> sorted = new TreeMap<String, Object>(); 2084 for (ClassInfo cl : classes) { 2085 if (cl.isHiddenOrRemoved()) { 2086 continue; 2087 } 2088 sorted.put(cl.qualifiedName(), cl); 2089 PackageInfo pkg = cl.containingPackage(); 2090 String name; 2091 if (pkg == null) { 2092 name = ""; 2093 } else { 2094 name = pkg.name(); 2095 } 2096 sorted.put(name, pkg); 2097 } 2098 2099 int i = 0; 2100 String listDir = javadocDir; 2101 if (USE_DEVSITE_LOCALE_OUTPUT_PATHS) { 2102 if (libraryRoot != null) { 2103 listDir = listDir + libraryRoot; 2104 } 2105 } 2106 for (String s : sorted.keySet()) { 2107 data.setValue("docs.pages." + i + ".id", "" + i); 2108 data.setValue("docs.pages." + i + ".label", s); 2109 2110 Object o = sorted.get(s); 2111 if (o instanceof PackageInfo) { 2112 PackageInfo pkg = (PackageInfo) o; 2113 data.setValue("docs.pages." + i + ".link", pkg.htmlPage()); 2114 data.setValue("docs.pages." + i + ".type", "package"); 2115 data.setValue("docs.pages." + i + ".deprecated", pkg.isDeprecated() ? "true" : "false"); 2116 } else if (o instanceof ClassInfo) { 2117 ClassInfo cl = (ClassInfo) o; 2118 data.setValue("docs.pages." + i + ".link", cl.htmlPage()); 2119 data.setValue("docs.pages." + i + ".type", "class"); 2120 data.setValue("docs.pages." + i + ".deprecated", cl.isDeprecated() ? "true" : "false"); 2121 } 2122 i++; 2123 } 2124 ClearPage.write(data, "lists.cs", listDir + "lists.js"); 2125 2126 2127 // Write the lists for JD documents (if there are HTML directories to process) 2128 // Skip this for devsite builds 2129 if ((inputPathHtmlDirs.size() > 0) && (!devsite)) { 2130 Data jddata = makeHDF(); 2131 Iterator counter = new Iterator(); 2132 for (String htmlDir : inputPathHtmlDirs) { 2133 File dir = new File(htmlDir); 2134 if (!dir.isDirectory()) { 2135 continue; 2136 } 2137 writeJdDirList(dir, jddata, counter); 2138 } 2139 ClearPage.write(jddata, "jd_lists.cs", javadocDir + "jd_lists.js"); 2140 } 2141 } 2142 2143 private static class Iterator { 2144 int i = 0; 2145 } 2146 2147 /** Write meta-data for a JD file, used for search suggestions */ writeJdDirList(File dir, Data data, Iterator counter)2148 private static void writeJdDirList(File dir, Data data, Iterator counter) { 2149 File[] files = dir.listFiles(); 2150 int i, count = files.length; 2151 // Loop all files in given directory 2152 for (i = 0; i < count; i++) { 2153 File f = files[i]; 2154 if (f.isFile()) { 2155 String filePath = f.getAbsolutePath(); 2156 String templ = f.getName(); 2157 int len = templ.length(); 2158 // If it's a .jd file we want to process 2159 if (len > 3 && ".jd".equals(templ.substring(len - 3))) { 2160 // remove the directories below the site root 2161 String webPath = filePath.substring(filePath.indexOf("docs/html/") + 10, 2162 filePath.length()); 2163 // replace .jd with .html 2164 webPath = webPath.substring(0, webPath.length() - 3) + htmlExtension; 2165 // Parse the .jd file for properties data at top of page 2166 Data hdf = Doclava.makeHDF(); 2167 String filedata = DocFile.readFile(filePath); 2168 Matcher lines = DocFile.LINE.matcher(filedata); 2169 String line = null; 2170 // Get each line to add the key-value to hdf 2171 while (lines.find()) { 2172 line = lines.group(1); 2173 if (line.length() > 0) { 2174 // Stop when we hit the body 2175 if (line.equals("@jd:body")) { 2176 break; 2177 } 2178 Matcher prop = DocFile.PROP.matcher(line); 2179 if (prop.matches()) { 2180 String key = prop.group(1); 2181 String value = prop.group(2); 2182 hdf.setValue(key, value); 2183 } else { 2184 break; 2185 } 2186 } 2187 } // done gathering page properties 2188 2189 // Insert the goods into HDF data (title, link, tags, type) 2190 String title = hdf.getValue("page.title", ""); 2191 title = title.replaceAll("\"", "'"); 2192 // if there's a <span> in the title, get rid of it 2193 if (title.indexOf("<span") != -1) { 2194 String[] splitTitle = title.split("<span(.*?)</span>"); 2195 title = splitTitle[0]; 2196 for (int j = 1; j < splitTitle.length; j++) { 2197 title.concat(splitTitle[j]); 2198 } 2199 } 2200 2201 StringBuilder tags = new StringBuilder(); 2202 String tagsList = hdf.getValue("page.tags", ""); 2203 if (!tagsList.equals("")) { 2204 tagsList = tagsList.replaceAll("\"", ""); 2205 String[] tagParts = tagsList.split(","); 2206 for (int iter = 0; iter < tagParts.length; iter++) { 2207 tags.append("\""); 2208 tags.append(tagParts[iter].trim()); 2209 tags.append("\""); 2210 if (iter < tagParts.length - 1) { 2211 tags.append(","); 2212 } 2213 } 2214 } 2215 2216 String dirName = (webPath.indexOf("/") != -1) 2217 ? webPath.substring(0, webPath.indexOf("/")) : ""; 2218 2219 if (!"".equals(title) && 2220 !"intl".equals(dirName) && 2221 !hdf.getBooleanValue("excludeFromSuggestions")) { 2222 data.setValue("docs.pages." + counter.i + ".label", title); 2223 data.setValue("docs.pages." + counter.i + ".link", webPath); 2224 data.setValue("docs.pages." + counter.i + ".tags", tags.toString()); 2225 data.setValue("docs.pages." + counter.i + ".type", dirName); 2226 counter.i++; 2227 } 2228 } 2229 } else if (f.isDirectory()) { 2230 writeJdDirList(f, data, counter); 2231 } 2232 } 2233 } 2234 cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable)2235 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) { 2236 if (!notStrippable.add(cl)) { 2237 // slight optimization: if it already contains cl, it already contains 2238 // all of cl's parents 2239 return; 2240 } 2241 ClassInfo supr = cl.superclass(); 2242 if (supr != null) { 2243 cantStripThis(supr, notStrippable); 2244 } 2245 for (ClassInfo iface : cl.interfaces()) { 2246 cantStripThis(iface, notStrippable); 2247 } 2248 } 2249 getPrintableName(ClassInfo cl)2250 private static String getPrintableName(ClassInfo cl) { 2251 ClassInfo containingClass = cl.containingClass(); 2252 if (containingClass != null) { 2253 // This is an inner class. 2254 String baseName = cl.name(); 2255 baseName = baseName.substring(baseName.lastIndexOf('.') + 1); 2256 return getPrintableName(containingClass) + '$' + baseName; 2257 } 2258 return cl.qualifiedName(); 2259 } 2260 2261 /** 2262 * Writes the list of classes that must be present in order to provide the non-hidden APIs known 2263 * to javadoc. 2264 * 2265 * @param filename the path to the file to write the list to 2266 */ writeKeepList(String filename)2267 public static void writeKeepList(String filename) { 2268 HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>(); 2269 Collection<ClassInfo> all = Converter.allClasses().stream().sorted(ClassInfo.comparator) 2270 .collect(Collectors.toList()); 2271 2272 // If a class is public and not hidden, then it and everything it derives 2273 // from cannot be stripped. Otherwise we can strip it. 2274 for (ClassInfo cl : all) { 2275 if (cl.isPublic() && !cl.isHiddenOrRemoved()) { 2276 cantStripThis(cl, notStrippable); 2277 } 2278 } 2279 PrintStream stream = null; 2280 try { 2281 stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(filename))); 2282 for (ClassInfo cl : notStrippable) { 2283 stream.println(getPrintableName(cl)); 2284 } 2285 } catch (FileNotFoundException e) { 2286 System.err.println("error writing file: " + filename); 2287 } finally { 2288 if (stream != null) { 2289 stream.close(); 2290 } 2291 } 2292 } 2293 2294 private static PackageInfo[] sVisiblePackages = null; 2295 choosePackages()2296 public static PackageInfo[] choosePackages() { 2297 if (sVisiblePackages != null) { 2298 return sVisiblePackages; 2299 } 2300 2301 Collection<ClassInfo> classes = Converter.rootClasses(); 2302 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>(); 2303 for (ClassInfo cl : classes) { 2304 PackageInfo pkg = cl.containingPackage(); 2305 String name; 2306 if (pkg == null) { 2307 name = ""; 2308 } else { 2309 name = pkg.name(); 2310 } 2311 sorted.put(name, pkg); 2312 } 2313 2314 ArrayList<PackageInfo> result = new ArrayList<PackageInfo>(); 2315 2316 for (String s : sorted.keySet()) { 2317 PackageInfo pkg = sorted.get(s); 2318 2319 if (pkg.isHiddenOrRemoved()) { 2320 continue; 2321 } 2322 2323 boolean allHiddenOrRemoved = true; 2324 int pass = 0; 2325 ClassInfo[] classesToCheck = null; 2326 while (pass < 6) { 2327 switch (pass) { 2328 case 0: 2329 classesToCheck = pkg.ordinaryClasses(); 2330 break; 2331 case 1: 2332 classesToCheck = pkg.enums(); 2333 break; 2334 case 2: 2335 classesToCheck = pkg.errors(); 2336 break; 2337 case 3: 2338 classesToCheck = pkg.exceptions(); 2339 break; 2340 case 4: 2341 classesToCheck = pkg.interfaces(); 2342 break; 2343 case 5: 2344 classesToCheck = pkg.annotations(); 2345 break; 2346 default: 2347 System.err.println("Error reading package: " + pkg.name()); 2348 break; 2349 } 2350 for (ClassInfo cl : classesToCheck) { 2351 if (!cl.isHiddenOrRemoved()) { 2352 allHiddenOrRemoved = false; 2353 break; 2354 } 2355 } 2356 if (!allHiddenOrRemoved) { 2357 break; 2358 } 2359 pass++; 2360 } 2361 if (allHiddenOrRemoved) { 2362 continue; 2363 } 2364 2365 result.add(pkg); 2366 } 2367 2368 sVisiblePackages = result.toArray(new PackageInfo[result.size()]); 2369 return sVisiblePackages; 2370 } 2371 writePackages(String filename)2372 public static void writePackages(String filename) { 2373 Data data = makePackageHDF(); 2374 2375 int i = 0; 2376 for (PackageInfo pkg : choosePackages()) { 2377 writePackage(pkg); 2378 2379 data.setValue("docs.packages." + i + ".name", pkg.name()); 2380 data.setValue("docs.packages." + i + ".link", pkg.htmlPage()); 2381 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", pkg.firstSentenceTags()); 2382 2383 i++; 2384 } 2385 2386 setPageTitle(data, "Package Index"); 2387 2388 TagInfo.makeHDF(data, "root.descr", Converter.convertTags(root.inlineTags(), null)); 2389 2390 String packageDir = javadocDir; 2391 if (USE_DEVSITE_LOCALE_OUTPUT_PATHS) { 2392 if (libraryRoot != null) { 2393 packageDir = packageDir + libraryRoot; 2394 } 2395 } 2396 data.setValue("page.not-api", "true"); 2397 ClearPage.write(data, "packages.cs", packageDir + filename); 2398 ClearPage.write(data, "package-list.cs", packageDir + "package-list"); 2399 2400 Proofread.writePackages(filename, Converter.convertTags(root.inlineTags(), null)); 2401 } 2402 writePackage(PackageInfo pkg)2403 public static void writePackage(PackageInfo pkg) { 2404 // these this and the description are in the same directory, 2405 // so it's okay 2406 Data data = makePackageHDF(); 2407 2408 String name = pkg.name(); 2409 2410 data.setValue("package.name", name); 2411 data.setValue("package.since", pkg.getSince()); 2412 data.setValue("package.descr", "...description..."); 2413 pkg.setFederatedReferences(data, "package"); 2414 2415 makeClassListHDF(data, "package.annotations", ClassInfo.sortByName(pkg.annotations())); 2416 makeClassListHDF(data, "package.interfaces", ClassInfo.sortByName(pkg.interfaces())); 2417 makeClassListHDF(data, "package.classes", ClassInfo.sortByName(pkg.ordinaryClasses())); 2418 makeClassListHDF(data, "package.enums", ClassInfo.sortByName(pkg.enums())); 2419 makeClassListHDF(data, "package.exceptions", ClassInfo.sortByName(pkg.exceptions())); 2420 makeClassListHDF(data, "package.errors", ClassInfo.sortByName(pkg.errors())); 2421 TagInfo.makeHDF(data, "package.shortDescr", pkg.firstSentenceTags()); 2422 TagInfo.makeHDF(data, "package.descr", pkg.inlineTags()); 2423 2424 String filename = pkg.htmlPage(); 2425 setPageTitle(data, name); 2426 ClearPage.write(data, "package.cs", filename); 2427 2428 Proofread.writePackage(filename, pkg.inlineTags()); 2429 } 2430 writeClassLists()2431 public static void writeClassLists() { 2432 int i; 2433 Data data = makePackageHDF(); 2434 2435 ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved( 2436 Converter.convertClasses(root.classes())); 2437 if (classes.length == 0) { 2438 return; 2439 } 2440 2441 Sorter[] sorted = new Sorter[classes.length]; 2442 for (i = 0; i < sorted.length; i++) { 2443 ClassInfo cl = classes[i]; 2444 String name = cl.name(); 2445 sorted[i] = new Sorter(name, cl); 2446 } 2447 2448 Arrays.sort(sorted); 2449 2450 // make a pass and resolve ones that have the same name 2451 int firstMatch = 0; 2452 String lastName = sorted[0].label; 2453 for (i = 1; i < sorted.length; i++) { 2454 String s = sorted[i].label; 2455 if (!lastName.equals(s)) { 2456 if (firstMatch != i - 1) { 2457 // there were duplicates 2458 for (int j = firstMatch; j < i; j++) { 2459 PackageInfo pkg = ((ClassInfo) sorted[j].data).containingPackage(); 2460 if (pkg != null) { 2461 sorted[j].label = sorted[j].label + " (" + pkg.name() + ")"; 2462 } 2463 } 2464 } 2465 firstMatch = i; 2466 lastName = s; 2467 } 2468 } 2469 2470 // and sort again 2471 Arrays.sort(sorted); 2472 2473 for (i = 0; i < sorted.length; i++) { 2474 String s = sorted[i].label; 2475 ClassInfo cl = (ClassInfo) sorted[i].data; 2476 char first = Character.toUpperCase(s.charAt(0)); 2477 cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i); 2478 } 2479 2480 String packageDir = javadocDir; 2481 if (USE_DEVSITE_LOCALE_OUTPUT_PATHS) { 2482 if (libraryRoot != null) { 2483 packageDir = packageDir + libraryRoot; 2484 } 2485 } 2486 2487 data.setValue("page.not-api", "true"); 2488 setPageTitle(data, "Class Index"); 2489 ClearPage.write(data, "classes.cs", packageDir + "classes" + htmlExtension); 2490 2491 if (!devsite) { 2492 // Index page redirects to the classes.html page, so use the same directory 2493 // This page is not needed for devsite builds, which should instead use _redirects.yaml 2494 writeIndex(packageDir); 2495 } 2496 } 2497 2498 // we use the word keywords because "index" means something else in html land 2499 // the user only ever sees the word index 2500 /* 2501 * public static void writeKeywords() { ArrayList<KeywordEntry> keywords = new 2502 * ArrayList<KeywordEntry>(); 2503 * 2504 * ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved(Converter.convertClasses(root.classes())); 2505 * 2506 * for (ClassInfo cl: classes) { cl.makeKeywordEntries(keywords); } 2507 * 2508 * HDF data = makeHDF(); 2509 * 2510 * Collections.sort(keywords); 2511 * 2512 * int i=0; for (KeywordEntry entry: keywords) { String base = "keywords." + entry.firstChar() + 2513 * "." + i; entry.makeHDF(data, base); i++; } 2514 * 2515 * setPageTitle(data, "Index"); ClearPage.write(data, "keywords.cs", javadocDir + "keywords" + 2516 * htmlExtension); } 2517 */ 2518 writeHierarchy()2519 public static void writeHierarchy() { 2520 Collection<ClassInfo> classes = Converter.rootClasses(); 2521 ArrayList<ClassInfo> info = new ArrayList<ClassInfo>(); 2522 for (ClassInfo cl : classes) { 2523 if (!cl.isHiddenOrRemoved()) { 2524 info.add(cl); 2525 } 2526 } 2527 Data data = makePackageHDF(); 2528 Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()])); 2529 setPageTitle(data, "Class Hierarchy"); 2530 ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension); 2531 } 2532 writeClasses()2533 public static void writeClasses() { 2534 Collection<ClassInfo> classes = Converter.rootClasses(); 2535 2536 for (ClassInfo cl : classes) { 2537 Data data = makePackageHDF(); 2538 if (!cl.isHiddenOrRemoved()) { 2539 writeClass(cl, data); 2540 } 2541 } 2542 } 2543 writeClass(ClassInfo cl, Data data)2544 public static void writeClass(ClassInfo cl, Data data) { 2545 cl.makeHDF(data); 2546 setPageTitle(data, cl.name()); 2547 String outfile = cl.htmlPage(); 2548 ClearPage.write(data, "class.cs", outfile); 2549 Proofread.writeClass(cl.htmlPage(), cl); 2550 } 2551 makeClassListHDF(Data data, String base, ClassInfo[] classes)2552 public static void makeClassListHDF(Data data, String base, ClassInfo[] classes) { 2553 for (int i = 0; i < classes.length; i++) { 2554 ClassInfo cl = classes[i]; 2555 if (!cl.isHiddenOrRemoved()) { 2556 cl.makeShortDescrHDF(data, base + "." + i); 2557 } 2558 } 2559 } 2560 linkTarget(String source, String target)2561 public static String linkTarget(String source, String target) { 2562 String[] src = source.split("/"); 2563 String[] tgt = target.split("/"); 2564 2565 int srclen = src.length; 2566 int tgtlen = tgt.length; 2567 2568 int same = 0; 2569 while (same < (srclen - 1) && same < (tgtlen - 1) && (src[same].equals(tgt[same]))) { 2570 same++; 2571 } 2572 2573 String s = ""; 2574 2575 int up = srclen - same - 1; 2576 for (int i = 0; i < up; i++) { 2577 s += "../"; 2578 } 2579 2580 2581 int N = tgtlen - 1; 2582 for (int i = same; i < N; i++) { 2583 s += tgt[i] + '/'; 2584 } 2585 s += tgt[tgtlen - 1]; 2586 2587 return s; 2588 } 2589 2590 /** 2591 * Returns true if the given element has an @hide, @removed or @pending annotation. 2592 */ hasHideOrRemovedAnnotation(Doc doc)2593 private static boolean hasHideOrRemovedAnnotation(Doc doc) { 2594 String comment = doc.getRawCommentText(); 2595 return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1 || 2596 comment.indexOf("@removed") != -1; 2597 } 2598 2599 /** 2600 * Returns true if the given element is hidden. 2601 */ isHiddenOrRemoved(Doc doc)2602 private static boolean isHiddenOrRemoved(Doc doc) { 2603 // Methods, fields, constructors. 2604 if (doc instanceof MemberDoc) { 2605 return hasHideOrRemovedAnnotation(doc); 2606 } 2607 2608 // Classes, interfaces, enums, annotation types. 2609 if (doc instanceof ClassDoc) { 2610 ClassDoc classDoc = (ClassDoc) doc; 2611 2612 // Check the containing package. 2613 if (hasHideOrRemovedAnnotation(classDoc.containingPackage())) { 2614 return true; 2615 } 2616 2617 // Check the class doc and containing class docs if this is a 2618 // nested class. 2619 ClassDoc current = classDoc; 2620 do { 2621 if (hasHideOrRemovedAnnotation(current)) { 2622 return true; 2623 } 2624 2625 current = current.containingClass(); 2626 } while (current != null); 2627 } 2628 2629 return false; 2630 } 2631 2632 /** 2633 * Filters out hidden and removed elements. 2634 */ filterHiddenAndRemoved(Object o, Class<?> expected)2635 private static Object filterHiddenAndRemoved(Object o, Class<?> expected) { 2636 if (o == null) { 2637 return null; 2638 } 2639 2640 Class type = o.getClass(); 2641 if (type.getName().startsWith("com.sun.")) { 2642 // TODO: Implement interfaces from superclasses, too. 2643 return Proxy 2644 .newProxyInstance(type.getClassLoader(), type.getInterfaces(), new HideHandler(o)); 2645 } else if (o instanceof Object[]) { 2646 Class<?> componentType = expected.getComponentType(); 2647 Object[] array = (Object[]) o; 2648 List<Object> list = new ArrayList<Object>(array.length); 2649 for (Object entry : array) { 2650 if ((entry instanceof Doc) && isHiddenOrRemoved((Doc) entry)) { 2651 continue; 2652 } 2653 list.add(filterHiddenAndRemoved(entry, componentType)); 2654 } 2655 return list.toArray((Object[]) Array.newInstance(componentType, list.size())); 2656 } else { 2657 return o; 2658 } 2659 } 2660 2661 /** 2662 * Filters hidden elements out of method return values. 2663 */ 2664 private static class HideHandler implements InvocationHandler { 2665 2666 private final Object target; 2667 HideHandler(Object target)2668 public HideHandler(Object target) { 2669 this.target = target; 2670 } 2671 invoke(Object proxy, Method method, Object[] args)2672 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 2673 String methodName = method.getName(); 2674 if (args != null) { 2675 if (methodName.equals("compareTo") || methodName.equals("equals") 2676 || methodName.equals("overrides") || methodName.equals("subclassOf")) { 2677 args[0] = unwrap(args[0]); 2678 } 2679 } 2680 2681 if (methodName.equals("getRawCommentText")) { 2682 return filterComment((String) method.invoke(target, args)); 2683 } 2684 2685 // escape "&" in disjunctive types. 2686 if (proxy instanceof Type && methodName.equals("toString")) { 2687 return ((String) method.invoke(target, args)).replace("&", "&"); 2688 } 2689 2690 try { 2691 return filterHiddenAndRemoved(method.invoke(target, args), method.getReturnType()); 2692 } catch (InvocationTargetException e) { 2693 throw e.getTargetException(); 2694 } 2695 } 2696 filterComment(String s)2697 private String filterComment(String s) { 2698 if (s == null) { 2699 return null; 2700 } 2701 2702 s = s.trim(); 2703 2704 // Work around off by one error 2705 while (s.length() >= 5 && s.charAt(s.length() - 5) == '{') { 2706 s += " "; 2707 } 2708 2709 return s; 2710 } 2711 unwrap(Object proxy)2712 private static Object unwrap(Object proxy) { 2713 if (proxy instanceof Proxy) return ((HideHandler) Proxy.getInvocationHandler(proxy)).target; 2714 return proxy; 2715 } 2716 } 2717 2718 /** 2719 * Collect the values used by the Dev tools and write them in files packaged with the SDK 2720 * 2721 * @param output the ouput directory for the files. 2722 */ writeSdkValues(String output)2723 private static void writeSdkValues(String output) { 2724 ArrayList<String> activityActions = new ArrayList<String>(); 2725 ArrayList<String> broadcastActions = new ArrayList<String>(); 2726 ArrayList<String> serviceActions = new ArrayList<String>(); 2727 ArrayList<String> categories = new ArrayList<String>(); 2728 ArrayList<String> features = new ArrayList<String>(); 2729 2730 ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>(); 2731 ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>(); 2732 ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>(); 2733 2734 Collection<ClassInfo> classes = Converter.allClasses(); 2735 2736 // The topmost LayoutParams class - android.view.ViewGroup.LayoutParams 2737 ClassInfo topLayoutParams = null; 2738 2739 // Go through all the fields of all the classes, looking SDK stuff. 2740 for (ClassInfo clazz : classes) { 2741 2742 // first check constant fields for the SdkConstant annotation. 2743 ArrayList<FieldInfo> fields = clazz.allSelfFields(); 2744 for (FieldInfo field : fields) { 2745 Object cValue = field.constantValue(); 2746 if (cValue != null) { 2747 ArrayList<AnnotationInstanceInfo> annotations = field.annotations(); 2748 if (!annotations.isEmpty()) { 2749 for (AnnotationInstanceInfo annotation : annotations) { 2750 if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) { 2751 if (!annotation.elementValues().isEmpty()) { 2752 String type = annotation.elementValues().get(0).valueString(); 2753 if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) { 2754 activityActions.add(cValue.toString()); 2755 } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) { 2756 broadcastActions.add(cValue.toString()); 2757 } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) { 2758 serviceActions.add(cValue.toString()); 2759 } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) { 2760 categories.add(cValue.toString()); 2761 } else if (SDK_CONSTANT_TYPE_FEATURE.equals(type)) { 2762 features.add(cValue.toString()); 2763 } 2764 } 2765 break; 2766 } 2767 } 2768 } 2769 } 2770 } 2771 2772 // Now check the class for @Widget or if its in the android.widget package 2773 // (unless the class is hidden or abstract, or non public) 2774 if (clazz.isHiddenOrRemoved() == false && clazz.isPublic() && clazz.isAbstract() == false) { 2775 boolean annotated = false; 2776 ArrayList<AnnotationInstanceInfo> annotations = clazz.annotations(); 2777 if (!annotations.isEmpty()) { 2778 for (AnnotationInstanceInfo annotation : annotations) { 2779 if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) { 2780 widgets.add(clazz); 2781 annotated = true; 2782 break; 2783 } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) { 2784 layouts.add(clazz); 2785 annotated = true; 2786 break; 2787 } 2788 } 2789 } 2790 2791 if (annotated == false) { 2792 if (topLayoutParams == null 2793 && "android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) { 2794 topLayoutParams = clazz; 2795 } 2796 // let's check if this is inside android.widget or android.view 2797 if (isIncludedPackage(clazz)) { 2798 // now we check what this class inherits either from android.view.ViewGroup 2799 // or android.view.View, or android.view.ViewGroup.LayoutParams 2800 int type = checkInheritance(clazz); 2801 switch (type) { 2802 case TYPE_WIDGET: 2803 widgets.add(clazz); 2804 break; 2805 case TYPE_LAYOUT: 2806 layouts.add(clazz); 2807 break; 2808 case TYPE_LAYOUT_PARAM: 2809 layoutParams.add(clazz); 2810 break; 2811 } 2812 } 2813 } 2814 } 2815 } 2816 2817 // now write the files, whether or not the list are empty. 2818 // the SDK built requires those files to be present. 2819 2820 Collections.sort(activityActions); 2821 writeValues(output + "/activity_actions.txt", activityActions); 2822 2823 Collections.sort(broadcastActions); 2824 writeValues(output + "/broadcast_actions.txt", broadcastActions); 2825 2826 Collections.sort(serviceActions); 2827 writeValues(output + "/service_actions.txt", serviceActions); 2828 2829 Collections.sort(categories); 2830 writeValues(output + "/categories.txt", categories); 2831 2832 Collections.sort(features); 2833 writeValues(output + "/features.txt", features); 2834 2835 // before writing the list of classes, we do some checks, to make sure the layout params 2836 // are enclosed by a layout class (and not one that has been declared as a widget) 2837 for (int i = 0; i < layoutParams.size();) { 2838 ClassInfo clazz = layoutParams.get(i); 2839 ClassInfo containingClass = clazz.containingClass(); 2840 boolean remove = containingClass == null || layouts.indexOf(containingClass) == -1; 2841 // Also ensure that super classes of the layout params are in android.widget or android.view. 2842 while (!remove && (clazz = clazz.superclass()) != null && !clazz.equals(topLayoutParams)) { 2843 remove = !isIncludedPackage(clazz); 2844 } 2845 if (remove) { 2846 layoutParams.remove(i); 2847 } else { 2848 i++; 2849 } 2850 } 2851 2852 writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams); 2853 } 2854 2855 /** 2856 * Check if the clazz is in package android.view or android.widget 2857 */ isIncludedPackage(ClassInfo clazz)2858 private static boolean isIncludedPackage(ClassInfo clazz) { 2859 String pckg = clazz.containingPackage().name(); 2860 return "android.widget".equals(pckg) || "android.view".equals(pckg); 2861 } 2862 2863 /** 2864 * Writes a list of values into a text files. 2865 * 2866 * @param pathname the absolute os path of the output file. 2867 * @param values the list of values to write. 2868 */ writeValues(String pathname, ArrayList<String> values)2869 private static void writeValues(String pathname, ArrayList<String> values) { 2870 FileWriter fw = null; 2871 BufferedWriter bw = null; 2872 try { 2873 fw = new FileWriter(pathname, false); 2874 bw = new BufferedWriter(fw); 2875 2876 for (String value : values) { 2877 bw.append(value).append('\n'); 2878 } 2879 } catch (IOException e) { 2880 // pass for now 2881 } finally { 2882 try { 2883 if (bw != null) bw.close(); 2884 } catch (IOException e) { 2885 // pass for now 2886 } 2887 try { 2888 if (fw != null) fw.close(); 2889 } catch (IOException e) { 2890 // pass for now 2891 } 2892 } 2893 } 2894 2895 /** 2896 * Writes the widget/layout/layout param classes into a text files. 2897 * 2898 * @param pathname the absolute os path of the output file. 2899 * @param widgets the list of widget classes to write. 2900 * @param layouts the list of layout classes to write. 2901 * @param layoutParams the list of layout param classes to write. 2902 */ writeClasses(String pathname, ArrayList<ClassInfo> widgets, ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams)2903 private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets, 2904 ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) { 2905 FileWriter fw = null; 2906 BufferedWriter bw = null; 2907 try { 2908 fw = new FileWriter(pathname, false); 2909 bw = new BufferedWriter(fw); 2910 2911 // write the 3 types of classes. 2912 for (ClassInfo clazz : widgets) { 2913 writeClass(bw, clazz, 'W'); 2914 } 2915 for (ClassInfo clazz : layoutParams) { 2916 writeClass(bw, clazz, 'P'); 2917 } 2918 for (ClassInfo clazz : layouts) { 2919 writeClass(bw, clazz, 'L'); 2920 } 2921 } catch (IOException e) { 2922 // pass for now 2923 } finally { 2924 try { 2925 if (bw != null) bw.close(); 2926 } catch (IOException e) { 2927 // pass for now 2928 } 2929 try { 2930 if (fw != null) fw.close(); 2931 } catch (IOException e) { 2932 // pass for now 2933 } 2934 } 2935 } 2936 2937 /** 2938 * Writes a class name and its super class names into a {@link BufferedWriter}. 2939 * 2940 * @param writer the BufferedWriter to write into 2941 * @param clazz the class to write 2942 * @param prefix the prefix to put at the beginning of the line. 2943 * @throws IOException 2944 */ writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)2945 private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix) 2946 throws IOException { 2947 writer.append(prefix).append(clazz.qualifiedName()); 2948 ClassInfo superClass = clazz; 2949 while ((superClass = superClass.superclass()) != null) { 2950 writer.append(' ').append(superClass.qualifiedName()); 2951 } 2952 writer.append('\n'); 2953 } 2954 2955 /** 2956 * Checks the inheritance of {@link ClassInfo} objects. This method return 2957 * <ul> 2958 * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li> 2959 * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li> 2960 * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends 2961 * <code>android.view.ViewGroup$LayoutParams</code></li> 2962 * <li>{@link #TYPE_NONE}: in all other cases</li> 2963 * </ul> 2964 * 2965 * @param clazz the {@link ClassInfo} to check. 2966 */ checkInheritance(ClassInfo clazz)2967 private static int checkInheritance(ClassInfo clazz) { 2968 if ("android.view.ViewGroup".equals(clazz.qualifiedName())) { 2969 return TYPE_LAYOUT; 2970 } else if ("android.view.View".equals(clazz.qualifiedName())) { 2971 return TYPE_WIDGET; 2972 } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) { 2973 return TYPE_LAYOUT_PARAM; 2974 } 2975 2976 ClassInfo parent = clazz.superclass(); 2977 if (parent != null) { 2978 return checkInheritance(parent); 2979 } 2980 2981 return TYPE_NONE; 2982 } 2983 2984 /** 2985 * Ensures a trailing '/' at the end of a string. 2986 */ ensureSlash(String path)2987 static String ensureSlash(String path) { 2988 return path.endsWith("/") ? path : path + "/"; 2989 } 2990 2991 /** 2992 * Process sample projects. Generate the TOC for the samples groups and project 2993 * and write it to a cs var, which is then written to files during templating to 2994 * html output. Collect metadata from sample project _index.jd files. Copy html 2995 * and specific source file types to the output directory. 2996 */ writeSamples(boolean offlineMode, ArrayList<SampleCode> sampleCodes, boolean sortNavByGroups)2997 public static void writeSamples(boolean offlineMode, ArrayList<SampleCode> sampleCodes, 2998 boolean sortNavByGroups) { 2999 samplesNavTree = makeHDF(); 3000 3001 // Go through samples processing files. Create a root list for SC nodes, 3002 // pass it to SCs for their NavTree children and append them. 3003 List<SampleCode.Node> samplesList = new ArrayList<SampleCode.Node>(); 3004 List<SampleCode.Node> sampleGroupsRootNodes = null; 3005 for (SampleCode sc : sampleCodes) { 3006 samplesList.add(sc.setSamplesTOC(offlineMode)); 3007 } 3008 if (sortNavByGroups) { 3009 sampleGroupsRootNodes = new ArrayList<SampleCode.Node>(); 3010 for (SampleCode gsc : sampleCodeGroups) { 3011 String link = ClearPage.toroot + "samples/" + gsc.mTitle.replaceAll(" ", "").trim().toLowerCase() + ".html"; 3012 sampleGroupsRootNodes.add(new SampleCode.Node.Builder().setLabel(gsc.mTitle).setLink(link).setType("groupholder").build()); 3013 } 3014 } 3015 // Pass full samplesList to SC to render the samples TOC to sampleNavTree hdf 3016 if (!offlineMode) { 3017 SampleCode.writeSamplesNavTree(samplesList, sampleGroupsRootNodes); 3018 } 3019 // Iterate the samplecode projects writing the files to out 3020 for (SampleCode sc : sampleCodes) { 3021 sc.writeSamplesFiles(offlineMode); 3022 } 3023 } 3024 3025 /** 3026 * Given an initial samples directory root, walk through the directory collecting 3027 * sample code project roots and adding them to an array of SampleCodes. 3028 * @param rootDir Root directory holding all browseable sample code projects, 3029 * defined in frameworks/base/Android.mk as "-sampleDir path". 3030 */ getSampleProjects(File rootDir)3031 public static void getSampleProjects(File rootDir) { 3032 for (File f : rootDir.listFiles()) { 3033 String name = f.getName(); 3034 if (f.isDirectory()) { 3035 if (isValidSampleProjectRoot(f)) { 3036 sampleCodes.add(new SampleCode(f.getAbsolutePath(), "samples/" + name, name)); 3037 } else { 3038 getSampleProjects(f); 3039 } 3040 } 3041 } 3042 } 3043 3044 /** 3045 * Test whether a given directory is the root directory for a sample code project. 3046 * Root directories must contain a valid _index.jd file and a src/ directory 3047 * or a module directory that contains a src/ directory. 3048 */ isValidSampleProjectRoot(File dir)3049 public static boolean isValidSampleProjectRoot(File dir) { 3050 File indexJd = new File(dir, "_index.jd"); 3051 if (!indexJd.exists()) { 3052 return false; 3053 } 3054 File srcDir = new File(dir, "src"); 3055 if (srcDir.exists()) { 3056 return true; 3057 } else { 3058 // Look for a src/ directory one level below the root directory, so 3059 // modules are supported. 3060 for (File childDir : dir.listFiles()) { 3061 if (childDir.isDirectory()) { 3062 srcDir = new File(childDir, "src"); 3063 if (srcDir.exists()) { 3064 return true; 3065 } 3066 } 3067 } 3068 return false; 3069 } 3070 } 3071 getDocumentationStringForAnnotation(String annotationName)3072 public static String getDocumentationStringForAnnotation(String annotationName) { 3073 if (!documentAnnotations) return null; 3074 if (annotationDocumentationMap == null) { 3075 annotationDocumentationMap = new HashMap<String, String>(); 3076 // parse the file for map 3077 try { 3078 BufferedReader in = new BufferedReader( 3079 new FileReader(documentAnnotationsPath)); 3080 try { 3081 String line = in.readLine(); 3082 String[] split; 3083 while (line != null) { 3084 split = line.split(":"); 3085 annotationDocumentationMap.put(split[0], split[1]); 3086 line = in.readLine(); 3087 } 3088 } finally { 3089 in.close(); 3090 } 3091 } catch (IOException e) { 3092 System.err.println("Unable to open annotations documentation file for reading: " 3093 + documentAnnotationsPath); 3094 } 3095 } 3096 return annotationDocumentationMap.get(annotationName); 3097 } 3098 writeCompatConfig()3099 public static void writeCompatConfig() { 3100 if (compatConfig == null) { 3101 return; 3102 } 3103 CompatInfo config = CompatInfo.readCompatConfig(compatConfig); 3104 Data data = makeHDF(); 3105 config.makeHDF(data); 3106 setPageTitle(data, "Compatibility changes"); 3107 // TODO - should we write the output to some other path? 3108 String outfile = "compatchanges.html"; 3109 ClearPage.write(data, "compatchanges.cs", outfile); 3110 } 3111 3112 } 3113