Thu, 15 Nov 2012 14:41:31 -0800
8003257: refactor javadoc tool option handling
Reviewed-by: darcy
1 /*
2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.tools.javadoc;
28 import java.io.File;
29 import java.io.FileNotFoundException;
30 import java.io.IOException;
31 import java.io.PrintWriter;
33 import com.sun.javadoc.*;
34 import com.sun.tools.javac.main.CommandLine;
35 import com.sun.tools.javac.util.Context;
36 import com.sun.tools.javac.util.List;
37 import com.sun.tools.javac.util.ListBuffer;
38 import com.sun.tools.javac.util.Log;
39 import com.sun.tools.javac.util.Options;
40 import static com.sun.tools.javac.code.Flags.*;
42 /**
43 * Main program of Javadoc.
44 * Previously named "Main".
45 *
46 * <p><b>This is NOT part of any supported API.
47 * If you write code that depends on this, you do so at your own risk.
48 * This code and its internal interfaces are subject to change or
49 * deletion without notice.</b>
50 *
51 * @since 1.2
52 * @author Robert Field
53 * @author Neal Gafter (rewrite)
54 */
55 public class Start extends ToolOption.Helper {
56 /** Context for this invocation. */
57 private final Context context;
59 private final String defaultDocletClassName;
60 private final ClassLoader docletParentClassLoader;
62 private static final String javadocName = "javadoc";
64 private static final String standardDocletClassName =
65 "com.sun.tools.doclets.standard.Standard";
67 private long defaultFilter = PUBLIC | PROTECTED;
69 private final Messager messager;
71 private DocletInvoker docletInvoker;
73 Start(String programName,
74 PrintWriter errWriter,
75 PrintWriter warnWriter,
76 PrintWriter noticeWriter,
77 String defaultDocletClassName) {
78 this(programName, errWriter, warnWriter, noticeWriter, defaultDocletClassName, null);
79 }
81 Start(String programName,
82 PrintWriter errWriter,
83 PrintWriter warnWriter,
84 PrintWriter noticeWriter,
85 String defaultDocletClassName,
86 ClassLoader docletParentClassLoader) {
87 context = new Context();
88 messager = new Messager(context, programName, errWriter, warnWriter, noticeWriter);
89 this.defaultDocletClassName = defaultDocletClassName;
90 this.docletParentClassLoader = docletParentClassLoader;
91 }
93 Start(String programName, String defaultDocletClassName) {
94 this(programName, defaultDocletClassName, null);
95 }
97 Start(String programName, String defaultDocletClassName,
98 ClassLoader docletParentClassLoader) {
99 context = new Context();
100 messager = new Messager(context, programName);
101 this.defaultDocletClassName = defaultDocletClassName;
102 this.docletParentClassLoader = docletParentClassLoader;
103 }
105 Start(String programName, ClassLoader docletParentClassLoader) {
106 this(programName, standardDocletClassName, docletParentClassLoader);
107 }
109 Start(String programName) {
110 this(programName, standardDocletClassName);
111 }
113 Start(ClassLoader docletParentClassLoader) {
114 this(javadocName, docletParentClassLoader);
115 }
117 Start() {
118 this(javadocName);
119 }
121 public Start(Context context) {
122 context.getClass(); // null check
123 this.context = context;
124 defaultDocletClassName = standardDocletClassName;
125 docletParentClassLoader = null;
127 Log log = context.get(Log.logKey);
128 if (log instanceof Messager)
129 messager = (Messager) log;
130 else {
131 PrintWriter out = context.get(Log.outKey);
132 messager = (out == null) ? new Messager(context, javadocName)
133 : new Messager(context, javadocName, out, out, out);
134 }
135 }
137 /**
138 * Usage
139 */
140 @Override
141 void usage() {
142 usage(true);
143 }
146 /**
147 * Usage
148 */
149 private void usage(boolean exit) {
150 // RFE: it would be better to replace the following with code to
151 // write a header, then help for each option, then a footer.
152 messager.notice("main.usage");
154 // let doclet print usage information (does nothing on error)
155 if (docletInvoker != null) {
156 docletInvoker.optionLength("-help");
157 }
159 if (exit) exit();
160 }
162 @Override
163 void Xusage() {
164 Xusage(true);
165 }
167 /**
168 * Usage
169 */
170 private void Xusage(boolean exit) {
171 messager.notice("main.Xusage");
172 if (exit) exit();
173 }
175 /**
176 * Exit
177 */
178 private void exit() {
179 messager.exit();
180 }
183 /**
184 * Main program - external wrapper
185 */
186 int begin(String... argv) {
187 boolean failed = false;
189 try {
190 failed = !parseAndExecute(argv);
191 } catch (Messager.ExitJavadoc exc) {
192 // ignore, we just exit this way
193 } catch (OutOfMemoryError ee) {
194 messager.error(Messager.NOPOS, "main.out.of.memory");
195 failed = true;
196 } catch (Error ee) {
197 ee.printStackTrace(System.err);
198 messager.error(Messager.NOPOS, "main.fatal.error");
199 failed = true;
200 } catch (Exception ee) {
201 ee.printStackTrace(System.err);
202 messager.error(Messager.NOPOS, "main.fatal.exception");
203 failed = true;
204 } finally {
205 messager.exitNotice();
206 messager.flush();
207 }
208 failed |= messager.nerrors() > 0;
209 failed |= rejectWarnings && messager.nwarnings() > 0;
210 return failed ? 1 : 0;
211 }
213 /**
214 * Main program - internal
215 */
216 private boolean parseAndExecute(String... argv) throws IOException {
217 long tm = System.currentTimeMillis();
219 ListBuffer<String> javaNames = new ListBuffer<String>();
221 // Preprocess @file arguments
222 try {
223 argv = CommandLine.parse(argv);
224 } catch (FileNotFoundException e) {
225 messager.error(Messager.NOPOS, "main.cant.read", e.getMessage());
226 exit();
227 } catch (IOException e) {
228 e.printStackTrace(System.err);
229 exit();
230 }
232 setDocletInvoker(argv);
234 compOpts = Options.instance(context);
236 // Parse arguments
237 for (int i = 0 ; i < argv.length ; i++) {
238 String arg = argv[i];
240 ToolOption o = ToolOption.get(arg);
241 if (o != null) {
242 // hack: this restriction should be removed
243 if (o == ToolOption.LOCALE && i > 0)
244 usageError("main.locale_first");
246 if (o.hasArg) {
247 oneArg(argv, i++);
248 o.process(this, argv[i]);
249 } else {
250 setOption(arg);
251 o.process(this);
252 }
254 } else if (arg.startsWith("-XD")) {
255 // hidden javac options
256 String s = arg.substring("-XD".length());
257 int eq = s.indexOf('=');
258 String key = (eq < 0) ? s : s.substring(0, eq);
259 String value = (eq < 0) ? s : s.substring(eq+1);
260 compOpts.put(key, value);
261 }
262 // call doclet for its options
263 // other arg starts with - is invalid
264 else if (arg.startsWith("-")) {
265 int optionLength;
266 optionLength = docletInvoker.optionLength(arg);
267 if (optionLength < 0) {
268 // error already displayed
269 exit();
270 } else if (optionLength == 0) {
271 // option not found
272 usageError("main.invalid_flag", arg);
273 } else {
274 // doclet added option
275 if ((i + optionLength) > argv.length) {
276 usageError("main.requires_argument", arg);
277 }
278 ListBuffer<String> args = new ListBuffer<String>();
279 for (int j = 0; j < optionLength-1; ++j) {
280 args.append(argv[++i]);
281 }
282 setOption(arg, args.toList());
283 }
284 } else {
285 javaNames.append(arg);
286 }
287 }
288 compOpts.notifyListeners();
290 if (javaNames.isEmpty() && subPackages.isEmpty()) {
291 usageError("main.No_packages_or_classes_specified");
292 }
294 if (!docletInvoker.validOptions(options.toList())) {
295 // error message already displayed
296 exit();
297 }
299 JavadocTool comp = JavadocTool.make0(context);
300 if (comp == null) return false;
302 if (showAccess == null) {
303 setFilter(defaultFilter);
304 }
306 LanguageVersion languageVersion = docletInvoker.languageVersion();
307 RootDocImpl root = comp.getRootDocImpl(
308 docLocale,
309 encoding,
310 showAccess,
311 javaNames.toList(),
312 options.toList(),
313 breakiterator,
314 subPackages.toList(),
315 excludedPackages.toList(),
316 docClasses,
317 // legacy?
318 languageVersion == null || languageVersion == LanguageVersion.JAVA_1_1,
319 quiet);
321 // release resources
322 comp = null;
324 // pass off control to the doclet
325 boolean ok = root != null;
326 if (ok) ok = docletInvoker.start(root);
328 // We're done.
329 if (compOpts.get("-verbose") != null) {
330 tm = System.currentTimeMillis() - tm;
331 messager.notice("main.done_in", Long.toString(tm));
332 }
334 return ok;
335 }
337 private void setDocletInvoker(String[] argv) {
338 String docletClassName = null;
339 String docletPath = null;
341 // Parse doclet specifying arguments
342 for (int i = 0 ; i < argv.length ; i++) {
343 String arg = argv[i];
344 if (arg.equals("-doclet")) {
345 oneArg(argv, i++);
346 if (docletClassName != null) {
347 usageError("main.more_than_one_doclet_specified_0_and_1",
348 docletClassName, argv[i]);
349 }
350 docletClassName = argv[i];
351 } else if (arg.equals("-docletpath")) {
352 oneArg(argv, i++);
353 if (docletPath == null) {
354 docletPath = argv[i];
355 } else {
356 docletPath += File.pathSeparator + argv[i];
357 }
358 }
359 }
361 if (docletClassName == null) {
362 docletClassName = defaultDocletClassName;
363 }
365 // attempt to find doclet
366 docletInvoker = new DocletInvoker(messager,
367 docletClassName, docletPath,
368 docletParentClassLoader);
369 }
371 /**
372 * Set one arg option.
373 * Error and exit if one argument is not provided.
374 */
375 private void oneArg(String[] args, int index) {
376 if ((index + 1) < args.length) {
377 setOption(args[index], args[index+1]);
378 } else {
379 usageError("main.requires_argument", args[index]);
380 }
381 }
383 @Override
384 void usageError(String key, Object... args) {
385 messager.error(Messager.NOPOS, key, args);
386 usage(true);
387 }
389 /**
390 * indicate an option with no arguments was given.
391 */
392 private void setOption(String opt) {
393 String[] option = { opt };
394 options.append(option);
395 }
397 /**
398 * indicate an option with one argument was given.
399 */
400 private void setOption(String opt, String argument) {
401 String[] option = { opt, argument };
402 options.append(option);
403 }
405 /**
406 * indicate an option with the specified list of arguments was given.
407 */
408 private void setOption(String opt, List<String> arguments) {
409 String[] args = new String[arguments.length() + 1];
410 int k = 0;
411 args[k++] = opt;
412 for (List<String> i = arguments; i.nonEmpty(); i=i.tail) {
413 args[k++] = i.head;
414 }
415 options.append(args);
416 }
417 }