--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/main/Start.java Mon May 09 16:52:15 2016 -0700
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 1997, 2016, 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 com.sun.tools.javadoc.main;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+
+import com.sun.javadoc.*;
+import com.sun.tools.javac.file.JavacFileManager;
+import com.sun.tools.javac.main.CommandLine;
+import com.sun.tools.javac.main.Option;
+import com.sun.tools.javac.file.BaseFileManager;
+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.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Options;
+
+import static com.sun.tools.javac.code.Flags.*;
+
+/**
+ * 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>
+ *
+ * @since 1.2
+ * @author Robert Field
+ * @author Neal Gafter (rewrite)
+ */
+public class Start extends ToolOption.Helper {
+ /** Context for this invocation. */
+ private final Context context;
+
+ private final String defaultDocletClassName;
+ private final ClassLoader docletParentClassLoader;
+
+ private static final String javadocName = "javadoc";
+
+ private static final String standardDocletClassName =
+ "com.sun.tools.doclets.standard.Standard";
+
+ private final long defaultFilter = PUBLIC | PROTECTED;
+
+ private final Messager messager;
+
+ private DocletInvoker docletInvoker;
+
+ /**
+ * In API mode, exceptions thrown while calling the doclet are
+ * propagated using ClientCodeException.
+ */
+ private boolean apiMode;
+
+ private JavaFileManager fileManager;
+
+ public Start(String programName,
+ PrintWriter errWriter,
+ PrintWriter warnWriter,
+ PrintWriter noticeWriter,
+ String defaultDocletClassName) {
+ this(programName, errWriter, warnWriter, noticeWriter, defaultDocletClassName, null);
+ }
+
+ public Start(PrintWriter pw) {
+ this(javadocName, pw, pw, pw, standardDocletClassName);
+ }
+
+ public Start(String programName,
+ PrintWriter errWriter,
+ PrintWriter warnWriter,
+ PrintWriter noticeWriter,
+ String defaultDocletClassName,
+ ClassLoader docletParentClassLoader) {
+ context = new Context();
+ messager = new Messager(context, programName, errWriter, warnWriter, noticeWriter);
+ this.defaultDocletClassName = defaultDocletClassName;
+ this.docletParentClassLoader = docletParentClassLoader;
+ }
+
+ public Start(String programName, String defaultDocletClassName) {
+ this(programName, defaultDocletClassName, null);
+ }
+
+ public Start(String programName, String defaultDocletClassName,
+ ClassLoader docletParentClassLoader) {
+ context = new Context();
+ messager = new Messager(context, programName);
+ this.defaultDocletClassName = defaultDocletClassName;
+ this.docletParentClassLoader = docletParentClassLoader;
+ }
+
+ public Start(String programName, ClassLoader docletParentClassLoader) {
+ this(programName, standardDocletClassName, docletParentClassLoader);
+ }
+
+ public Start(String programName) {
+ this(programName, standardDocletClassName);
+ }
+
+ public Start(ClassLoader docletParentClassLoader) {
+ this(javadocName, docletParentClassLoader);
+ }
+
+ public Start() {
+ this(javadocName);
+ }
+
+ public Start(Context context) {
+ this.context = Objects.requireNonNull(context);
+ apiMode = true;
+ defaultDocletClassName = standardDocletClassName;
+ docletParentClassLoader = null;
+
+ Log log = context.get(Log.logKey);
+ if (log instanceof Messager)
+ messager = (Messager) log;
+ else {
+ PrintWriter out = context.get(Log.outKey);
+ messager = (out == null) ? new Messager(context, javadocName)
+ : new Messager(context, javadocName, out, out, out);
+ }
+ }
+
+ /**
+ * Usage
+ */
+ @Override
+ void usage() {
+ usage(true);
+ }
+
+ void usage(boolean exit) {
+ usage("main.usage", "-help", null, exit);
+ }
+
+ @Override
+ void Xusage() {
+ Xusage(true);
+ }
+
+ void Xusage(boolean exit) {
+ usage("main.Xusage", "-X", "main.Xusage.foot", exit);
+ }
+
+ private void usage(String main, String doclet, String foot, boolean exit) {
+ // RFE: it would be better to replace the following with code to
+ // write a header, then help for each option, then a footer.
+ messager.notice(main);
+
+ // let doclet print usage information (does nothing on error)
+ if (docletInvoker != null) {
+ // RFE: this is a pretty bad way to get the doclet to show
+ // help info. Moreover, the output appears on stdout,
+ // and <i>not</i> on any of the standard streams passed
+ // to javadoc, and in particular, not to the noticeWriter
+ // But, to fix this, we need to fix the Doclet API.
+ docletInvoker.optionLength(doclet);
+ }
+
+ if (foot != null)
+ messager.notice(foot);
+
+ if (exit) exit();
+ }
+
+ /**
+ * Exit
+ */
+ private void exit() {
+ messager.exit();
+ }
+
+
+ /**
+ * Main program - external wrapper
+ */
+ public int begin(String... argv) {
+ boolean ok = begin(null, argv, Collections.<JavaFileObject> emptySet());
+ return ok ? 0 : 1;
+ }
+
+ public boolean begin(Class<?> docletClass, Iterable<String> options, Iterable<? extends JavaFileObject> fileObjects) {
+ Collection<String> opts = new ArrayList<>();
+ for (String opt: options) opts.add(opt);
+ return begin(docletClass, opts.toArray(new String[opts.size()]), fileObjects);
+ }
+
+ private boolean begin(Class<?> docletClass, String[] options, Iterable<? extends JavaFileObject> fileObjects) {
+ boolean failed = false;
+
+ try {
+ failed = !parseAndExecute(docletClass, options, fileObjects);
+ } catch (Messager.ExitJavadoc exc) {
+ // ignore, we just exit this way
+ } catch (OutOfMemoryError ee) {
+ messager.error(Messager.NOPOS, "main.out.of.memory");
+ failed = true;
+ } catch (ClientCodeException e) {
+ // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
+ throw e;
+ } catch (Error ee) {
+ ee.printStackTrace(System.err);
+ messager.error(Messager.NOPOS, "main.fatal.error");
+ failed = true;
+ } catch (Exception ee) {
+ ee.printStackTrace(System.err);
+ messager.error(Messager.NOPOS, "main.fatal.exception");
+ failed = true;
+ } finally {
+ if (fileManager != null
+ && fileManager instanceof BaseFileManager
+ && ((BaseFileManager) fileManager).autoClose) {
+ try {
+ fileManager.close();
+ } catch (IOException ignore) {
+ }
+ }
+ messager.exitNotice();
+ messager.flush();
+ }
+ failed |= messager.nerrors() > 0;
+ failed |= rejectWarnings && messager.nwarnings() > 0;
+ return !failed;
+ }
+
+ /**
+ * Main program - internal
+ */
+ private boolean parseAndExecute(
+ Class<?> docletClass,
+ String[] argv,
+ Iterable<? extends JavaFileObject> fileObjects) throws IOException {
+ long tm = System.currentTimeMillis();
+
+ ListBuffer<String> javaNames = new ListBuffer<>();
+
+ // Preprocess @file arguments
+ try {
+ argv = CommandLine.parse(argv);
+ } catch (FileNotFoundException e) {
+ messager.error(Messager.NOPOS, "main.cant.read", e.getMessage());
+ exit();
+ } catch (IOException e) {
+ e.printStackTrace(System.err);
+ exit();
+ }
+
+
+ fileManager = context.get(JavaFileManager.class);
+
+ setDocletInvoker(docletClass, fileManager, argv);
+
+ compOpts = Options.instance(context);
+ // Make sure no obsolete source/target messages are reported
+ compOpts.put("-Xlint:-options", "-Xlint:-options");
+
+ // Parse arguments
+ for (int i = 0 ; i < argv.length ; i++) {
+ String arg = argv[i];
+
+ ToolOption o = ToolOption.get(arg);
+ if (o != null) {
+ // hack: this restriction should be removed
+ if (o == ToolOption.LOCALE && i > 0)
+ usageError("main.locale_first");
+
+ if (o.hasArg) {
+ oneArg(argv, i++);
+ o.process(this, argv[i]);
+ } else {
+ setOption(arg);
+ o.process(this);
+ }
+ } else if (arg.equals("-XDaccessInternalAPI")) {
+ // pass this hidden option down to the doclet, if it wants it
+ if (docletInvoker.optionLength("-XDaccessInternalAPI") == 1) {
+ setOption(arg);
+ }
+ } 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);
+ }
+ // call doclet for its options
+ // other arg starts with - is invalid
+ else if (arg.startsWith("-")) {
+ int optionLength;
+ optionLength = docletInvoker.optionLength(arg);
+ if (optionLength < 0) {
+ // error already displayed
+ exit();
+ } else if (optionLength == 0) {
+ // option not found
+ usageError("main.invalid_flag", arg);
+ } else {
+ // doclet added option
+ if ((i + optionLength) > argv.length) {
+ usageError("main.requires_argument", arg);
+ }
+ ListBuffer<String> args = new ListBuffer<>();
+ for (int j = 0; j < optionLength-1; ++j) {
+ args.append(argv[++i]);
+ }
+ setOption(arg, args.toList());
+ }
+ } else {
+ javaNames.append(arg);
+ }
+ }
+
+ if (fileManager == null) {
+ JavacFileManager.preRegister(context);
+ fileManager = context.get(JavaFileManager.class);
+ if (fileManager instanceof BaseFileManager) {
+ ((BaseFileManager) fileManager).autoClose = true;
+ }
+ }
+ if (fileManager instanceof BaseFileManager) {
+ ((BaseFileManager) fileManager).handleOptions(fileManagerOpts);
+ }
+
+ String platformString = compOpts.get("-release");
+
+ if (platformString != null) {
+ if (compOpts.isSet("-source")) {
+ usageError("main.release.bootclasspath.conflict", "-source");
+ }
+ if (fileManagerOpts.containsKey(Option.BOOTCLASSPATH)) {
+ usageError("main.release.bootclasspath.conflict", Option.BOOTCLASSPATH.getText());
+ }
+
+ PlatformDescription platformDescription =
+ PlatformUtils.lookupPlatformDescription(platformString);
+
+ if (platformDescription == null) {
+ usageError("main.unsupported.release.version", platformString);
+ }
+
+ compOpts.put(Option.SOURCE, platformDescription.getSourceVersion());
+
+ context.put(PlatformDescription.class, platformDescription);
+
+ Collection<Path> platformCP = platformDescription.getPlatformPath();
+
+ if (platformCP != null) {
+ if (fileManager instanceof StandardJavaFileManager) {
+ StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
+
+ sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP);
+ } else {
+ usageError("main.release.not.standard.file.manager", platformString);
+ }
+ }
+ }
+
+ compOpts.notifyListeners();
+
+ if (javaNames.isEmpty() && subPackages.isEmpty() && isEmpty(fileObjects)) {
+ usageError("main.No_packages_or_classes_specified");
+ }
+
+ if (!docletInvoker.validOptions(options.toList())) {
+ // error message already displayed
+ exit();
+ }
+
+ JavadocTool comp = JavadocTool.make0(context);
+ if (comp == null) return false;
+
+ if (showAccess == null) {
+ setFilter(defaultFilter);
+ }
+
+ LanguageVersion languageVersion = docletInvoker.languageVersion();
+ RootDocImpl root = comp.getRootDocImpl(
+ docLocale,
+ encoding,
+ showAccess,
+ javaNames.toList(),
+ options.toList(),
+ fileObjects,
+ breakiterator,
+ subPackages.toList(),
+ excludedPackages.toList(),
+ docClasses,
+ // legacy?
+ languageVersion == null || languageVersion == LanguageVersion.JAVA_1_1,
+ quiet);
+
+ // release resources
+ comp = null;
+
+ // pass off control to the doclet
+ boolean ok = root != null;
+ if (ok) ok = docletInvoker.start(root);
+
+ // We're done.
+ if (compOpts.get("-verbose") != null) {
+ tm = System.currentTimeMillis() - tm;
+ messager.notice("main.done_in", Long.toString(tm));
+ }
+
+ return ok;
+ }
+
+ private <T> boolean isEmpty(Iterable<T> iter) {
+ return !iter.iterator().hasNext();
+ }
+
+ /**
+ * Init the doclet invoker.
+ * The doclet class may be given explicitly, or via the -doclet option in
+ * argv.
+ * If the doclet class is not given explicitly, it will be loaded from
+ * the file manager's DOCLET_PATH location, if available, or via the
+ * -doclet path option in argv.
+ * @param docletClass The doclet class. May be null.
+ * @param fileManager The file manager used to get the class loader to load
+ * the doclet class if required. May be null.
+ * @param argv Args containing -doclet and -docletpath, in case they are required.
+ */
+ private void setDocletInvoker(Class<?> docletClass, JavaFileManager fileManager, String[] argv) {
+ boolean exportInternalAPI = false;
+ String docletClassName = null;
+ String docletPath = null;
+
+ // Parse doclet specifying arguments
+ for (int i = 0 ; i < argv.length ; i++) {
+ String arg = argv[i];
+ if (arg.equals(ToolOption.DOCLET.opt)) {
+ oneArg(argv, i++);
+ if (docletClassName != null) {
+ usageError("main.more_than_one_doclet_specified_0_and_1",
+ docletClassName, argv[i]);
+ }
+ docletClassName = argv[i];
+ } else if (arg.equals(ToolOption.DOCLETPATH.opt)) {
+ oneArg(argv, i++);
+ if (docletPath == null) {
+ docletPath = argv[i];
+ } else {
+ docletPath += File.pathSeparator + argv[i];
+ }
+ } else if (arg.equals("-XDaccessInternalAPI")) {
+ exportInternalAPI = true;
+ }
+ }
+
+ if (docletClass != null) {
+ // TODO, check no -doclet, -docletpath
+ docletInvoker = new DocletInvoker(messager, docletClass, apiMode, exportInternalAPI);
+ } else {
+ if (docletClassName == null) {
+ docletClassName = defaultDocletClassName;
+ }
+
+ // attempt to find doclet
+ docletInvoker = new DocletInvoker(messager, fileManager,
+ docletClassName, docletPath,
+ docletParentClassLoader,
+ apiMode,
+ exportInternalAPI);
+ }
+ }
+
+ /**
+ * Set one arg option.
+ * Error and exit if one argument is not provided.
+ */
+ private void oneArg(String[] args, int index) {
+ if ((index + 1) < args.length) {
+ setOption(args[index], args[index+1]);
+ } else {
+ usageError("main.requires_argument", args[index]);
+ }
+ }
+
+ @Override
+ void usageError(String key, Object... args) {
+ messager.error(Messager.NOPOS, key, args);
+ usage(true);
+ }
+
+ /**
+ * indicate an option with no arguments was given.
+ */
+ private void setOption(String opt) {
+ String[] option = { opt };
+ options.append(option);
+ }
+
+ /**
+ * indicate an option with one argument was given.
+ */
+ private void setOption(String opt, String argument) {
+ String[] option = { opt, argument };
+ options.append(option);
+ }
+
+ /**
+ * indicate an option with the specified list of arguments was given.
+ */
+ private void setOption(String opt, List<String> arguments) {
+ String[] args = new String[arguments.length() + 1];
+ int k = 0;
+ args[k++] = opt;
+ for (List<String> i = arguments; i.nonEmpty(); i=i.tail) {
+ args[k++] = i.head;
+ }
+ options.append(args);
+ }
+}