aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.javadoc; aoqi@0: aoqi@0: import java.io.File; aoqi@0: import java.io.FileNotFoundException; aoqi@0: import java.io.IOException; aoqi@0: import java.io.PrintWriter; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Collection; aoqi@0: import java.util.Collections; aoqi@0: aoqi@0: import javax.tools.JavaFileManager; aoqi@0: import javax.tools.JavaFileObject; aoqi@0: aoqi@0: import com.sun.javadoc.*; aoqi@0: import com.sun.tools.javac.main.CommandLine; aoqi@0: import com.sun.tools.javac.util.ClientCodeException; aoqi@0: import com.sun.tools.javac.util.Context; aoqi@0: import com.sun.tools.javac.util.List; aoqi@0: import com.sun.tools.javac.util.ListBuffer; aoqi@0: import com.sun.tools.javac.util.Log; aoqi@0: import com.sun.tools.javac.util.Options; aoqi@0: import static com.sun.tools.javac.code.Flags.*; aoqi@0: aoqi@0: /** aoqi@0: * Main program of Javadoc. aoqi@0: * Previously named "Main". aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own risk. aoqi@0: * This code and its internal interfaces are subject to change or aoqi@0: * deletion without notice. aoqi@0: * aoqi@0: * @since 1.2 aoqi@0: * @author Robert Field aoqi@0: * @author Neal Gafter (rewrite) aoqi@0: */ aoqi@0: public class Start extends ToolOption.Helper { aoqi@0: /** Context for this invocation. */ aoqi@0: private final Context context; aoqi@0: aoqi@0: private final String defaultDocletClassName; aoqi@0: private final ClassLoader docletParentClassLoader; aoqi@0: aoqi@0: private static final String javadocName = "javadoc"; aoqi@0: aoqi@0: private static final String standardDocletClassName = aoqi@0: "com.sun.tools.doclets.standard.Standard"; aoqi@0: aoqi@0: private long defaultFilter = PUBLIC | PROTECTED; aoqi@0: aoqi@0: private final Messager messager; aoqi@0: aoqi@0: private DocletInvoker docletInvoker; aoqi@0: aoqi@0: /** aoqi@0: * In API mode, exceptions thrown while calling the doclet are aoqi@0: * propagated using ClientCodeException. aoqi@0: */ aoqi@0: private boolean apiMode; aoqi@0: aoqi@0: Start(String programName, aoqi@0: PrintWriter errWriter, aoqi@0: PrintWriter warnWriter, aoqi@0: PrintWriter noticeWriter, aoqi@0: String defaultDocletClassName) { aoqi@0: this(programName, errWriter, warnWriter, noticeWriter, defaultDocletClassName, null); aoqi@0: } aoqi@0: aoqi@0: Start(String programName, aoqi@0: PrintWriter errWriter, aoqi@0: PrintWriter warnWriter, aoqi@0: PrintWriter noticeWriter, aoqi@0: String defaultDocletClassName, aoqi@0: ClassLoader docletParentClassLoader) { aoqi@0: context = new Context(); aoqi@0: messager = new Messager(context, programName, errWriter, warnWriter, noticeWriter); aoqi@0: this.defaultDocletClassName = defaultDocletClassName; aoqi@0: this.docletParentClassLoader = docletParentClassLoader; aoqi@0: } aoqi@0: aoqi@0: Start(String programName, String defaultDocletClassName) { aoqi@0: this(programName, defaultDocletClassName, null); aoqi@0: } aoqi@0: aoqi@0: Start(String programName, String defaultDocletClassName, aoqi@0: ClassLoader docletParentClassLoader) { aoqi@0: context = new Context(); aoqi@0: messager = new Messager(context, programName); aoqi@0: this.defaultDocletClassName = defaultDocletClassName; aoqi@0: this.docletParentClassLoader = docletParentClassLoader; aoqi@0: } aoqi@0: aoqi@0: Start(String programName, ClassLoader docletParentClassLoader) { aoqi@0: this(programName, standardDocletClassName, docletParentClassLoader); aoqi@0: } aoqi@0: aoqi@0: Start(String programName) { aoqi@0: this(programName, standardDocletClassName); aoqi@0: } aoqi@0: aoqi@0: Start(ClassLoader docletParentClassLoader) { aoqi@0: this(javadocName, docletParentClassLoader); aoqi@0: } aoqi@0: aoqi@0: Start() { aoqi@0: this(javadocName); aoqi@0: } aoqi@0: aoqi@0: public Start(Context context) { aoqi@0: context.getClass(); // null check aoqi@0: this.context = context; aoqi@0: apiMode = true; aoqi@0: defaultDocletClassName = standardDocletClassName; aoqi@0: docletParentClassLoader = null; aoqi@0: aoqi@0: Log log = context.get(Log.logKey); aoqi@0: if (log instanceof Messager) aoqi@0: messager = (Messager) log; aoqi@0: else { aoqi@0: PrintWriter out = context.get(Log.outKey); aoqi@0: messager = (out == null) ? new Messager(context, javadocName) aoqi@0: : new Messager(context, javadocName, out, out, out); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Usage aoqi@0: */ aoqi@0: @Override aoqi@0: void usage() { aoqi@0: usage(true); aoqi@0: } aoqi@0: aoqi@0: void usage(boolean exit) { aoqi@0: usage("main.usage", "-help", null, exit); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: void Xusage() { aoqi@0: Xusage(true); aoqi@0: } aoqi@0: aoqi@0: void Xusage(boolean exit) { aoqi@0: usage("main.Xusage", "-X", "main.Xusage.foot", exit); aoqi@0: } aoqi@0: aoqi@0: private void usage(String main, String doclet, String foot, boolean exit) { aoqi@0: // RFE: it would be better to replace the following with code to aoqi@0: // write a header, then help for each option, then a footer. aoqi@0: messager.notice(main); aoqi@0: aoqi@0: // let doclet print usage information (does nothing on error) aoqi@0: if (docletInvoker != null) { aoqi@0: // RFE: this is a pretty bad way to get the doclet to show aoqi@0: // help info. Moreover, the output appears on stdout, aoqi@0: // and not on any of the standard streams passed aoqi@0: // to javadoc, and in particular, not to the noticeWriter aoqi@0: // But, to fix this, we need to fix the Doclet API. aoqi@0: docletInvoker.optionLength(doclet); aoqi@0: } aoqi@0: aoqi@0: if (foot != null) aoqi@0: messager.notice(foot); aoqi@0: aoqi@0: if (exit) exit(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Exit aoqi@0: */ aoqi@0: private void exit() { aoqi@0: messager.exit(); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Main program - external wrapper aoqi@0: */ aoqi@0: int begin(String... argv) { aoqi@0: boolean ok = begin(null, argv, Collections. emptySet()); aoqi@0: return ok ? 0 : 1; aoqi@0: } aoqi@0: aoqi@0: public boolean begin(Class docletClass, Iterable options, Iterable fileObjects) { aoqi@0: Collection opts = new ArrayList(); aoqi@0: for (String opt: options) opts.add(opt); aoqi@0: return begin(docletClass, opts.toArray(new String[opts.size()]), fileObjects); aoqi@0: } aoqi@0: aoqi@0: private boolean begin(Class docletClass, String[] options, Iterable fileObjects) { aoqi@0: boolean failed = false; aoqi@0: aoqi@0: try { aoqi@0: failed = !parseAndExecute(docletClass, options, fileObjects); aoqi@0: } catch (Messager.ExitJavadoc exc) { aoqi@0: // ignore, we just exit this way aoqi@0: } catch (OutOfMemoryError ee) { aoqi@0: messager.error(Messager.NOPOS, "main.out.of.memory"); aoqi@0: failed = true; aoqi@0: } catch (ClientCodeException e) { aoqi@0: // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl aoqi@0: throw e; aoqi@0: } catch (Error ee) { aoqi@0: ee.printStackTrace(System.err); aoqi@0: messager.error(Messager.NOPOS, "main.fatal.error"); aoqi@0: failed = true; aoqi@0: } catch (Exception ee) { aoqi@0: ee.printStackTrace(System.err); aoqi@0: messager.error(Messager.NOPOS, "main.fatal.exception"); aoqi@0: failed = true; aoqi@0: } finally { aoqi@0: messager.exitNotice(); aoqi@0: messager.flush(); aoqi@0: } aoqi@0: failed |= messager.nerrors() > 0; aoqi@0: failed |= rejectWarnings && messager.nwarnings() > 0; aoqi@0: return !failed; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Main program - internal aoqi@0: */ aoqi@0: private boolean parseAndExecute( aoqi@0: Class docletClass, aoqi@0: String[] argv, aoqi@0: Iterable fileObjects) throws IOException { aoqi@0: long tm = System.currentTimeMillis(); aoqi@0: aoqi@0: ListBuffer javaNames = new ListBuffer(); aoqi@0: aoqi@0: // Preprocess @file arguments aoqi@0: try { aoqi@0: argv = CommandLine.parse(argv); aoqi@0: } catch (FileNotFoundException e) { aoqi@0: messager.error(Messager.NOPOS, "main.cant.read", e.getMessage()); aoqi@0: exit(); aoqi@0: } catch (IOException e) { aoqi@0: e.printStackTrace(System.err); aoqi@0: exit(); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: JavaFileManager fileManager = context.get(JavaFileManager.class); aoqi@0: setDocletInvoker(docletClass, fileManager, argv); aoqi@0: aoqi@0: compOpts = Options.instance(context); aoqi@0: // Make sure no obsolete source/target messages are reported aoqi@0: compOpts.put("-Xlint:-options", "-Xlint:-options"); aoqi@0: aoqi@0: // Parse arguments aoqi@0: for (int i = 0 ; i < argv.length ; i++) { aoqi@0: String arg = argv[i]; aoqi@0: aoqi@0: ToolOption o = ToolOption.get(arg); aoqi@0: if (o != null) { aoqi@0: // hack: this restriction should be removed aoqi@0: if (o == ToolOption.LOCALE && i > 0) aoqi@0: usageError("main.locale_first"); aoqi@0: aoqi@0: if (o.hasArg) { aoqi@0: oneArg(argv, i++); aoqi@0: o.process(this, argv[i]); aoqi@0: } else { aoqi@0: setOption(arg); aoqi@0: o.process(this); aoqi@0: } aoqi@0: aoqi@0: } else if (arg.startsWith("-XD")) { aoqi@0: // hidden javac options aoqi@0: String s = arg.substring("-XD".length()); aoqi@0: int eq = s.indexOf('='); aoqi@0: String key = (eq < 0) ? s : s.substring(0, eq); aoqi@0: String value = (eq < 0) ? s : s.substring(eq+1); aoqi@0: compOpts.put(key, value); aoqi@0: } aoqi@0: // call doclet for its options aoqi@0: // other arg starts with - is invalid aoqi@0: else if (arg.startsWith("-")) { aoqi@0: int optionLength; aoqi@0: optionLength = docletInvoker.optionLength(arg); aoqi@0: if (optionLength < 0) { aoqi@0: // error already displayed aoqi@0: exit(); aoqi@0: } else if (optionLength == 0) { aoqi@0: // option not found aoqi@0: usageError("main.invalid_flag", arg); aoqi@0: } else { aoqi@0: // doclet added option aoqi@0: if ((i + optionLength) > argv.length) { aoqi@0: usageError("main.requires_argument", arg); aoqi@0: } aoqi@0: ListBuffer args = new ListBuffer(); aoqi@0: for (int j = 0; j < optionLength-1; ++j) { aoqi@0: args.append(argv[++i]); aoqi@0: } aoqi@0: setOption(arg, args.toList()); aoqi@0: } aoqi@0: } else { aoqi@0: javaNames.append(arg); aoqi@0: } aoqi@0: } aoqi@0: compOpts.notifyListeners(); aoqi@0: aoqi@0: if (javaNames.isEmpty() && subPackages.isEmpty() && isEmpty(fileObjects)) { aoqi@0: usageError("main.No_packages_or_classes_specified"); aoqi@0: } aoqi@0: aoqi@0: if (!docletInvoker.validOptions(options.toList())) { aoqi@0: // error message already displayed aoqi@0: exit(); aoqi@0: } aoqi@0: aoqi@0: JavadocTool comp = JavadocTool.make0(context); aoqi@0: if (comp == null) return false; aoqi@0: aoqi@0: if (showAccess == null) { aoqi@0: setFilter(defaultFilter); aoqi@0: } aoqi@0: aoqi@0: LanguageVersion languageVersion = docletInvoker.languageVersion(); aoqi@0: RootDocImpl root = comp.getRootDocImpl( aoqi@0: docLocale, aoqi@0: encoding, aoqi@0: showAccess, aoqi@0: javaNames.toList(), aoqi@0: options.toList(), aoqi@0: fileObjects, aoqi@0: breakiterator, aoqi@0: subPackages.toList(), aoqi@0: excludedPackages.toList(), aoqi@0: docClasses, aoqi@0: // legacy? aoqi@0: languageVersion == null || languageVersion == LanguageVersion.JAVA_1_1, aoqi@0: quiet); aoqi@0: aoqi@0: // release resources aoqi@0: comp = null; aoqi@0: aoqi@0: // pass off control to the doclet aoqi@0: boolean ok = root != null; aoqi@0: if (ok) ok = docletInvoker.start(root); aoqi@0: aoqi@0: // We're done. aoqi@0: if (compOpts.get("-verbose") != null) { aoqi@0: tm = System.currentTimeMillis() - tm; aoqi@0: messager.notice("main.done_in", Long.toString(tm)); aoqi@0: } aoqi@0: aoqi@0: return ok; aoqi@0: } aoqi@0: aoqi@0: private boolean isEmpty(Iterable iter) { aoqi@0: return !iter.iterator().hasNext(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Init the doclet invoker. aoqi@0: * The doclet class may be given explicitly, or via the -doclet option in aoqi@0: * argv. aoqi@0: * If the doclet class is not given explicitly, it will be loaded from aoqi@0: * the file manager's DOCLET_PATH location, if available, or via the aoqi@0: * -doclet path option in argv. aoqi@0: * @param docletClass The doclet class. May be null. aoqi@0: * @param fileManager The file manager used to get the class loader to load aoqi@0: * the doclet class if required. May be null. aoqi@0: * @param argv Args containing -doclet and -docletpath, in case they are required. aoqi@0: */ aoqi@0: private void setDocletInvoker(Class docletClass, JavaFileManager fileManager, String[] argv) { aoqi@0: if (docletClass != null) { aoqi@0: docletInvoker = new DocletInvoker(messager, docletClass, apiMode); aoqi@0: // TODO, check no -doclet, -docletpath aoqi@0: return; aoqi@0: } aoqi@0: aoqi@0: String docletClassName = null; aoqi@0: String docletPath = null; aoqi@0: aoqi@0: // Parse doclet specifying arguments aoqi@0: for (int i = 0 ; i < argv.length ; i++) { aoqi@0: String arg = argv[i]; aoqi@0: if (arg.equals(ToolOption.DOCLET.opt)) { aoqi@0: oneArg(argv, i++); aoqi@0: if (docletClassName != null) { aoqi@0: usageError("main.more_than_one_doclet_specified_0_and_1", aoqi@0: docletClassName, argv[i]); aoqi@0: } aoqi@0: docletClassName = argv[i]; aoqi@0: } else if (arg.equals(ToolOption.DOCLETPATH.opt)) { aoqi@0: oneArg(argv, i++); aoqi@0: if (docletPath == null) { aoqi@0: docletPath = argv[i]; aoqi@0: } else { aoqi@0: docletPath += File.pathSeparator + argv[i]; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: if (docletClassName == null) { aoqi@0: docletClassName = defaultDocletClassName; aoqi@0: } aoqi@0: aoqi@0: // attempt to find doclet aoqi@0: docletInvoker = new DocletInvoker(messager, fileManager, aoqi@0: docletClassName, docletPath, aoqi@0: docletParentClassLoader, aoqi@0: apiMode); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Set one arg option. aoqi@0: * Error and exit if one argument is not provided. aoqi@0: */ aoqi@0: private void oneArg(String[] args, int index) { aoqi@0: if ((index + 1) < args.length) { aoqi@0: setOption(args[index], args[index+1]); aoqi@0: } else { aoqi@0: usageError("main.requires_argument", args[index]); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: void usageError(String key, Object... args) { aoqi@0: messager.error(Messager.NOPOS, key, args); aoqi@0: usage(true); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * indicate an option with no arguments was given. aoqi@0: */ aoqi@0: private void setOption(String opt) { aoqi@0: String[] option = { opt }; aoqi@0: options.append(option); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * indicate an option with one argument was given. aoqi@0: */ aoqi@0: private void setOption(String opt, String argument) { aoqi@0: String[] option = { opt, argument }; aoqi@0: options.append(option); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * indicate an option with the specified list of arguments was given. aoqi@0: */ aoqi@0: private void setOption(String opt, List arguments) { aoqi@0: String[] args = new String[arguments.length() + 1]; aoqi@0: int k = 0; aoqi@0: args[k++] = opt; aoqi@0: for (List i = arguments; i.nonEmpty(); i=i.tail) { aoqi@0: args[k++] = i.head; aoqi@0: } aoqi@0: options.append(args); aoqi@0: } aoqi@0: }