duke@1: /* xdono@174: * Copyright 1997-2008 Sun Microsystems, Inc. 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 duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun 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: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javadoc; duke@1: duke@1: import com.sun.javadoc.*; duke@1: 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; duke@1: import com.sun.tools.javac.util.Options; duke@1: duke@1: import java.io.IOException; duke@1: import java.io.File; duke@1: import java.io.FileNotFoundException; duke@1: import java.io.PrintWriter; duke@1: duke@1: import java.util.StringTokenizer; duke@1: 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: * duke@1: * @since 1.2 duke@1: * @author Robert Field duke@1: * @author Neal Gafter (rewrite) duke@1: */ duke@1: class Start { duke@1: /** Context for this invocation. */ duke@1: private final Context context; 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: private static final int F_VERBOSE = 1 << 0; duke@1: private static final int F_WARNINGS = 1 << 2; 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) { duke@1: context = new Context(); duke@1: messager = new Messager(context, 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) { duke@1: context = new Context(); duke@1: messager = new Messager(context, 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: /** 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) { duke@1: ee.printStackTrace(); duke@1: messager.error(null, "main.fatal.error"); duke@1: failed = true; duke@1: } catch (Exception ee) { duke@1: ee.printStackTrace(); 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) { duke@1: e.printStackTrace(); duke@1: exit(); duke@1: } duke@1: duke@1: setDocletInvoker(argv); duke@1: ListBuffer subPackages = new ListBuffer(); duke@1: ListBuffer excludedPackages = new ListBuffer(); 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]; 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: 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: }