langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/main/Start.java
changeset 37938 42baa89d2156
parent 36526 3b41f1c69604
child 38617 d93a7f64e231
equal deleted inserted replaced
37858:7c04fcb12bd4 37938:42baa89d2156
       
     1 /*
       
     2  * Copyright (c) 1997, 2016, 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  */
       
    25 
       
    26 package com.sun.tools.javadoc.main;
       
    27 
       
    28 import java.io.File;
       
    29 import java.io.FileNotFoundException;
       
    30 import java.io.IOException;
       
    31 import java.io.PrintWriter;
       
    32 import java.nio.file.Path;
       
    33 import java.util.ArrayList;
       
    34 import java.util.Collection;
       
    35 import java.util.Collections;
       
    36 import java.util.Objects;
       
    37 
       
    38 import javax.tools.JavaFileManager;
       
    39 import javax.tools.JavaFileObject;
       
    40 import javax.tools.StandardJavaFileManager;
       
    41 import javax.tools.StandardLocation;
       
    42 
       
    43 import com.sun.javadoc.*;
       
    44 import com.sun.tools.javac.file.JavacFileManager;
       
    45 import com.sun.tools.javac.main.CommandLine;
       
    46 import com.sun.tools.javac.main.Option;
       
    47 import com.sun.tools.javac.file.BaseFileManager;
       
    48 import com.sun.tools.javac.platform.PlatformDescription;
       
    49 import com.sun.tools.javac.platform.PlatformUtils;
       
    50 import com.sun.tools.javac.util.ClientCodeException;
       
    51 import com.sun.tools.javac.util.Context;
       
    52 import com.sun.tools.javac.util.List;
       
    53 import com.sun.tools.javac.util.ListBuffer;
       
    54 import com.sun.tools.javac.util.Log;
       
    55 import com.sun.tools.javac.util.Options;
       
    56 
       
    57 import static com.sun.tools.javac.code.Flags.*;
       
    58 
       
    59 /**
       
    60  * Main program of Javadoc.
       
    61  * Previously named "Main".
       
    62  *
       
    63  *  <p><b>This is NOT part of any supported API.
       
    64  *  If you write code that depends on this, you do so at your own risk.
       
    65  *  This code and its internal interfaces are subject to change or
       
    66  *  deletion without notice.</b>
       
    67  *
       
    68  * @since 1.2
       
    69  * @author Robert Field
       
    70  * @author Neal Gafter (rewrite)
       
    71  */
       
    72 public class Start extends ToolOption.Helper {
       
    73     /** Context for this invocation. */
       
    74     private final Context context;
       
    75 
       
    76     private final String defaultDocletClassName;
       
    77     private final ClassLoader docletParentClassLoader;
       
    78 
       
    79     private static final String javadocName = "javadoc";
       
    80 
       
    81     private static final String standardDocletClassName =
       
    82         "com.sun.tools.doclets.standard.Standard";
       
    83 
       
    84     private final long defaultFilter = PUBLIC | PROTECTED;
       
    85 
       
    86     private final Messager messager;
       
    87 
       
    88     private DocletInvoker docletInvoker;
       
    89 
       
    90     /**
       
    91      * In API mode, exceptions thrown while calling the doclet are
       
    92      * propagated using ClientCodeException.
       
    93      */
       
    94     private boolean apiMode;
       
    95 
       
    96     private JavaFileManager fileManager;
       
    97 
       
    98     public Start(String programName,
       
    99           PrintWriter errWriter,
       
   100           PrintWriter warnWriter,
       
   101           PrintWriter noticeWriter,
       
   102           String defaultDocletClassName) {
       
   103         this(programName, errWriter, warnWriter, noticeWriter, defaultDocletClassName, null);
       
   104     }
       
   105 
       
   106     public Start(PrintWriter pw) {
       
   107         this(javadocName, pw, pw, pw, standardDocletClassName);
       
   108     }
       
   109 
       
   110     public Start(String programName,
       
   111           PrintWriter errWriter,
       
   112           PrintWriter warnWriter,
       
   113           PrintWriter noticeWriter,
       
   114           String defaultDocletClassName,
       
   115           ClassLoader docletParentClassLoader) {
       
   116         context = new Context();
       
   117         messager = new Messager(context, programName, errWriter, warnWriter, noticeWriter);
       
   118         this.defaultDocletClassName = defaultDocletClassName;
       
   119         this.docletParentClassLoader = docletParentClassLoader;
       
   120     }
       
   121 
       
   122     public Start(String programName, String defaultDocletClassName) {
       
   123         this(programName, defaultDocletClassName, null);
       
   124     }
       
   125 
       
   126     public Start(String programName, String defaultDocletClassName,
       
   127           ClassLoader docletParentClassLoader) {
       
   128         context = new Context();
       
   129         messager = new Messager(context, programName);
       
   130         this.defaultDocletClassName = defaultDocletClassName;
       
   131         this.docletParentClassLoader = docletParentClassLoader;
       
   132     }
       
   133 
       
   134     public Start(String programName, ClassLoader docletParentClassLoader) {
       
   135         this(programName, standardDocletClassName, docletParentClassLoader);
       
   136     }
       
   137 
       
   138     public Start(String programName) {
       
   139         this(programName, standardDocletClassName);
       
   140     }
       
   141 
       
   142     public Start(ClassLoader docletParentClassLoader) {
       
   143         this(javadocName, docletParentClassLoader);
       
   144     }
       
   145 
       
   146     public Start() {
       
   147         this(javadocName);
       
   148     }
       
   149 
       
   150     public Start(Context context) {
       
   151         this.context = Objects.requireNonNull(context);
       
   152         apiMode = true;
       
   153         defaultDocletClassName = standardDocletClassName;
       
   154         docletParentClassLoader = null;
       
   155 
       
   156         Log log = context.get(Log.logKey);
       
   157         if (log instanceof Messager)
       
   158             messager = (Messager) log;
       
   159         else {
       
   160             PrintWriter out = context.get(Log.outKey);
       
   161             messager = (out == null) ? new Messager(context, javadocName)
       
   162                     : new Messager(context, javadocName, out, out, out);
       
   163         }
       
   164     }
       
   165 
       
   166     /**
       
   167      * Usage
       
   168      */
       
   169     @Override
       
   170     void usage() {
       
   171         usage(true);
       
   172     }
       
   173 
       
   174     void usage(boolean exit) {
       
   175         usage("main.usage", "-help", null, exit);
       
   176     }
       
   177 
       
   178     @Override
       
   179     void Xusage() {
       
   180         Xusage(true);
       
   181     }
       
   182 
       
   183     void Xusage(boolean exit) {
       
   184         usage("main.Xusage", "-X", "main.Xusage.foot", exit);
       
   185     }
       
   186 
       
   187     private void usage(String main, String doclet, String foot, boolean exit) {
       
   188         // RFE: it would be better to replace the following with code to
       
   189         // write a header, then help for each option, then a footer.
       
   190         messager.notice(main);
       
   191 
       
   192         // let doclet print usage information (does nothing on error)
       
   193         if (docletInvoker != null) {
       
   194             // RFE: this is a pretty bad way to get the doclet to show
       
   195             // help info. Moreover, the output appears on stdout,
       
   196             // and <i>not</i> on any of the standard streams passed
       
   197             // to javadoc, and in particular, not to the noticeWriter
       
   198             // But, to fix this, we need to fix the Doclet API.
       
   199             docletInvoker.optionLength(doclet);
       
   200         }
       
   201 
       
   202         if (foot != null)
       
   203             messager.notice(foot);
       
   204 
       
   205         if (exit) exit();
       
   206     }
       
   207 
       
   208     /**
       
   209      * Exit
       
   210      */
       
   211     private void exit() {
       
   212         messager.exit();
       
   213     }
       
   214 
       
   215 
       
   216     /**
       
   217      * Main program - external wrapper
       
   218      */
       
   219     public int begin(String... argv) {
       
   220         boolean ok = begin(null, argv, Collections.<JavaFileObject> emptySet());
       
   221         return ok ? 0 : 1;
       
   222     }
       
   223 
       
   224     public boolean begin(Class<?> docletClass, Iterable<String> options, Iterable<? extends JavaFileObject> fileObjects) {
       
   225         Collection<String> opts = new ArrayList<>();
       
   226         for (String opt: options) opts.add(opt);
       
   227         return begin(docletClass, opts.toArray(new String[opts.size()]), fileObjects);
       
   228     }
       
   229 
       
   230     private boolean begin(Class<?> docletClass, String[] options, Iterable<? extends JavaFileObject> fileObjects) {
       
   231         boolean failed = false;
       
   232 
       
   233         try {
       
   234             failed = !parseAndExecute(docletClass, options, fileObjects);
       
   235         } catch (Messager.ExitJavadoc exc) {
       
   236             // ignore, we just exit this way
       
   237         } catch (OutOfMemoryError ee) {
       
   238             messager.error(Messager.NOPOS, "main.out.of.memory");
       
   239             failed = true;
       
   240         } catch (ClientCodeException e) {
       
   241             // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
       
   242             throw e;
       
   243         } catch (Error ee) {
       
   244             ee.printStackTrace(System.err);
       
   245             messager.error(Messager.NOPOS, "main.fatal.error");
       
   246             failed = true;
       
   247         } catch (Exception ee) {
       
   248             ee.printStackTrace(System.err);
       
   249             messager.error(Messager.NOPOS, "main.fatal.exception");
       
   250             failed = true;
       
   251         } finally {
       
   252             if (fileManager != null
       
   253                     && fileManager instanceof BaseFileManager
       
   254                     && ((BaseFileManager) fileManager).autoClose) {
       
   255                 try {
       
   256                     fileManager.close();
       
   257                 } catch (IOException ignore) {
       
   258                 }
       
   259             }
       
   260             messager.exitNotice();
       
   261             messager.flush();
       
   262         }
       
   263         failed |= messager.nerrors() > 0;
       
   264         failed |= rejectWarnings && messager.nwarnings() > 0;
       
   265         return !failed;
       
   266     }
       
   267 
       
   268     /**
       
   269      * Main program - internal
       
   270      */
       
   271     private boolean parseAndExecute(
       
   272             Class<?> docletClass,
       
   273             String[] argv,
       
   274             Iterable<? extends JavaFileObject> fileObjects) throws IOException {
       
   275         long tm = System.currentTimeMillis();
       
   276 
       
   277         ListBuffer<String> javaNames = new ListBuffer<>();
       
   278 
       
   279         // Preprocess @file arguments
       
   280         try {
       
   281             argv = CommandLine.parse(argv);
       
   282         } catch (FileNotFoundException e) {
       
   283             messager.error(Messager.NOPOS, "main.cant.read", e.getMessage());
       
   284             exit();
       
   285         } catch (IOException e) {
       
   286             e.printStackTrace(System.err);
       
   287             exit();
       
   288         }
       
   289 
       
   290 
       
   291         fileManager = context.get(JavaFileManager.class);
       
   292 
       
   293         setDocletInvoker(docletClass, fileManager, argv);
       
   294 
       
   295         compOpts = Options.instance(context);
       
   296         // Make sure no obsolete source/target messages are reported
       
   297         compOpts.put("-Xlint:-options", "-Xlint:-options");
       
   298 
       
   299         // Parse arguments
       
   300         for (int i = 0 ; i < argv.length ; i++) {
       
   301             String arg = argv[i];
       
   302 
       
   303             ToolOption o = ToolOption.get(arg);
       
   304             if (o != null) {
       
   305                 // hack: this restriction should be removed
       
   306                 if (o == ToolOption.LOCALE && i > 0)
       
   307                     usageError("main.locale_first");
       
   308 
       
   309                 if (o.hasArg) {
       
   310                     oneArg(argv, i++);
       
   311                     o.process(this, argv[i]);
       
   312                 } else {
       
   313                     setOption(arg);
       
   314                     o.process(this);
       
   315                 }
       
   316             } else if (arg.equals("-XDaccessInternalAPI")) {
       
   317                 // pass this hidden option down to the doclet, if it wants it
       
   318                 if (docletInvoker.optionLength("-XDaccessInternalAPI") == 1) {
       
   319                     setOption(arg);
       
   320                 }
       
   321             } else if (arg.startsWith("-XD")) {
       
   322                 // hidden javac options
       
   323                 String s = arg.substring("-XD".length());
       
   324                 int eq = s.indexOf('=');
       
   325                 String key = (eq < 0) ? s : s.substring(0, eq);
       
   326                 String value = (eq < 0) ? s : s.substring(eq+1);
       
   327                 compOpts.put(key, value);
       
   328             }
       
   329             // call doclet for its options
       
   330             // other arg starts with - is invalid
       
   331             else if (arg.startsWith("-")) {
       
   332                 int optionLength;
       
   333                 optionLength = docletInvoker.optionLength(arg);
       
   334                 if (optionLength < 0) {
       
   335                     // error already displayed
       
   336                     exit();
       
   337                 } else if (optionLength == 0) {
       
   338                     // option not found
       
   339                     usageError("main.invalid_flag", arg);
       
   340                 } else {
       
   341                     // doclet added option
       
   342                     if ((i + optionLength) > argv.length) {
       
   343                         usageError("main.requires_argument", arg);
       
   344                     }
       
   345                     ListBuffer<String> args = new ListBuffer<>();
       
   346                     for (int j = 0; j < optionLength-1; ++j) {
       
   347                         args.append(argv[++i]);
       
   348                     }
       
   349                     setOption(arg, args.toList());
       
   350                 }
       
   351             } else {
       
   352                 javaNames.append(arg);
       
   353             }
       
   354         }
       
   355 
       
   356         if (fileManager == null) {
       
   357             JavacFileManager.preRegister(context);
       
   358             fileManager = context.get(JavaFileManager.class);
       
   359             if (fileManager instanceof BaseFileManager) {
       
   360                 ((BaseFileManager) fileManager).autoClose = true;
       
   361             }
       
   362         }
       
   363         if (fileManager instanceof BaseFileManager) {
       
   364             ((BaseFileManager) fileManager).handleOptions(fileManagerOpts);
       
   365         }
       
   366 
       
   367         String platformString = compOpts.get("-release");
       
   368 
       
   369         if (platformString != null) {
       
   370             if (compOpts.isSet("-source")) {
       
   371                 usageError("main.release.bootclasspath.conflict", "-source");
       
   372             }
       
   373             if (fileManagerOpts.containsKey(Option.BOOTCLASSPATH)) {
       
   374                 usageError("main.release.bootclasspath.conflict", Option.BOOTCLASSPATH.getText());
       
   375             }
       
   376 
       
   377             PlatformDescription platformDescription =
       
   378                     PlatformUtils.lookupPlatformDescription(platformString);
       
   379 
       
   380             if (platformDescription == null) {
       
   381                 usageError("main.unsupported.release.version", platformString);
       
   382             }
       
   383 
       
   384             compOpts.put(Option.SOURCE, platformDescription.getSourceVersion());
       
   385 
       
   386             context.put(PlatformDescription.class, platformDescription);
       
   387 
       
   388             Collection<Path> platformCP = platformDescription.getPlatformPath();
       
   389 
       
   390             if (platformCP != null) {
       
   391                 if (fileManager instanceof StandardJavaFileManager) {
       
   392                     StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
       
   393 
       
   394                     sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP);
       
   395                 } else {
       
   396                     usageError("main.release.not.standard.file.manager", platformString);
       
   397                 }
       
   398             }
       
   399         }
       
   400 
       
   401         compOpts.notifyListeners();
       
   402 
       
   403         if (javaNames.isEmpty() && subPackages.isEmpty() && isEmpty(fileObjects)) {
       
   404             usageError("main.No_packages_or_classes_specified");
       
   405         }
       
   406 
       
   407         if (!docletInvoker.validOptions(options.toList())) {
       
   408             // error message already displayed
       
   409             exit();
       
   410         }
       
   411 
       
   412         JavadocTool comp = JavadocTool.make0(context);
       
   413         if (comp == null) return false;
       
   414 
       
   415         if (showAccess == null) {
       
   416             setFilter(defaultFilter);
       
   417         }
       
   418 
       
   419         LanguageVersion languageVersion = docletInvoker.languageVersion();
       
   420         RootDocImpl root = comp.getRootDocImpl(
       
   421                 docLocale,
       
   422                 encoding,
       
   423                 showAccess,
       
   424                 javaNames.toList(),
       
   425                 options.toList(),
       
   426                 fileObjects,
       
   427                 breakiterator,
       
   428                 subPackages.toList(),
       
   429                 excludedPackages.toList(),
       
   430                 docClasses,
       
   431                 // legacy?
       
   432                 languageVersion == null || languageVersion == LanguageVersion.JAVA_1_1,
       
   433                 quiet);
       
   434 
       
   435         // release resources
       
   436         comp = null;
       
   437 
       
   438         // pass off control to the doclet
       
   439         boolean ok = root != null;
       
   440         if (ok) ok = docletInvoker.start(root);
       
   441 
       
   442         // We're done.
       
   443         if (compOpts.get("-verbose") != null) {
       
   444             tm = System.currentTimeMillis() - tm;
       
   445             messager.notice("main.done_in", Long.toString(tm));
       
   446         }
       
   447 
       
   448         return ok;
       
   449     }
       
   450 
       
   451     private <T> boolean isEmpty(Iterable<T> iter) {
       
   452         return !iter.iterator().hasNext();
       
   453     }
       
   454 
       
   455     /**
       
   456      * Init the doclet invoker.
       
   457      * The doclet class may be given explicitly, or via the -doclet option in
       
   458      * argv.
       
   459      * If the doclet class is not given explicitly, it will be loaded from
       
   460      * the file manager's DOCLET_PATH location, if available, or via the
       
   461      * -doclet path option in argv.
       
   462      * @param docletClass The doclet class. May be null.
       
   463      * @param fileManager The file manager used to get the class loader to load
       
   464      * the doclet class if required. May be null.
       
   465      * @param argv Args containing -doclet and -docletpath, in case they are required.
       
   466      */
       
   467     private void setDocletInvoker(Class<?> docletClass, JavaFileManager fileManager, String[] argv) {
       
   468         boolean exportInternalAPI = false;
       
   469         String docletClassName = null;
       
   470         String docletPath = null;
       
   471 
       
   472         // Parse doclet specifying arguments
       
   473         for (int i = 0 ; i < argv.length ; i++) {
       
   474             String arg = argv[i];
       
   475             if (arg.equals(ToolOption.DOCLET.opt)) {
       
   476                 oneArg(argv, i++);
       
   477                 if (docletClassName != null) {
       
   478                     usageError("main.more_than_one_doclet_specified_0_and_1",
       
   479                                docletClassName, argv[i]);
       
   480                 }
       
   481                 docletClassName = argv[i];
       
   482             } else if (arg.equals(ToolOption.DOCLETPATH.opt)) {
       
   483                 oneArg(argv, i++);
       
   484                 if (docletPath == null) {
       
   485                     docletPath = argv[i];
       
   486                 } else {
       
   487                     docletPath += File.pathSeparator + argv[i];
       
   488                 }
       
   489             } else if (arg.equals("-XDaccessInternalAPI")) {
       
   490                 exportInternalAPI = true;
       
   491             }
       
   492         }
       
   493 
       
   494         if (docletClass != null) {
       
   495             // TODO, check no -doclet, -docletpath
       
   496             docletInvoker = new DocletInvoker(messager, docletClass, apiMode, exportInternalAPI);
       
   497         } else {
       
   498             if (docletClassName == null) {
       
   499                 docletClassName = defaultDocletClassName;
       
   500             }
       
   501 
       
   502             // attempt to find doclet
       
   503             docletInvoker = new DocletInvoker(messager, fileManager,
       
   504                     docletClassName, docletPath,
       
   505                     docletParentClassLoader,
       
   506                     apiMode,
       
   507                     exportInternalAPI);
       
   508         }
       
   509     }
       
   510 
       
   511     /**
       
   512      * Set one arg option.
       
   513      * Error and exit if one argument is not provided.
       
   514      */
       
   515     private void oneArg(String[] args, int index) {
       
   516         if ((index + 1) < args.length) {
       
   517             setOption(args[index], args[index+1]);
       
   518         } else {
       
   519             usageError("main.requires_argument", args[index]);
       
   520         }
       
   521     }
       
   522 
       
   523     @Override
       
   524     void usageError(String key, Object... args) {
       
   525         messager.error(Messager.NOPOS, key, args);
       
   526         usage(true);
       
   527     }
       
   528 
       
   529     /**
       
   530      * indicate an option with no arguments was given.
       
   531      */
       
   532     private void setOption(String opt) {
       
   533         String[] option = { opt };
       
   534         options.append(option);
       
   535     }
       
   536 
       
   537     /**
       
   538      * indicate an option with one argument was given.
       
   539      */
       
   540     private void setOption(String opt, String argument) {
       
   541         String[] option = { opt, argument };
       
   542         options.append(option);
       
   543     }
       
   544 
       
   545     /**
       
   546      * indicate an option with the specified list of arguments was given.
       
   547      */
       
   548     private void setOption(String opt, List<String> arguments) {
       
   549         String[] args = new String[arguments.length() + 1];
       
   550         int k = 0;
       
   551         args[k++] = opt;
       
   552         for (List<String> i = arguments; i.nonEmpty(); i=i.tail) {
       
   553             args[k++] = i.head;
       
   554         }
       
   555         options.append(args);
       
   556     }
       
   557 }