Tue, 24 Dec 2013 09:17:37 -0800
8029230: Update copyright year to match last edit in jdk8 langtools repository for 2013
Reviewed-by: ksrini
Contributed-by: steve.sides@oracle.com
1 /*
2 * Copyright (c) 1997, 2013, 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;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
36 import javax.tools.JavaFileManager;
37 import javax.tools.JavaFileObject;
39 import com.sun.javadoc.*;
40 import com.sun.tools.javac.main.CommandLine;
41 import com.sun.tools.javac.util.ClientCodeException;
42 import com.sun.tools.javac.util.Context;
43 import com.sun.tools.javac.util.List;
44 import com.sun.tools.javac.util.ListBuffer;
45 import com.sun.tools.javac.util.Log;
46 import com.sun.tools.javac.util.Options;
47 import static com.sun.tools.javac.code.Flags.*;
49 /**
50 * Main program of Javadoc.
51 * Previously named "Main".
52 *
53 * <p><b>This is NOT part of any supported API.
54 * If you write code that depends on this, you do so at your own risk.
55 * This code and its internal interfaces are subject to change or
56 * deletion without notice.</b>
57 *
58 * @since 1.2
59 * @author Robert Field
60 * @author Neal Gafter (rewrite)
61 */
62 public class Start extends ToolOption.Helper {
63 /** Context for this invocation. */
64 private final Context context;
66 private final String defaultDocletClassName;
67 private final ClassLoader docletParentClassLoader;
69 private static final String javadocName = "javadoc";
71 private static final String standardDocletClassName =
72 "com.sun.tools.doclets.standard.Standard";
74 private long defaultFilter = PUBLIC | PROTECTED;
76 private final Messager messager;
78 private DocletInvoker docletInvoker;
80 /**
81 * In API mode, exceptions thrown while calling the doclet are
82 * propagated using ClientCodeException.
83 */
84 private boolean apiMode;
86 Start(String programName,
87 PrintWriter errWriter,
88 PrintWriter warnWriter,
89 PrintWriter noticeWriter,
90 String defaultDocletClassName) {
91 this(programName, errWriter, warnWriter, noticeWriter, defaultDocletClassName, null);
92 }
94 Start(String programName,
95 PrintWriter errWriter,
96 PrintWriter warnWriter,
97 PrintWriter noticeWriter,
98 String defaultDocletClassName,
99 ClassLoader docletParentClassLoader) {
100 context = new Context();
101 messager = new Messager(context, programName, errWriter, warnWriter, noticeWriter);
102 this.defaultDocletClassName = defaultDocletClassName;
103 this.docletParentClassLoader = docletParentClassLoader;
104 }
106 Start(String programName, String defaultDocletClassName) {
107 this(programName, defaultDocletClassName, null);
108 }
110 Start(String programName, String defaultDocletClassName,
111 ClassLoader docletParentClassLoader) {
112 context = new Context();
113 messager = new Messager(context, programName);
114 this.defaultDocletClassName = defaultDocletClassName;
115 this.docletParentClassLoader = docletParentClassLoader;
116 }
118 Start(String programName, ClassLoader docletParentClassLoader) {
119 this(programName, standardDocletClassName, docletParentClassLoader);
120 }
122 Start(String programName) {
123 this(programName, standardDocletClassName);
124 }
126 Start(ClassLoader docletParentClassLoader) {
127 this(javadocName, docletParentClassLoader);
128 }
130 Start() {
131 this(javadocName);
132 }
134 public Start(Context context) {
135 context.getClass(); // null check
136 this.context = context;
137 apiMode = true;
138 defaultDocletClassName = standardDocletClassName;
139 docletParentClassLoader = null;
141 Log log = context.get(Log.logKey);
142 if (log instanceof Messager)
143 messager = (Messager) log;
144 else {
145 PrintWriter out = context.get(Log.outKey);
146 messager = (out == null) ? new Messager(context, javadocName)
147 : new Messager(context, javadocName, out, out, out);
148 }
149 }
151 /**
152 * Usage
153 */
154 @Override
155 void usage() {
156 usage(true);
157 }
159 void usage(boolean exit) {
160 usage("main.usage", "-help", null, exit);
161 }
163 @Override
164 void Xusage() {
165 Xusage(true);
166 }
168 void Xusage(boolean exit) {
169 usage("main.Xusage", "-X", "main.Xusage.foot", exit);
170 }
172 private void usage(String main, String doclet, String foot, boolean exit) {
173 // RFE: it would be better to replace the following with code to
174 // write a header, then help for each option, then a footer.
175 messager.notice(main);
177 // let doclet print usage information (does nothing on error)
178 if (docletInvoker != null) {
179 // RFE: this is a pretty bad way to get the doclet to show
180 // help info. Moreover, the output appears on stdout,
181 // and <i>not</i> on any of the standard streams passed
182 // to javadoc, and in particular, not to the noticeWriter
183 // But, to fix this, we need to fix the Doclet API.
184 docletInvoker.optionLength(doclet);
185 }
187 if (foot != null)
188 messager.notice(foot);
190 if (exit) exit();
191 }
193 /**
194 * Exit
195 */
196 private void exit() {
197 messager.exit();
198 }
201 /**
202 * Main program - external wrapper
203 */
204 int begin(String... argv) {
205 boolean ok = begin(null, argv, Collections.<JavaFileObject> emptySet());
206 return ok ? 0 : 1;
207 }
209 public boolean begin(Class<?> docletClass, Iterable<String> options, Iterable<? extends JavaFileObject> fileObjects) {
210 Collection<String> opts = new ArrayList<String>();
211 for (String opt: options) opts.add(opt);
212 return begin(docletClass, opts.toArray(new String[opts.size()]), fileObjects);
213 }
215 private boolean begin(Class<?> docletClass, String[] options, Iterable<? extends JavaFileObject> fileObjects) {
216 boolean failed = false;
218 try {
219 failed = !parseAndExecute(docletClass, options, fileObjects);
220 } catch (Messager.ExitJavadoc exc) {
221 // ignore, we just exit this way
222 } catch (OutOfMemoryError ee) {
223 messager.error(Messager.NOPOS, "main.out.of.memory");
224 failed = true;
225 } catch (ClientCodeException e) {
226 // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
227 throw e;
228 } catch (Error ee) {
229 ee.printStackTrace(System.err);
230 messager.error(Messager.NOPOS, "main.fatal.error");
231 failed = true;
232 } catch (Exception ee) {
233 ee.printStackTrace(System.err);
234 messager.error(Messager.NOPOS, "main.fatal.exception");
235 failed = true;
236 } finally {
237 messager.exitNotice();
238 messager.flush();
239 }
240 failed |= messager.nerrors() > 0;
241 failed |= rejectWarnings && messager.nwarnings() > 0;
242 return !failed;
243 }
245 /**
246 * Main program - internal
247 */
248 private boolean parseAndExecute(
249 Class<?> docletClass,
250 String[] argv,
251 Iterable<? extends JavaFileObject> fileObjects) throws IOException {
252 long tm = System.currentTimeMillis();
254 ListBuffer<String> javaNames = new ListBuffer<String>();
256 // Preprocess @file arguments
257 try {
258 argv = CommandLine.parse(argv);
259 } catch (FileNotFoundException e) {
260 messager.error(Messager.NOPOS, "main.cant.read", e.getMessage());
261 exit();
262 } catch (IOException e) {
263 e.printStackTrace(System.err);
264 exit();
265 }
268 JavaFileManager fileManager = context.get(JavaFileManager.class);
269 setDocletInvoker(docletClass, fileManager, argv);
271 compOpts = Options.instance(context);
272 // Make sure no obsolete source/target messages are reported
273 compOpts.put("-Xlint:-options", "-Xlint:-options");
275 // Parse arguments
276 for (int i = 0 ; i < argv.length ; i++) {
277 String arg = argv[i];
279 ToolOption o = ToolOption.get(arg);
280 if (o != null) {
281 // hack: this restriction should be removed
282 if (o == ToolOption.LOCALE && i > 0)
283 usageError("main.locale_first");
285 if (o.hasArg) {
286 oneArg(argv, i++);
287 o.process(this, argv[i]);
288 } else {
289 setOption(arg);
290 o.process(this);
291 }
293 } else if (arg.startsWith("-XD")) {
294 // hidden javac options
295 String s = arg.substring("-XD".length());
296 int eq = s.indexOf('=');
297 String key = (eq < 0) ? s : s.substring(0, eq);
298 String value = (eq < 0) ? s : s.substring(eq+1);
299 compOpts.put(key, value);
300 }
301 // call doclet for its options
302 // other arg starts with - is invalid
303 else if (arg.startsWith("-")) {
304 int optionLength;
305 optionLength = docletInvoker.optionLength(arg);
306 if (optionLength < 0) {
307 // error already displayed
308 exit();
309 } else if (optionLength == 0) {
310 // option not found
311 usageError("main.invalid_flag", arg);
312 } else {
313 // doclet added option
314 if ((i + optionLength) > argv.length) {
315 usageError("main.requires_argument", arg);
316 }
317 ListBuffer<String> args = new ListBuffer<String>();
318 for (int j = 0; j < optionLength-1; ++j) {
319 args.append(argv[++i]);
320 }
321 setOption(arg, args.toList());
322 }
323 } else {
324 javaNames.append(arg);
325 }
326 }
327 compOpts.notifyListeners();
329 if (javaNames.isEmpty() && subPackages.isEmpty() && isEmpty(fileObjects)) {
330 usageError("main.No_packages_or_classes_specified");
331 }
333 if (!docletInvoker.validOptions(options.toList())) {
334 // error message already displayed
335 exit();
336 }
338 JavadocTool comp = JavadocTool.make0(context);
339 if (comp == null) return false;
341 if (showAccess == null) {
342 setFilter(defaultFilter);
343 }
345 LanguageVersion languageVersion = docletInvoker.languageVersion();
346 RootDocImpl root = comp.getRootDocImpl(
347 docLocale,
348 encoding,
349 showAccess,
350 javaNames.toList(),
351 options.toList(),
352 fileObjects,
353 breakiterator,
354 subPackages.toList(),
355 excludedPackages.toList(),
356 docClasses,
357 // legacy?
358 languageVersion == null || languageVersion == LanguageVersion.JAVA_1_1,
359 quiet);
361 // release resources
362 comp = null;
364 // pass off control to the doclet
365 boolean ok = root != null;
366 if (ok) ok = docletInvoker.start(root);
368 // We're done.
369 if (compOpts.get("-verbose") != null) {
370 tm = System.currentTimeMillis() - tm;
371 messager.notice("main.done_in", Long.toString(tm));
372 }
374 return ok;
375 }
377 private <T> boolean isEmpty(Iterable<T> iter) {
378 return !iter.iterator().hasNext();
379 }
381 /**
382 * Init the doclet invoker.
383 * The doclet class may be given explicitly, or via the -doclet option in
384 * argv.
385 * If the doclet class is not given explicitly, it will be loaded from
386 * the file manager's DOCLET_PATH location, if available, or via the
387 * -doclet path option in argv.
388 * @param docletClass The doclet class. May be null.
389 * @param fileManager The file manager used to get the class loader to load
390 * the doclet class if required. May be null.
391 * @param argv Args containing -doclet and -docletpath, in case they are required.
392 */
393 private void setDocletInvoker(Class<?> docletClass, JavaFileManager fileManager, String[] argv) {
394 if (docletClass != null) {
395 docletInvoker = new DocletInvoker(messager, docletClass, apiMode);
396 // TODO, check no -doclet, -docletpath
397 return;
398 }
400 String docletClassName = null;
401 String docletPath = null;
403 // Parse doclet specifying arguments
404 for (int i = 0 ; i < argv.length ; i++) {
405 String arg = argv[i];
406 if (arg.equals(ToolOption.DOCLET.opt)) {
407 oneArg(argv, i++);
408 if (docletClassName != null) {
409 usageError("main.more_than_one_doclet_specified_0_and_1",
410 docletClassName, argv[i]);
411 }
412 docletClassName = argv[i];
413 } else if (arg.equals(ToolOption.DOCLETPATH.opt)) {
414 oneArg(argv, i++);
415 if (docletPath == null) {
416 docletPath = argv[i];
417 } else {
418 docletPath += File.pathSeparator + argv[i];
419 }
420 }
421 }
423 if (docletClassName == null) {
424 docletClassName = defaultDocletClassName;
425 }
427 // attempt to find doclet
428 docletInvoker = new DocletInvoker(messager, fileManager,
429 docletClassName, docletPath,
430 docletParentClassLoader,
431 apiMode);
432 }
434 /**
435 * Set one arg option.
436 * Error and exit if one argument is not provided.
437 */
438 private void oneArg(String[] args, int index) {
439 if ((index + 1) < args.length) {
440 setOption(args[index], args[index+1]);
441 } else {
442 usageError("main.requires_argument", args[index]);
443 }
444 }
446 @Override
447 void usageError(String key, Object... args) {
448 messager.error(Messager.NOPOS, key, args);
449 usage(true);
450 }
452 /**
453 * indicate an option with no arguments was given.
454 */
455 private void setOption(String opt) {
456 String[] option = { opt };
457 options.append(option);
458 }
460 /**
461 * indicate an option with one argument was given.
462 */
463 private void setOption(String opt, String argument) {
464 String[] option = { opt, argument };
465 options.append(option);
466 }
468 /**
469 * indicate an option with the specified list of arguments was given.
470 */
471 private void setOption(String opt, List<String> arguments) {
472 String[] args = new String[arguments.length() + 1];
473 int k = 0;
474 args[k++] = opt;
475 for (List<String> i = arguments; i.nonEmpty(); i=i.tail) {
476 args[k++] = i.head;
477 }
478 options.append(args);
479 }
480 }