| /* |
| * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.javadoc.internal.tool; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.nio.file.Path; |
| import java.text.BreakIterator; |
| import java.text.Collator; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.MissingResourceException; |
| import java.util.Objects; |
| import java.util.ResourceBundle; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| import javax.tools.JavaFileManager; |
| import javax.tools.JavaFileObject; |
| import javax.tools.StandardJavaFileManager; |
| import javax.tools.StandardLocation; |
| |
| import com.sun.tools.javac.api.JavacTrees; |
| import com.sun.tools.javac.file.BaseFileManager; |
| import com.sun.tools.javac.file.JavacFileManager; |
| import com.sun.tools.javac.main.Arguments; |
| import com.sun.tools.javac.main.CommandLine; |
| import com.sun.tools.javac.main.OptionHelper; |
| import com.sun.tools.javac.main.OptionHelper.GrumpyHelper; |
| import com.sun.tools.javac.platform.PlatformDescription; |
| import com.sun.tools.javac.platform.PlatformUtils; |
| import com.sun.tools.javac.util.ClientCodeException; |
| import com.sun.tools.javac.util.Context; |
| import com.sun.tools.javac.util.Log; |
| import com.sun.tools.javac.util.Log.WriterKind; |
| import com.sun.tools.javac.util.Options; |
| import com.sun.tools.javac.util.StringUtils; |
| |
| import jdk.javadoc.doclet.Doclet; |
| import jdk.javadoc.doclet.Doclet.Option; |
| import jdk.javadoc.doclet.DocletEnvironment; |
| import jdk.javadoc.internal.tool.Main.Result; |
| |
| import static javax.tools.DocumentationTool.Location.*; |
| |
| import static com.sun.tools.javac.main.Option.*; |
| import static jdk.javadoc.internal.tool.Main.Result.*; |
| |
| /** |
| * Main program of Javadoc. |
| * Previously named "Main". |
| * |
| * <p><b>This is NOT part of any supported API. |
| * If you write code that depends on this, you do so at your own risk. |
| * This code and its internal interfaces are subject to change or |
| * deletion without notice.</b> |
| * |
| * @author Robert Field |
| * @author Neal Gafter (rewrite) |
| */ |
| public class Start extends ToolOption.Helper { |
| |
| private static final Class<?> StdDoclet = |
| jdk.javadoc.doclet.StandardDoclet.class; |
| /** Context for this invocation. */ |
| private final Context context; |
| |
| private static final String ProgramName = "javadoc"; |
| |
| private Messager messager; |
| |
| private final String docletName; |
| |
| private final ClassLoader classLoader; |
| |
| private Class<?> docletClass; |
| |
| private Doclet doclet; |
| |
| // used to determine the locale for the messager |
| private Locale locale; |
| |
| |
| /** |
| * In API mode, exceptions thrown while calling the doclet are |
| * propagated using ClientCodeException. |
| */ |
| private boolean apiMode; |
| |
| private JavaFileManager fileManager; |
| |
| Start() { |
| this(null, null, null, null, null, null); |
| } |
| |
| Start(PrintWriter outWriter, PrintWriter errWriter) { |
| this(null, null, outWriter, errWriter, null, null); |
| } |
| |
| Start(Context context, String programName, |
| PrintWriter outWriter, PrintWriter errWriter, |
| String docletName, ClassLoader classLoader) { |
| this.context = context == null ? new Context() : context; |
| String pname = programName == null ? ProgramName : programName; |
| this.messager = (outWriter == null && errWriter == null) |
| ? new Messager(this.context, pname) |
| : new Messager(this.context, pname, outWriter, errWriter); |
| this.docletName = docletName; |
| this.classLoader = classLoader; |
| this.docletClass = null; |
| this.locale = Locale.getDefault(); |
| } |
| |
| public Start(Context context) { |
| this.docletClass = null; |
| this.context = Objects.requireNonNull(context); |
| this.apiMode = true; |
| this.docletName = null; |
| this.classLoader = null; |
| this.locale = Locale.getDefault(); |
| } |
| |
| void initMessager() { |
| if (!apiMode) |
| return; |
| if (messager == null) { |
| Log log = context.get(Log.logKey); |
| if (log instanceof Messager) { |
| messager = (Messager) log; |
| } else { |
| PrintWriter out = context.get(Log.errKey); |
| messager = (out == null) |
| ? new Messager(context, ProgramName) |
| : new Messager(context, ProgramName, out, out); |
| } |
| } |
| } |
| |
| /** |
| * Usage |
| */ |
| @Override |
| void usage() { |
| usage("main.usage", OptionKind.STANDARD, "main.usage.foot"); |
| } |
| |
| @Override |
| void Xusage() { |
| usage("main.Xusage", OptionKind.EXTENDED, "main.Xusage.foot"); |
| } |
| |
| @Override |
| void version() { |
| messager.notice("javadoc.version", messager.programName, version("release")); |
| } |
| |
| @Override |
| void fullVersion() { |
| messager.notice("javadoc.fullversion", messager.programName, version("full")); |
| } |
| |
| private void usage(String headerKey, OptionKind kind, String footerKey) { |
| messager.notice(headerKey); |
| showToolOptions(kind); |
| |
| // let doclet print usage information |
| if (docletClass != null) { |
| String name = doclet.getName(); |
| messager.notice("main.doclet.usage.header", name); |
| showDocletOptions(kind == OptionKind.EXTENDED |
| ? Option.Kind.EXTENDED |
| : Option.Kind.STANDARD); |
| } |
| if (footerKey != null) |
| messager.notice(footerKey); |
| } |
| |
| private static final String versionRBName = "jdk.javadoc.internal.tool.resources.version"; |
| private static ResourceBundle versionRB; |
| |
| private static String version(String key) { |
| if (versionRB == null) { |
| try { |
| versionRB = ResourceBundle.getBundle(versionRBName); |
| } catch (MissingResourceException e) { |
| return Log.getLocalizedString("version.not.available"); |
| } |
| } |
| try { |
| return versionRB.getString(key); |
| } catch (MissingResourceException e) { |
| return Log.getLocalizedString("version.not.available"); |
| } |
| } |
| |
| void showToolOptions(OptionKind kind) { |
| Comparator<ToolOption> comp = new Comparator<ToolOption>() { |
| final Collator collator = Collator.getInstance(Locale.US); |
| { collator.setStrength(Collator.PRIMARY); } |
| |
| @Override |
| public int compare(ToolOption o1, ToolOption o2) { |
| return collator.compare(o1.primaryName, o2.primaryName); |
| } |
| }; |
| |
| Stream.of(ToolOption.values()) |
| .filter(opt -> opt.kind == kind) |
| .sorted(comp) |
| .forEach(this::showToolOption); |
| } |
| |
| void showToolOption(ToolOption option) { |
| List<String> names = option.getNames(); |
| String parameters; |
| if (option.hasArg || option.primaryName.endsWith(":")) { |
| String sep = (option == ToolOption.J) || option.primaryName.endsWith(":") ? "" : " "; |
| parameters = sep + option.getParameters(messager); |
| } else { |
| parameters = ""; |
| } |
| String description = option.getDescription(messager); |
| showUsage(names, parameters, description); |
| } |
| |
| void showDocletOptions(Option.Kind kind) { |
| Comparator<Doclet.Option> comp = new Comparator<Doclet.Option>() { |
| final Collator collator = Collator.getInstance(Locale.US); |
| { collator.setStrength(Collator.PRIMARY); } |
| |
| @Override |
| public int compare(Doclet.Option o1, Doclet.Option o2) { |
| return collator.compare(o1.getNames().get(0), o2.getNames().get(0)); |
| } |
| }; |
| |
| doclet.getSupportedOptions().stream() |
| .filter(opt -> opt.getKind() == kind) |
| .sorted(comp) |
| .forEach(this::showDocletOption); |
| } |
| |
| void showDocletOption(Doclet.Option option) { |
| List<String> names = option.getNames(); |
| String parameters; |
| String optname = names.get(0); |
| if (option.getArgumentCount() > 0 || optname.endsWith(":")) { |
| String sep = optname.endsWith(":") ? "" : " "; |
| parameters = sep + option.getParameters(); |
| } else { |
| parameters = ""; |
| } |
| String description = option.getDescription(); |
| showUsage(names, parameters, description); |
| } |
| |
| // The following constants are intended to format the output to |
| // be similar to that of the java launcher: i.e. "java -help". |
| |
| /** The indent for the option synopsis. */ |
| private static final String SMALL_INDENT = " "; |
| /** The automatic indent for the description. */ |
| private static final String LARGE_INDENT = " "; |
| /** The space allowed for the synopsis, if the description is to be shown on the same line. */ |
| private static final int DEFAULT_SYNOPSIS_WIDTH = 13; |
| /** The nominal maximum line length, when seeing if text will fit on a line. */ |
| private static final int DEFAULT_MAX_LINE_LENGTH = 80; |
| /** The format for a single-line help entry. */ |
| private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s"; |
| |
| void showUsage(List<String> names, String parameters, String description) { |
| String synopses = names.stream() |
| .map(s -> s + parameters) |
| .collect(Collectors.joining(", ")); |
| // If option synopses and description fit on a single line of reasonable length, |
| // display using COMPACT_FORMAT |
| if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH |
| && !description.contains("\n") |
| && (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + description.length() <= DEFAULT_MAX_LINE_LENGTH)) { |
| messager.printNotice(String.format(COMPACT_FORMAT, synopses, description)); |
| return; |
| } |
| |
| // If option synopses fit on a single line of reasonable length, show that; |
| // otherwise, show 1 per line |
| if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) { |
| messager.printNotice(SMALL_INDENT + synopses); |
| } else { |
| for (String name: names) { |
| messager.printNotice(SMALL_INDENT + name + parameters); |
| } |
| } |
| |
| // Finally, show the description |
| messager.printNotice(LARGE_INDENT + description.replace("\n", "\n" + LARGE_INDENT)); |
| } |
| |
| |
| /** |
| * Main program - external wrapper. In order to maintain backward |
| * CLI compatibility, the execution is dispatched to the appropriate |
| * Start mechanism, depending on the doclet variant. |
| * |
| * The doclet tests are performed in the begin method, further on, |
| * this is to minimize argument processing and most importantly the impact |
| * of class loader creation, needed to detect the doclet class variants. |
| */ |
| @SuppressWarnings("deprecation") |
| Result begin(String... argv) { |
| // Preprocess @file arguments |
| try { |
| argv = CommandLine.parse(argv); |
| return begin(Arrays.asList(argv), Collections.emptySet()); |
| } catch (IOException e) { |
| error("main.cant.read", e.getMessage()); |
| return ERROR; |
| } |
| } |
| |
| // Called by 199 API. |
| public boolean begin(Class<?> docletClass, |
| Iterable<String> options, |
| Iterable<? extends JavaFileObject> fileObjects) { |
| this.docletClass = docletClass; |
| List<String> opts = new ArrayList<>(); |
| for (String opt: options) |
| opts.add(opt); |
| |
| return begin(opts, fileObjects).isOK(); |
| } |
| |
| @SuppressWarnings("deprecation") |
| private Result begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) { |
| fileManager = context.get(JavaFileManager.class); |
| if (fileManager == null) { |
| JavacFileManager.preRegister(context); |
| fileManager = context.get(JavaFileManager.class); |
| if (fileManager instanceof BaseFileManager) { |
| ((BaseFileManager) fileManager).autoClose = true; |
| } |
| } |
| |
| // locale, doclet and maybe taglet, needs to be determined first |
| try { |
| docletClass = preprocess(fileManager, options); |
| } catch (ToolException te) { |
| if (!te.result.isOK()) { |
| if (te.message != null) { |
| messager.printError(te.message); |
| } |
| Throwable t = te.getCause(); |
| dumpStack(t == null ? te : t); |
| } |
| return te.result; |
| } catch (OptionException oe) { |
| if (oe.message != null) { |
| messager.printError(oe.message); |
| } |
| oe.m.run(); |
| Throwable t = oe.getCause(); |
| dumpStack(t == null ? oe : t); |
| return oe.result; |
| } |
| if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) { |
| // no need to dispatch to old, safe to init now |
| initMessager(); |
| messager.setLocale(locale); |
| try { |
| Object o = docletClass.getConstructor().newInstance(); |
| doclet = (Doclet) o; |
| } catch (ReflectiveOperationException exc) { |
| if (apiMode) { |
| throw new ClientCodeException(exc); |
| } |
| error("main.could_not_instantiate_class", docletClass); |
| return ERROR; |
| } |
| } else { |
| if (apiMode) { |
| com.sun.tools.javadoc.main.Start ostart |
| = new com.sun.tools.javadoc.main.Start(context); |
| return ostart.begin(docletClass, options, fileObjects) |
| ? OK |
| : ERROR; |
| } |
| warn("main.legacy_api"); |
| String[] array = options.toArray(new String[options.size()]); |
| int rc = com.sun.tools.javadoc.Main.execute( |
| messager.programName, |
| messager.getWriter(WriterKind.ERROR), |
| messager.getWriter(WriterKind.WARNING), |
| messager.getWriter(WriterKind.NOTICE), |
| docletClass.getName(), |
| array); |
| return (rc == 0) ? OK : ERROR; |
| } |
| |
| Result result = OK; |
| try { |
| result = parseAndExecute(options, fileObjects); |
| } catch (com.sun.tools.javac.main.Option.InvalidValueException e) { |
| messager.printError(e.getMessage()); |
| Throwable t = e.getCause(); |
| dumpStack(t == null ? e : t); |
| return ERROR; |
| } catch (OptionException toe) { |
| if (toe.message != null) |
| messager.printError(toe.message); |
| |
| toe.m.run(); |
| Throwable t = toe.getCause(); |
| dumpStack(t == null ? toe : t); |
| return toe.result; |
| } catch (ToolException exc) { |
| if (exc.message != null) { |
| messager.printError(exc.message); |
| } |
| Throwable t = exc.getCause(); |
| if (result == ABNORMAL) { |
| reportInternalError(t == null ? exc : t); |
| } else { |
| dumpStack(t == null ? exc : t); |
| } |
| return exc.result; |
| } catch (OutOfMemoryError ee) { |
| error("main.out.of.memory"); |
| result = SYSERR; |
| dumpStack(ee); |
| } catch (ClientCodeException e) { |
| // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl |
| throw e; |
| } catch (Error | Exception ee) { |
| error("main.fatal.error", ee); |
| reportInternalError(ee); |
| result = ABNORMAL; |
| } finally { |
| if (fileManager != null |
| && fileManager instanceof BaseFileManager |
| && ((BaseFileManager) fileManager).autoClose) { |
| try { |
| fileManager.close(); |
| } catch (IOException ignore) {} |
| } |
| boolean haveErrorWarnings = messager.hasErrors() |
| || (rejectWarnings && messager.hasWarnings()); |
| if (!result.isOK() && !haveErrorWarnings) { |
| // the doclet failed, but nothing reported, flag it!. |
| error("main.unknown.error"); |
| } |
| if (haveErrorWarnings && result.isOK()) { |
| result = ERROR; |
| } |
| messager.printErrorWarningCounts(); |
| messager.flush(); |
| } |
| return result; |
| } |
| |
| private void reportInternalError(Throwable t) { |
| messager.printErrorUsingKey("doclet.internal.report.bug"); |
| dumpStack(true, t); |
| } |
| |
| private void dumpStack(Throwable t) { |
| dumpStack(false, t); |
| } |
| |
| private void dumpStack(boolean enabled, Throwable t) { |
| if (t != null && (enabled || dumpOnError)) { |
| t.printStackTrace(System.err); |
| } |
| } |
| |
| /** |
| * Main program - internal |
| */ |
| @SuppressWarnings("unchecked") |
| private Result parseAndExecute(List<String> argList, Iterable<? extends JavaFileObject> fileObjects) |
| throws ToolException, OptionException, com.sun.tools.javac.main.Option.InvalidValueException { |
| long tm = System.currentTimeMillis(); |
| |
| List<String> javaNames = new ArrayList<>(); |
| |
| compOpts = Options.instance(context); |
| |
| // Make sure no obsolete source/target messages are reported |
| try { |
| com.sun.tools.javac.main.Option.XLINT_CUSTOM.process(getOptionHelper(), "-Xlint:-options"); |
| } catch (com.sun.tools.javac.main.Option.InvalidValueException ignore) { |
| } |
| |
| Arguments arguments = Arguments.instance(context); |
| arguments.init(ProgramName); |
| arguments.allowEmpty(); |
| |
| doclet.init(locale, messager); |
| parseArgs(argList, javaNames); |
| |
| if (!arguments.handleReleaseOptions(extra -> true)) { |
| // Arguments does not always increase the error count in the |
| // case of errors, so increment the error count only if it has |
| // not been updated previously, preventing complaints by callers |
| if (!messager.hasErrors() && !messager.hasWarnings()) |
| messager.nerrors++; |
| return CMDERR; |
| } |
| |
| if (!arguments.validate()) { |
| // Arguments does not always increase the error count in the |
| // case of errors, so increment the error count only if it has |
| // not been updated previously, preventing complaints by callers |
| if (!messager.hasErrors() && !messager.hasWarnings()) |
| messager.nerrors++; |
| return CMDERR; |
| } |
| |
| if (fileManager instanceof BaseFileManager) { |
| ((BaseFileManager) fileManager).handleOptions(fileManagerOpts); |
| } |
| |
| compOpts.notifyListeners(); |
| List<String> modules = (List<String>) jdtoolOpts.computeIfAbsent(ToolOption.MODULE, |
| s -> Collections.EMPTY_LIST); |
| |
| if (modules.isEmpty()) { |
| List<String> subpkgs = (List<String>) jdtoolOpts.computeIfAbsent(ToolOption.SUBPACKAGES, |
| s -> Collections.EMPTY_LIST); |
| if (subpkgs.isEmpty()) { |
| if (javaNames.isEmpty() && isEmpty(fileObjects)) { |
| String text = messager.getText("main.No_modules_packages_or_classes_specified"); |
| throw new ToolException(CMDERR, text); |
| } |
| } |
| } |
| |
| JavadocTool comp = JavadocTool.make0(context); |
| if (comp == null) return ABNORMAL; |
| |
| DocletEnvironment docEnv = comp.getEnvironment(jdtoolOpts, |
| javaNames, |
| fileObjects); |
| |
| // release resources |
| comp = null; |
| |
| if (breakiterator || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { |
| JavacTrees trees = JavacTrees.instance(context); |
| trees.setBreakIterator(BreakIterator.getSentenceInstance(locale)); |
| } |
| // pass off control to the doclet |
| Result returnStatus = docEnv != null && doclet.run(docEnv) |
| ? OK |
| : ERROR; |
| |
| // We're done. |
| if (compOpts.get("-verbose") != null) { |
| tm = System.currentTimeMillis() - tm; |
| messager.notice("main.done_in", Long.toString(tm)); |
| } |
| |
| return returnStatus; |
| } |
| |
| boolean matches(List<String> names, String arg) { |
| for (String name : names) { |
| if (StringUtils.toLowerCase(name).equals(StringUtils.toLowerCase(arg))) |
| return true; |
| } |
| return false; |
| } |
| |
| boolean matches(Doclet.Option option, String arg) { |
| if (matches(option.getNames(), arg)) |
| return true; |
| int sep = arg.indexOf(':'); |
| String targ = arg.substring(0, sep + 1); |
| return matches(option.getNames(), targ); |
| } |
| |
| Set<? extends Doclet.Option> docletOptions = null; |
| int handleDocletOptions(int idx, List<String> args, boolean isToolOption) |
| throws OptionException { |
| if (docletOptions == null) { |
| docletOptions = doclet.getSupportedOptions(); |
| } |
| String arg = args.get(idx); |
| String argBase, argVal; |
| if (arg.startsWith("--") && arg.contains("=")) { |
| int sep = arg.indexOf("="); |
| argBase = arg.substring(0, sep); |
| argVal = arg.substring(sep + 1); |
| } else { |
| argBase = arg; |
| argVal = null; |
| } |
| String text = null; |
| for (Doclet.Option opt : docletOptions) { |
| if (matches(opt, argBase)) { |
| if (argVal != null) { |
| switch (opt.getArgumentCount()) { |
| case 0: |
| text = messager.getText("main.unnecessary_arg_provided", argBase); |
| throw new OptionException(ERROR, this::usage, text); |
| case 1: |
| opt.process(arg, Arrays.asList(argVal)); |
| break; |
| default: |
| text = messager.getText("main.only_one_argument_with_equals", argBase); |
| throw new OptionException(ERROR, this::usage, text); |
| } |
| } else { |
| if (args.size() - idx -1 < opt.getArgumentCount()) { |
| text = messager.getText("main.requires_argument", arg); |
| throw new OptionException(ERROR, this::usage, text); |
| } |
| opt.process(arg, args.subList(idx + 1, args.size())); |
| idx += opt.getArgumentCount(); |
| } |
| return idx; |
| } |
| } |
| // check if arg is accepted by the tool before emitting error |
| if (!isToolOption) { |
| text = messager.getText("main.invalid_flag", arg); |
| throw new OptionException(ERROR, this::usage, text); |
| } |
| return idx; |
| } |
| |
| private Class<?> preprocess(JavaFileManager jfm, |
| List<String> argv) throws ToolException, OptionException { |
| // doclet specifying arguments |
| String userDocletPath = null; |
| String userDocletName = null; |
| |
| // taglet specifying arguments, since tagletpath is a doclet |
| // functionality, assume they are repeated and inspect all. |
| List<File> userTagletPath = new ArrayList<>(); |
| List<String> userTagletNames = new ArrayList<>(); |
| |
| // Step 1: loop through the args, set locale early on, if found. |
| for (int i = 0 ; i < argv.size() ; i++) { |
| String arg = argv.get(i); |
| if (arg.equals(ToolOption.DUMPONERROR.primaryName)) { |
| dumpOnError = true; |
| } else if (arg.equals(ToolOption.LOCALE.primaryName)) { |
| checkOneArg(argv, i++); |
| String lname = argv.get(i); |
| locale = getLocale(lname); |
| } else if (arg.equals(ToolOption.DOCLET.primaryName)) { |
| checkOneArg(argv, i++); |
| if (userDocletName != null) { |
| if (apiMode) { |
| throw new IllegalArgumentException("More than one doclet specified (" + |
| userDocletName + " and " + argv.get(i) + ")."); |
| } |
| String text = messager.getText("main.more_than_one_doclet_specified_0_and_1", |
| userDocletName, argv.get(i)); |
| throw new ToolException(CMDERR, text); |
| } |
| if (docletName != null) { |
| if (apiMode) { |
| throw new IllegalArgumentException("More than one doclet specified (" + |
| docletName + " and " + argv.get(i) + ")."); |
| } |
| String text = messager.getText("main.more_than_one_doclet_specified_0_and_1", |
| docletName, argv.get(i)); |
| throw new ToolException(CMDERR, text); |
| } |
| userDocletName = argv.get(i); |
| } else if (arg.equals(ToolOption.DOCLETPATH.primaryName)) { |
| checkOneArg(argv, i++); |
| if (userDocletPath == null) { |
| userDocletPath = argv.get(i); |
| } else { |
| userDocletPath += File.pathSeparator + argv.get(i); |
| } |
| } else if ("-taglet".equals(arg)) { |
| userTagletNames.add(argv.get(i + 1)); |
| } else if ("-tagletpath".equals(arg)) { |
| for (String pathname : argv.get(i + 1).split(File.pathSeparator)) { |
| userTagletPath.add(new File(pathname)); |
| } |
| } |
| } |
| |
| // Step 2: a doclet is provided, nothing more to do. |
| if (docletClass != null) { |
| return docletClass; |
| } |
| |
| // Step 3: doclet name specified ? if so find a ClassLoader, |
| // and load it. |
| if (userDocletName != null) { |
| ClassLoader cl = classLoader; |
| if (cl == null) { |
| if (!fileManager.hasLocation(DOCLET_PATH)) { |
| List<File> paths = new ArrayList<>(); |
| if (userDocletPath != null) { |
| for (String pathname : userDocletPath.split(File.pathSeparator)) { |
| paths.add(new File(pathname)); |
| } |
| } |
| try { |
| ((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths); |
| } catch (IOException ioe) { |
| if (apiMode) { |
| throw new IllegalArgumentException("Could not set location for " + |
| userDocletPath, ioe); |
| } |
| String text = messager.getText("main.doclet_could_not_set_location", |
| userDocletPath); |
| throw new ToolException(CMDERR, text, ioe); |
| } |
| } |
| cl = fileManager.getClassLoader(DOCLET_PATH); |
| if (cl == null) { |
| // despite doclet specified on cmdline no classloader found! |
| if (apiMode) { |
| throw new IllegalArgumentException("Could not obtain classloader to load " |
| + userDocletPath); |
| } |
| String text = messager.getText("main.doclet_no_classloader_found", |
| userDocletName); |
| throw new ToolException(CMDERR, text); |
| } |
| } |
| try { |
| return cl.loadClass(userDocletName); |
| } catch (ClassNotFoundException cnfe) { |
| if (apiMode) { |
| throw new IllegalArgumentException("Cannot find doclet class " + userDocletName, |
| cnfe); |
| } |
| String text = messager.getText("main.doclet_class_not_found", userDocletName); |
| throw new ToolException(CMDERR, text, cnfe); |
| } |
| } |
| |
| // Step 4: we have a doclet, try loading it |
| if (docletName != null) { |
| return loadDocletClass(docletName); |
| } |
| |
| // finally |
| return StdDoclet; |
| } |
| |
| private Class<?> loadDocletClass(String docletName) throws ToolException { |
| try { |
| return Class.forName(docletName, true, getClass().getClassLoader()); |
| } catch (ClassNotFoundException cnfe) { |
| if (apiMode) { |
| throw new IllegalArgumentException("Cannot find doclet class " + docletName); |
| } |
| String text = messager.getText("main.doclet_class_not_found", docletName); |
| throw new ToolException(CMDERR, text, cnfe); |
| } |
| } |
| |
| private void parseArgs(List<String> args, List<String> javaNames) throws ToolException, |
| OptionException, com.sun.tools.javac.main.Option.InvalidValueException { |
| for (int i = 0 ; i < args.size() ; i++) { |
| String arg = args.get(i); |
| ToolOption o = ToolOption.get(arg); |
| if (o != null) { |
| // handle a doclet argument that may be needed however |
| // don't increment the index, and allow the tool to consume args |
| handleDocletOptions(i, args, true); |
| if (o.hasArg) { |
| if (arg.startsWith("--") && arg.contains("=")) { |
| o.process(this, arg.substring(arg.indexOf('=') + 1)); |
| } else { |
| checkOneArg(args, i++); |
| o.process(this, args.get(i)); |
| } |
| } else if (o.hasSuffix) { |
| o.process(this, arg); |
| } else { |
| o.process(this); |
| } |
| } else if (arg.startsWith("-XD")) { |
| // hidden javac options |
| String s = arg.substring("-XD".length()); |
| int eq = s.indexOf('='); |
| String key = (eq < 0) ? s : s.substring(0, eq); |
| String value = (eq < 0) ? s : s.substring(eq+1); |
| compOpts.put(key, value); |
| } else if (arg.startsWith("-")) { |
| i = handleDocletOptions(i, args, false); |
| } else { |
| javaNames.add(arg); |
| } |
| } |
| } |
| |
| private <T> boolean isEmpty(Iterable<T> iter) { |
| return !iter.iterator().hasNext(); |
| } |
| |
| /** |
| * Check the one arg option. |
| * Error and exit if one argument is not provided. |
| */ |
| private void checkOneArg(List<String> args, int index) throws OptionException { |
| if ((index + 1) >= args.size() || args.get(index + 1).startsWith("-d")) { |
| String text = messager.getText("main.requires_argument", args.get(index)); |
| throw new OptionException(CMDERR, this::usage, text); |
| } |
| } |
| |
| void error(String key, Object... args) { |
| messager.printErrorUsingKey(key, args); |
| } |
| |
| void warn(String key, Object... args) { |
| messager.printWarningUsingKey(key, args); |
| } |
| |
| /** |
| * Get the locale if specified on the command line |
| * else return null and if locale option is not used |
| * then return default locale. |
| */ |
| private Locale getLocale(String localeName) throws ToolException { |
| Locale userlocale = null; |
| if (localeName == null || localeName.isEmpty()) { |
| return Locale.getDefault(); |
| } |
| int firstuscore = localeName.indexOf('_'); |
| int seconduscore = -1; |
| String language = null; |
| String country = null; |
| String variant = null; |
| if (firstuscore == 2) { |
| language = localeName.substring(0, firstuscore); |
| seconduscore = localeName.indexOf('_', firstuscore + 1); |
| if (seconduscore > 0) { |
| if (seconduscore != firstuscore + 3 |
| || localeName.length() <= seconduscore + 1) { |
| String text = messager.getText("main.malformed_locale_name", localeName); |
| throw new ToolException(CMDERR, text); |
| } |
| country = localeName.substring(firstuscore + 1, |
| seconduscore); |
| variant = localeName.substring(seconduscore + 1); |
| } else if (localeName.length() == firstuscore + 3) { |
| country = localeName.substring(firstuscore + 1); |
| } else { |
| String text = messager.getText("main.malformed_locale_name", localeName); |
| throw new ToolException(CMDERR, text); |
| } |
| } else if (firstuscore == -1 && localeName.length() == 2) { |
| language = localeName; |
| } else { |
| String text = messager.getText("main.malformed_locale_name", localeName); |
| throw new ToolException(CMDERR, text); |
| } |
| userlocale = searchLocale(language, country, variant); |
| if (userlocale == null) { |
| String text = messager.getText("main.illegal_locale_name", localeName); |
| throw new ToolException(CMDERR, text); |
| } else { |
| return userlocale; |
| } |
| } |
| |
| /** |
| * Search the locale for specified language, specified country and |
| * specified variant. |
| */ |
| private Locale searchLocale(String language, String country, |
| String variant) { |
| for (Locale loc : Locale.getAvailableLocales()) { |
| if (loc.getLanguage().equals(language) && |
| (country == null || loc.getCountry().equals(country)) && |
| (variant == null || loc.getVariant().equals(variant))) { |
| return loc; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| OptionHelper getOptionHelper() { |
| return new GrumpyHelper(messager) { |
| @Override |
| public String get(com.sun.tools.javac.main.Option option) { |
| return compOpts.get(option); |
| } |
| |
| @Override |
| public void put(String name, String value) { |
| compOpts.put(name, value); |
| } |
| |
| @Override |
| public void remove(String name) { |
| compOpts.remove(name); |
| } |
| |
| @Override |
| public boolean handleFileManagerOption(com.sun.tools.javac.main.Option option, String value) { |
| fileManagerOpts.put(option, value); |
| return true; |
| } |
| }; |
| } |
| |
| @Override |
| String getLocalizedMessage(String msg, Object... args) { |
| return messager.getText(msg, args); |
| } |
| } |