src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java

Print this page


   1 /*
   2  * Copyright (c) 1997, 2014, 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;
  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     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     Start(String programName,
 107           PrintWriter errWriter,
 108           PrintWriter warnWriter,
 109           PrintWriter noticeWriter,
 110           String defaultDocletClassName,
 111           ClassLoader docletParentClassLoader) {
 112         context = new Context();
 113         messager = new Messager(context, programName, errWriter, warnWriter, noticeWriter);
 114         this.defaultDocletClassName = defaultDocletClassName;
 115         this.docletParentClassLoader = docletParentClassLoader;
 116     }
 117 
 118     Start(String programName, String defaultDocletClassName) {
 119         this(programName, defaultDocletClassName, null);









 120     }
 121 
 122     Start(String programName, String defaultDocletClassName,
 123           ClassLoader docletParentClassLoader) {
 124         context = new Context();
 125         messager = new Messager(context, programName);
 126         this.defaultDocletClassName = defaultDocletClassName;
 127         this.docletParentClassLoader = docletParentClassLoader;
 128     }
 129 
 130     Start(String programName, ClassLoader docletParentClassLoader) {
 131         this(programName, standardDocletClassName, docletParentClassLoader);
 132     }
 133 
 134     Start(String programName) {
 135         this(programName, standardDocletClassName);
 136     }
 137 
 138     Start(ClassLoader docletParentClassLoader) {
 139         this(javadocName, docletParentClassLoader);
 140     }
 141 
 142     Start() {
 143         this(javadocName);
 144     }
 145 
 146     public Start(Context context) {

 147         this.context = Objects.requireNonNull(context);
 148         apiMode = true;
 149         defaultDocletClassName = standardDocletClassName;
 150         docletParentClassLoader = null;


 151 




 152         Log log = context.get(Log.logKey);
 153         if (log instanceof Messager)
 154             messager = (Messager) log;
 155         else {
 156             PrintWriter out = context.get(Log.outKey);
 157             messager = (out == null) ? new Messager(context, javadocName)
 158                     : new Messager(context, javadocName, out, out, out);

 159         }
 160     }

 161 
 162     /**
 163      * Usage
 164      */
 165     @Override
 166     void usage() {
 167         usage(true);
 168     }
 169 
 170     void usage(boolean exit) {
 171         usage("main.usage", "-help", null, exit);
 172     }
 173 
 174     @Override
 175     void Xusage() {
 176         Xusage(true);
 177     }
 178 
 179     void Xusage(boolean exit) {
 180         usage("main.Xusage", "-X", "main.Xusage.foot", exit);
 181     }
 182 
 183     private void usage(String main, String doclet, String foot, boolean exit) {
 184         // RFE: it would be better to replace the following with code to
 185         // write a header, then help for each option, then a footer.
 186         messager.notice(main);
 187 
 188         // let doclet print usage information (does nothing on error)
 189         if (docletInvoker != null) {
 190             // RFE: this is a pretty bad way to get the doclet to show
 191             // help info. Moreover, the output appears on stdout,
 192             // and <i>not</i> on any of the standard streams passed
 193             // to javadoc, and in particular, not to the noticeWriter
 194             // But, to fix this, we need to fix the Doclet API.
 195             docletInvoker.optionLength(doclet);



 196         }
 197 
 198         if (foot != null)
 199             messager.notice(foot);
 200 
 201         if (exit) exit();
 202     }
 203 
 204     /**
 205      * Exit
 206      */
 207     private void exit() {
 208         messager.exit();
 209     }
 210 
 211 
 212     /**
 213      * Main program - external wrapper
 214      */
 215     int begin(String... argv) {
 216         boolean ok = begin(null, argv, Collections.<JavaFileObject> emptySet());












 217         return ok ? 0 : 1;
 218     }
 219 
 220     public boolean begin(Class<?> docletClass, Iterable<String> options, Iterable<? extends JavaFileObject> fileObjects) {
 221         Collection<String> opts = new ArrayList<>();
 222         for (String opt: options) opts.add(opt);
 223         return begin(docletClass, opts.toArray(new String[opts.size()]), fileObjects);





 224     }
 225 
 226     private boolean begin(Class<?> docletClass, String[] options, Iterable<? extends JavaFileObject> fileObjects) {
 227         boolean failed = false;
 228 















 229         try {
 230             failed = !parseAndExecute(docletClass, options, fileObjects);




















 231         } catch (Messager.ExitJavadoc exc) {
 232             // ignore, we just exit this way
 233         } catch (OutOfMemoryError ee) {
 234             messager.error(Messager.NOPOS, "main.out.of.memory");
 235             failed = true;
 236         } catch (ClientCodeException e) {
 237             // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
 238             throw e;
 239         } catch (Error ee) {
 240             ee.printStackTrace(System.err);
 241             messager.error(Messager.NOPOS, "main.fatal.error");
 242             failed = true;
 243         } catch (Exception ee) {
 244             ee.printStackTrace(System.err);
 245             messager.error(Messager.NOPOS, "main.fatal.exception");
 246             failed = true;
 247         } finally {
 248             if (fileManager != null
 249                     && fileManager instanceof BaseFileManager
 250                     && ((BaseFileManager) fileManager).autoClose) {
 251                 try {
 252                     fileManager.close();
 253                 } catch (IOException ignore) {
 254                 }





 255             }

 256             messager.exitNotice();
 257             messager.flush();
 258         }
 259         failed |= messager.nerrors() > 0;
 260         failed |= rejectWarnings && messager.nwarnings() > 0;
 261         return !failed;
 262     }
 263 
 264     /**
 265      * Main program - internal
 266      */
 267     private boolean parseAndExecute(
 268             Class<?> docletClass,
 269             String[] argv,
 270             Iterable<? extends JavaFileObject> fileObjects) throws IOException {
 271         long tm = System.currentTimeMillis();
 272 
 273         ListBuffer<String> javaNames = new ListBuffer<>();
 274 
 275         // Preprocess @file arguments
 276         try {
 277             argv = CommandLine.parse(argv);
 278         } catch (FileNotFoundException e) {
 279             messager.error(Messager.NOPOS, "main.cant.read", e.getMessage());
 280             exit();
 281         } catch (IOException e) {
 282             e.printStackTrace(System.err);
 283             exit();
 284         }
 285 
 286 
 287         fileManager = context.get(JavaFileManager.class);
 288 
 289         setDocletInvoker(docletClass, fileManager, argv);
 290 
 291         compOpts = Options.instance(context);

 292         // Make sure no obsolete source/target messages are reported
 293         compOpts.put("-Xlint:-options", "-Xlint:-options");
 294 
 295         // Parse arguments
 296         for (int i = 0 ; i < argv.length ; i++) {
 297             String arg = argv[i];
 298 
 299             ToolOption o = ToolOption.get(arg);
 300             if (o != null) {
 301                 // hack: this restriction should be removed
 302                 if (o == ToolOption.LOCALE && i > 0)
 303                     usageError("main.locale_first");
 304 
 305                 if (o.hasArg) {
 306                     oneArg(argv, i++);
 307                     o.process(this, argv[i]);
 308                 } else {
 309                     setOption(arg);
 310                     o.process(this);
 311                 }
 312 
 313             } else if (arg.startsWith("-XD")) {
 314                 // hidden javac options
 315                 String s = arg.substring("-XD".length());
 316                 int eq = s.indexOf('=');
 317                 String key = (eq < 0) ? s : s.substring(0, eq);
 318                 String value = (eq < 0) ? s : s.substring(eq+1);
 319                 compOpts.put(key, value);
 320             }
 321             // call doclet for its options
 322             // other arg starts with - is invalid
 323             else if (arg.startsWith("-")) {
 324                 int optionLength;
 325                 optionLength = docletInvoker.optionLength(arg);
 326                 if (optionLength < 0) {
 327                     // error already displayed
 328                     exit();
 329                 } else if (optionLength == 0) {
 330                     // option not found
 331                     usageError("main.invalid_flag", arg);
 332                 } else {
 333                     // doclet added option
 334                     if ((i + optionLength) > argv.length) {
 335                         usageError("main.requires_argument", arg);
 336                     }
 337                     ListBuffer<String> args = new ListBuffer<>();
 338                     for (int j = 0; j < optionLength-1; ++j) {
 339                         args.append(argv[++i]);
 340                     }
 341                     setOption(arg, args.toList());
 342                 }
 343             } else {
 344                 javaNames.append(arg);
 345             }
 346         }
 347 
 348         if (fileManager == null) {
 349             JavacFileManager.preRegister(context);
 350             fileManager = context.get(JavaFileManager.class);
 351             if (fileManager instanceof BaseFileManager) {
 352                 ((BaseFileManager) fileManager).autoClose = true;
 353             }
 354         }
 355         if (fileManager instanceof BaseFileManager) {
 356             ((BaseFileManager) fileManager).handleOptions(fileManagerOpts);
 357         }
 358 
 359         String platformString = compOpts.get("-release");
 360 
 361         if (platformString != null) {
 362             if (compOpts.isSet("-source")) {
 363                 usageError("main.release.bootclasspath.conflict", "-source");
 364             }
 365             if (fileManagerOpts.containsKey(Option.BOOTCLASSPATH)) {
 366                 usageError("main.release.bootclasspath.conflict", Option.BOOTCLASSPATH.getText());
 367             }
 368 
 369             PlatformDescription platformDescription =
 370                     PlatformUtils.lookupPlatformDescription(platformString);
 371 
 372             if (platformDescription == null) {
 373                 usageError("main.unsupported.release.version", platformString);
 374             }
 375 
 376             compOpts.put(Option.SOURCE, platformDescription.getSourceVersion());
 377 
 378             context.put(PlatformDescription.class, platformDescription);
 379 
 380             Collection<Path> platformCP = platformDescription.getPlatformPath();
 381 
 382             if (platformCP != null) {
 383                 if (fileManager instanceof StandardJavaFileManager) {
 384                     StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
 385 
 386                     sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP);
 387                 } else {
 388                     usageError("main.release.not.standard.file.manager", platformString);
 389                 }
 390             }
 391         }
 392 
 393         compOpts.notifyListeners();
 394 
 395         if (javaNames.isEmpty() && subPackages.isEmpty() && isEmpty(fileObjects)) {
 396             usageError("main.No_packages_or_classes_specified");
 397         }
 398 
 399         if (!docletInvoker.validOptions(options.toList())) {
 400             // error message already displayed
 401             exit();
 402         }
 403 
 404         JavadocTool comp = JavadocTool.make0(context);
 405         if (comp == null) return false;
 406 
 407         if (showAccess == null) {
 408             setFilter(defaultFilter);
 409         }
 410 
 411         LanguageVersion languageVersion = docletInvoker.languageVersion();
 412         RootDocImpl root = comp.getRootDocImpl(
 413                 docLocale,
 414                 encoding,
 415                 showAccess,
 416                 javaNames.toList(),
 417                 options.toList(),
 418                 fileObjects,
 419                 breakiterator,
 420                 subPackages.toList(),
 421                 excludedPackages.toList(),
 422                 docClasses,
 423                 // legacy?
 424                 languageVersion == null || languageVersion == LanguageVersion.JAVA_1_1,
 425                 quiet);
 426 
 427         // release resources
 428         comp = null;
 429 




 430         // pass off control to the doclet
 431         boolean ok = root != null;
 432         if (ok) ok = docletInvoker.start(root);
 433 
 434         // We're done.
 435         if (compOpts.get("-verbose") != null) {
 436             tm = System.currentTimeMillis() - tm;
 437             messager.notice("main.done_in", Long.toString(tm));
 438         }
 439 
 440         return ok;
 441     }
 442 
 443     private <T> boolean isEmpty(Iterable<T> iter) {
 444         return !iter.iterator().hasNext();


 445     }

 446 
 447     /**
 448      * Init the doclet invoker.
 449      * The doclet class may be given explicitly, or via the -doclet option in
 450      * argv.
 451      * If the doclet class is not given explicitly, it will be loaded from
 452      * the file manager's DOCLET_PATH location, if available, or via the
 453      * -doclet path option in argv.
 454      * @param docletClass The doclet class. May be null.
 455      * @param fileManager The file manager used to get the class loader to load
 456      * the doclet class if required. May be null.
 457      * @param argv Args containing -doclet and -docletpath, in case they are required.
 458      */
 459     private void setDocletInvoker(Class<?> docletClass, JavaFileManager fileManager, String[] argv) {
 460         if (docletClass != null) {
 461             docletInvoker = new DocletInvoker(messager, docletClass, apiMode);
 462             // TODO, check no -doclet, -docletpath
 463             return;
 464         }










 465 
 466         String docletClassName = null;
 467         String docletPath = null;


 468 
 469         // Parse doclet specifying arguments
 470         for (int i = 0 ; i < argv.length ; i++) {
 471             String arg = argv[i];
 472             if (arg.equals(ToolOption.DOCLET.opt)) {
 473                 oneArg(argv, i++);
 474                 if (docletClassName != null) {




 475                     usageError("main.more_than_one_doclet_specified_0_and_1",
 476                                docletClassName, argv[i]);
 477                 }
 478                 docletClassName = argv[i];




 479             } else if (arg.equals(ToolOption.DOCLETPATH.opt)) {
 480                 oneArg(argv, i++);
 481                 if (docletPath == null) {
 482                     docletPath = argv[i];
 483                 } else {
 484                     docletPath += File.pathSeparator + argv[i];
 485                 }
 486             }
 487         }



















































 488 
 489         if (docletClassName == null) {
 490             docletClassName = defaultDocletClassName;













 491         }














 492 
 493         // attempt to find doclet
 494         docletInvoker = new DocletInvoker(messager, fileManager,
 495                 docletClassName, docletPath,
 496                 docletParentClassLoader,
 497                 apiMode);
 498     }
 499 
 500     /**
 501      * Set one arg option.
 502      * Error and exit if one argument is not provided.
 503      */
 504     private void oneArg(String[] args, int index) {
 505         if ((index + 1) < args.length) {
 506             setOption(args[index], args[index+1]);
 507         } else {
 508             usageError("main.requires_argument", args[index]);
 509         }
 510     }
 511 
 512     @Override
 513     void usageError(String key, Object... args) {
 514         messager.error(Messager.NOPOS, key, args);
 515         usage(true);
 516     }
 517 










 518     /**
 519      * indicate an option with no arguments was given.
 520      */
 521     private void setOption(String opt) {
 522         String[] option = { opt };
 523         options.append(option);
 524     }
 525 
 526     /**
 527      * indicate an option with one argument was given.
 528      */
 529     private void setOption(String opt, String argument) {
 530         String[] option = { opt, argument };
 531         options.append(option);
 532     }
 533 
 534     /**
 535      * indicate an option with the specified list of arguments was given.
 536      */
 537     private void setOption(String opt, List<String> arguments) {
 538         String[] args = new String[arguments.length() + 1];
 539         int k = 0;
 540         args[k++] = opt;
 541         for (List<String> i = arguments; i.nonEmpty(); i=i.tail) {
 542             args[k++] = i.head;
 543         }
 544         options.append(args);









 545     }





















































 546 }
   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 jdk.javadoc.internal.tool;
  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.text.BreakIterator;
  34 import java.util.ArrayList;
  35 import java.util.Arrays;
  36 import java.util.Collection;
  37 import java.util.Collections;
  38 import java.util.List;
  39 import java.util.Locale;
  40 import java.util.Objects;
  41 import java.util.Set;
  42 import static javax.tools.DocumentationTool.Location.*;
  43 import javax.tools.JavaFileManager;
  44 import javax.tools.JavaFileObject;
  45 import javax.tools.StandardJavaFileManager;
  46 import javax.tools.StandardLocation;
  47 
  48 import com.sun.tools.javac.api.JavacTrees;
  49 import com.sun.tools.javac.file.BaseFileManager;
  50 import com.sun.tools.javac.file.JavacFileManager;
  51 import com.sun.tools.javac.main.CommandLine;


  52 import com.sun.tools.javac.platform.PlatformDescription;
  53 import com.sun.tools.javac.platform.PlatformUtils;
  54 import com.sun.tools.javac.util.ClientCodeException;
  55 import com.sun.tools.javac.util.Context;


  56 import com.sun.tools.javac.util.Log;
  57 import com.sun.tools.javac.util.Options;
  58 
  59 import jdk.javadoc.doclet.Doclet;
  60 import jdk.javadoc.doclet.Doclet.Option;
  61 import jdk.javadoc.doclet.DocletEnvironment;
  62 
  63 import static com.sun.tools.javac.main.Option.*;
  64 /**
  65  * Main program of Javadoc.
  66  * Previously named "Main".
  67  *
  68  *  <p><b>This is NOT part of any supported API.
  69  *  If you write code that depends on this, you do so at your own risk.
  70  *  This code and its internal interfaces are subject to change or
  71  *  deletion without notice.</b>
  72  *
  73  * @since 1.2
  74  * @author Robert Field
  75  * @author Neal Gafter (rewrite)
  76  */
  77 public class Start extends ToolOption.Helper {
  78     /** Context for this invocation. */
  79     private final Context context;
  80 
  81     private static final String ProgramName = "javadoc";

  82 
  83     // meaning we allow all visibility of PROTECTED and PUBLIC
  84     private static final String defaultModifier = "protected";
  85 
  86     private Messager messager;

  87 
  88     private final String docletName;
  89 
  90     private final ClassLoader classLoader;
  91 
  92     private Class<?> docletClass;
  93 
  94     private Doclet doclet;
  95 
  96     // used to determine the locale for the messager
  97     private Locale locale;
  98 
  99 
 100     /**
 101      * In API mode, exceptions thrown while calling the doclet are
 102      * propagated using ClientCodeException.
 103      */
 104     private boolean apiMode;
 105 
 106     private JavaFileManager fileManager;
 107 
 108     Start() {
 109         this(null, null, null, null, null);




 110     }
 111 
 112     Start(PrintWriter writer) {
 113         this(null, null, writer, null, null);








 114     }
 115 
 116     Start(Context context, String programName, PrintWriter writer,
 117             String docletName, ClassLoader classLoader) {
 118         this.context = context == null ? new Context() : context;
 119         String pname = programName == null ? ProgramName : programName;
 120         this.messager = writer == null
 121                 ? new Messager(this.context, pname)
 122                 : new Messager(this.context, pname, writer, writer);
 123         this.docletName = docletName;
 124         this.classLoader = classLoader;
 125         this.docletClass = null;
 126         this.locale = Locale.getDefault();
 127     }
 128 
























 129     public Start(Context context) {
 130         this.docletClass = null;
 131         this.context = Objects.requireNonNull(context);
 132         this.apiMode = true;
 133         this.docletName = null;
 134         this.classLoader = null;
 135         this.locale = Locale.getDefault();
 136     }
 137 
 138     void initMessager() {
 139         if (!apiMode)
 140             return;
 141         if (messager == null) {
 142             Log log = context.get(Log.logKey);
 143             if (log instanceof Messager) {
 144                 messager = (Messager) log;
 145             } else {
 146                 PrintWriter out = context.get(Log.outKey);
 147                 messager = (out == null)
 148                         ? new Messager(context, ProgramName)
 149                         : new Messager(context, ProgramName, out, out);
 150             }
 151         }
 152     }
 153 
 154     /**
 155      * Usage
 156      */
 157     @Override
 158     void usage() {
 159         usage(true);
 160     }
 161 
 162     void usage(boolean exit) {
 163         usage("main.usage", "-help", null, exit);
 164     }
 165 
 166     @Override
 167     void Xusage() {
 168         Xusage(true);
 169     }
 170 
 171     void Xusage(boolean exit) {
 172         usage("main.Xusage", "-X", "main.Xusage.foot", exit);
 173     }
 174 
 175     private void usage(String main, String option, String foot, boolean exit) {


 176         messager.notice(main);

 177         // let doclet print usage information (does nothing on error)
 178         if (docletClass != null) {
 179             String name = doclet.getName();
 180             Set<Option> supportedOptions = doclet.getSupportedOptions();
 181             messager.notice("main.doclet.usage.header", name);
 182             Option.Kind myKind = option.equals("-X")
 183                     ? Option.Kind.EXTENDED
 184                     : Option.Kind.STANDARD;
 185             supportedOptions.stream()
 186                     .filter(opt -> opt.getKind() == myKind)
 187                     .forEach(opt -> messager.printNotice(opt.toString()));
 188         }

 189         if (foot != null)
 190             messager.notice(foot);
 191 
 192         if (exit) exit();
 193     }
 194 
 195     /**
 196      * Exit
 197      */
 198     private void exit() {
 199         messager.exit();
 200     }
 201 

 202     /**
 203      * Main program - external wrapper
 204      */
 205     int begin(String... argv) {
 206         // Preprocess @file arguments
 207         try {
 208             argv = CommandLine.parse(argv);
 209         } catch (FileNotFoundException e) {
 210             messager.error("main.cant.read", e.getMessage());
 211             exit();
 212         } catch (IOException e) {
 213             e.printStackTrace(System.err);
 214             exit();
 215         }
 216 
 217         List<String> argList = Arrays.asList(argv);
 218         boolean ok = begin(argList, Collections.<JavaFileObject> emptySet());
 219         return ok ? 0 : 1;
 220     }
 221 
 222     // Called by 199 API.
 223     public boolean begin(Class<?> docletClass,
 224             Iterable<String> options,
 225             Iterable<? extends JavaFileObject> fileObjects) {
 226         this.docletClass = docletClass;
 227         List<String> opts = new ArrayList<>();
 228         for (String opt: options)
 229             opts.add(opt);
 230         return begin(opts, fileObjects);
 231     }
 232 
 233     private boolean begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) {

 234 
 235         fileManager = context.get(JavaFileManager.class);
 236         if (fileManager == null) {
 237             JavacFileManager.preRegister(context);
 238             fileManager = context.get(JavaFileManager.class);
 239             if (fileManager instanceof BaseFileManager) {
 240                 ((BaseFileManager) fileManager).autoClose = true;
 241             }
 242         }
 243         // locale and doclet needs to be determined first
 244         docletClass = preProcess(fileManager, options);
 245 
 246         if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) {
 247             // no need to dispatch to old, safe to init now
 248             initMessager();
 249             messager.setLocale(locale);
 250             try {
 251                 doclet = (Doclet) docletClass.newInstance();
 252             } catch (InstantiationException | IllegalAccessException exc) {
 253                 if (!apiMode) {
 254                     error("main.could_not_instantiate_class", docletClass);
 255                     messager.exit();
 256                 }
 257                 throw new ClientCodeException(exc);
 258             }
 259         } else {
 260             if (this.apiMode) {
 261                 com.sun.tools.javadoc.Start ostart
 262                         = new com.sun.tools.javadoc.Start(context);
 263                 return ostart.begin(docletClass, options, fileObjects);
 264             }
 265             String[] array = options.toArray(new String[options.size()]);
 266             return com.sun.tools.javadoc.Main.execute(array) == 0;
 267         }
 268 
 269         boolean failed = false;
 270         try {
 271             failed = !parseAndExecute(options, fileObjects);
 272         } catch (Messager.ExitJavadoc exc) {
 273             // ignore, we just exit this way
 274         } catch (OutOfMemoryError ee) {
 275             messager.error("main.out.of.memory");
 276             failed = true;
 277         } catch (ClientCodeException e) {
 278             // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
 279             throw e;
 280         } catch (Error ee) {
 281             ee.printStackTrace(System.err);
 282             messager.error("main.fatal.error");
 283             failed = true;
 284         } catch (Exception ee) {
 285             ee.printStackTrace(System.err);
 286             messager.error("main.fatal.exception");
 287             failed = true;
 288         } finally {
 289             if (fileManager != null
 290                     && fileManager instanceof BaseFileManager
 291                     && ((BaseFileManager) fileManager).autoClose) {
 292                 try {
 293                     fileManager.close();
 294                 } catch (IOException ignore) {}
 295             }
 296             boolean haveErrorWarnings = messager.nerrors() > 0 ||
 297                     (rejectWarnings && messager.nwarnings() > 0);
 298             if (failed && !haveErrorWarnings) {
 299                 // the doclet failed, but nothing reported, flag it!.
 300                 messager.error("main.unknown.error");
 301             }
 302             failed |= haveErrorWarnings;
 303             messager.exitNotice();
 304             messager.flush();
 305         }


 306         return !failed;
 307     }
 308 
 309     /**
 310      * Main program - internal
 311      */
 312     private boolean parseAndExecute(List<String> argList,


 313             Iterable<? extends JavaFileObject> fileObjects) throws IOException {
 314         long tm = System.currentTimeMillis();
 315 
 316         List<String> javaNames = new ArrayList<>();
 317 
















 318         compOpts = Options.instance(context);
 319 
 320         // Make sure no obsolete source/target messages are reported
 321         compOpts.put("-Xlint:-options", "-Xlint:-options");
 322 
 323         doclet.init(locale, messager);
 324         parseArgs(argList, javaNames);

 325 




















































 326         if (fileManager instanceof BaseFileManager) {




 327             ((BaseFileManager) fileManager).handleOptions(fileManagerOpts);
 328         }
 329 
 330         String platformString = compOpts.get("-release");
 331 
 332         if (platformString != null) {
 333             if (compOpts.isSet("-source")) {
 334                 usageError("main.release.bootclasspath.conflict", "-source");
 335             }
 336             if (fileManagerOpts.containsKey(BOOTCLASSPATH)) {
 337                 usageError("main.release.bootclasspath.conflict", BOOTCLASSPATH.getText());
 338             }
 339 
 340             PlatformDescription platformDescription =
 341                     PlatformUtils.lookupPlatformDescription(platformString);
 342 
 343             if (platformDescription == null) {
 344                 usageError("main.unsupported.release.version", platformString);
 345             }
 346 
 347             compOpts.put(SOURCE, platformDescription.getSourceVersion());
 348 
 349             context.put(PlatformDescription.class, platformDescription);
 350 
 351             Collection<Path> platformCP = platformDescription.getPlatformPath();
 352 
 353             if (platformCP != null) {
 354                 if (fileManager instanceof StandardJavaFileManager) {
 355                     StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
 356 
 357                     sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP);
 358                 } else {
 359                     usageError("main.release.not.standard.file.manager", platformString);
 360                 }
 361             }
 362         }
 363 
 364         compOpts.notifyListeners();
 365 
 366         if (javaNames.isEmpty() && subPackages.isEmpty() && isEmpty(fileObjects)) {
 367             usageError("main.No_packages_or_classes_specified");
 368         }
 369 





 370         JavadocTool comp = JavadocTool.make0(context);
 371         if (comp == null) return false;
 372 
 373         if (showAccess == null) {
 374             setFilter(defaultModifier);
 375         }
 376 
 377         DocletEnvironment root = comp.getEnvironment(


 378                 encoding,
 379                 showAccess,
 380                 overviewpath,
 381                 javaNames,
 382                 fileObjects,
 383                 subPackages,
 384                 excludedPackages,

 385                 docClasses,


 386                 quiet);
 387 
 388         // release resources
 389         comp = null;
 390 
 391         if (breakiterator || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
 392             JavacTrees trees = JavacTrees.instance(context);
 393             trees.setBreakIterator(BreakIterator.getSentenceInstance(locale));
 394         }
 395         // pass off control to the doclet
 396         boolean ok = root != null;
 397         if (ok) ok = doclet.run(root);
 398 
 399         // We're done.
 400         if (compOpts.get("-verbose") != null) {
 401             tm = System.currentTimeMillis() - tm;
 402             messager.notice("main.done_in", Long.toString(tm));
 403         }
 404 
 405         return ok;
 406     }
 407 
 408     Set<Doclet.Option> docletOptions = null;
 409     int handleDocletOptions(int idx, List<String> args, boolean isToolOption) {
 410         if (docletOptions == null) {
 411             docletOptions = doclet.getSupportedOptions();
 412         }
 413         String arg = args.get(idx);
 414 
 415         for (Doclet.Option opt : docletOptions) {
 416             if (opt.matches(arg)) {
 417                 if (args.size() - idx < opt.getArgumentCount()) {
 418                     usageError("main.requires_argument", arg);













 419                 }
 420                 opt.process(arg, args.listIterator(idx + 1));
 421                 idx += opt.getArgumentCount();
 422                 return idx;
 423             }
 424         }
 425         // check if arg is accepted by the tool before emitting error
 426         if (!isToolOption)
 427             usageError("main.invalid_flag", arg);
 428         return idx;
 429     }
 430 
 431     private Class<?> preProcess(JavaFileManager jfm, List<String> argv) {
 432         // doclet specifying arguments
 433         String userDocletPath = null;
 434         String userDocletName = null;
 435 
 436         // Step 1: loop through the args, set locale early on, if found.
 437         for (int i = 0 ; i < argv.size() ; i++) {
 438             String arg = argv.get(i);
 439             if (arg.equals(ToolOption.LOCALE.opt)) {
 440                 oneArg(argv, i++);
 441                 String lname = argv.get(i);
 442                 locale = getLocale(lname);
 443             } else if (arg.equals(ToolOption.DOCLET.opt)) {
 444                 oneArg(argv, i++);
 445                 if (userDocletName != null) {
 446                     usageError("main.more_than_one_doclet_specified_0_and_1",
 447                                userDocletName, argv.get(i));
 448                 }
 449                 if (docletName != null) {
 450                     usageError("main.more_than_one_doclet_specified_0_and_1",
 451                             docletName, argv.get(i));
 452                 }
 453                 userDocletName = argv.get(i);
 454             } else if (arg.equals(ToolOption.DOCLETPATH.opt)) {
 455                 oneArg(argv, i++);
 456                 if (userDocletPath == null) {
 457                     userDocletPath = argv.get(i);
 458                 } else {
 459                     userDocletPath += File.pathSeparator + argv.get(i);
 460                 }
 461             }
 462         }
 463         // Step 2: a doclet has already been provided,
 464         // nothing more to do.
 465         if (docletClass != null) {
 466             return docletClass;
 467         }
 468         // Step 3: doclet name specified ? if so find a ClassLoader,
 469         // and load it.
 470         if (userDocletName != null) {
 471             ClassLoader cl = classLoader;
 472             if (cl == null) {
 473                 if (!fileManager.hasLocation(DOCLET_PATH)) {
 474                     List<File> paths = new ArrayList<>();
 475                     if (userDocletPath != null) {
 476                         for (String pathname : userDocletPath.split(File.pathSeparator)) {
 477                             paths.add(new File(pathname));
 478                         }
 479                     }
 480                     try {
 481                         ((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);
 482                     } catch (IOException ioe) {
 483                         panic("main.doclet_no_classloader_found", ioe);
 484                         return null; // keep compiler happy
 485                     }
 486                 }
 487                 cl = fileManager.getClassLoader(DOCLET_PATH);
 488                 if (cl == null) {
 489                     // despite doclet specified on cmdline no classloader found!
 490                     panic("main.doclet_no_classloader_found", userDocletName);
 491                     return null; // keep compiler happy
 492                 }
 493                 try {
 494                     return cl.loadClass(userDocletName);
 495                 } catch (ClassNotFoundException cnfe) {
 496                     panic("main.doclet_class_not_found", userDocletName);
 497                     return null; // keep compiler happy
 498                 }
 499             }
 500         }
 501         // Step 4: we have a doclet, try loading it, otherwise
 502         // return back the standard doclet
 503         if (docletName != null) {
 504             try {
 505                 return Class.forName(docletName, true, getClass().getClassLoader());
 506             } catch (ClassNotFoundException cnfe) {
 507                 panic("main.doclet_class_not_found", userDocletName);
 508                 return null; // happy compiler, should not happen
 509             }
 510         } else {
 511             return jdk.javadoc.internal.doclets.standard.Standard.class;
 512         }
 513     }
 514 
 515     private void parseArgs(List<String> args, List<String> javaNames) {
 516         for (int i = 0 ; i < args.size() ; i++) {
 517             String arg = args.get(i);
 518             ToolOption o = ToolOption.get(arg);
 519             if (o != null) {
 520                 // handle a doclet argument that may be needed however
 521                 // don't increment the index, and allow the tool to consume args
 522                 handleDocletOptions(i, args, true);
 523 
 524                 if (o.hasArg) {
 525                     oneArg(args, i++);
 526                     o.process(this, args.get(i));
 527                 } else {
 528                     setOption(arg);
 529                     o.process(this);
 530                 }
 531             } else if (arg.startsWith("-XD")) {
 532                 // hidden javac options
 533                 String s = arg.substring("-XD".length());
 534                 int eq = s.indexOf('=');
 535                 String key = (eq < 0) ? s : s.substring(0, eq);
 536                 String value = (eq < 0) ? s : s.substring(eq+1);
 537                 compOpts.put(key, value);
 538             } else if (arg.startsWith("-")) {
 539                 i = handleDocletOptions(i, args, false);
 540             } else {
 541                 javaNames.add(arg);
 542             }
 543         }
 544     }
 545 
 546     private <T> boolean isEmpty(Iterable<T> iter) {
 547         return !iter.iterator().hasNext();



 548     }
 549 
 550     /**
 551      * Set one arg option.
 552      * Error and exit if one argument is not provided.
 553      */
 554     private void oneArg(List<String> args, int index) {
 555         if ((index + 1) < args.size()) {
 556             setOption(args.get(index), args.get(index+1));
 557         } else {
 558             usageError("main.requires_argument", args.get(index));
 559         }
 560     }
 561 
 562     @Override
 563     void usageError(String key, Object... args) {
 564         error(key, args);
 565         usage(true);
 566     }
 567 
 568     // a terminal call, will not return
 569     void panic(String key, Object... args) {
 570         error(key, args);
 571         messager.exit();
 572     }
 573 
 574     void error(String key, Object... args) {
 575         messager.error(key, args);
 576     }
 577 
 578     /**
 579      * indicate an option with no arguments was given.
 580      */
 581     private void setOption(String opt) {
 582         String[] option = { opt };
 583         options.add(Arrays.asList(option));
 584     }
 585 
 586     /**
 587      * indicate an option with one argument was given.
 588      */
 589     private void setOption(String opt, String argument) {
 590         String[] option = { opt, argument };
 591         options.add(Arrays.asList(option));
 592     }
 593 
 594     /**
 595      * indicate an option with the specified list of arguments was given.
 596      */
 597     private void setOption(String opt, List<String> arguments) {
 598         List<String> args = new ArrayList<>(arguments.size() + 1);
 599         args.add(opt);
 600         args.addAll(arguments);
 601         options.add(args);

 602     }
 603 
 604     /**
 605      * Get the locale if specified on the command line
 606      * else return null and if locale option is not used
 607      * then return default locale.
 608      */
 609     private Locale getLocale(String localeName) {
 610         Locale userlocale = null;
 611         if (localeName == null || localeName.isEmpty()) {
 612             return Locale.getDefault();
 613         }
 614         int firstuscore = localeName.indexOf('_');
 615         int seconduscore = -1;
 616         String language = null;
 617         String country = null;
 618         String variant = null;
 619         if (firstuscore == 2) {
 620             language = localeName.substring(0, firstuscore);
 621             seconduscore = localeName.indexOf('_', firstuscore + 1);
 622             if (seconduscore > 0) {
 623                 if (seconduscore != firstuscore + 3
 624                         || localeName.length() <= seconduscore + 1) {
 625                     usageError("main.malformed_locale_name", localeName);
 626                     return null;
 627                 }
 628                 country = localeName.substring(firstuscore + 1,
 629                         seconduscore);
 630                 variant = localeName.substring(seconduscore + 1);
 631             } else if (localeName.length() == firstuscore + 3) {
 632                 country = localeName.substring(firstuscore + 1);
 633             } else {
 634                 usageError("main.malformed_locale_name", localeName);
 635                 return null;
 636             }
 637         } else if (firstuscore == -1 && localeName.length() == 2) {
 638             language = localeName;
 639         } else {
 640             usageError("main.malformed_locale_name", localeName);
 641             return null;
 642         }
 643         userlocale = searchLocale(language, country, variant);
 644         if (userlocale == null) {
 645             usageError("main.illegal_locale_name", localeName);
 646             return null;
 647         } else {
 648             return userlocale;
 649         }
 650     }
 651 
 652     /**
 653      * Search the locale for specified language, specified country and
 654      * specified variant.
 655      */
 656     private Locale searchLocale(String language, String country,
 657                                 String variant) {
 658         for (Locale loc : Locale.getAvailableLocales()) {
 659             if (loc.getLanguage().equals(language) &&
 660                 (country == null || loc.getCountry().equals(country)) &&
 661                 (variant == null || loc.getVariant().equals(variant))) {
 662                 return loc;
 663             }
 664         }
 665         return null;
 666     }
 667 }