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