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