duke@1: /*
jjg@1357: * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1: *
duke@1: * This code is free software; you can redistribute it and/or modify it
duke@1: * under the terms of the GNU General Public License version 2 only, as
ohair@554: * published by the Free Software Foundation. Oracle designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
ohair@554: * by Oracle in the LICENSE file that accompanied this code.
duke@1: *
duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1: * version 2 for more details (a copy is included in the LICENSE file that
duke@1: * accompanied this code).
duke@1: *
duke@1: * You should have received a copy of the GNU General Public License version
duke@1: * 2 along with this work; if not, write to the Free Software Foundation,
duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1: *
ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554: * or visit www.oracle.com if you need additional information or have any
ohair@554: * questions.
duke@1: */
duke@1:
duke@1: package com.sun.tools.javadoc;
duke@1:
jjg@1357: import java.io.File;
jjg@1357: import java.io.FileNotFoundException;
jjg@1357: import java.io.IOException;
jjg@1357: import java.io.PrintWriter;
jjg@1357: import java.util.StringTokenizer;
jjg@1357:
duke@1: import com.sun.javadoc.*;
duke@1: import com.sun.tools.javac.main.CommandLine;
duke@1: import com.sun.tools.javac.util.Context;
duke@1: import com.sun.tools.javac.util.List;
duke@1: import com.sun.tools.javac.util.ListBuffer;
jjg@1135: import com.sun.tools.javac.util.Log;
duke@1: import com.sun.tools.javac.util.Options;
duke@1: import static com.sun.tools.javac.code.Flags.*;
duke@1:
duke@1: /**
duke@1: * Main program of Javadoc.
duke@1: * Previously named "Main".
duke@1: *
jjg@1359: *
This is NOT part of any supported API.
jjg@1359: * If you write code that depends on this, you do so at your own risk.
jjg@1359: * This code and its internal interfaces are subject to change or
jjg@1359: * deletion without notice.
jjg@1359: *
duke@1: * @since 1.2
duke@1: * @author Robert Field
duke@1: * @author Neal Gafter (rewrite)
duke@1: */
duke@1: class Start {
duke@1:
duke@1: private final String defaultDocletClassName;
jjg@129: private final ClassLoader docletParentClassLoader;
duke@1:
duke@1: private static final String javadocName = "javadoc";
duke@1:
duke@1: private static final String standardDocletClassName =
duke@1: "com.sun.tools.doclets.standard.Standard";
duke@1:
duke@1: private ListBuffer options = new ListBuffer();
duke@1:
duke@1: private ModifierFilter showAccess = null;
duke@1:
duke@1: private long defaultFilter = PUBLIC | PROTECTED;
duke@1:
duke@1: private Messager messager;
duke@1:
duke@1: String docLocale = "";
duke@1:
duke@1: boolean breakiterator = false;
duke@1: boolean quiet = false;
duke@1: String encoding = null;
duke@1:
duke@1: private DocletInvoker docletInvoker;
duke@1:
duke@1: /* Treat warnings as errors. */
duke@1: private boolean rejectWarnings = false;
duke@1:
duke@1: Start(String programName,
duke@1: PrintWriter errWriter,
duke@1: PrintWriter warnWriter,
duke@1: PrintWriter noticeWriter,
duke@1: String defaultDocletClassName) {
jjg@129: this(programName, errWriter, warnWriter, noticeWriter, defaultDocletClassName, null);
jjg@129: }
jjg@129:
jjg@129: Start(String programName,
jjg@129: PrintWriter errWriter,
jjg@129: PrintWriter warnWriter,
jjg@129: PrintWriter noticeWriter,
jjg@129: String defaultDocletClassName,
jjg@129: ClassLoader docletParentClassLoader) {
jjg@584: Context tempContext = new Context(); // interim context until option decoding completed
jjg@584: messager = new Messager(tempContext, programName, errWriter, warnWriter, noticeWriter);
duke@1: this.defaultDocletClassName = defaultDocletClassName;
jjg@129: this.docletParentClassLoader = docletParentClassLoader;
duke@1: }
duke@1:
duke@1: Start(String programName, String defaultDocletClassName) {
jjg@129: this(programName, defaultDocletClassName, null);
jjg@129: }
jjg@129:
jjg@129: Start(String programName, String defaultDocletClassName,
jjg@129: ClassLoader docletParentClassLoader) {
jjg@584: Context tempContext = new Context(); // interim context until option decoding completed
jjg@584: messager = new Messager(tempContext, programName);
duke@1: this.defaultDocletClassName = defaultDocletClassName;
jjg@129: this.docletParentClassLoader = docletParentClassLoader;
jjg@129: }
jjg@129:
jjg@129: Start(String programName, ClassLoader docletParentClassLoader) {
jjg@129: this(programName, standardDocletClassName, docletParentClassLoader);
duke@1: }
duke@1:
duke@1: Start(String programName) {
duke@1: this(programName, standardDocletClassName);
duke@1: }
duke@1:
jjg@129: Start(ClassLoader docletParentClassLoader) {
jjg@129: this(javadocName, docletParentClassLoader);
jjg@129: }
jjg@129:
duke@1: Start() {
duke@1: this(javadocName);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Usage
duke@1: */
duke@1: private void usage() {
duke@1: messager.notice("main.usage");
duke@1:
duke@1: // let doclet print usage information (does nothing on error)
duke@1: if (docletInvoker != null) {
duke@1: docletInvoker.optionLength("-help");
duke@1: }
duke@1: }
duke@1:
duke@1: /**
jjg@584: * Usage
jjg@584: */
jjg@584: private void Xusage() {
jjg@584: messager.notice("main.Xusage");
jjg@584: }
jjg@584:
jjg@584: /**
duke@1: * Exit
duke@1: */
duke@1: private void exit() {
duke@1: messager.exit();
duke@1: }
duke@1:
duke@1:
duke@1: /**
duke@1: * Main program - external wrapper
duke@1: */
jjg@127: int begin(String... argv) {
duke@1: boolean failed = false;
duke@1:
duke@1: try {
duke@1: failed = !parseAndExecute(argv);
duke@1: } catch(Messager.ExitJavadoc exc) {
duke@1: // ignore, we just exit this way
duke@1: } catch (OutOfMemoryError ee) {
duke@1: messager.error(null, "main.out.of.memory");
duke@1: failed = true;
duke@1: } catch (Error ee) {
jjg@1135: ee.printStackTrace(System.err);
duke@1: messager.error(null, "main.fatal.error");
duke@1: failed = true;
duke@1: } catch (Exception ee) {
jjg@1135: ee.printStackTrace(System.err);
duke@1: messager.error(null, "main.fatal.exception");
duke@1: failed = true;
duke@1: } finally {
duke@1: messager.exitNotice();
duke@1: messager.flush();
duke@1: }
duke@1: failed |= messager.nerrors() > 0;
duke@1: failed |= rejectWarnings && messager.nwarnings() > 0;
duke@1: return failed ? 1 : 0;
duke@1: }
duke@1:
duke@1: private void addToList(ListBuffer list, String str){
duke@1: StringTokenizer st = new StringTokenizer(str, ":");
duke@1: String current;
duke@1: while(st.hasMoreTokens()){
duke@1: current = st.nextToken();
duke@1: list.append(current);
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Main program - internal
duke@1: */
jjg@127: private boolean parseAndExecute(String... argv) throws IOException {
duke@1: long tm = System.currentTimeMillis();
duke@1:
duke@1: ListBuffer javaNames = new ListBuffer();
duke@1:
duke@1: // Preprocess @file arguments
duke@1: try {
duke@1: argv = CommandLine.parse(argv);
duke@1: } catch (FileNotFoundException e) {
duke@1: messager.error(null, "main.cant.read", e.getMessage());
duke@1: exit();
duke@1: } catch (IOException e) {
jjg@1135: e.printStackTrace(System.err);
duke@1: exit();
duke@1: }
duke@1:
duke@1: setDocletInvoker(argv);
duke@1: ListBuffer subPackages = new ListBuffer();
duke@1: ListBuffer excludedPackages = new ListBuffer();
jjg@584:
jjg@584: Context context = new Context();
jjg@584: // Setup a new Messager, using the same initial parameters as the
jjg@584: // existing Messager, except that this one will be able to use any
jjg@584: // options that may be set up below.
jjg@584: Messager.preRegister(context,
jjg@584: messager.programName,
jjg@1135: messager.getWriter(Log.WriterKind.ERROR),
jjg@1135: messager.getWriter(Log.WriterKind.WARNING),
jjg@1135: messager.getWriter(Log.WriterKind.NOTICE));
jjg@584:
duke@1: Options compOpts = Options.instance(context);
duke@1: boolean docClasses = false;
duke@1:
duke@1: // Parse arguments
duke@1: for (int i = 0 ; i < argv.length ; i++) {
duke@1: String arg = argv[i];
duke@1: if (arg.equals("-subpackages")) {
duke@1: oneArg(argv, i++);
duke@1: addToList(subPackages, argv[i]);
duke@1: } else if (arg.equals("-exclude")){
duke@1: oneArg(argv, i++);
duke@1: addToList(excludedPackages, argv[i]);
duke@1: } else if (arg.equals("-verbose")) {
duke@1: setOption(arg);
duke@1: compOpts.put("-verbose", "");
duke@1: } else if (arg.equals("-encoding")) {
duke@1: oneArg(argv, i++);
duke@1: encoding = argv[i];
duke@1: compOpts.put("-encoding", argv[i]);
duke@1: } else if (arg.equals("-breakiterator")) {
duke@1: breakiterator = true;
duke@1: setOption("-breakiterator");
duke@1: } else if (arg.equals("-quiet")) {
duke@1: quiet = true;
duke@1: setOption("-quiet");
duke@1: } else if (arg.equals("-help")) {
duke@1: usage();
duke@1: exit();
duke@1: } else if (arg.equals("-Xclasses")) {
duke@1: setOption(arg);
duke@1: docClasses = true;
duke@1: } else if (arg.equals("-Xwerror")) {
duke@1: setOption(arg);
duke@1: rejectWarnings = true;
duke@1: } else if (arg.equals("-private")) {
duke@1: setOption(arg);
duke@1: setFilter(ModifierFilter.ALL_ACCESS);
duke@1: } else if (arg.equals("-package")) {
duke@1: setOption(arg);
duke@1: setFilter(PUBLIC | PROTECTED |
duke@1: ModifierFilter.PACKAGE );
duke@1: } else if (arg.equals("-protected")) {
duke@1: setOption(arg);
duke@1: setFilter(PUBLIC | PROTECTED );
duke@1: } else if (arg.equals("-public")) {
duke@1: setOption(arg);
duke@1: setFilter(PUBLIC);
duke@1: } else if (arg.equals("-source")) {
duke@1: oneArg(argv, i++);
duke@1: if (compOpts.get("-source") != null) {
duke@1: usageError("main.option.already.seen", arg);
duke@1: }
duke@1: compOpts.put("-source", argv[i]);
duke@1: } else if (arg.equals("-prompt")) {
duke@1: compOpts.put("-prompt", "-prompt");
duke@1: messager.promptOnError = true;
duke@1: } else if (arg.equals("-sourcepath")) {
duke@1: oneArg(argv, i++);
duke@1: if (compOpts.get("-sourcepath") != null) {
duke@1: usageError("main.option.already.seen", arg);
duke@1: }
duke@1: compOpts.put("-sourcepath", argv[i]);
duke@1: } else if (arg.equals("-classpath")) {
duke@1: oneArg(argv, i++);
duke@1: if (compOpts.get("-classpath") != null) {
duke@1: usageError("main.option.already.seen", arg);
duke@1: }
duke@1: compOpts.put("-classpath", argv[i]);
duke@1: } else if (arg.equals("-sysclasspath")) {
duke@1: oneArg(argv, i++);
duke@1: if (compOpts.get("-bootclasspath") != null) {
duke@1: usageError("main.option.already.seen", arg);
duke@1: }
duke@1: compOpts.put("-bootclasspath", argv[i]);
duke@1: } else if (arg.equals("-bootclasspath")) {
duke@1: oneArg(argv, i++);
duke@1: if (compOpts.get("-bootclasspath") != null) {
duke@1: usageError("main.option.already.seen", arg);
duke@1: }
duke@1: compOpts.put("-bootclasspath", argv[i]);
duke@1: } else if (arg.equals("-extdirs")) {
duke@1: oneArg(argv, i++);
duke@1: if (compOpts.get("-extdirs") != null) {
duke@1: usageError("main.option.already.seen", arg);
duke@1: }
duke@1: compOpts.put("-extdirs", argv[i]);
duke@1: } else if (arg.equals("-overview")) {
duke@1: oneArg(argv, i++);
duke@1: } else if (arg.equals("-doclet")) {
duke@1: i++; // handled in setDocletInvoker
duke@1: } else if (arg.equals("-docletpath")) {
duke@1: i++; // handled in setDocletInvoker
duke@1: } else if (arg.equals("-locale")) {
duke@1: if (i != 0)
duke@1: usageError("main.locale_first");
duke@1: oneArg(argv, i++);
duke@1: docLocale = argv[i];
jjg@584: } else if (arg.equals("-Xmaxerrs") || arg.equals("-Xmaxwarns")) {
jjg@584: oneArg(argv, i++);
jjg@584: if (compOpts.get(arg) != null) {
jjg@584: usageError("main.option.already.seen", arg);
jjg@584: }
jjg@584: compOpts.put(arg, argv[i]);
jjg@584: } else if (arg.equals("-X")) {
jjg@584: Xusage();
jjg@584: exit();
duke@1: } else if (arg.startsWith("-XD")) {
duke@1: String s = arg.substring("-XD".length());
duke@1: int eq = s.indexOf('=');
duke@1: String key = (eq < 0) ? s : s.substring(0, eq);
duke@1: String value = (eq < 0) ? s : s.substring(eq+1);
duke@1: compOpts.put(key, value);
duke@1: }
duke@1: // call doclet for its options
duke@1: // other arg starts with - is invalid
duke@1: else if ( arg.startsWith("-") ) {
duke@1: int optionLength;
duke@1: optionLength = docletInvoker.optionLength(arg);
duke@1: if (optionLength < 0) {
duke@1: // error already displayed
duke@1: exit();
duke@1: } else if (optionLength == 0) {
duke@1: // option not found
duke@1: usageError("main.invalid_flag", arg);
duke@1: } else {
duke@1: // doclet added option
duke@1: if ((i + optionLength) > argv.length) {
duke@1: usageError("main.requires_argument", arg);
duke@1: }
duke@1: ListBuffer args = new ListBuffer();
duke@1: for (int j = 0; j < optionLength-1; ++j) {
duke@1: args.append(argv[++i]);
duke@1: }
duke@1: setOption(arg, args.toList());
duke@1: }
duke@1: } else {
duke@1: javaNames.append(arg);
duke@1: }
duke@1: }
duke@1:
duke@1: if (javaNames.isEmpty() && subPackages.isEmpty()) {
duke@1: usageError("main.No_packages_or_classes_specified");
duke@1: }
duke@1:
duke@1: if (!docletInvoker.validOptions(options.toList())) {
duke@1: // error message already displayed
duke@1: exit();
duke@1: }
duke@1:
duke@1: JavadocTool comp = JavadocTool.make0(context);
duke@1: if (comp == null) return false;
duke@1:
duke@1: if (showAccess == null) {
duke@1: setFilter(defaultFilter);
duke@1: }
duke@1:
duke@1: LanguageVersion languageVersion = docletInvoker.languageVersion();
duke@1: RootDocImpl root = comp.getRootDocImpl(
duke@1: docLocale, encoding, showAccess,
duke@1: javaNames.toList(), options.toList(), breakiterator,
duke@1: subPackages.toList(), excludedPackages.toList(),
duke@1: docClasses,
duke@1: // legacy?
duke@1: languageVersion == null || languageVersion == LanguageVersion.JAVA_1_1, quiet);
duke@1:
duke@1: // pass off control to the doclet
duke@1: boolean ok = root != null;
duke@1: if (ok) ok = docletInvoker.start(root);
duke@1:
jjg@912: Messager docletMessager = Messager.instance0(context);
jjg@912: messager.nwarnings += docletMessager.nwarnings;
jjg@912: messager.nerrors += docletMessager.nerrors;
jjg@912:
duke@1: // We're done.
duke@1: if (compOpts.get("-verbose") != null) {
duke@1: tm = System.currentTimeMillis() - tm;
duke@1: messager.notice("main.done_in", Long.toString(tm));
duke@1: }
duke@1:
duke@1: return ok;
duke@1: }
duke@1:
duke@1: private void setDocletInvoker(String[] argv) {
duke@1: String docletClassName = null;
duke@1: String docletPath = null;
duke@1:
duke@1: // Parse doclet specifying arguments
duke@1: for (int i = 0 ; i < argv.length ; i++) {
duke@1: String arg = argv[i];
duke@1: if (arg.equals("-doclet")) {
duke@1: oneArg(argv, i++);
duke@1: if (docletClassName != null) {
duke@1: usageError("main.more_than_one_doclet_specified_0_and_1",
duke@1: docletClassName, argv[i]);
duke@1: }
duke@1: docletClassName = argv[i];
duke@1: } else if (arg.equals("-docletpath")) {
duke@1: oneArg(argv, i++);
duke@1: if (docletPath == null) {
duke@1: docletPath = argv[i];
duke@1: } else {
duke@1: docletPath += File.pathSeparator + argv[i];
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: if (docletClassName == null) {
duke@1: docletClassName = defaultDocletClassName;
duke@1: }
duke@1:
duke@1: // attempt to find doclet
duke@1: docletInvoker = new DocletInvoker(messager,
jjg@129: docletClassName, docletPath,
jjg@129: docletParentClassLoader);
duke@1: }
duke@1:
duke@1: private void setFilter(long filterBits) {
duke@1: if (showAccess != null) {
duke@1: messager.error(null, "main.incompatible.access.flags");
duke@1: usage();
duke@1: exit();
duke@1: }
duke@1: showAccess = new ModifierFilter(filterBits);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Set one arg option.
duke@1: * Error and exit if one argument is not provided.
duke@1: */
duke@1: private void oneArg(String[] args, int index) {
duke@1: if ((index + 1) < args.length) {
duke@1: setOption(args[index], args[index+1]);
duke@1: } else {
duke@1: usageError("main.requires_argument", args[index]);
duke@1: }
duke@1: }
duke@1:
duke@1: private void usageError(String key) {
duke@1: messager.error(null, key);
duke@1: usage();
duke@1: exit();
duke@1: }
duke@1:
duke@1: private void usageError(String key, String a1) {
duke@1: messager.error(null, key, a1);
duke@1: usage();
duke@1: exit();
duke@1: }
duke@1:
duke@1: private void usageError(String key, String a1, String a2) {
duke@1: messager.error(null, key, a1, a2);
duke@1: usage();
duke@1: exit();
duke@1: }
duke@1:
duke@1: /**
duke@1: * indicate an option with no arguments was given.
duke@1: */
duke@1: private void setOption(String opt) {
duke@1: String[] option = { opt };
duke@1: options.append(option);
duke@1: }
duke@1:
duke@1: /**
duke@1: * indicate an option with one argument was given.
duke@1: */
duke@1: private void setOption(String opt, String argument) {
duke@1: String[] option = { opt, argument };
duke@1: options.append(option);
duke@1: }
duke@1:
duke@1: /**
duke@1: * indicate an option with the specified list of arguments was given.
duke@1: */
duke@1: private void setOption(String opt, List arguments) {
duke@1: String[] args = new String[arguments.length() + 1];
duke@1: int k = 0;
duke@1: args[k++] = opt;
duke@1: for (List i = arguments; i.nonEmpty(); i=i.tail) {
duke@1: args[k++] = i.head;
duke@1: }
duke@1: options = options.append(args);
duke@1: }
duke@1:
duke@1: }