Start.java revision 3688:7752e4f2a338
1/* 2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.javadoc.internal.tool; 27 28import java.io.File; 29import java.io.FileNotFoundException; 30import java.io.IOException; 31import java.io.PrintWriter; 32import java.nio.file.Path; 33import java.text.BreakIterator; 34import java.text.Collator; 35import java.util.ArrayList; 36import java.util.Arrays; 37import java.util.Collection; 38import java.util.Collections; 39import java.util.Comparator; 40import java.util.List; 41import java.util.Locale; 42import java.util.Objects; 43import java.util.Set; 44import java.util.stream.Collectors; 45import java.util.stream.Stream; 46 47import javax.tools.JavaFileManager; 48import javax.tools.JavaFileObject; 49import javax.tools.StandardJavaFileManager; 50import javax.tools.StandardLocation; 51 52import com.sun.tools.javac.api.JavacTrees; 53import com.sun.tools.javac.file.BaseFileManager; 54import com.sun.tools.javac.file.JavacFileManager; 55import com.sun.tools.javac.main.CommandLine; 56import com.sun.tools.javac.main.OptionHelper; 57import com.sun.tools.javac.main.OptionHelper.GrumpyHelper; 58import com.sun.tools.javac.platform.PlatformDescription; 59import com.sun.tools.javac.platform.PlatformUtils; 60import com.sun.tools.javac.util.ClientCodeException; 61import com.sun.tools.javac.util.Context; 62import com.sun.tools.javac.util.Log; 63import com.sun.tools.javac.util.Options; 64 65import jdk.javadoc.doclet.Doclet; 66import jdk.javadoc.doclet.Doclet.Option; 67import jdk.javadoc.doclet.DocletEnvironment; 68import jdk.javadoc.internal.doclets.toolkit.Resources; 69 70import static javax.tools.DocumentationTool.Location.*; 71 72import static com.sun.tools.javac.main.Option.*; 73 74/** 75 * Main program of Javadoc. 76 * Previously named "Main". 77 * 78 * <p><b>This is NOT part of any supported API. 79 * If you write code that depends on this, you do so at your own risk. 80 * This code and its internal interfaces are subject to change or 81 * deletion without notice.</b> 82 * 83 * @author Robert Field 84 * @author Neal Gafter (rewrite) 85 */ 86public class Start extends ToolOption.Helper { 87 88 @SuppressWarnings("deprecation") 89 private static final Class<?> OldStdDoclet = 90 com.sun.tools.doclets.standard.Standard.class; 91 92 private static final Class<?> StdDoclet = 93 jdk.javadoc.doclets.StandardDoclet.class; 94 /** Context for this invocation. */ 95 private final Context context; 96 97 private static final String ProgramName = "javadoc"; 98 99 private Messager messager; 100 101 private final String docletName; 102 103 private final ClassLoader classLoader; 104 105 private Class<?> docletClass; 106 107 private Doclet doclet; 108 109 // used to determine the locale for the messager 110 private Locale locale; 111 112 113 /** 114 * In API mode, exceptions thrown while calling the doclet are 115 * propagated using ClientCodeException. 116 */ 117 private boolean apiMode; 118 119 private JavaFileManager fileManager; 120 121 Start() { 122 this(null, null, null, null, null, null); 123 } 124 125 Start(PrintWriter outWriter, PrintWriter errWriter) { 126 this(null, null, outWriter, errWriter, null, null); 127 } 128 129 Start(Context context, String programName, 130 PrintWriter outWriter, PrintWriter errWriter, 131 String docletName, ClassLoader classLoader) { 132 this.context = context == null ? new Context() : context; 133 String pname = programName == null ? ProgramName : programName; 134 this.messager = (outWriter == null && errWriter == null) 135 ? new Messager(this.context, pname) 136 : new Messager(this.context, pname, outWriter, errWriter); 137 this.docletName = docletName; 138 this.classLoader = classLoader; 139 this.docletClass = null; 140 this.locale = Locale.getDefault(); 141 } 142 143 public Start(Context context) { 144 this.docletClass = null; 145 this.context = Objects.requireNonNull(context); 146 this.apiMode = true; 147 this.docletName = null; 148 this.classLoader = null; 149 this.locale = Locale.getDefault(); 150 } 151 152 void initMessager() { 153 if (!apiMode) 154 return; 155 if (messager == null) { 156 Log log = context.get(Log.logKey); 157 if (log instanceof Messager) { 158 messager = (Messager) log; 159 } else { 160 PrintWriter out = context.get(Log.errKey); 161 messager = (out == null) 162 ? new Messager(context, ProgramName) 163 : new Messager(context, ProgramName, out, out); 164 } 165 } 166 } 167 168 /** 169 * Usage 170 */ 171 @Override 172 void usage() { 173 usage(true); 174 } 175 176 void usage(boolean exit) { 177 usage("main.usage", "-help", "main.usage.foot"); 178 179 if (exit) 180 throw new Messager.ExitJavadoc(); 181 } 182 183 @Override 184 void Xusage() { 185 Xusage(true); 186 } 187 188 void Xusage(boolean exit) { 189 usage("main.Xusage", "-X", "main.Xusage.foot"); 190 191 if (exit) 192 throw new Messager.ExitJavadoc(); 193 } 194 195 private void usage(String header, String option, String footer) { 196 messager.notice(header); 197 showToolOptions(option.equals("-X") ? OptionKind.EXTENDED : OptionKind.STANDARD); 198 199 // let doclet print usage information 200 if (docletClass != null) { 201 String name = doclet.getName(); 202 messager.notice("main.doclet.usage.header", name); 203 showDocletOptions(option.equals("-X") ? Option.Kind.EXTENDED : Option.Kind.STANDARD); 204 } 205 206 if (footer != null) 207 messager.notice(footer); 208 } 209 210 void showToolOptions(OptionKind kind) { 211 Comparator<ToolOption> comp = new Comparator<ToolOption>() { 212 final Collator collator = Collator.getInstance(Locale.US); 213 { collator.setStrength(Collator.PRIMARY); } 214 215 @Override 216 public int compare(ToolOption o1, ToolOption o2) { 217 return collator.compare(o1.primaryName, o2.primaryName); 218 } 219 }; 220 221 Stream.of(ToolOption.values()) 222 .filter(opt -> opt.kind == kind) 223 .sorted(comp) 224 .forEach(opt -> showToolOption(opt)); 225 } 226 227 void showToolOption(ToolOption option) { 228 List<String> names = option.getNames(); 229 String parameters; 230 if (option.hasArg || option.primaryName.endsWith(":")) { 231 String sep = (option == ToolOption.J) || option.primaryName.endsWith(":") ? "" : " "; 232 parameters = sep + option.getParameters(messager); 233 } else { 234 parameters = ""; 235 } 236 String description = option.getDescription(messager); 237 showUsage(names, parameters, description); 238 } 239 240 void showDocletOptions(Option.Kind kind) { 241 Comparator<Doclet.Option> comp = new Comparator<Doclet.Option>() { 242 final Collator collator = Collator.getInstance(Locale.US); 243 { collator.setStrength(Collator.PRIMARY); } 244 245 @Override 246 public int compare(Doclet.Option o1, Doclet.Option o2) { 247 return collator.compare(o1.getName(), o2.getName()); 248 } 249 }; 250 251 doclet.getSupportedOptions().stream() 252 .filter(opt -> opt.getKind() == kind) 253 .sorted(comp) 254 .forEach(opt -> showDocletOption(opt)); 255 } 256 257 void showDocletOption(Doclet.Option option) { 258 List<String> names = Arrays.asList(option.getName()); 259 String parameters; 260 if (option.getArgumentCount() > 0 || option.getName().endsWith(":")) { 261 String sep = option.getName().endsWith(":") ? "" : " "; 262 parameters = sep + option.getParameters(); 263 } else { 264 parameters = ""; 265 } 266 String description = option.getDescription(); 267 showUsage(names, parameters, description); 268 } 269 270 // The following constants are intended to format the output to 271 // be similar to that of the java launcher: i.e. "java -help". 272 273 /** The indent for the option synopsis. */ 274 private static final String SMALL_INDENT = " "; 275 /** The automatic indent for the description. */ 276 private static final String LARGE_INDENT = " "; 277 /** The space allowed for the synopsis, if the description is to be shown on the same line. */ 278 private static final int DEFAULT_SYNOPSIS_WIDTH = 13; 279 /** The nominal maximum line length, when seeing if text will fit on a line. */ 280 private static final int DEFAULT_MAX_LINE_LENGTH = 80; 281 /** The format for a single-line help entry. */ 282 private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s"; 283 284 void showUsage(List<String> names, String parameters, String description) { 285 String synopses = names.stream() 286 .map(s -> s + parameters) 287 .collect(Collectors.joining(", ")); 288 // If option synopses and description fit on a single line of reasonable length, 289 // display using COMPACT_FORMAT 290 if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH 291 && !description.contains("\n") 292 && (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + description.length() <= DEFAULT_MAX_LINE_LENGTH)) { 293 messager.printNotice(String.format(COMPACT_FORMAT, synopses, description)); 294 return; 295 } 296 297 // If option synopses fit on a single line of reasonable length, show that; 298 // otherwise, show 1 per line 299 if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) { 300 messager.printNotice(SMALL_INDENT + synopses); 301 } else { 302 for (String name: names) { 303 messager.printNotice(SMALL_INDENT + name + parameters); 304 } 305 } 306 307 // Finally, show the description 308 messager.printNotice(LARGE_INDENT + description.replace("\n", "\n" + LARGE_INDENT)); 309 } 310 311 312 /** 313 * Main program - external wrapper. In order to maintain backward 314 * CLI compatibility, we dispatch to the old tool or the old doclet's 315 * Start mechanism, based on the options present on the command line 316 * with the following precedence: 317 * 1. presence of -Xold, dispatch to old tool 318 * 2. doclet variant, if old, dispatch to old Start 319 * 3. taglet variant, if old, dispatch to old Start 320 * 321 * Thus the presence of -Xold switches the tool, soon after command files 322 * if any, are expanded, this is performed here, noting that the messager 323 * is available at this point in time. 324 * The doclet/taglet tests are performed in the begin method, further on, 325 * this is to minimize argument processing and most importantly the impact 326 * of class loader creation, needed to detect the doclet/taglet class variants. 327 */ 328 @SuppressWarnings("deprecation") 329 int begin(String... argv) { 330 // Preprocess @file arguments 331 try { 332 argv = CommandLine.parse(argv); 333 } catch (FileNotFoundException e) { 334 messager.error("main.cant.read", e.getMessage()); 335 throw new Messager.ExitJavadoc(); 336 } catch (IOException e) { 337 e.printStackTrace(System.err); 338 throw new Messager.ExitJavadoc(); 339 } 340 341 if (argv.length > 0 && "-Xold".equals(argv[0])) { 342 messager.warning("main.legacy_api"); 343 String[] nargv = Arrays.copyOfRange(argv, 1, argv.length); 344 return com.sun.tools.javadoc.Main.execute(nargv); 345 } 346 boolean ok = begin(Arrays.asList(argv), Collections.<JavaFileObject> emptySet()); 347 return ok ? 0 : 1; 348 } 349 350 // Called by 199 API. 351 public boolean begin(Class<?> docletClass, 352 Iterable<String> options, 353 Iterable<? extends JavaFileObject> fileObjects) { 354 this.docletClass = docletClass; 355 List<String> opts = new ArrayList<>(); 356 for (String opt: options) 357 opts.add(opt); 358 359 return begin(opts, fileObjects); 360 } 361 362 @SuppressWarnings("deprecation") 363 private boolean begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) { 364 fileManager = context.get(JavaFileManager.class); 365 if (fileManager == null) { 366 JavacFileManager.preRegister(context); 367 fileManager = context.get(JavaFileManager.class); 368 if (fileManager instanceof BaseFileManager) { 369 ((BaseFileManager) fileManager).autoClose = true; 370 } 371 } 372 // locale, doclet and maybe taglet, needs to be determined first 373 docletClass = preProcess(fileManager, options); 374 if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) { 375 // no need to dispatch to old, safe to init now 376 initMessager(); 377 messager.setLocale(locale); 378 try { 379 Object o = docletClass.getConstructor().newInstance(); 380 doclet = (Doclet) o; 381 } catch (ReflectiveOperationException exc) { 382 exc.printStackTrace(); 383 if (!apiMode) { 384 error("main.could_not_instantiate_class", docletClass); 385 throw new Messager.ExitJavadoc(); 386 } 387 throw new ClientCodeException(exc); 388 } 389 } else { 390 if (this.apiMode) { 391 com.sun.tools.javadoc.main.Start ostart 392 = new com.sun.tools.javadoc.main.Start(context); 393 return ostart.begin(docletClass, options, fileObjects); 394 } 395 warn("main.legacy_api"); 396 String[] array = options.toArray(new String[options.size()]); 397 return com.sun.tools.javadoc.Main.execute(array) == 0; 398 } 399 400 boolean failed = false; 401 try { 402 failed = !parseAndExecute(options, fileObjects); 403 } catch (Messager.ExitJavadoc exc) { 404 // ignore, we just exit this way 405 } catch (OutOfMemoryError ee) { 406 messager.error("main.out.of.memory"); 407 failed = true; 408 } catch (ClientCodeException e) { 409 // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl 410 throw e; 411 } catch (Error ee) { 412 ee.printStackTrace(System.err); 413 messager.error("main.fatal.error"); 414 failed = true; 415 } catch (Exception ee) { 416 ee.printStackTrace(System.err); 417 messager.error("main.fatal.exception"); 418 failed = true; 419 } finally { 420 if (fileManager != null 421 && fileManager instanceof BaseFileManager 422 && ((BaseFileManager) fileManager).autoClose) { 423 try { 424 fileManager.close(); 425 } catch (IOException ignore) {} 426 } 427 boolean haveErrorWarnings = messager.nerrors() > 0 || 428 (rejectWarnings && messager.nwarnings() > 0); 429 if (failed && !haveErrorWarnings) { 430 // the doclet failed, but nothing reported, flag it!. 431 messager.error("main.unknown.error"); 432 } 433 failed |= haveErrorWarnings; 434 messager.exitNotice(); 435 messager.flush(); 436 } 437 return !failed; 438 } 439 440 /** 441 * Main program - internal 442 */ 443 @SuppressWarnings("unchecked") 444 private boolean parseAndExecute(List<String> argList, 445 Iterable<? extends JavaFileObject> fileObjects) throws IOException { 446 long tm = System.currentTimeMillis(); 447 448 List<String> javaNames = new ArrayList<>(); 449 450 compOpts = Options.instance(context); 451 452 // Make sure no obsolete source/target messages are reported 453 com.sun.tools.javac.main.Option.XLINT.process(getOptionHelper(), "-Xlint:-options"); 454 455 doclet.init(locale, messager); 456 parseArgs(argList, javaNames); 457 458 if (fileManager instanceof BaseFileManager) { 459 ((BaseFileManager) fileManager).handleOptions(fileManagerOpts); 460 } 461 462 String platformString = compOpts.get("--release"); 463 464 if (platformString != null) { 465 if (compOpts.isSet("-source")) { 466 usageError("main.release.bootclasspath.conflict", "-source"); 467 } 468 if (fileManagerOpts.containsKey(BOOT_CLASS_PATH)) { 469 usageError("main.release.bootclasspath.conflict", BOOT_CLASS_PATH.getPrimaryName()); 470 } 471 472 PlatformDescription platformDescription = 473 PlatformUtils.lookupPlatformDescription(platformString); 474 475 if (platformDescription == null) { 476 usageError("main.unsupported.release.version", platformString); 477 } 478 479 compOpts.put(SOURCE, platformDescription.getSourceVersion()); 480 481 context.put(PlatformDescription.class, platformDescription); 482 483 Collection<Path> platformCP = platformDescription.getPlatformPath(); 484 485 if (platformCP != null) { 486 if (fileManager instanceof StandardJavaFileManager) { 487 StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager; 488 489 sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP); 490 } else { 491 usageError("main.release.not.standard.file.manager", platformString); 492 } 493 } 494 } 495 496 compOpts.notifyListeners(); 497 List<String> modules = (List<String>) jdtoolOpts.computeIfAbsent(ToolOption.MODULE, 498 s -> Collections.EMPTY_LIST); 499 500 if (modules.isEmpty()) { 501 List<String> subpkgs = (List<String>) jdtoolOpts.computeIfAbsent(ToolOption.SUBPACKAGES, 502 s -> Collections.EMPTY_LIST); 503 if (subpkgs.isEmpty()) { 504 if (javaNames.isEmpty() && isEmpty(fileObjects)) { 505 usageError("main.No_modules_packages_or_classes_specified"); 506 } 507 } 508 } 509 510 JavadocTool comp = JavadocTool.make0(context); 511 if (comp == null) return false; 512 513 DocletEnvironment docEnv = comp.getEnvironment(jdtoolOpts, 514 javaNames, 515 fileObjects); 516 517 // release resources 518 comp = null; 519 520 if (breakiterator || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { 521 JavacTrees trees = JavacTrees.instance(context); 522 trees.setBreakIterator(BreakIterator.getSentenceInstance(locale)); 523 } 524 // pass off control to the doclet 525 boolean ok = docEnv != null; 526 if (ok) ok = doclet.run(docEnv); 527 528 // We're done. 529 if (compOpts.get("-verbose") != null) { 530 tm = System.currentTimeMillis() - tm; 531 messager.notice("main.done_in", Long.toString(tm)); 532 } 533 534 return ok; 535 } 536 537 Set<Doclet.Option> docletOptions = null; 538 int handleDocletOptions(int idx, List<String> args, boolean isToolOption) { 539 if (docletOptions == null) { 540 docletOptions = doclet.getSupportedOptions(); 541 } 542 String arg = args.get(idx); 543 String argBase, argVal; 544 if (arg.startsWith("--") && arg.contains("=")) { 545 int sep = arg.indexOf("="); 546 argBase = arg.substring(0, sep); 547 argVal = arg.substring(sep + 1); 548 } else { 549 argBase = arg; 550 argVal = null; 551 } 552 553 for (Doclet.Option opt : docletOptions) { 554 if (opt.matches(argBase)) { 555 if (argVal != null) { 556 switch (opt.getArgumentCount()) { 557 case 0: 558 usageError("main.unnecessary_arg_provided", argBase); 559 break; 560 case 1: 561 opt.process(arg, Arrays.asList(argVal).listIterator()); 562 break; 563 default: 564 usageError("main.only_one_argument_with_equals", argBase); 565 break; 566 } 567 } else { 568 if (args.size() - idx -1 < opt.getArgumentCount()) { 569 usageError("main.requires_argument", arg); 570 } 571 opt.process(arg, args.listIterator(idx + 1)); 572 idx += opt.getArgumentCount(); 573 } 574 return idx; 575 } 576 } 577 // check if arg is accepted by the tool before emitting error 578 if (!isToolOption) 579 usageError("main.invalid_flag", arg); 580 return idx; 581 } 582 583 private Class<?> preProcess(JavaFileManager jfm, List<String> argv) { 584 // doclet specifying arguments 585 String userDocletPath = null; 586 String userDocletName = null; 587 588 // taglet specifying arguments, since tagletpath is a doclet 589 // functionality, assume they are repeated and inspect all. 590 List<File> userTagletPath = new ArrayList<>(); 591 List<String> userTagletNames = new ArrayList<>(); 592 593 // Step 1: loop through the args, set locale early on, if found. 594 for (int i = 0 ; i < argv.size() ; i++) { 595 String arg = argv.get(i); 596 if (arg.equals(ToolOption.LOCALE.primaryName)) { 597 checkOneArg(argv, i++); 598 String lname = argv.get(i); 599 locale = getLocale(lname); 600 } else if (arg.equals(ToolOption.DOCLET.primaryName)) { 601 checkOneArg(argv, i++); 602 if (userDocletName != null) { 603 usageError("main.more_than_one_doclet_specified_0_and_1", 604 userDocletName, argv.get(i)); 605 } 606 if (docletName != null) { 607 usageError("main.more_than_one_doclet_specified_0_and_1", 608 docletName, argv.get(i)); 609 } 610 userDocletName = argv.get(i); 611 } else if (arg.equals(ToolOption.DOCLETPATH.primaryName)) { 612 checkOneArg(argv, i++); 613 if (userDocletPath == null) { 614 userDocletPath = argv.get(i); 615 } else { 616 userDocletPath += File.pathSeparator + argv.get(i); 617 } 618 } else if ("-taglet".equals(arg)) { 619 userTagletNames.add(argv.get(i + 1)); 620 } else if ("-tagletpath".equals(arg)) { 621 for (String pathname : argv.get(i + 1).split(File.pathSeparator)) { 622 userTagletPath.add(new File(pathname)); 623 } 624 } 625 } 626 627 // Step 2: a doclet is provided, nothing more to do. 628 if (docletClass != null) { 629 return docletClass; 630 } 631 632 // Step 3: doclet name specified ? if so find a ClassLoader, 633 // and load it. 634 if (userDocletName != null) { 635 ClassLoader cl = classLoader; 636 if (cl == null) { 637 if (!fileManager.hasLocation(DOCLET_PATH)) { 638 List<File> paths = new ArrayList<>(); 639 if (userDocletPath != null) { 640 for (String pathname : userDocletPath.split(File.pathSeparator)) { 641 paths.add(new File(pathname)); 642 } 643 } 644 try { 645 ((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths); 646 } catch (IOException ioe) { 647 error("main.doclet_could_not_set_location", paths); 648 throw new Messager.ExitJavadoc(); 649 } 650 } 651 cl = fileManager.getClassLoader(DOCLET_PATH); 652 if (cl == null) { 653 // despite doclet specified on cmdline no classloader found! 654 error("main.doclet_no_classloader_found", userDocletName); 655 throw new Messager.ExitJavadoc(); 656 } 657 } 658 try { 659 Class<?> klass = cl.loadClass(userDocletName); 660 return klass; 661 } catch (ClassNotFoundException cnfe) { 662 error("main.doclet_class_not_found", userDocletName); 663 throw new Messager.ExitJavadoc(); 664 } 665 } 666 667 // Step 4: we have a doclet, try loading it 668 if (docletName != null) { 669 try { 670 return Class.forName(docletName, true, getClass().getClassLoader()); 671 } catch (ClassNotFoundException cnfe) { 672 error("main.doclet_class_not_found", userDocletName); 673 throw new Messager.ExitJavadoc(); 674 } 675 } 676 677 // Step 5: we don't have a doclet specified, do we have taglets ? 678 if (!userTagletNames.isEmpty() && hasOldTaglet(userTagletNames, userTagletPath)) { 679 // found a bogey, return the old doclet 680 return OldStdDoclet; 681 } 682 683 // finally 684 return StdDoclet; 685 } 686 687 /* 688 * This method returns true iff it finds a legacy taglet, but for 689 * all other conditions including errors it returns false, allowing 690 * nature to take its own course. 691 */ 692 @SuppressWarnings("deprecation") 693 private boolean hasOldTaglet(List<String> tagletNames, List<File> tagletPaths) { 694 if (!fileManager.hasLocation(TAGLET_PATH)) { 695 try { 696 ((StandardJavaFileManager) fileManager).setLocation(TAGLET_PATH, tagletPaths); 697 } catch (IOException ioe) { 698 error("main.doclet_could_not_set_location", tagletPaths); 699 throw new Messager.ExitJavadoc(); 700 } 701 } 702 ClassLoader cl = fileManager.getClassLoader(TAGLET_PATH); 703 if (cl == null) { 704 // no classloader found! 705 error("main.doclet_no_classloader_found", tagletNames.get(0)); 706 throw new Messager.ExitJavadoc(); 707 } 708 for (String tagletName : tagletNames) { 709 try { 710 Class<?> klass = cl.loadClass(tagletName); 711 if (com.sun.tools.doclets.Taglet.class.isAssignableFrom(klass)) { 712 return true; 713 } 714 } catch (ClassNotFoundException cnfe) { 715 error("main.doclet_class_not_found", tagletName); 716 throw new Messager.ExitJavadoc(); 717 } 718 } 719 return false; 720 } 721 722 private void parseArgs(List<String> args, List<String> javaNames) { 723 for (int i = 0 ; i < args.size() ; i++) { 724 String arg = args.get(i); 725 ToolOption o = ToolOption.get(arg); 726 if (o != null) { 727 // handle a doclet argument that may be needed however 728 // don't increment the index, and allow the tool to consume args 729 handleDocletOptions(i, args, true); 730 731 if (o.hasArg) { 732 if (arg.startsWith("--") && arg.contains("=")) { 733 o.process(this, arg.substring(arg.indexOf('=') + 1)); 734 } else { 735 checkOneArg(args, i++); 736 o.process(this, args.get(i)); 737 } 738 } else if (o.hasSuffix) { 739 o.process(this, arg); 740 } else { 741 o.process(this); 742 } 743 } else if (arg.startsWith("-XD")) { 744 // hidden javac options 745 String s = arg.substring("-XD".length()); 746 int eq = s.indexOf('='); 747 String key = (eq < 0) ? s : s.substring(0, eq); 748 String value = (eq < 0) ? s : s.substring(eq+1); 749 compOpts.put(key, value); 750 } else if (arg.startsWith("-")) { 751 i = handleDocletOptions(i, args, false); 752 } else { 753 javaNames.add(arg); 754 } 755 } 756 } 757 758 private <T> boolean isEmpty(Iterable<T> iter) { 759 return !iter.iterator().hasNext(); 760 } 761 762 /** 763 * Check the one arg option. 764 * Error and exit if one argument is not provided. 765 */ 766 private void checkOneArg(List<String> args, int index) { 767 if ((index + 1) >= args.size() || args.get(index + 1).startsWith("-d")) { 768 usageError("main.requires_argument", args.get(index)); 769 } 770 } 771 772 @Override 773 void usageError(String key, Object... args) { 774 error(key, args); 775 usage(true); 776 } 777 778 void error(String key, Object... args) { 779 messager.error(key, args); 780 } 781 782 void warn(String key, Object... args) { 783 messager.warning(key, args); 784 } 785 786 /** 787 * Get the locale if specified on the command line 788 * else return null and if locale option is not used 789 * then return default locale. 790 */ 791 private Locale getLocale(String localeName) { 792 Locale userlocale = null; 793 if (localeName == null || localeName.isEmpty()) { 794 return Locale.getDefault(); 795 } 796 int firstuscore = localeName.indexOf('_'); 797 int seconduscore = -1; 798 String language = null; 799 String country = null; 800 String variant = null; 801 if (firstuscore == 2) { 802 language = localeName.substring(0, firstuscore); 803 seconduscore = localeName.indexOf('_', firstuscore + 1); 804 if (seconduscore > 0) { 805 if (seconduscore != firstuscore + 3 806 || localeName.length() <= seconduscore + 1) { 807 usageError("main.malformed_locale_name", localeName); 808 return null; 809 } 810 country = localeName.substring(firstuscore + 1, 811 seconduscore); 812 variant = localeName.substring(seconduscore + 1); 813 } else if (localeName.length() == firstuscore + 3) { 814 country = localeName.substring(firstuscore + 1); 815 } else { 816 usageError("main.malformed_locale_name", localeName); 817 return null; 818 } 819 } else if (firstuscore == -1 && localeName.length() == 2) { 820 language = localeName; 821 } else { 822 usageError("main.malformed_locale_name", localeName); 823 return null; 824 } 825 userlocale = searchLocale(language, country, variant); 826 if (userlocale == null) { 827 usageError("main.illegal_locale_name", localeName); 828 return null; 829 } else { 830 return userlocale; 831 } 832 } 833 834 /** 835 * Search the locale for specified language, specified country and 836 * specified variant. 837 */ 838 private Locale searchLocale(String language, String country, 839 String variant) { 840 for (Locale loc : Locale.getAvailableLocales()) { 841 if (loc.getLanguage().equals(language) && 842 (country == null || loc.getCountry().equals(country)) && 843 (variant == null || loc.getVariant().equals(variant))) { 844 return loc; 845 } 846 } 847 return null; 848 } 849 850 @Override 851 OptionHelper getOptionHelper() { 852 return new GrumpyHelper(null) { 853 @Override 854 public String get(com.sun.tools.javac.main.Option option) { 855 return compOpts.get(option); 856 } 857 858 @Override 859 public void put(String name, String value) { 860 compOpts.put(name, value); 861 } 862 }; 863 } 864} 865