src/share/classes/com/sun/tools/javadoc/Start.java

Mon, 03 Jun 2013 17:24:47 -0700

author
jjg
date
Mon, 03 Jun 2013 17:24:47 -0700
changeset 1797
019063968164
parent 1413
bdcef2ef52d2
child 1961
58da1296c6b3
permissions
-rw-r--r--

8007687: javadoc -X does not include -Xdoclint
Reviewed-by: darcy

duke@1 1 /*
jjg@1797 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
duke@1 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1 4 *
duke@1 5 * This code is free software; you can redistribute it and/or modify it
duke@1 6 * under the terms of the GNU General Public License version 2 only, as
ohair@554 7 * published by the Free Software Foundation. Oracle designates this
duke@1 8 * particular file as subject to the "Classpath" exception as provided
ohair@554 9 * by Oracle in the LICENSE file that accompanied this code.
duke@1 10 *
duke@1 11 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1 14 * version 2 for more details (a copy is included in the LICENSE file that
duke@1 15 * accompanied this code).
duke@1 16 *
duke@1 17 * You should have received a copy of the GNU General Public License version
duke@1 18 * 2 along with this work; if not, write to the Free Software Foundation,
duke@1 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1 20 *
ohair@554 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554 22 * or visit www.oracle.com if you need additional information or have any
ohair@554 23 * questions.
duke@1 24 */
duke@1 25
duke@1 26 package com.sun.tools.javadoc;
duke@1 27
jjg@1357 28 import java.io.File;
jjg@1357 29 import java.io.FileNotFoundException;
jjg@1357 30 import java.io.IOException;
jjg@1357 31 import java.io.PrintWriter;
jjg@1413 32 import java.util.ArrayList;
jjg@1413 33 import java.util.Collection;
jjg@1413 34 import java.util.Collections;
jjg@1413 35
jjg@1413 36 import javax.tools.JavaFileManager;
jjg@1413 37 import javax.tools.JavaFileObject;
jjg@1357 38
duke@1 39 import com.sun.javadoc.*;
duke@1 40 import com.sun.tools.javac.main.CommandLine;
jjg@1413 41 import com.sun.tools.javac.util.ClientCodeException;
duke@1 42 import com.sun.tools.javac.util.Context;
duke@1 43 import com.sun.tools.javac.util.List;
duke@1 44 import com.sun.tools.javac.util.ListBuffer;
jjg@1135 45 import com.sun.tools.javac.util.Log;
duke@1 46 import com.sun.tools.javac.util.Options;
duke@1 47 import static com.sun.tools.javac.code.Flags.*;
duke@1 48
duke@1 49 /**
duke@1 50 * Main program of Javadoc.
duke@1 51 * Previously named "Main".
duke@1 52 *
jjg@1359 53 * <p><b>This is NOT part of any supported API.
jjg@1359 54 * If you write code that depends on this, you do so at your own risk.
jjg@1359 55 * This code and its internal interfaces are subject to change or
jjg@1359 56 * deletion without notice.</b>
jjg@1359 57 *
duke@1 58 * @since 1.2
duke@1 59 * @author Robert Field
duke@1 60 * @author Neal Gafter (rewrite)
duke@1 61 */
jjg@1411 62 public class Start extends ToolOption.Helper {
jjg@1392 63 /** Context for this invocation. */
jjg@1392 64 private final Context context;
duke@1 65
duke@1 66 private final String defaultDocletClassName;
jjg@129 67 private final ClassLoader docletParentClassLoader;
duke@1 68
duke@1 69 private static final String javadocName = "javadoc";
duke@1 70
duke@1 71 private static final String standardDocletClassName =
duke@1 72 "com.sun.tools.doclets.standard.Standard";
duke@1 73
duke@1 74 private long defaultFilter = PUBLIC | PROTECTED;
duke@1 75
jjg@1392 76 private final Messager messager;
duke@1 77
duke@1 78 private DocletInvoker docletInvoker;
duke@1 79
jjg@1413 80 /**
jjg@1413 81 * In API mode, exceptions thrown while calling the doclet are
jjg@1413 82 * propagated using ClientCodeException.
jjg@1413 83 */
jjg@1413 84 private boolean apiMode;
jjg@1413 85
duke@1 86 Start(String programName,
duke@1 87 PrintWriter errWriter,
duke@1 88 PrintWriter warnWriter,
duke@1 89 PrintWriter noticeWriter,
duke@1 90 String defaultDocletClassName) {
jjg@129 91 this(programName, errWriter, warnWriter, noticeWriter, defaultDocletClassName, null);
jjg@129 92 }
jjg@129 93
jjg@129 94 Start(String programName,
jjg@129 95 PrintWriter errWriter,
jjg@129 96 PrintWriter warnWriter,
jjg@129 97 PrintWriter noticeWriter,
jjg@129 98 String defaultDocletClassName,
jjg@129 99 ClassLoader docletParentClassLoader) {
jjg@1392 100 context = new Context();
jjg@1392 101 messager = new Messager(context, programName, errWriter, warnWriter, noticeWriter);
duke@1 102 this.defaultDocletClassName = defaultDocletClassName;
jjg@129 103 this.docletParentClassLoader = docletParentClassLoader;
duke@1 104 }
duke@1 105
duke@1 106 Start(String programName, String defaultDocletClassName) {
jjg@129 107 this(programName, defaultDocletClassName, null);
jjg@129 108 }
jjg@129 109
jjg@129 110 Start(String programName, String defaultDocletClassName,
jjg@129 111 ClassLoader docletParentClassLoader) {
jjg@1392 112 context = new Context();
jjg@1392 113 messager = new Messager(context, programName);
duke@1 114 this.defaultDocletClassName = defaultDocletClassName;
jjg@129 115 this.docletParentClassLoader = docletParentClassLoader;
jjg@129 116 }
jjg@129 117
jjg@129 118 Start(String programName, ClassLoader docletParentClassLoader) {
jjg@129 119 this(programName, standardDocletClassName, docletParentClassLoader);
duke@1 120 }
duke@1 121
duke@1 122 Start(String programName) {
duke@1 123 this(programName, standardDocletClassName);
duke@1 124 }
duke@1 125
jjg@129 126 Start(ClassLoader docletParentClassLoader) {
jjg@129 127 this(javadocName, docletParentClassLoader);
jjg@129 128 }
jjg@129 129
duke@1 130 Start() {
duke@1 131 this(javadocName);
duke@1 132 }
duke@1 133
jjg@1411 134 public Start(Context context) {
jjg@1411 135 context.getClass(); // null check
jjg@1411 136 this.context = context;
jjg@1413 137 apiMode = true;
jjg@1411 138 defaultDocletClassName = standardDocletClassName;
jjg@1411 139 docletParentClassLoader = null;
jjg@1411 140
jjg@1411 141 Log log = context.get(Log.logKey);
jjg@1411 142 if (log instanceof Messager)
jjg@1411 143 messager = (Messager) log;
jjg@1411 144 else {
jjg@1411 145 PrintWriter out = context.get(Log.outKey);
jjg@1411 146 messager = (out == null) ? new Messager(context, javadocName)
jjg@1411 147 : new Messager(context, javadocName, out, out, out);
jjg@1411 148 }
jjg@1411 149 }
jjg@1411 150
duke@1 151 /**
duke@1 152 * Usage
duke@1 153 */
jjg@1411 154 @Override
jjg@1411 155 void usage() {
jjg@1411 156 usage(true);
jjg@1411 157 }
jjg@1411 158
jjg@1797 159 void usage(boolean exit) {
jjg@1797 160 usage("main.usage", "-help", null, exit);
jjg@1411 161 }
jjg@1411 162
jjg@1411 163 @Override
jjg@1411 164 void Xusage() {
jjg@1411 165 Xusage(true);
duke@1 166 }
duke@1 167
jjg@1797 168 void Xusage(boolean exit) {
jjg@1797 169 usage("main.Xusage", "-X", "main.Xusage.foot", exit);
jjg@1797 170 }
jjg@1797 171
jjg@1797 172 private void usage(String main, String doclet, String foot, boolean exit) {
jjg@1797 173 // RFE: it would be better to replace the following with code to
jjg@1797 174 // write a header, then help for each option, then a footer.
jjg@1797 175 messager.notice(main);
jjg@1797 176
jjg@1797 177 // let doclet print usage information (does nothing on error)
jjg@1797 178 if (docletInvoker != null) {
jjg@1797 179 // RFE: this is a pretty bad way to get the doclet to show
jjg@1797 180 // help info. Moreover, the output appears on stdout,
jjg@1797 181 // and <i>not</i> on any of the standard streams passed
jjg@1797 182 // to javadoc, and in particular, not to the noticeWriter
jjg@1797 183 // But, to fix this, we need to fix the Doclet API.
jjg@1797 184 docletInvoker.optionLength(doclet);
jjg@1797 185 }
jjg@1797 186
jjg@1797 187 if (foot != null)
jjg@1797 188 messager.notice(foot);
jjg@1797 189
jjg@1411 190 if (exit) exit();
jjg@584 191 }
jjg@584 192
jjg@584 193 /**
duke@1 194 * Exit
duke@1 195 */
duke@1 196 private void exit() {
duke@1 197 messager.exit();
duke@1 198 }
duke@1 199
duke@1 200
duke@1 201 /**
duke@1 202 * Main program - external wrapper
duke@1 203 */
jjg@127 204 int begin(String... argv) {
jjg@1413 205 boolean ok = begin(null, argv, Collections.<JavaFileObject> emptySet());
jjg@1413 206 return ok ? 0 : 1;
jjg@1413 207 }
jjg@1413 208
jjg@1413 209 public boolean begin(Class<?> docletClass, Iterable<String> options, Iterable<? extends JavaFileObject> fileObjects) {
jjg@1413 210 Collection<String> opts = new ArrayList<String>();
jjg@1413 211 for (String opt: options) opts.add(opt);
jjg@1413 212 return begin(docletClass, opts.toArray(new String[opts.size()]), fileObjects);
jjg@1413 213 }
jjg@1413 214
jjg@1413 215 private boolean begin(Class<?> docletClass, String[] options, Iterable<? extends JavaFileObject> fileObjects) {
duke@1 216 boolean failed = false;
duke@1 217
duke@1 218 try {
jjg@1413 219 failed = !parseAndExecute(docletClass, options, fileObjects);
jjg@1411 220 } catch (Messager.ExitJavadoc exc) {
duke@1 221 // ignore, we just exit this way
duke@1 222 } catch (OutOfMemoryError ee) {
jjg@1411 223 messager.error(Messager.NOPOS, "main.out.of.memory");
duke@1 224 failed = true;
jjg@1413 225 } catch (ClientCodeException e) {
jjg@1413 226 // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
jjg@1413 227 throw e;
duke@1 228 } catch (Error ee) {
jjg@1135 229 ee.printStackTrace(System.err);
jjg@1411 230 messager.error(Messager.NOPOS, "main.fatal.error");
duke@1 231 failed = true;
duke@1 232 } catch (Exception ee) {
jjg@1135 233 ee.printStackTrace(System.err);
jjg@1411 234 messager.error(Messager.NOPOS, "main.fatal.exception");
duke@1 235 failed = true;
duke@1 236 } finally {
duke@1 237 messager.exitNotice();
duke@1 238 messager.flush();
duke@1 239 }
duke@1 240 failed |= messager.nerrors() > 0;
duke@1 241 failed |= rejectWarnings && messager.nwarnings() > 0;
jjg@1413 242 return !failed;
duke@1 243 }
duke@1 244
duke@1 245 /**
duke@1 246 * Main program - internal
duke@1 247 */
jjg@1413 248 private boolean parseAndExecute(
jjg@1413 249 Class<?> docletClass,
jjg@1413 250 String[] argv,
jjg@1413 251 Iterable<? extends JavaFileObject> fileObjects) throws IOException {
duke@1 252 long tm = System.currentTimeMillis();
duke@1 253
duke@1 254 ListBuffer<String> javaNames = new ListBuffer<String>();
duke@1 255
duke@1 256 // Preprocess @file arguments
duke@1 257 try {
duke@1 258 argv = CommandLine.parse(argv);
duke@1 259 } catch (FileNotFoundException e) {
jjg@1411 260 messager.error(Messager.NOPOS, "main.cant.read", e.getMessage());
duke@1 261 exit();
duke@1 262 } catch (IOException e) {
jjg@1135 263 e.printStackTrace(System.err);
duke@1 264 exit();
duke@1 265 }
duke@1 266
jjg@1413 267
jjg@1413 268 JavaFileManager fileManager = context.get(JavaFileManager.class);
jjg@1413 269 setDocletInvoker(docletClass, fileManager, argv);
jjg@584 270
jjg@1411 271 compOpts = Options.instance(context);
duke@1 272
duke@1 273 // Parse arguments
duke@1 274 for (int i = 0 ; i < argv.length ; i++) {
duke@1 275 String arg = argv[i];
jjg@1411 276
jjg@1411 277 ToolOption o = ToolOption.get(arg);
jjg@1411 278 if (o != null) {
jjg@1411 279 // hack: this restriction should be removed
jjg@1411 280 if (o == ToolOption.LOCALE && i > 0)
jjg@1411 281 usageError("main.locale_first");
jjg@1411 282
jjg@1411 283 if (o.hasArg) {
jjg@1411 284 oneArg(argv, i++);
jjg@1411 285 o.process(this, argv[i]);
jjg@1411 286 } else {
jjg@1411 287 setOption(arg);
jjg@1411 288 o.process(this);
duke@1 289 }
jjg@1411 290
duke@1 291 } else if (arg.startsWith("-XD")) {
jjg@1411 292 // hidden javac options
duke@1 293 String s = arg.substring("-XD".length());
duke@1 294 int eq = s.indexOf('=');
duke@1 295 String key = (eq < 0) ? s : s.substring(0, eq);
duke@1 296 String value = (eq < 0) ? s : s.substring(eq+1);
duke@1 297 compOpts.put(key, value);
duke@1 298 }
duke@1 299 // call doclet for its options
duke@1 300 // other arg starts with - is invalid
jjg@1411 301 else if (arg.startsWith("-")) {
duke@1 302 int optionLength;
duke@1 303 optionLength = docletInvoker.optionLength(arg);
duke@1 304 if (optionLength < 0) {
duke@1 305 // error already displayed
duke@1 306 exit();
duke@1 307 } else if (optionLength == 0) {
duke@1 308 // option not found
duke@1 309 usageError("main.invalid_flag", arg);
duke@1 310 } else {
duke@1 311 // doclet added option
duke@1 312 if ((i + optionLength) > argv.length) {
duke@1 313 usageError("main.requires_argument", arg);
duke@1 314 }
duke@1 315 ListBuffer<String> args = new ListBuffer<String>();
duke@1 316 for (int j = 0; j < optionLength-1; ++j) {
duke@1 317 args.append(argv[++i]);
duke@1 318 }
duke@1 319 setOption(arg, args.toList());
duke@1 320 }
duke@1 321 } else {
duke@1 322 javaNames.append(arg);
duke@1 323 }
duke@1 324 }
jjg@1392 325 compOpts.notifyListeners();
duke@1 326
jjg@1413 327 if (javaNames.isEmpty() && subPackages.isEmpty() && isEmpty(fileObjects)) {
duke@1 328 usageError("main.No_packages_or_classes_specified");
duke@1 329 }
duke@1 330
duke@1 331 if (!docletInvoker.validOptions(options.toList())) {
duke@1 332 // error message already displayed
duke@1 333 exit();
duke@1 334 }
duke@1 335
duke@1 336 JavadocTool comp = JavadocTool.make0(context);
duke@1 337 if (comp == null) return false;
duke@1 338
duke@1 339 if (showAccess == null) {
duke@1 340 setFilter(defaultFilter);
duke@1 341 }
duke@1 342
duke@1 343 LanguageVersion languageVersion = docletInvoker.languageVersion();
duke@1 344 RootDocImpl root = comp.getRootDocImpl(
jjg@1411 345 docLocale,
jjg@1411 346 encoding,
jjg@1411 347 showAccess,
jjg@1411 348 javaNames.toList(),
jjg@1411 349 options.toList(),
jjg@1413 350 fileObjects,
jjg@1411 351 breakiterator,
jjg@1411 352 subPackages.toList(),
jjg@1411 353 excludedPackages.toList(),
duke@1 354 docClasses,
duke@1 355 // legacy?
jjg@1411 356 languageVersion == null || languageVersion == LanguageVersion.JAVA_1_1,
jjg@1411 357 quiet);
duke@1 358
jjg@1391 359 // release resources
jjg@1391 360 comp = null;
jjg@1391 361
duke@1 362 // pass off control to the doclet
duke@1 363 boolean ok = root != null;
duke@1 364 if (ok) ok = docletInvoker.start(root);
duke@1 365
duke@1 366 // We're done.
duke@1 367 if (compOpts.get("-verbose") != null) {
duke@1 368 tm = System.currentTimeMillis() - tm;
duke@1 369 messager.notice("main.done_in", Long.toString(tm));
duke@1 370 }
duke@1 371
duke@1 372 return ok;
duke@1 373 }
duke@1 374
jjg@1413 375 private <T> boolean isEmpty(Iterable<T> iter) {
jjg@1413 376 return !iter.iterator().hasNext();
jjg@1413 377 }
jjg@1413 378
jjg@1413 379 /**
jjg@1413 380 * Init the doclet invoker.
jjg@1413 381 * The doclet class may be given explicitly, or via the -doclet option in
jjg@1413 382 * argv.
jjg@1413 383 * If the doclet class is not given explicitly, it will be loaded from
jjg@1413 384 * the file manager's DOCLET_PATH location, if available, or via the
jjg@1413 385 * -doclet path option in argv.
jjg@1413 386 * @param docletClass The doclet class. May be null.
jjg@1413 387 * @param fileManager The file manager used to get the class loader to load
jjg@1413 388 * the doclet class if required. May be null.
jjg@1413 389 * @param argv Args containing -doclet and -docletpath, in case they are required.
jjg@1413 390 */
jjg@1413 391 private void setDocletInvoker(Class<?> docletClass, JavaFileManager fileManager, String[] argv) {
jjg@1413 392 if (docletClass != null) {
jjg@1413 393 docletInvoker = new DocletInvoker(messager, docletClass, apiMode);
jjg@1413 394 // TODO, check no -doclet, -docletpath
jjg@1413 395 return;
jjg@1413 396 }
jjg@1413 397
duke@1 398 String docletClassName = null;
duke@1 399 String docletPath = null;
duke@1 400
duke@1 401 // Parse doclet specifying arguments
duke@1 402 for (int i = 0 ; i < argv.length ; i++) {
duke@1 403 String arg = argv[i];
jjg@1413 404 if (arg.equals(ToolOption.DOCLET.opt)) {
duke@1 405 oneArg(argv, i++);
duke@1 406 if (docletClassName != null) {
duke@1 407 usageError("main.more_than_one_doclet_specified_0_and_1",
duke@1 408 docletClassName, argv[i]);
duke@1 409 }
duke@1 410 docletClassName = argv[i];
jjg@1413 411 } else if (arg.equals(ToolOption.DOCLETPATH.opt)) {
duke@1 412 oneArg(argv, i++);
duke@1 413 if (docletPath == null) {
duke@1 414 docletPath = argv[i];
duke@1 415 } else {
duke@1 416 docletPath += File.pathSeparator + argv[i];
duke@1 417 }
duke@1 418 }
duke@1 419 }
duke@1 420
duke@1 421 if (docletClassName == null) {
duke@1 422 docletClassName = defaultDocletClassName;
duke@1 423 }
duke@1 424
duke@1 425 // attempt to find doclet
jjg@1413 426 docletInvoker = new DocletInvoker(messager, fileManager,
jjg@1413 427 docletClassName, docletPath,
jjg@1413 428 docletParentClassLoader,
jjg@1413 429 apiMode);
duke@1 430 }
duke@1 431
duke@1 432 /**
duke@1 433 * Set one arg option.
duke@1 434 * Error and exit if one argument is not provided.
duke@1 435 */
duke@1 436 private void oneArg(String[] args, int index) {
duke@1 437 if ((index + 1) < args.length) {
duke@1 438 setOption(args[index], args[index+1]);
duke@1 439 } else {
duke@1 440 usageError("main.requires_argument", args[index]);
duke@1 441 }
duke@1 442 }
duke@1 443
jjg@1411 444 @Override
jjg@1411 445 void usageError(String key, Object... args) {
jjg@1411 446 messager.error(Messager.NOPOS, key, args);
jjg@1411 447 usage(true);
duke@1 448 }
duke@1 449
duke@1 450 /**
duke@1 451 * indicate an option with no arguments was given.
duke@1 452 */
duke@1 453 private void setOption(String opt) {
duke@1 454 String[] option = { opt };
duke@1 455 options.append(option);
duke@1 456 }
duke@1 457
duke@1 458 /**
duke@1 459 * indicate an option with one argument was given.
duke@1 460 */
duke@1 461 private void setOption(String opt, String argument) {
duke@1 462 String[] option = { opt, argument };
duke@1 463 options.append(option);
duke@1 464 }
duke@1 465
duke@1 466 /**
duke@1 467 * indicate an option with the specified list of arguments was given.
duke@1 468 */
duke@1 469 private void setOption(String opt, List<String> arguments) {
duke@1 470 String[] args = new String[arguments.length() + 1];
duke@1 471 int k = 0;
duke@1 472 args[k++] = opt;
duke@1 473 for (List<String> i = arguments; i.nonEmpty(); i=i.tail) {
duke@1 474 args[k++] = i.head;
duke@1 475 }
jjg@1411 476 options.append(args);
duke@1 477 }
duke@1 478 }

mercurial