Path: blob/master/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java
41161 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 Messager messager;8788private final String docletName;8990private final ClassLoader classLoader;9192private Class<?> docletClass;9394private Doclet doclet;9596// used to determine the locale for the messager97private 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.messager = (outWriter == null && errWriter == null)123? new Messager(this.context, pname)124: new Messager(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 Messager m){143messager = m;144} else {145PrintWriter out = context.get(Log.errKey);146messager = (out == null)147? new Messager(context, ProgramName)148: new Messager(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, messager, 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) {197messager.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)207messager.noticeUsingKey(footerKey);208}209210private void showVersion(String labelKey, String value) {211messager.noticeUsingKey(labelKey, messager.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(messager);241} else {242parameters = "";243}244String description = option.getDescription(messager);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}254messager.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)) {309messager.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) {316messager.notice(SMALL_INDENT + synopses);317} else {318for (String name: names) {319messager.notice(SMALL_INDENT + name + parameters);320}321}322323// Finally, show the description324messager.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) {374messager.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) {382messager.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 example399messager.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)408messager.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) {416messager.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() && messager.hasWarnings()) {444error("main.warnings.Werror");445}446boolean haveErrors = messager.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}454messager.printErrorWarningCounts();455messager.flush();456}457return result;458}459460private void reportInternalError(Throwable t) {461messager.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, messager);496int beforeCount = messager.nerrors;497boolean success = parseArgs(argList, javaNames);498int afterCount = messager.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 (!messager.hasErrors() && !messager.hasWarnings())508messager.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 (!messager.hasErrors() && !messager.hasWarnings())517messager.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 = messager.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;562messager.noticeUsingKey("main.done_in", Long.toString(elapsedMillis));563}564565return returnStatus;566}567568boolean matches(List<String> names, String arg) {569for (String name : names) {570if (StringUtils.toLowerCase(name).equals(StringUtils.toLowerCase(arg)))571return true;572}573return false;574}575576boolean matches(Doclet.Option option, String arg) {577if (matches(option.getNames(), arg))578return true;579int sep = arg.indexOf(':');580String targ = arg.substring(0, sep + 1);581return matches(option.getNames(), targ);582}583584private Set<? extends Doclet.Option> docletOptions = null;585586/*587* Consumes an option along with its arguments. Returns an advanced index588* modulo the sign. If the value is negative, it means there was a failure589* processing one or more options.590*/591int consumeDocletOption(int idx, List<String> args, boolean isToolOption) throws OptionException {592if (docletOptions == null) {593docletOptions = getSupportedOptionsOf(doclet);594}595String arg = args.get(idx);596String argBase, argVal;597if (arg.startsWith("--") && arg.contains("=")) {598int sep = arg.indexOf("=");599argBase = arg.substring(0, sep);600argVal = arg.substring(sep + 1);601} else {602argBase = arg;603argVal = null;604}605int m = 1;606String text = null;607for (Doclet.Option opt : docletOptions) {608if (matches(opt, argBase)) {609if (argVal != null) {610switch (opt.getArgumentCount()) {611case 0:612text = messager.getText("main.unnecessary_arg_provided", argBase);613throw new OptionException(ERROR, this::showUsage, text);614case 1:615if (!opt.process(arg, Collections.singletonList(argVal))) {616m = -1;617}618break;619default:620text = messager.getText("main.only_one_argument_with_equals", argBase);621throw new OptionException(ERROR, this::showUsage, text);622}623} else {624if (args.size() - idx - 1 < opt.getArgumentCount()) {625text = messager.getText("main.requires_argument", arg);626throw new OptionException(ERROR, this::showUsage, text);627}628if (!opt.process(arg, args.subList(idx + 1, idx + 1 + opt.getArgumentCount()))) {629m = -1;630}631idx += opt.getArgumentCount();632}633return m * idx;634}635}636// check if arg is accepted by the tool before emitting error637if (!isToolOption) {638text = messager.getText("main.invalid_flag", arg);639throw new OptionException(ERROR, this::showUsage, text);640}641return m * idx;642}643644private static Set<? extends Option> getSupportedOptionsOf(Doclet doclet) {645Set<? extends Option> options = doclet.getSupportedOptions();646return options == null ? Set.of() : options;647}648649/**650* Performs an initial pass over the options, primarily to determine651* the doclet to be used (if any), so that it may participate in the652* main round of option decoding. This avoids having to specify that653* the options to specify the doclet should appear before any options654* that are handled by the doclet.655*656* The downside of this initial phase is that we have to skip over657* unknown options, and assume that we can reliably detect the options658* we need to handle.659*660* @param argv the arguments to be processed661* @return the doclet662* @throws ToolException if an error occurs initializing the doclet663* @throws OptionException if an error occurs while processing an option664*/665private Doclet preprocess(List<String> argv) throws ToolException, OptionException {666// doclet specifying arguments667String userDocletPath = null;668String userDocletName = null;669670// Step 1: loop through the args, set locale early on, if found.671for (int i = 0; i < argv.size(); i++) {672String arg = argv.get(i);673if (arg.equals(ToolOptions.DUMP_ON_ERROR)) {674// although this option is not needed in order to initialize the doclet,675// it is helpful if it is set before trying to initialize the doclet676options.setDumpOnError(true);677} else if (arg.equals(ToolOptions.LOCALE)) {678checkOneArg(argv, i++);679String lname = argv.get(i);680locale = getLocale(lname);681} else if (arg.equals(ToolOptions.DOCLET)) {682checkOneArg(argv, i++);683if (userDocletName != null) {684if (apiMode) {685throw new IllegalArgumentException("More than one doclet specified (" +686userDocletName + " and " + argv.get(i) + ").");687}688String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",689userDocletName, argv.get(i));690throw new ToolException(CMDERR, text);691}692if (docletName != null) {693if (apiMode) {694throw new IllegalArgumentException("More than one doclet specified (" +695docletName + " and " + argv.get(i) + ").");696}697String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",698docletName, argv.get(i));699throw new ToolException(CMDERR, text);700}701userDocletName = argv.get(i);702} else if (arg.equals(ToolOptions.DOCLET_PATH)) {703checkOneArg(argv, i++);704if (userDocletPath == null) {705userDocletPath = argv.get(i);706} else {707userDocletPath += File.pathSeparator + argv.get(i);708}709}710}711712// Step 3: doclet name specified ? if so find a ClassLoader,713// and load it.714if (docletClass == null) {715if (userDocletName != null) {716ClassLoader cl = classLoader;717if (cl == null) {718if (!fileManager.hasLocation(DOCLET_PATH)) {719List<File> paths = new ArrayList<>();720if (userDocletPath != null) {721for (String pathname : userDocletPath.split(File.pathSeparator)) {722paths.add(new File(pathname));723}724}725try {726((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);727} catch (IOException ioe) {728if (apiMode) {729throw new IllegalArgumentException("Could not set location for " +730userDocletPath, ioe);731}732String text = messager.getText("main.doclet_could_not_set_location",733userDocletPath);734throw new ToolException(CMDERR, text, ioe);735}736}737cl = fileManager.getClassLoader(DOCLET_PATH);738if (cl == null) {739// despite doclet specified on cmdline no classloader found!740if (apiMode) {741throw new IllegalArgumentException("Could not obtain classloader to load "742743+ userDocletPath);744}745String text = messager.getText("main.doclet_no_classloader_found",746userDocletName);747throw new ToolException(CMDERR, text);748}749}750docletClass = loadDocletClass(userDocletName, cl);751} else if (docletName != null){752docletClass = loadDocletClass(docletName, getClass().getClassLoader());753} else {754docletClass = StandardDoclet.class;755}756}757758if (Doclet.class.isAssignableFrom(docletClass)) {759messager.setLocale(Locale.getDefault()); // use default locale for console messages760try {761Object o = docletClass.getConstructor().newInstance();762doclet = (Doclet) o;763} catch (ReflectiveOperationException exc) {764if (apiMode) {765throw new ClientCodeException(exc);766}767String text = messager.getText("main.could_not_instantiate_class", docletClass.getName());768throw new ToolException(ERROR, text);769}770} else {771String text = messager.getText("main.not_a_doclet", docletClass.getName());772throw new ToolException(ERROR, text);773}774return doclet;775}776777private Class<?> loadDocletClass(String docletName, ClassLoader classLoader) throws ToolException {778try {779return classLoader == null ? Class.forName(docletName) : classLoader.loadClass(docletName);780} catch (ClassNotFoundException cnfe) {781if (apiMode) {782throw new IllegalArgumentException("Cannot find doclet class " + docletName);783}784String text = messager.getText("main.doclet_class_not_found", docletName);785throw new ToolException(CMDERR, text, cnfe);786}787}788789private boolean parseArgs(List<String> args, List<String> javaNames)790throws OptionException, com.sun.tools.javac.main.Option.InvalidValueException791{792boolean success = true;793for (int i = 0; i < args.size(); i++) {794String arg = args.get(i);795ToolOption o = options.getOption(arg);796if (o != null) {797// handle a doclet argument that may be needed however798// don't increment the index, and allow the tool to consume args799if (consumeDocletOption(i, args, true) < 0) {800success = false;801}802if (o.hasArg) {803if (arg.startsWith("--") && arg.contains("=")) {804o.process(arg.substring(arg.indexOf('=') + 1));805} else {806checkOneArg(args, i++);807o.process(args.get(i));808}809} else if (o.hasSuffix) {810o.process(arg);811} else {812o.process();813}814} else if (arg.startsWith("-XD")) {815// hidden javac options816String s = arg.substring("-XD".length());817int eq = s.indexOf('=');818String key = (eq < 0) ? s : s.substring(0, eq);819String value = (eq < 0) ? s : s.substring(eq + 1);820options.compilerOptions().put(key, value);821} else if (arg.startsWith("-")) {822i = consumeDocletOption(i, args, false);823if (i < 0) {824i = -i;825success = false;826}827} else {828javaNames.add(arg);829}830}831return success;832}833834private <T> boolean isEmpty(Iterable<T> iter) {835return !iter.iterator().hasNext();836}837838/**839* Check the one arg option.840* Error and exit if one argument is not provided.841*/842private void checkOneArg(List<String> args, int index) throws OptionException {843if ((index + 1) >= args.size() || args.get(index + 1).startsWith("-d")) {844String text = messager.getText("main.requires_argument", args.get(index));845throw new OptionException(CMDERR, this::showUsage, text);846}847}848849void error(String key, Object... args) {850messager.printErrorUsingKey(key, args);851}852853/**854* Get the locale if specified on the command line855* else return null and if locale option is not used856* then return default locale.857*/858private Locale getLocale(String localeName) throws ToolException {859try {860// Tolerate, at least for a while, the older syntax accepted by javadoc,861// using _ as the separator862localeName = localeName.replace("_", "-");863Locale l = new Locale.Builder().setLanguageTag(localeName).build();864// Ensure that a non-empty language is available for the <HTML lang=...> element865return (l.getLanguage().isEmpty()) ? Locale.ENGLISH : l;866} catch (IllformedLocaleException e) {867String text = messager.getText("main.malformed_locale_name", localeName);868throw new ToolException(CMDERR, text);869}870}871872}873874875