< prev index next >

src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java

Print this page




  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 
  26 package jdk.javadoc.internal.tool;
  27 
  28 import java.io.File;
  29 import java.io.IOException;
  30 import java.io.PrintWriter;
  31 import java.nio.file.Path;
  32 import java.text.BreakIterator;
  33 import java.text.Collator;
  34 import java.util.ArrayList;
  35 import java.util.Arrays;
  36 import java.util.Collection;
  37 import java.util.Collections;
  38 import java.util.Comparator;
  39 import java.util.IllformedLocaleException;
  40 import java.util.List;
  41 import java.util.Locale;
  42 import java.util.MissingResourceException;
  43 import java.util.Objects;
  44 import java.util.ResourceBundle;
  45 import java.util.Set;
  46 import java.util.stream.Collectors;
  47 import java.util.stream.Stream;
  48 
  49 import javax.tools.JavaFileManager;
  50 import javax.tools.JavaFileObject;
  51 import javax.tools.StandardJavaFileManager;
  52 import javax.tools.StandardLocation;
  53 
  54 import com.sun.tools.javac.api.JavacTrees;
  55 import com.sun.tools.javac.file.BaseFileManager;
  56 import com.sun.tools.javac.file.JavacFileManager;
  57 import com.sun.tools.javac.jvm.Target;
  58 import com.sun.tools.javac.main.Arguments;
  59 import com.sun.tools.javac.main.CommandLine;
  60 import com.sun.tools.javac.main.OptionHelper;
  61 import com.sun.tools.javac.main.OptionHelper.GrumpyHelper;
  62 import com.sun.tools.javac.platform.PlatformDescription;
  63 import com.sun.tools.javac.platform.PlatformUtils;
  64 import com.sun.tools.javac.util.ClientCodeException;
  65 import com.sun.tools.javac.util.Context;
  66 import com.sun.tools.javac.util.Log;
  67 import com.sun.tools.javac.util.Log.WriterKind;
  68 import com.sun.tools.javac.util.Options;
  69 import com.sun.tools.javac.util.StringUtils;
  70 
  71 import jdk.javadoc.doclet.Doclet;
  72 import jdk.javadoc.doclet.Doclet.Option;
  73 import jdk.javadoc.doclet.DocletEnvironment;

  74 import jdk.javadoc.internal.tool.Main.Result;

  75 
  76 import static javax.tools.DocumentationTool.Location.*;
  77 
  78 import static com.sun.tools.javac.main.Option.*;
  79 import static jdk.javadoc.internal.tool.Main.Result.*;
  80 
  81 /**
  82  * Main program of Javadoc.
  83  * Previously named "Main".
  84  *
  85  *  <p><b>This is NOT part of any supported API.
  86  *  If you write code that depends on this, you do so at your own risk.
  87  *  This code and its internal interfaces are subject to change or
  88  *  deletion without notice.</b>
  89  */
  90 public class Start extends ToolOption.Helper {
  91 
  92     private static final Class<?> StdDoclet =
  93             jdk.javadoc.doclet.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("main.usage", OptionKind.STANDARD, "main.usage.foot");
 174     }
 175 
 176     @Override
 177     void Xusage() {
 178         usage("main.Xusage", OptionKind.EXTENDED, "main.Xusage.foot");
 179     }
 180 
 181     @Override
 182     void version() {
 183         messager.notice("javadoc.version", messager.programName, version("release"));
 184     }
 185 
 186     @Override
 187     void fullVersion() {
 188         messager.notice("javadoc.fullversion", messager.programName, version("full"));







 189     }
 190 
 191     private void usage(String headerKey, OptionKind kind, String footerKey) {
 192         messager.notice(headerKey);
 193         showToolOptions(kind);
 194 
 195         // let doclet print usage information
 196         if (docletClass != null) {
 197             String name = doclet.getName();
 198             messager.notice("main.doclet.usage.header", name);
 199             showDocletOptions(kind == OptionKind.EXTENDED
 200                     ? Option.Kind.EXTENDED
 201                     : Option.Kind.STANDARD);
 202         }
 203         if (footerKey != null)
 204             messager.notice(footerKey);
 205     }
 206 
 207     private static final String versionRBName = "jdk.javadoc.internal.tool.resources.version";
 208     private static ResourceBundle versionRB;
 209 
 210     private static String version(String key) {




 211         if (versionRB == null) {
 212             try {
 213                 versionRB = ResourceBundle.getBundle(versionRBName);
 214             } catch (MissingResourceException e) {
 215                 return Log.getLocalizedString("version.not.available");
 216             }
 217         }
 218         try {
 219             return versionRB.getString(key);
 220         } catch (MissingResourceException e) {
 221             return Log.getLocalizedString("version.not.available");
 222         }
 223     }
 224 
 225     void showToolOptions(OptionKind kind) {
 226         Comparator<ToolOption> comp = new Comparator<ToolOption>() {
 227             final Collator collator = Collator.getInstance(Locale.US);
 228             { collator.setStrength(Collator.PRIMARY); }
 229 
 230             @Override
 231             public int compare(ToolOption o1, ToolOption o2) {
 232                 return collator.compare(o1.primaryName, o2.primaryName);
 233             }
 234         };
 235 
 236         Stream.of(ToolOption.values())
 237                     .filter(opt -> opt.kind == kind)
 238                     .sorted(comp)
 239                     .forEach(this::showToolOption);
 240     }
 241 
 242     void showToolOption(ToolOption option) {
 243         List<String> names = option.getNames();

 244         String parameters;
 245         if (option.hasArg || option.primaryName.endsWith(":")) {
 246             String sep = (option == ToolOption.J) || option.primaryName.endsWith(":") ? "" : " ";
 247             parameters = sep + option.getParameters(messager);
 248         } else {
 249             parameters = "";
 250         }
 251         String description = option.getDescription(messager);
 252         showUsage(names, parameters, description);
 253     }
 254 
 255     void showDocletOptions(Option.Kind kind) {
 256         Comparator<Doclet.Option> comp = new Comparator<Doclet.Option>() {
 257             final Collator collator = Collator.getInstance(Locale.US);
 258             { collator.setStrength(Collator.PRIMARY); }
 259 
 260             @Override
 261             public int compare(Doclet.Option o1, Doclet.Option o2) {
 262                 return collator.compare(o1.getNames().get(0), o2.getNames().get(0));
 263             }
 264         };
 265 
 266         doclet.getSupportedOptions().stream()
 267                 .filter(opt -> opt.getKind() == kind)
 268                 .sorted(comp)
 269                 .forEach(this::showDocletOption);
 270     }
 271 
 272     void showDocletOption(Doclet.Option option) {
 273         List<String> names = option.getNames();
 274         String parameters;
 275         String optname = names.get(0);
 276         if (option.getArgumentCount() > 0 || optname.endsWith(":")) {
 277             String sep = optname.endsWith(":") ? "" : " ";
 278             parameters = sep + option.getParameters();
 279         } else {
 280             parameters = "";
 281         }
 282         String description = option.getDescription();
 283         showUsage(names, parameters, description);
 284     }
 285 
 286     // The following constants are intended to format the output to
 287     // be similar to that of the java launcher: i.e. "java -help".
 288 
 289     /** The indent for the option synopsis. */
 290     private static final String SMALL_INDENT = "    ";
 291     /** The automatic indent for the description. */
 292     private static final String LARGE_INDENT = "                  ";
 293     /** The space allowed for the synopsis, if the description is to be shown on the same line. */
 294     private static final int DEFAULT_SYNOPSIS_WIDTH = 13;
 295     /** The nominal maximum line length, when seeing if text will fit on a line. */
 296     private static final int DEFAULT_MAX_LINE_LENGTH = 80;
 297     /** The format for a single-line help entry. */
 298     private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s";
 299 
 300     void showUsage(List<String> names, String parameters, String description) {
 301         String synopses = names.stream()
 302                 .map(s -> s + parameters)
 303                 .collect(Collectors.joining(", "));
 304         // If option synopses and description fit on a single line of reasonable length,
 305         // display using COMPACT_FORMAT
 306         if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH
 307                 && !description.contains("\n")
 308                 && (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + description.length() <= DEFAULT_MAX_LINE_LENGTH)) {
 309             messager.printNotice(String.format(COMPACT_FORMAT, synopses, description));
 310             return;
 311         }
 312 
 313         // If option synopses fit on a single line of reasonable length, show that;
 314         // otherwise, show 1 per line
 315         if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) {
 316             messager.printNotice(SMALL_INDENT + synopses);
 317         } else {
 318             for (String name: names) {
 319                 messager.printNotice(SMALL_INDENT + name + parameters);
 320             }


 421         } catch (OutOfMemoryError ee) {
 422             error("main.out.of.memory");
 423             result = SYSERR;
 424             dumpStack(ee);
 425         } catch (ClientCodeException e) {
 426             // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
 427             throw e;
 428         } catch (Error | Exception ee) {
 429             error("main.fatal.error", ee);
 430             reportInternalError(ee);
 431             result = ABNORMAL;
 432         } finally {
 433             if (fileManager != null
 434                     && fileManager instanceof BaseFileManager
 435                     && ((BaseFileManager) fileManager).autoClose) {
 436                 try {
 437                     fileManager.close();
 438                 } catch (IOException ignore) {}
 439             }
 440             boolean haveErrorWarnings = messager.hasErrors()
 441                     || (rejectWarnings && messager.hasWarnings());
 442             if (!result.isOK() && !haveErrorWarnings) {
 443                 // the doclet failed, but nothing reported, flag it!.
 444                 error("main.unknown.error");
 445             }
 446             if (haveErrorWarnings && result.isOK()) {
 447                 result = ERROR;
 448             }
 449             messager.printErrorWarningCounts();
 450             messager.flush();
 451         }
 452         return result;
 453     }
 454 
 455     private void reportInternalError(Throwable t) {
 456         messager.printErrorUsingKey("doclet.internal.report.bug");
 457         dumpStack(true, t);
 458     }
 459 
 460     private void dumpStack(Throwable t) {
 461         dumpStack(false, t);
 462     }
 463 
 464     private void dumpStack(boolean enabled, Throwable t) {
 465         if (t != null && (enabled || dumpOnError)) {
 466             t.printStackTrace(System.err);
 467         }
 468     }
 469 
 470     /**
 471      * Main program - internal
 472      */
 473     @SuppressWarnings("unchecked")
 474     private Result parseAndExecute(List<String> argList, Iterable<? extends JavaFileObject> fileObjects)
 475             throws ToolException, OptionException, com.sun.tools.javac.main.Option.InvalidValueException {
 476         long tm = System.currentTimeMillis();
 477 
 478         List<String> javaNames = new ArrayList<>();
 479 
 480         compOpts = Options.instance(context);
 481 
 482         // Make sure no obsolete source/target messages are reported
 483         try {
 484             com.sun.tools.javac.main.Option.XLINT_CUSTOM.process(getOptionHelper(), "-Xlint:-options");
 485         } catch (com.sun.tools.javac.main.Option.InvalidValueException ignore) {
 486         }
 487 
 488         Arguments arguments = Arguments.instance(context);
 489         arguments.init(ProgramName);
 490         arguments.allowEmpty();
 491 
 492         doclet.init(locale, messager);
 493         parseArgs(argList, javaNames);
 494 
 495         if (!arguments.handleReleaseOptions(extra -> true)) {
 496             // Arguments does not always increase the error count in the
 497             // case of errors, so increment the error count only if it has
 498             // not been updated previously, preventing complaints by callers
 499             if (!messager.hasErrors() && !messager.hasWarnings())
 500                 messager.nerrors++;
 501             return CMDERR;
 502         }
 503 
 504         if (!arguments.validate()) {
 505             // Arguments does not always increase the error count in the
 506             // case of errors, so increment the error count only if it has
 507             // not been updated previously, preventing complaints by callers
 508             if (!messager.hasErrors() && !messager.hasWarnings())
 509                 messager.nerrors++;
 510             return CMDERR;
 511         }
 512 
 513         if (fileManager instanceof BaseFileManager) {
 514             ((BaseFileManager) fileManager).handleOptions(fileManagerOpts);
 515         }
 516 
 517         if (fileManager.isSupportedOption(MULTIRELEASE.primaryName) == 1) {
 518             Target target = Target.instance(context);
 519             List<String> list = List.of(target.multiReleaseValue());
 520             fileManager.handleOption(MULTIRELEASE.primaryName, list.iterator());
 521         }
 522         compOpts.notifyListeners();
 523         List<String> modules = (List<String>) jdtoolOpts.computeIfAbsent(ToolOption.MODULE,
 524                 s -> Collections.EMPTY_LIST);
 525 
 526         if (modules.isEmpty()) {
 527             List<String> subpkgs = (List<String>) jdtoolOpts.computeIfAbsent(ToolOption.SUBPACKAGES,
 528                     s -> Collections.EMPTY_LIST);
 529             if (subpkgs.isEmpty()) {
 530                 if (javaNames.isEmpty() && isEmpty(fileObjects)) {
 531                     String text = messager.getText("main.No_modules_packages_or_classes_specified");
 532                     throw new ToolException(CMDERR, text);
 533                 }
 534             }
 535         }
 536 
 537         JavadocTool comp = JavadocTool.make0(context);
 538         if (comp == null) return ABNORMAL;
 539 
 540         DocletEnvironment docEnv = comp.getEnvironment(jdtoolOpts,
 541                 javaNames,
 542                 fileObjects);
 543 
 544         // release resources
 545         comp = null;
 546 
 547         if (breakiterator || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
 548             JavacTrees trees = JavacTrees.instance(context);
 549             trees.setBreakIterator(BreakIterator.getSentenceInstance(locale));
 550         }
 551         // pass off control to the doclet
 552         Result returnStatus = docEnv != null && doclet.run(docEnv)
 553                 ? OK
 554                 : ERROR;
 555 
 556         // We're done.
 557         if (compOpts.get("-verbose") != null) {
 558             tm = System.currentTimeMillis() - tm;
 559             messager.notice("main.done_in", Long.toString(tm));
 560         }
 561 
 562         return returnStatus;
 563     }
 564 
 565     boolean matches(List<String> names, String arg) {
 566         for (String name : names) {
 567             if (StringUtils.toLowerCase(name).equals(StringUtils.toLowerCase(arg)))
 568                 return true;
 569         }
 570         return false;
 571     }
 572 
 573     boolean matches(Doclet.Option option, String arg) {
 574         if (matches(option.getNames(), arg))
 575              return true;
 576         int sep = arg.indexOf(':');
 577         String targ = arg.substring(0, sep + 1);
 578         return matches(option.getNames(), targ);
 579     }
 580 
 581     Set<? extends Doclet.Option> docletOptions = null;
 582     int handleDocletOptions(int idx, List<String> args, boolean isToolOption)
 583             throws OptionException {
 584         if (docletOptions == null) {
 585             docletOptions = doclet.getSupportedOptions();
 586         }
 587         String arg = args.get(idx);
 588         String argBase, argVal;
 589         if (arg.startsWith("--") && arg.contains("=")) {
 590             int sep = arg.indexOf("=");
 591             argBase = arg.substring(0, sep);
 592             argVal = arg.substring(sep + 1);
 593         } else {
 594             argBase = arg;
 595             argVal = null;
 596         }
 597         String text = null;
 598         for (Doclet.Option opt : docletOptions) {
 599             if (matches(opt, argBase)) {
 600                 if (argVal != null) {
 601                     switch (opt.getArgumentCount()) {
 602                         case 0:
 603                             text = messager.getText("main.unnecessary_arg_provided", argBase);
 604                             throw new OptionException(ERROR, this::usage, text);
 605                         case 1:
 606                             opt.process(arg, Arrays.asList(argVal));
 607                             break;
 608                         default:
 609                             text = messager.getText("main.only_one_argument_with_equals", argBase);
 610                             throw new OptionException(ERROR, this::usage, text);
 611                     }
 612                 } else {
 613                     if (args.size() - idx -1 < opt.getArgumentCount()) {
 614                         text = messager.getText("main.requires_argument", arg);
 615                         throw new OptionException(ERROR, this::usage, text);
 616                     }
 617                     opt.process(arg, args.subList(idx + 1, args.size()));
 618                     idx += opt.getArgumentCount();
 619                 }
 620                 return idx;
 621             }
 622         }
 623         // check if arg is accepted by the tool before emitting error
 624         if (!isToolOption) {
 625             text = messager.getText("main.invalid_flag", arg);
 626             throw new OptionException(ERROR, this::usage, text);
 627         }
 628         return idx;
 629     }
 630 
 631     private Doclet preprocess(JavaFileManager jfm,
 632             List<String> argv) throws ToolException, OptionException {
 633         // doclet specifying arguments
 634         String userDocletPath = null;
 635         String userDocletName = null;
 636 
 637         // taglet specifying arguments, since tagletpath is a doclet
 638         // functionality, assume they are repeated and inspect all.
 639         List<File> userTagletPath = new ArrayList<>();
 640         List<String> userTagletNames = new ArrayList<>();
 641 
 642         // Step 1: loop through the args, set locale early on, if found.
 643         for (int i = 0 ; i < argv.size() ; i++) {
 644             String arg = argv.get(i);
 645             if (arg.equals(ToolOption.DUMPONERROR.primaryName)) {
 646                 dumpOnError = true;
 647             } else if (arg.equals(ToolOption.LOCALE.primaryName)) {
 648                 checkOneArg(argv, i++);
 649                 String lname = argv.get(i);
 650                 locale = getLocale(lname);
 651             } else if (arg.equals(ToolOption.DOCLET.primaryName)) {
 652                 checkOneArg(argv, i++);
 653                 if (userDocletName != null) {
 654                     if (apiMode) {
 655                         throw new IllegalArgumentException("More than one doclet specified (" +
 656                                 userDocletName + " and " + argv.get(i) + ").");
 657                     }
 658                     String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",
 659                             userDocletName, argv.get(i));
 660                     throw new ToolException(CMDERR, text);
 661                 }
 662                 if (docletName != null) {
 663                     if (apiMode) {
 664                         throw new IllegalArgumentException("More than one doclet specified (" +
 665                                 docletName + " and " + argv.get(i) + ").");
 666                     }
 667                     String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",
 668                             docletName, argv.get(i));
 669                     throw new ToolException(CMDERR, text);
 670                 }
 671                 userDocletName = argv.get(i);
 672             } else if (arg.equals(ToolOption.DOCLETPATH.primaryName)) {
 673                 checkOneArg(argv, i++);
 674                 if (userDocletPath == null) {
 675                     userDocletPath = argv.get(i);
 676                 } else {
 677                     userDocletPath += File.pathSeparator + argv.get(i);
 678                 }
 679             } else if ("-taglet".equals(arg)) {
 680                 userTagletNames.add(argv.get(i + 1));
 681             } else if ("-tagletpath".equals(arg)) {
 682                 for (String pathname : argv.get(i + 1).split(File.pathSeparator)) {
 683                     userTagletPath.add(new File(pathname));
 684                 }
 685             }
 686         }
 687 
 688 
 689         // Step 3: doclet name specified ? if so find a ClassLoader,
 690         // and load it.
 691         if(docletClass == null) {
 692             if (userDocletName != null) {
 693                 ClassLoader cl = classLoader;
 694                 if (cl == null) {
 695                     if (!fileManager.hasLocation(DOCLET_PATH)) {
 696                         List<File> paths = new ArrayList<>();
 697                         if (userDocletPath != null) {
 698                             for (String pathname : userDocletPath.split(File.pathSeparator)) {
 699                                 paths.add(new File(pathname));
 700                             }
 701                         }
 702                         try {
 703                             ((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);
 704                         } catch (IOException ioe) {
 705                             if (apiMode) {
 706                                 throw new IllegalArgumentException("Could not set location for " +
 707                                         userDocletPath, ioe);
 708                             }
 709                             String text = messager.getText("main.doclet_could_not_set_location",
 710                                     userDocletPath);
 711                             throw new ToolException(CMDERR, text, ioe);
 712                         }
 713                     }
 714                     cl = fileManager.getClassLoader(DOCLET_PATH);
 715                     if (cl == null) {
 716                         // despite doclet specified on cmdline no classloader found!
 717                         if (apiMode) {
 718                             throw new IllegalArgumentException("Could not obtain classloader to load "
 719 
 720                                     + userDocletPath);
 721                         }
 722                         String text = messager.getText("main.doclet_no_classloader_found",
 723                                 userDocletName);
 724                         throw new ToolException(CMDERR, text);
 725                     }
 726                 }
 727                 docletClass = loadDocletClass(userDocletName, cl);
 728             } else if (docletName != null){
 729                 docletClass = loadDocletClass(docletName, getClass().getClassLoader());
 730             } else {
 731                 docletClass = StdDoclet;
 732             }
 733         }
 734 
 735         if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) {
 736             // no need to dispatch to old, safe to init now
 737             initMessager();
 738             messager.setLocale(locale);
 739             try {
 740                 Object o = docletClass.getConstructor().newInstance();
 741                 doclet = (Doclet) o;
 742             } catch (ReflectiveOperationException exc) {
 743                 if (apiMode) {
 744                     throw new ClientCodeException(exc);
 745                 }
 746                 String text = messager.getText("main.could_not_instantiate_class", docletClass.getName());
 747                 throw new ToolException(ERROR, text);
 748             }
 749         } else {
 750             String text = messager.getText("main.not_a_doclet", docletClass.getName());
 751             throw new ToolException(ERROR, text);
 752         }
 753         return doclet;
 754     }
 755 
 756     private Class<?> loadDocletClass(String docletName, ClassLoader classLoader) throws ToolException {
 757         try {
 758             return classLoader == null ? Class.forName(docletName) : classLoader.loadClass(docletName);
 759         } catch (ClassNotFoundException cnfe) {
 760             if (apiMode) {
 761                 throw new IllegalArgumentException("Cannot find doclet class " + docletName);
 762             }
 763             String text = messager.getText("main.doclet_class_not_found", docletName);
 764             throw new ToolException(CMDERR, text, cnfe);
 765         }
 766     }
 767 
 768     private void parseArgs(List<String> args, List<String> javaNames) throws ToolException,
 769             OptionException, com.sun.tools.javac.main.Option.InvalidValueException {
 770         for (int i = 0 ; i < args.size() ; i++) {
 771             String arg = args.get(i);
 772             ToolOption o = ToolOption.get(arg);
 773             if (o != null) {
 774                 // handle a doclet argument that may be needed however
 775                 // don't increment the index, and allow the tool to consume args
 776                 handleDocletOptions(i, args, true);
 777                 if (o.hasArg) {
 778                     if (arg.startsWith("--") && arg.contains("=")) {
 779                         o.process(this, arg.substring(arg.indexOf('=') + 1));
 780                     } else {
 781                         checkOneArg(args, i++);
 782                         o.process(this, args.get(i));
 783                     }
 784                 } else if (o.hasSuffix) {
 785                     o.process(this, arg);
 786                 } else {
 787                     o.process(this);
 788                 }
 789             } else if (arg.startsWith("-XD")) {
 790                 // hidden javac options
 791                 String s = arg.substring("-XD".length());
 792                 int eq = s.indexOf('=');
 793                 String key = (eq < 0) ? s : s.substring(0, eq);
 794                 String value = (eq < 0) ? s : s.substring(eq+1);
 795                 compOpts.put(key, value);
 796             } else if (arg.startsWith("-")) {
 797                 i = handleDocletOptions(i, args, false);
 798             } else {
 799                 javaNames.add(arg);
 800             }
 801         }
 802     }
 803 
 804     private <T> boolean isEmpty(Iterable<T> iter) {
 805         return !iter.iterator().hasNext();
 806     }
 807 
 808     /**
 809      * Check the one arg option.
 810      * Error and exit if one argument is not provided.
 811      */
 812     private void checkOneArg(List<String> args, int index) throws OptionException {
 813         if ((index + 1) >= args.size() || args.get(index + 1).startsWith("-d")) {
 814             String text = messager.getText("main.requires_argument", args.get(index));
 815             throw new OptionException(CMDERR, this::usage, text);
 816         }
 817     }
 818 
 819     void error(String key, Object... args) {
 820         messager.printErrorUsingKey(key, args);
 821     }
 822 
 823     void warn(String key, Object... args)  {
 824         messager.printWarningUsingKey(key, args);
 825     }
 826 
 827     /**
 828      * Get the locale if specified on the command line
 829      * else return null and if locale option is not used
 830      * then return default locale.
 831      */
 832     private Locale getLocale(String localeName) throws ToolException {
 833         try {
 834             // Tolerate, at least for a while, the older syntax accepted by javadoc,
 835             // using _ as the separator


 842             throw new ToolException(CMDERR, text);
 843         }
 844     }
 845 
 846     /**
 847      * Search the locale for specified language, specified country and
 848      * specified variant.
 849      */
 850     private Locale searchLocale(String language, String country,
 851                                 String variant) {
 852         for (Locale loc : Locale.getAvailableLocales()) {
 853             if (loc.getLanguage().equals(language) &&
 854                 (country == null || loc.getCountry().equals(country)) &&
 855                 (variant == null || loc.getVariant().equals(variant))) {
 856                 return loc;
 857             }
 858         }
 859         return null;
 860     }
 861 
 862     @Override
 863     OptionHelper getOptionHelper() {
 864         return new GrumpyHelper(messager) {
 865             @Override
 866             public String get(com.sun.tools.javac.main.Option option) {
 867                 return compOpts.get(option);
 868             }
 869 
 870             @Override
 871             public void put(String name, String value) {
 872                 compOpts.put(name, value);
 873             }
 874 
 875             @Override
 876             public void remove(String name) {
 877                 compOpts.remove(name);
 878             }
 879 
 880             @Override
 881             public boolean handleFileManagerOption(com.sun.tools.javac.main.Option option, String value) {
 882                 fileManagerOpts.put(option, value);
 883                 return true;
 884             }
 885         };
 886     }
 887 
 888     @Override
 889     String getLocalizedMessage(String msg, Object... args) {
 890         return messager.getText(msg, args);
 891     }
 892 }


  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 
  26 package jdk.javadoc.internal.tool;
  27 
  28 import java.io.File;
  29 import java.io.IOException;
  30 import java.io.PrintWriter;

  31 import java.text.BreakIterator;
  32 import java.text.Collator;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;

  35 import java.util.Collections;
  36 import java.util.Comparator;
  37 import java.util.IllformedLocaleException;
  38 import java.util.List;
  39 import java.util.Locale;
  40 import java.util.MissingResourceException;
  41 import java.util.Objects;
  42 import java.util.ResourceBundle;
  43 import java.util.Set;
  44 import java.util.stream.Collectors;

  45 
  46 import javax.tools.JavaFileManager;
  47 import javax.tools.JavaFileObject;
  48 import javax.tools.StandardJavaFileManager;

  49 
  50 import com.sun.tools.javac.api.JavacTrees;
  51 import com.sun.tools.javac.file.BaseFileManager;
  52 import com.sun.tools.javac.file.JavacFileManager;
  53 import com.sun.tools.javac.jvm.Target;
  54 import com.sun.tools.javac.main.Arguments;
  55 import com.sun.tools.javac.main.CommandLine;




  56 import com.sun.tools.javac.util.ClientCodeException;
  57 import com.sun.tools.javac.util.Context;
  58 import com.sun.tools.javac.util.Log;


  59 import com.sun.tools.javac.util.StringUtils;
  60 
  61 import jdk.javadoc.doclet.Doclet;
  62 import jdk.javadoc.doclet.Doclet.Option;
  63 import jdk.javadoc.doclet.DocletEnvironment;
  64 import jdk.javadoc.doclet.StandardDoclet;
  65 import jdk.javadoc.internal.tool.Main.Result;
  66 import jdk.javadoc.internal.tool.ToolOptions.ToolOption;
  67 
  68 import static javax.tools.DocumentationTool.Location.*;
  69 
  70 import static com.sun.tools.javac.main.Option.*;
  71 import static jdk.javadoc.internal.tool.Main.Result.*;
  72 
  73 /**
  74  * Main program of Javadoc.
  75  * Previously named "Main".
  76  *
  77  *  <p><b>This is NOT part of any supported API.
  78  *  If you write code that depends on this, you do so at your own risk.
  79  *  This code and its internal interfaces are subject to change or
  80  *  deletion without notice.</b>
  81  */
  82 public class Start {
  83 


  84     /** Context for this invocation. */
  85     private final Context context;
  86 
  87     private static final String ProgramName = "javadoc";
  88 
  89     private Messager messager;
  90 
  91     private final String docletName;
  92 
  93     private final ClassLoader classLoader;
  94 
  95     private Class<?> docletClass;
  96 
  97     private Doclet doclet;
  98 
  99     // used to determine the locale for the messager
 100     private Locale locale;
 101 
 102 
 103     /**
 104      * In API mode, exceptions thrown while calling the doclet are
 105      * propagated using ClientCodeException.
 106      */
 107     private boolean apiMode;
 108 
 109     private JavaFileManager fileManager;
 110 
 111     private final ToolOptions options;
 112 
 113     Start() {
 114         this(null, null, null, null, null, null);
 115     }
 116 
 117     Start(PrintWriter outWriter, PrintWriter errWriter) {
 118         this(null, null, outWriter, errWriter, null, null);
 119     }
 120 
 121     Start(Context context, String programName,
 122             PrintWriter outWriter, PrintWriter errWriter,
 123             String docletName, ClassLoader classLoader) {
 124         this.context = context == null ? new Context() : context;
 125         String pname = programName == null ? ProgramName : programName;
 126         this.messager = (outWriter == null && errWriter == null)
 127                 ? new Messager(this.context, pname)
 128                 : new Messager(this.context, pname, outWriter, errWriter);
 129         this.docletName = docletName;
 130         this.classLoader = classLoader;
 131         this.docletClass = null;
 132         this.locale = Locale.getDefault();
 133 
 134         options = getToolOptions();
 135     }
 136 
 137     public Start(Context context) {
 138         this.docletClass = null;
 139         this.context = Objects.requireNonNull(context);
 140         this.apiMode = true;
 141         this.docletName = null;
 142         this.classLoader = null;
 143         this.locale = Locale.getDefault();

 144 




 145         Log log = context.get(Log.logKey);
 146         if (log instanceof Messager) {
 147             messager = (Messager) log;
 148         } else {
 149             PrintWriter out = context.get(Log.errKey);
 150             messager = (out == null)
 151                     ? new Messager(context, ProgramName)
 152                     : new Messager(context, ProgramName, out, out);
 153         }
 154 
 155         options = getToolOptions();
 156     }
 157 
 158     private ToolOptions getToolOptions() {
 159         ToolOptions.ShowHelper helper =  new ToolOptions.ShowHelper() {

 160             @Override
 161             public void usage() {
 162                 showUsage("main.usage", OptionKind.STANDARD, "main.usage.foot");
 163             }
 164 
 165             @Override
 166             public void Xusage() {
 167                 showUsage("main.Xusage", OptionKind.EXTENDED, "main.Xusage.foot");
 168             }
 169 
 170             @Override
 171             public void version() {
 172                 showVersion("javadoc.version", "release");
 173             }
 174 
 175             @Override
 176             public void fullVersion() {
 177                 showVersion("javadoc.fullversion", "full");
 178             }
 179         };
 180         return new ToolOptions(context, messager, helper);
 181     }
 182 
 183     private void showUsage() {
 184         showUsage("main.usage", OptionKind.STANDARD, "main.usage.foot");
 185     }
 186 
 187     private void showUsage(String headerKey, OptionKind kind, String footerKey) {
 188         messager.notice(headerKey);
 189         showToolOptions(kind);
 190 
 191         // let doclet print usage information
 192         if (docletClass != null) {
 193             String name = doclet.getName();
 194             messager.notice("main.doclet.usage.header", name);
 195             showDocletOptions(kind == OptionKind.EXTENDED
 196                     ? Option.Kind.EXTENDED
 197                     : Option.Kind.STANDARD);
 198         }
 199         if (footerKey != null)
 200             messager.notice(footerKey);
 201     }
 202 
 203     private static final String versionRBName = "jdk.javadoc.internal.tool.resources.version";
 204     private static ResourceBundle versionRB;
 205 
 206     private void showVersion(String labelKey, String versionKey) {
 207         messager.notice(labelKey, messager.programName, getVersion(versionKey));
 208     }
 209 
 210     private static String getVersion(String key) {
 211         if (versionRB == null) {
 212             try {
 213                 versionRB = ResourceBundle.getBundle(versionRBName);
 214             } catch (MissingResourceException e) {
 215                 return Log.getLocalizedString("version.not.available");
 216             }
 217         }
 218         try {
 219             return versionRB.getString(key);
 220         } catch (MissingResourceException e) {
 221             return Log.getLocalizedString("version.not.available");
 222         }
 223     }
 224 
 225     private void showToolOptions(OptionKind kind) {
 226         Comparator<ToolOption> comp = new Comparator<ToolOption>() {
 227             final Collator collator = Collator.getInstance(Locale.US);
 228             { collator.setStrength(Collator.PRIMARY); }
 229 
 230             @Override
 231             public int compare(ToolOption o1, ToolOption o2) {
 232                 return collator.compare(o1.primaryName, o2.primaryName);
 233             }
 234         };
 235 
 236         options.getSupportedOptions().stream()
 237                     .filter(opt -> opt.kind == kind)
 238                     .sorted(comp)
 239                     .forEach(this::showToolOption);
 240     }
 241 
 242     private void showToolOption(ToolOption option) {
 243         List<String> names = option.getNames();
 244         String primaryName = option.primaryName;
 245         String parameters;
 246         if (option.hasArg || primaryName.endsWith(":")) {
 247             String sep = primaryName.equals(ToolOptions.J) || primaryName.endsWith(":") ? "" : " ";
 248             parameters = sep + option.getParameters(messager);
 249         } else {
 250             parameters = "";
 251         }
 252         String description = option.getDescription(messager);
 253         showOption(names, parameters, description);
 254     }
 255 
 256     private void showDocletOptions(Option.Kind kind) {
 257         Comparator<Doclet.Option> comp = new Comparator<Doclet.Option>() {
 258             final Collator collator = Collator.getInstance(Locale.US);
 259             { collator.setStrength(Collator.PRIMARY); }
 260 
 261             @Override
 262             public int compare(Doclet.Option o1, Doclet.Option o2) {
 263                 return collator.compare(o1.getNames().get(0), o2.getNames().get(0));
 264             }
 265         };
 266 
 267         doclet.getSupportedOptions().stream()
 268                 .filter(opt -> opt.getKind() == kind)
 269                 .sorted(comp)
 270                 .forEach(this::showDocletOption);
 271     }
 272 
 273     private void showDocletOption(Doclet.Option option) {
 274         List<String> names = option.getNames();
 275         String parameters;
 276         String primaryName = names.get(0);
 277         if (option.getArgumentCount() > 0 || primaryName.endsWith(":")) {
 278             String sep = primaryName.endsWith(":") ? "" : " ";
 279             parameters = sep + option.getParameters();
 280         } else {
 281             parameters = "";
 282         }
 283         String description = option.getDescription();
 284         showOption(names, parameters, description);
 285     }
 286 
 287     // The following constants are intended to format the output to
 288     // be similar to that of the java launcher: i.e. "java -help".
 289 
 290     /** The indent for the option synopsis. */
 291     private static final String SMALL_INDENT = "    ";
 292     /** The automatic indent for the description. */
 293     private static final String LARGE_INDENT = "                  ";
 294     /** The space allowed for the synopsis, if the description is to be shown on the same line. */
 295     private static final int DEFAULT_SYNOPSIS_WIDTH = 13;
 296     /** The nominal maximum line length, when seeing if text will fit on a line. */
 297     private static final int DEFAULT_MAX_LINE_LENGTH = 80;
 298     /** The format for a single-line help entry. */
 299     private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s";
 300 
 301     void showOption(List<String> names, String parameters, String description) {
 302         String synopses = names.stream()
 303                 .map(s -> s + parameters)
 304                 .collect(Collectors.joining(", "));
 305         // If option synopses and description fit on a single line of reasonable length,
 306         // display using COMPACT_FORMAT
 307         if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH
 308                 && !description.contains("\n")
 309                 && (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + description.length() <= DEFAULT_MAX_LINE_LENGTH)) {
 310             messager.printNotice(String.format(COMPACT_FORMAT, synopses, description));
 311             return;
 312         }
 313 
 314         // If option synopses fit on a single line of reasonable length, show that;
 315         // otherwise, show 1 per line
 316         if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) {
 317             messager.printNotice(SMALL_INDENT + synopses);
 318         } else {
 319             for (String name: names) {
 320                 messager.printNotice(SMALL_INDENT + name + parameters);
 321             }


 422         } catch (OutOfMemoryError ee) {
 423             error("main.out.of.memory");
 424             result = SYSERR;
 425             dumpStack(ee);
 426         } catch (ClientCodeException e) {
 427             // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
 428             throw e;
 429         } catch (Error | Exception ee) {
 430             error("main.fatal.error", ee);
 431             reportInternalError(ee);
 432             result = ABNORMAL;
 433         } finally {
 434             if (fileManager != null
 435                     && fileManager instanceof BaseFileManager
 436                     && ((BaseFileManager) fileManager).autoClose) {
 437                 try {
 438                     fileManager.close();
 439                 } catch (IOException ignore) {}
 440             }
 441             boolean haveErrorWarnings = messager.hasErrors()
 442                     || (this.options.rejectWarnings() && messager.hasWarnings());
 443             if (!result.isOK() && !haveErrorWarnings) {
 444                 // the doclet failed, but nothing reported, flag it!.
 445                 error("main.unknown.error");
 446             }
 447             if (haveErrorWarnings && result.isOK()) {
 448                 result = ERROR;
 449             }
 450             messager.printErrorWarningCounts();
 451             messager.flush();
 452         }
 453         return result;
 454     }
 455 
 456     private void reportInternalError(Throwable t) {
 457         messager.printErrorUsingKey("doclet.internal.report.bug");
 458         dumpStack(true, t);
 459     }
 460 
 461     private void dumpStack(Throwable t) {
 462         dumpStack(false, t);
 463     }
 464 
 465     private void dumpStack(boolean enabled, Throwable t) {
 466         if (t != null && (enabled || options.dumpOnError())) {
 467             t.printStackTrace(System.err);
 468         }
 469     }
 470 
 471     /**
 472      * Main program - internal
 473      */
 474     @SuppressWarnings("unchecked")
 475     private Result parseAndExecute(List<String> argList, Iterable<? extends JavaFileObject> fileObjects)
 476             throws ToolException, OptionException, com.sun.tools.javac.main.Option.InvalidValueException {
 477         long tm = System.currentTimeMillis();
 478 
 479         List<String> javaNames = new ArrayList<>();
 480 


 481         // Make sure no obsolete source/target messages are reported
 482         try {
 483             options.processCompilerOption(com.sun.tools.javac.main.Option.XLINT_CUSTOM, "-Xlint:-options");
 484         } catch (com.sun.tools.javac.main.Option.InvalidValueException ignore) {
 485         }
 486 
 487         Arguments arguments = Arguments.instance(context);
 488         arguments.init(ProgramName);
 489         arguments.allowEmpty();
 490 
 491         doclet.init(locale, messager);
 492         parseArgs(argList, javaNames);
 493 
 494         if (!arguments.handleReleaseOptions(extra -> true)) {
 495             // Arguments does not always increase the error count in the
 496             // case of errors, so increment the error count only if it has
 497             // not been updated previously, preventing complaints by callers
 498             if (!messager.hasErrors() && !messager.hasWarnings())
 499                 messager.nerrors++;
 500             return CMDERR;
 501         }
 502 
 503         if (!arguments.validate()) {
 504             // Arguments does not always increase the error count in the
 505             // case of errors, so increment the error count only if it has
 506             // not been updated previously, preventing complaints by callers
 507             if (!messager.hasErrors() && !messager.hasWarnings())
 508                 messager.nerrors++;
 509             return CMDERR;
 510         }
 511 
 512         if (fileManager instanceof BaseFileManager) {
 513             ((BaseFileManager) fileManager).handleOptions(options.fileManagerOptions());
 514         }
 515 
 516         if (fileManager.isSupportedOption(MULTIRELEASE.primaryName) == 1) {
 517             Target target = Target.instance(context);
 518             List<String> list = List.of(target.multiReleaseValue());
 519             fileManager.handleOption(MULTIRELEASE.primaryName, list.iterator());
 520         }
 521         options.compilerOptions().notifyListeners();
 522 
 523         if (options.modules().isEmpty()) {
 524             if (options.subpackages().isEmpty()) {




 525                 if (javaNames.isEmpty() && isEmpty(fileObjects)) {
 526                     String text = messager.getText("main.No_modules_packages_or_classes_specified");
 527                     throw new ToolException(CMDERR, text);
 528                 }
 529             }
 530         }
 531 
 532         JavadocTool comp = JavadocTool.make0(context);
 533         if (comp == null) return ABNORMAL;
 534 
 535         DocletEnvironment docEnv = comp.getEnvironment(options, javaNames, fileObjects);


 536 
 537         // release resources
 538         comp = null;
 539 
 540         if (options.breakIterator() || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
 541             JavacTrees trees = JavacTrees.instance(context);
 542             trees.setBreakIterator(BreakIterator.getSentenceInstance(locale));
 543         }
 544         // pass off control to the doclet
 545         Result returnStatus = docEnv != null && doclet.run(docEnv)
 546                 ? OK
 547                 : ERROR;
 548 
 549         // We're done.
 550         if (options.verbose()) {
 551             tm = System.currentTimeMillis() - tm;
 552             messager.notice("main.done_in", Long.toString(tm));
 553         }
 554 
 555         return returnStatus;
 556     }
 557 
 558     boolean matches(List<String> names, String arg) {
 559         for (String name : names) {
 560             if (StringUtils.toLowerCase(name).equals(StringUtils.toLowerCase(arg)))
 561                 return true;
 562         }
 563         return false;
 564     }
 565 
 566     boolean matches(Doclet.Option option, String arg) {
 567         if (matches(option.getNames(), arg))
 568              return true;
 569         int sep = arg.indexOf(':');
 570         String targ = arg.substring(0, sep + 1);
 571         return matches(option.getNames(), targ);
 572     }
 573 
 574     private Set<? extends Doclet.Option> docletOptions = null;
 575     int handleDocletOption(int idx, List<String> args, boolean isToolOption)
 576             throws OptionException {
 577         if (docletOptions == null) {
 578             docletOptions = doclet.getSupportedOptions();
 579         }
 580         String arg = args.get(idx);
 581         String argBase, argVal;
 582         if (arg.startsWith("--") && arg.contains("=")) {
 583             int sep = arg.indexOf("=");
 584             argBase = arg.substring(0, sep);
 585             argVal = arg.substring(sep + 1);
 586         } else {
 587             argBase = arg;
 588             argVal = null;
 589         }
 590         String text = null;
 591         for (Doclet.Option opt : docletOptions) {
 592             if (matches(opt, argBase)) {
 593                 if (argVal != null) {
 594                     switch (opt.getArgumentCount()) {
 595                         case 0:
 596                             text = messager.getText("main.unnecessary_arg_provided", argBase);
 597                             throw new OptionException(ERROR, this::showUsage, text);
 598                         case 1:
 599                             opt.process(arg, Arrays.asList(argVal));
 600                             break;
 601                         default:
 602                             text = messager.getText("main.only_one_argument_with_equals", argBase);
 603                             throw new OptionException(ERROR, this::showUsage, text);
 604                     }
 605                 } else {
 606                     if (args.size() - idx -1 < opt.getArgumentCount()) {
 607                         text = messager.getText("main.requires_argument", arg);
 608                         throw new OptionException(ERROR, this::showUsage, text);
 609                     }
 610                     opt.process(arg, args.subList(idx + 1, args.size()));
 611                     idx += opt.getArgumentCount();
 612                 }
 613                 return idx;
 614             }
 615         }
 616         // check if arg is accepted by the tool before emitting error
 617         if (!isToolOption) {
 618             text = messager.getText("main.invalid_flag", arg);
 619             throw new OptionException(ERROR, this::showUsage, text);
 620         }
 621         return idx;
 622     }
 623 
 624     private Doclet preprocess(JavaFileManager jfm, List<String> argv)
 625             throws ToolException, OptionException {
 626         // doclet specifying arguments
 627         String userDocletPath = null;
 628         String userDocletName = null;
 629 
 630         // taglet specifying arguments, since tagletpath is a doclet
 631         // functionality, assume they are repeated and inspect all.
 632         List<File> userTagletPath = new ArrayList<>();
 633         List<String> userTagletNames = new ArrayList<>();
 634 
 635         // Step 1: loop through the args, set locale early on, if found.
 636         for (int i = 0 ; i < argv.size() ; i++) {
 637             String arg = argv.get(i);
 638             if (arg.equals(ToolOptions.DUMP_ON_ERROR)) {
 639                 options.setDumpOnError(true);
 640             } else if (arg.equals(ToolOptions.LOCALE)) {
 641                 checkOneArg(argv, i++);
 642                 String lname = argv.get(i);
 643                 locale = getLocale(lname);
 644             } else if (arg.equals(ToolOptions.DOCLET)) {
 645                 checkOneArg(argv, i++);
 646                 if (userDocletName != null) {
 647                     if (apiMode) {
 648                         throw new IllegalArgumentException("More than one doclet specified (" +
 649                                 userDocletName + " and " + argv.get(i) + ").");
 650                     }
 651                     String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",
 652                             userDocletName, argv.get(i));
 653                     throw new ToolException(CMDERR, text);
 654                 }
 655                 if (docletName != null) {
 656                     if (apiMode) {
 657                         throw new IllegalArgumentException("More than one doclet specified (" +
 658                                 docletName + " and " + argv.get(i) + ").");
 659                     }
 660                     String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",
 661                             docletName, argv.get(i));
 662                     throw new ToolException(CMDERR, text);
 663                 }
 664                 userDocletName = argv.get(i);
 665             } else if (arg.equals(ToolOptions.DOCLET_PATH)) {
 666                 checkOneArg(argv, i++);
 667                 if (userDocletPath == null) {
 668                     userDocletPath = argv.get(i);
 669                 } else {
 670                     userDocletPath += File.pathSeparator + argv.get(i);
 671                 }
 672             } else if ("-taglet".equals(arg)) {
 673                 userTagletNames.add(argv.get(i + 1));
 674             } else if ("-tagletpath".equals(arg)) {
 675                 for (String pathname : argv.get(i + 1).split(File.pathSeparator)) {
 676                     userTagletPath.add(new File(pathname));
 677                 }
 678             }
 679         }
 680 

 681         // Step 3: doclet name specified ? if so find a ClassLoader,
 682         // and load it.
 683         if (docletClass == null) {
 684             if (userDocletName != null) {
 685                 ClassLoader cl = classLoader;
 686                 if (cl == null) {
 687                     if (!fileManager.hasLocation(DOCLET_PATH)) {
 688                         List<File> paths = new ArrayList<>();
 689                         if (userDocletPath != null) {
 690                             for (String pathname : userDocletPath.split(File.pathSeparator)) {
 691                                 paths.add(new File(pathname));
 692                             }
 693                         }
 694                         try {
 695                             ((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);
 696                         } catch (IOException ioe) {
 697                             if (apiMode) {
 698                                 throw new IllegalArgumentException("Could not set location for " +
 699                                         userDocletPath, ioe);
 700                             }
 701                             String text = messager.getText("main.doclet_could_not_set_location",
 702                                     userDocletPath);
 703                             throw new ToolException(CMDERR, text, ioe);
 704                         }
 705                     }
 706                     cl = fileManager.getClassLoader(DOCLET_PATH);
 707                     if (cl == null) {
 708                         // despite doclet specified on cmdline no classloader found!
 709                         if (apiMode) {
 710                             throw new IllegalArgumentException("Could not obtain classloader to load "
 711 
 712                                     + userDocletPath);
 713                         }
 714                         String text = messager.getText("main.doclet_no_classloader_found",
 715                                 userDocletName);
 716                         throw new ToolException(CMDERR, text);
 717                     }
 718                 }
 719                 docletClass = loadDocletClass(userDocletName, cl);
 720             } else if (docletName != null){
 721                 docletClass = loadDocletClass(docletName, getClass().getClassLoader());
 722             } else {
 723                 docletClass = StandardDoclet.class;
 724             }
 725         }
 726 
 727         if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) {


 728             messager.setLocale(locale);
 729             try {
 730                 Object o = docletClass.getConstructor().newInstance();
 731                 doclet = (Doclet) o;
 732             } catch (ReflectiveOperationException exc) {
 733                 if (apiMode) {
 734                     throw new ClientCodeException(exc);
 735                 }
 736                 String text = messager.getText("main.could_not_instantiate_class", docletClass.getName());
 737                 throw new ToolException(ERROR, text);
 738             }
 739         } else {
 740             String text = messager.getText("main.not_a_doclet", docletClass.getName());
 741             throw new ToolException(ERROR, text);
 742         }
 743         return doclet;
 744     }
 745 
 746     private Class<?> loadDocletClass(String docletName, ClassLoader classLoader) throws ToolException {
 747         try {
 748             return classLoader == null ? Class.forName(docletName) : classLoader.loadClass(docletName);
 749         } catch (ClassNotFoundException cnfe) {
 750             if (apiMode) {
 751                 throw new IllegalArgumentException("Cannot find doclet class " + docletName);
 752             }
 753             String text = messager.getText("main.doclet_class_not_found", docletName);
 754             throw new ToolException(CMDERR, text, cnfe);
 755         }
 756     }
 757 
 758     private void parseArgs(List<String> args, List<String> javaNames) throws ToolException,
 759             OptionException, com.sun.tools.javac.main.Option.InvalidValueException {
 760         for (int i = 0 ; i < args.size() ; i++) {
 761             String arg = args.get(i);
 762             ToolOption o = options.getOption(arg);
 763             if (o != null) {
 764                 // handle a doclet argument that may be needed however
 765                 // don't increment the index, and allow the tool to consume args
 766                 handleDocletOption(i, args, true);
 767                 if (o.hasArg) {
 768                     if (arg.startsWith("--") && arg.contains("=")) {
 769                         o.process(arg.substring(arg.indexOf('=') + 1));
 770                     } else {
 771                         checkOneArg(args, i++);
 772                         o.process(args.get(i));
 773                     }
 774                 } else if (o.hasSuffix) {
 775                     o.process(arg);
 776                 } else {
 777                     o.process();
 778                 }
 779             } else if (arg.startsWith("-XD")) {
 780                 // hidden javac options
 781                 String s = arg.substring("-XD".length());
 782                 int eq = s.indexOf('=');
 783                 String key = (eq < 0) ? s : s.substring(0, eq);
 784                 String value = (eq < 0) ? s : s.substring(eq+1);
 785                 options.compilerOptions().put(key, value);
 786             } else if (arg.startsWith("-")) {
 787                 i = handleDocletOption(i, args, false);
 788             } else {
 789                 javaNames.add(arg);
 790             }
 791         }
 792     }
 793 
 794     private <T> boolean isEmpty(Iterable<T> iter) {
 795         return !iter.iterator().hasNext();
 796     }
 797 
 798     /**
 799      * Check the one arg option.
 800      * Error and exit if one argument is not provided.
 801      */
 802     private void checkOneArg(List<String> args, int index) throws OptionException {
 803         if ((index + 1) >= args.size() || args.get(index + 1).startsWith("-d")) {
 804             String text = messager.getText("main.requires_argument", args.get(index));
 805             throw new OptionException(CMDERR, this::showUsage, text);
 806         }
 807     }
 808 
 809     void error(String key, Object... args) {
 810         messager.printErrorUsingKey(key, args);
 811     }
 812 
 813     void warn(String key, Object... args)  {
 814         messager.printWarningUsingKey(key, args);
 815     }
 816 
 817     /**
 818      * Get the locale if specified on the command line
 819      * else return null and if locale option is not used
 820      * then return default locale.
 821      */
 822     private Locale getLocale(String localeName) throws ToolException {
 823         try {
 824             // Tolerate, at least for a while, the older syntax accepted by javadoc,
 825             // using _ as the separator


 832             throw new ToolException(CMDERR, text);
 833         }
 834     }
 835 
 836     /**
 837      * Search the locale for specified language, specified country and
 838      * specified variant.
 839      */
 840     private Locale searchLocale(String language, String country,
 841                                 String variant) {
 842         for (Locale loc : Locale.getAvailableLocales()) {
 843             if (loc.getLanguage().equals(language) &&
 844                 (country == null || loc.getCountry().equals(country)) &&
 845                 (variant == null || loc.getVariant().equals(variant))) {
 846                 return loc;
 847             }
 848         }
 849         return null;
 850     }
 851 

















 852 












 853 }
< prev index next >