Path: blob/master/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java
66655 views
/*1* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.javadoc.internal.tool;2627import java.io.File;28import java.io.IOException;29import java.io.PrintWriter;30import java.text.BreakIterator;31import java.text.Collator;32import java.util.ArrayList;33import java.util.Collections;34import java.util.Comparator;35import java.util.IllformedLocaleException;36import java.util.List;37import java.util.Locale;38import java.util.Objects;39import java.util.Set;40import java.util.function.Supplier;41import java.util.stream.Collectors;4243import javax.tools.JavaFileManager;44import javax.tools.JavaFileObject;45import javax.tools.StandardJavaFileManager;4647import com.sun.tools.javac.api.JavacTrees;48import com.sun.tools.javac.file.BaseFileManager;49import com.sun.tools.javac.file.JavacFileManager;50import com.sun.tools.javac.jvm.Target;51import com.sun.tools.javac.main.Arguments;52import com.sun.tools.javac.main.CommandLine;53import com.sun.tools.javac.util.ClientCodeException;54import com.sun.tools.javac.util.Context;55import com.sun.tools.javac.util.Log;56import com.sun.tools.javac.util.StringUtils;5758import jdk.javadoc.doclet.Doclet;59import jdk.javadoc.doclet.Doclet.Option;60import jdk.javadoc.doclet.DocletEnvironment;61import jdk.javadoc.doclet.StandardDoclet;62import jdk.javadoc.internal.Versions;63import jdk.javadoc.internal.tool.Main.Result;64import jdk.javadoc.internal.tool.ToolOptions.ToolOption;6566import static javax.tools.DocumentationTool.Location.*;6768import static jdk.javadoc.internal.tool.Main.Result.*;6970/**71* Main program of Javadoc.72* Previously named "Main".73*74* <p><b>This is NOT part of any supported API.75* If you write code that depends on this, you do so at your own risk.76* This code and its internal interfaces are subject to change or77* deletion without notice.</b>78*/79public class Start {8081/** Context for this invocation. */82private final Context context;8384private static final String ProgramName = "javadoc";8586private JavadocLog log;8788private final String docletName;8990private final ClassLoader classLoader;9192private Class<?> docletClass;9394private Doclet doclet;9596// used to determine the locale for the log97private Locale locale;9899/**100* In API mode, exceptions thrown while calling the doclet are101* propagated using ClientCodeException.102*/103private boolean apiMode;104105private JavaFileManager fileManager;106107private final ToolOptions options;108109Start() {110this(null, null, null, null, null, null);111}112113Start(PrintWriter outWriter, PrintWriter errWriter) {114this(null, null, outWriter, errWriter, null, null);115}116117Start(Context context, String programName,118PrintWriter outWriter, PrintWriter errWriter,119String docletName, ClassLoader classLoader) {120this.context = context == null ? new Context() : context;121String pname = programName == null ? ProgramName : programName;122this.log = (outWriter == null && errWriter == null)123? new JavadocLog(this.context, pname)124: new JavadocLog(this.context, pname, outWriter, errWriter);125this.docletName = docletName;126this.classLoader = classLoader;127this.docletClass = null;128this.locale = Locale.getDefault();129130options = getToolOptions();131}132133public Start(Context context) {134this.docletClass = null;135this.context = Objects.requireNonNull(context);136this.apiMode = true;137this.docletName = null;138this.classLoader = null;139this.locale = Locale.getDefault();140141Log log = context.get(Log.logKey);142if (log instanceof JavadocLog l){143this.log = l;144} else {145PrintWriter out = context.get(Log.errKey);146this.log = (out == null)147? new JavadocLog(context, ProgramName)148: new JavadocLog(context, ProgramName, out, out);149}150151options = getToolOptions();152}153154private ToolOptions getToolOptions() {155ToolOptions.ShowHelper helper = new ToolOptions.ShowHelper() {156@Override157public void usage() {158showUsage("main.usage", ToolOption.Kind.STANDARD, "main.usage.foot");159}160161@Override162public void Xusage() {163showUsage("main.Xusage", ToolOption.Kind.EXTENDED, "main.Xusage.foot");164}165166@Override167public void version() {168showVersion("javadoc.version", orDefault(() -> Versions.shortVersionStringOf(toolVersion())));169}170171@Override172public void fullVersion() {173showVersion("javadoc.fullversion", orDefault(() -> Versions.fullVersionStringOf(toolVersion())));174}175176private String orDefault(Supplier<String> s) {177try {178return s.get();179} catch (RuntimeException e) {180assert false : e;181return Log.getLocalizedString("version.not.available");182}183}184};185return new ToolOptions(context, log, helper);186}187188private Runtime.Version toolVersion() {189return Versions.javadocVersion();190}191192private void showUsage() {193showUsage("main.usage", ToolOption.Kind.STANDARD, "main.usage.foot");194}195196private void showUsage(String headerKey, ToolOption.Kind kind, String footerKey) {197log.noticeUsingKey(headerKey);198showToolOptions(kind);199200// let doclet print usage information201if (docletClass != null) {202showDocletOptions(kind == ToolOption.Kind.EXTENDED203? Option.Kind.EXTENDED204: Option.Kind.STANDARD);205}206if (footerKey != null)207log.noticeUsingKey(footerKey);208}209210private void showVersion(String labelKey, String value) {211log.noticeUsingKey(labelKey, log.programName, value);212}213214private void showToolOptions(ToolOption.Kind kind) {215Comparator<ToolOption> comp = new Comparator<ToolOption>() {216final Collator collator = Collator.getInstance(Locale.US);217{ collator.setStrength(Collator.PRIMARY); }218219@Override220public int compare(ToolOption o1, ToolOption o2) {221return collator.compare(o1.primaryName, o2.primaryName);222}223};224225options.getSupportedOptions().stream()226.filter(opt -> opt.kind == kind)227.sorted(comp)228.forEach(this::showToolOption);229}230231private void showToolOption(ToolOption option) {232List<String> names = option.getNames();233String primaryName = option.primaryName;234String parameters;235if (option.hasArg || primaryName.endsWith(":")) {236String sep = primaryName.endsWith(":")237|| primaryName.equals(ToolOptions.AT)238|| primaryName.equals(ToolOptions.J)239? "" : " ";240parameters = sep + option.getParameters(log);241} else {242parameters = "";243}244String description = option.getDescription(log);245showOption(names, parameters, description);246}247248private void showDocletOptions(Option.Kind kind) {249String name = doclet.getName();250Set<? extends Option> options = getSupportedOptionsOf(doclet);251if (options.isEmpty()) {252return;253}254log.noticeUsingKey("main.doclet.usage.header", name);255256Comparator<Doclet.Option> comp = new Comparator<Doclet.Option>() {257final Collator collator = Collator.getInstance(Locale.US);258{ collator.setStrength(Collator.PRIMARY); }259260@Override261public int compare(Doclet.Option o1, Doclet.Option o2) {262return collator.compare(o1.getNames().get(0), o2.getNames().get(0));263}264};265266options.stream()267.filter(opt -> opt.getKind() == kind)268.sorted(comp)269.forEach(this::showDocletOption);270}271272private void showDocletOption(Doclet.Option option) {273List<String> names = option.getNames();274String parameters;275String primaryName = names.get(0);276if (option.getArgumentCount() > 0 || primaryName.endsWith(":")) {277String sep = primaryName.endsWith(":") ? "" : " ";278parameters = sep + option.getParameters();279} else {280parameters = "";281}282String description = option.getDescription();283showOption(names, parameters, description);284}285286// The following constants are intended to format the output to287// be similar to that of the java launcher: i.e. "java -help".288289/** The indent for the option synopsis. */290private static final String SMALL_INDENT = " ".repeat(4);291/** The automatic indent for the description. */292private static final String LARGE_INDENT = " ".repeat(18);293/** The space allowed for the synopsis, if the description is to be shown on the same line. */294private static final int DEFAULT_SYNOPSIS_WIDTH = 13;295/** The nominal maximum line length, when seeing if text will fit on a line. */296private static final int DEFAULT_MAX_LINE_LENGTH = 80;297/** The format for a single-line help entry. */298private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s";299300void showOption(List<String> names, String parameters, String description) {301String 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_FORMAT306if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH307&& !description.contains("\n")308&& (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + description.length() <= DEFAULT_MAX_LINE_LENGTH)) {309log.notice(String.format(COMPACT_FORMAT, synopses, description));310return;311}312313// If option synopses fit on a single line of reasonable length, show that;314// otherwise, show 1 per line315if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) {316log.notice(SMALL_INDENT + synopses);317} else {318for (String name: names) {319log.notice(SMALL_INDENT + name + parameters);320}321}322323// Finally, show the description324log.notice(LARGE_INDENT + description.replace("\n", "\n" + LARGE_INDENT));325}326327328/**329* Main program - external wrapper.330*/331@SuppressWarnings("deprecation")332Result begin(String... argv) {333// Preprocess @file arguments334List<String> allArgs;335try {336allArgs = CommandLine.parse(List.of(argv));337} catch (IOException e) {338error("main.cant.read", e.getMessage());339return ERROR;340}341return begin(allArgs, Collections.emptySet());342}343344// Called by the JSR 199 API345public boolean begin(Class<?> docletClass,346Iterable<String> options,347Iterable<? extends JavaFileObject> fileObjects)348{349this.docletClass = docletClass;350List<String> opts = new ArrayList<>();351for (String opt: options)352opts.add(opt);353354return begin(opts, fileObjects).isOK();355}356357private Result begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) {358fileManager = context.get(JavaFileManager.class);359if (fileManager == null) {360JavacFileManager.preRegister(context);361fileManager = context.get(JavaFileManager.class);362if (fileManager instanceof BaseFileManager bfm) {363bfm.autoClose = true;364}365}366367// Perform an initial scan of the options to determine the doclet to be used (if any),368// so that it may participate in the main round of option processing.369try {370doclet = preprocess(options);371} catch (ToolException te) {372if (!te.result.isOK()) {373if (te.message != null) {374log.printError(te.message);375}376Throwable t = te.getCause();377dumpStack(t == null ? te : t);378}379return te.result;380} catch (OptionException oe) {381if (oe.message != null) {382log.printError(oe.message);383}384oe.m.run();385Throwable t = oe.getCause();386dumpStack(t == null ? oe : t);387return oe.result;388}389390Result result = OK;391try {392result = parseAndExecute(options, fileObjects);393} catch (com.sun.tools.javac.main.Option.InvalidValueException e) {394// The detail message from javac already includes a localized "error: " prefix,395// so print the message directly.396// It would be even better to rethrow this as IllegalArgumentException397// when invoked via the API.398// See javac Arguments.error(InvalidValueException) for an example399log.printRawLines(e.getMessage());400Throwable t = e.getCause();401dumpStack(t == null ? e : t);402return ERROR;403} catch (OptionException oe) {404// It would be even better to rethrow this as IllegalArgumentException405// when invoked via the API.406// See javac Arguments.error(InvalidValueException) for an example407if (oe.message != null)408log.printError(oe.message);409410oe.m.run();411Throwable t = oe.getCause();412dumpStack(t == null ? oe : t);413return oe.result;414} catch (ToolException exc) {415if (exc.message != null) {416log.printError(exc.message);417}418Throwable t = exc.getCause();419if (result == ABNORMAL) {420reportInternalError(t == null ? exc : t);421} else {422dumpStack(t == null ? exc : t);423}424return exc.result;425} catch (OutOfMemoryError ee) {426error("main.out.of.memory");427result = SYSERR;428dumpStack(ee);429} catch (ClientCodeException e) {430// simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl431throw e;432} catch (Error | Exception ee) {433error("main.fatal.error", ee);434reportInternalError(ee);435result = ABNORMAL;436} finally {437if (fileManager instanceof BaseFileManager bfm438&& bfm.autoClose) {439try {440fileManager.close();441} catch (IOException ignore) {}442}443if (this.options.rejectWarnings() && log.hasWarnings()) {444error("main.warnings.Werror");445}446boolean haveErrors = log.hasErrors();447if (!result.isOK() && !haveErrors) {448// the doclet failed, but nothing reported, flag it!.449error("main.unknown.error");450}451if (haveErrors && result.isOK()) {452result = ERROR;453}454log.printErrorWarningCounts();455log.flush();456}457return result;458}459460private void reportInternalError(Throwable t) {461log.printErrorUsingKey("doclet.internal.report.bug");462dumpStack(true, t);463}464465private void dumpStack(Throwable t) {466dumpStack(false, t);467}468469private void dumpStack(boolean enabled, Throwable t) {470if (t != null && (enabled || options.dumpOnError())) {471t.printStackTrace(System.err);472}473}474475/**476* Main program - internal477*/478private Result parseAndExecute(List<String> argList, Iterable<? extends JavaFileObject> fileObjects)479throws ToolException, OptionException, com.sun.tools.javac.main.Option.InvalidValueException480{481final long startNanos = System.nanoTime();482483List<String> javaNames = new ArrayList<>();484485// Make sure no obsolete source/target messages are reported486try {487options.processCompilerOption(com.sun.tools.javac.main.Option.XLINT_CUSTOM, "-Xlint:-options");488} catch (com.sun.tools.javac.main.Option.InvalidValueException ignore) {489}490491Arguments arguments = Arguments.instance(context);492arguments.init(ProgramName);493arguments.allowEmpty();494495doclet.init(locale, log);496int beforeCount = log.nerrors;497boolean success = parseArgs(argList, javaNames);498int afterCount = log.nerrors;499if (!success && beforeCount == afterCount) { // if there were failures but they have not been reported500return CMDERR;501}502503if (!arguments.handleReleaseOptions(extra -> true)) {504// Arguments does not always increase the error count in the505// case of errors, so increment the error count only if it has506// not been updated previously, preventing complaints by callers507if (!log.hasErrors() && !log.hasWarnings())508log.nerrors++;509return CMDERR;510}511512if (!arguments.validate()) {513// Arguments does not always increase the error count in the514// case of errors, so increment the error count only if it has515// not been updated previously, preventing complaints by callers516if (!log.hasErrors() && !log.hasWarnings())517log.nerrors++;518return CMDERR;519}520521if (fileManager instanceof BaseFileManager bfm) {522bfm.handleOptions(options.fileManagerOptions());523}524525String mr = com.sun.tools.javac.main.Option.MULTIRELEASE.primaryName;526if (fileManager.isSupportedOption(mr) == 1) {527Target target = Target.instance(context);528List<String> list = List.of(target.multiReleaseValue());529fileManager.handleOption(mr, list.iterator());530}531options.compilerOptions().notifyListeners();532533if (options.modules().isEmpty()) {534if (options.subpackages().isEmpty()) {535if (javaNames.isEmpty() && isEmpty(fileObjects)) {536String text = log.getText("main.No_modules_packages_or_classes_specified");537throw new ToolException(CMDERR, text);538}539}540}541542JavadocTool comp = JavadocTool.make0(context);543if (comp == null) return ABNORMAL;544545DocletEnvironment docEnv = comp.getEnvironment(options, javaNames, fileObjects);546547// release resources548comp = null;549550if (options.breakIterator() || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {551JavacTrees trees = JavacTrees.instance(context);552trees.setBreakIterator(BreakIterator.getSentenceInstance(locale));553}554// pass off control to the doclet555Result returnStatus = docEnv != null && doclet.run(docEnv)556? OK557: ERROR;558559// We're done.560if (options.verbose()) {561long elapsedMillis = (System.nanoTime() - startNanos) / 1_000_000;562JavadocLog.printRawLines(log.getDiagnosticWriter(),563log.getText("main.done_in", Long.toString(elapsedMillis)));564}565566return returnStatus;567}568569boolean matches(List<String> names, String arg) {570for (String name : names) {571if (StringUtils.toLowerCase(name).equals(StringUtils.toLowerCase(arg)))572return true;573}574return false;575}576577boolean matches(Doclet.Option option, String arg) {578if (matches(option.getNames(), arg))579return true;580int sep = arg.indexOf(':');581String targ = arg.substring(0, sep + 1);582return matches(option.getNames(), targ);583}584585private Set<? extends Doclet.Option> docletOptions = null;586587/*588* Consumes an option along with its arguments. Returns an advanced index589* modulo the sign. If the value is negative, it means there was a failure590* processing one or more options.591*/592int consumeDocletOption(int idx, List<String> args, boolean isToolOption) throws OptionException {593if (docletOptions == null) {594docletOptions = getSupportedOptionsOf(doclet);595}596String arg = args.get(idx);597String argBase, argVal;598if (arg.startsWith("--") && arg.contains("=")) {599int sep = arg.indexOf("=");600argBase = arg.substring(0, sep);601argVal = arg.substring(sep + 1);602} else {603argBase = arg;604argVal = null;605}606int m = 1;607String text = null;608for (Doclet.Option opt : docletOptions) {609if (matches(opt, argBase)) {610if (argVal != null) {611switch (opt.getArgumentCount()) {612case 0:613text = log.getText("main.unnecessary_arg_provided", argBase);614throw new OptionException(ERROR, this::showUsage, text);615case 1:616if (!opt.process(arg, Collections.singletonList(argVal))) {617m = -1;618}619break;620default:621text = log.getText("main.only_one_argument_with_equals", argBase);622throw new OptionException(ERROR, this::showUsage, text);623}624} else {625if (args.size() - idx - 1 < opt.getArgumentCount()) {626text = log.getText("main.requires_argument", arg);627throw new OptionException(ERROR, this::showUsage, text);628}629if (!opt.process(arg, args.subList(idx + 1, idx + 1 + opt.getArgumentCount()))) {630m = -1;631}632idx += opt.getArgumentCount();633}634return m * idx;635}636}637// check if arg is accepted by the tool before emitting error638if (!isToolOption) {639text = log.getText("main.invalid_flag", arg);640throw new OptionException(ERROR, this::showUsage, text);641}642return m * idx;643}644645private static Set<? extends Option> getSupportedOptionsOf(Doclet doclet) {646Set<? extends Option> options = doclet.getSupportedOptions();647return options == null ? Set.of() : options;648}649650/**651* Performs an initial pass over the options, primarily to determine652* the doclet to be used (if any), so that it may participate in the653* main round of option decoding. This avoids having to specify that654* the options to specify the doclet should appear before any options655* that are handled by the doclet.656*657* The downside of this initial phase is that we have to skip over658* unknown options, and assume that we can reliably detect the options659* we need to handle.660*661* @param argv the arguments to be processed662* @return the doclet663* @throws ToolException if an error occurs initializing the doclet664* @throws OptionException if an error occurs while processing an option665*/666private Doclet preprocess(List<String> argv) throws ToolException, OptionException {667// doclet specifying arguments668String userDocletPath = null;669String userDocletName = null;670671// Step 1: loop through the args, set locale early on, if found.672for (int i = 0; i < argv.size(); i++) {673String arg = argv.get(i);674if (arg.equals(ToolOptions.DUMP_ON_ERROR)) {675// although this option is not needed in order to initialize the doclet,676// it is helpful if it is set before trying to initialize the doclet677options.setDumpOnError(true);678} else if (arg.equals(ToolOptions.LOCALE)) {679checkOneArg(argv, i++);680String lname = argv.get(i);681locale = getLocale(lname);682} else if (arg.equals(ToolOptions.DOCLET)) {683checkOneArg(argv, i++);684if (userDocletName != null) {685if (apiMode) {686throw new IllegalArgumentException("More than one doclet specified (" +687userDocletName + " and " + argv.get(i) + ").");688}689String text = log.getText("main.more_than_one_doclet_specified_0_and_1",690userDocletName, argv.get(i));691throw new ToolException(CMDERR, text);692}693if (docletName != null) {694if (apiMode) {695throw new IllegalArgumentException("More than one doclet specified (" +696docletName + " and " + argv.get(i) + ").");697}698String text = log.getText("main.more_than_one_doclet_specified_0_and_1",699docletName, argv.get(i));700throw new ToolException(CMDERR, text);701}702userDocletName = argv.get(i);703} else if (arg.equals(ToolOptions.DOCLET_PATH)) {704checkOneArg(argv, i++);705if (userDocletPath == null) {706userDocletPath = argv.get(i);707} else {708userDocletPath += File.pathSeparator + argv.get(i);709}710}711}712713// Step 3: doclet name specified ? if so find a ClassLoader,714// and load it.715if (docletClass == null) {716if (userDocletName != null) {717ClassLoader cl = classLoader;718if (cl == null) {719if (!fileManager.hasLocation(DOCLET_PATH)) {720List<File> paths = new ArrayList<>();721if (userDocletPath != null) {722for (String pathname : userDocletPath.split(File.pathSeparator)) {723paths.add(new File(pathname));724}725}726try {727((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);728} catch (IOException ioe) {729if (apiMode) {730throw new IllegalArgumentException("Could not set location for " +731userDocletPath, ioe);732}733String text = log.getText("main.doclet_could_not_set_location",734userDocletPath);735throw new ToolException(CMDERR, text, ioe);736}737}738cl = fileManager.getClassLoader(DOCLET_PATH);739if (cl == null) {740// despite doclet specified on cmdline no classloader found!741if (apiMode) {742throw new IllegalArgumentException("Could not obtain classloader to load "743744+ userDocletPath);745}746String text = log.getText("main.doclet_no_classloader_found",747userDocletName);748throw new ToolException(CMDERR, text);749}750}751docletClass = loadDocletClass(userDocletName, cl);752} else if (docletName != null){753docletClass = loadDocletClass(docletName, getClass().getClassLoader());754} else {755docletClass = StandardDoclet.class;756}757}758759if (Doclet.class.isAssignableFrom(docletClass)) {760log.setLocale(Locale.getDefault()); // use default locale for console messages761try {762Object o = docletClass.getConstructor().newInstance();763doclet = (Doclet) o;764} catch (ReflectiveOperationException exc) {765if (apiMode) {766throw new ClientCodeException(exc);767}768String text = log.getText("main.could_not_instantiate_class", docletClass.getName());769throw new ToolException(ERROR, text);770}771} else {772String text = log.getText("main.not_a_doclet", docletClass.getName());773throw new ToolException(ERROR, text);774}775return doclet;776}777778private Class<?> loadDocletClass(String docletName, ClassLoader classLoader) throws ToolException {779try {780return classLoader == null ? Class.forName(docletName) : classLoader.loadClass(docletName);781} catch (ClassNotFoundException cnfe) {782if (apiMode) {783throw new IllegalArgumentException("Cannot find doclet class " + docletName);784}785String text = log.getText("main.doclet_class_not_found", docletName);786throw new ToolException(CMDERR, text, cnfe);787}788}789790private boolean parseArgs(List<String> args, List<String> javaNames)791throws OptionException, com.sun.tools.javac.main.Option.InvalidValueException792{793boolean success = true;794for (int i = 0; i < args.size(); i++) {795String arg = args.get(i);796ToolOption o = options.getOption(arg);797if (o != null) {798// handle a doclet argument that may be needed however799// don't increment the index, and allow the tool to consume args800if (consumeDocletOption(i, args, true) < 0) {801success = false;802}803if (o.hasArg) {804if (arg.startsWith("--") && arg.contains("=")) {805o.process(arg.substring(arg.indexOf('=') + 1));806} else {807checkOneArg(args, i++);808o.process(args.get(i));809}810} else if (o.hasSuffix) {811o.process(arg);812} else {813o.process();814}815} else if (arg.startsWith("-XD")) {816// hidden javac options817String s = arg.substring("-XD".length());818int eq = s.indexOf('=');819String key = (eq < 0) ? s : s.substring(0, eq);820String value = (eq < 0) ? s : s.substring(eq + 1);821options.compilerOptions().put(key, value);822} else if (arg.startsWith("-")) {823i = consumeDocletOption(i, args, false);824if (i < 0) {825i = -i;826success = false;827}828} else {829javaNames.add(arg);830}831}832return success;833}834835private <T> boolean isEmpty(Iterable<T> iter) {836return !iter.iterator().hasNext();837}838839/**840* Check the one arg option.841* Error and exit if one argument is not provided.842*/843private void checkOneArg(List<String> args, int index) throws OptionException {844if ((index + 1) >= args.size() || args.get(index + 1).startsWith("-d")) {845String text = log.getText("main.requires_argument", args.get(index));846throw new OptionException(CMDERR, this::showUsage, text);847}848}849850void error(String key, Object... args) {851log.printErrorUsingKey(key, args);852}853854/**855* Get the locale if specified on the command line856* else return null and if locale option is not used857* then return default locale.858*/859private Locale getLocale(String localeName) throws ToolException {860try {861// Tolerate, at least for a while, the older syntax accepted by javadoc,862// using _ as the separator863localeName = localeName.replace("_", "-");864Locale l = new Locale.Builder().setLanguageTag(localeName).build();865// Ensure that a non-empty language is available for the <HTML lang=...> element866return (l.getLanguage().isEmpty()) ? Locale.ENGLISH : l;867} catch (IllformedLocaleException e) {868String text = log.getText("main.malformed_locale_name", localeName);869throw new ToolException(CMDERR, text);870}871}872873}874875876