Start.java revision 3573:c4a18ee691c4
1179055Sjfv/*
2171384Sjfv * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
3235528Sjfv * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4171384Sjfv *
5171384Sjfv * This code is free software; you can redistribute it and/or modify it
6171384Sjfv * under the terms of the GNU General Public License version 2 only, as
7171384Sjfv * published by the Free Software Foundation.  Oracle designates this
8171384Sjfv * particular file as subject to the "Classpath" exception as provided
9171384Sjfv * by Oracle in the LICENSE file that accompanied this code.
10171384Sjfv *
11171384Sjfv * This code is distributed in the hope that it will be useful, but WITHOUT
12171384Sjfv * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13171384Sjfv * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14171384Sjfv * version 2 for more details (a copy is included in the LICENSE file that
15171384Sjfv * accompanied this code).
16171384Sjfv *
17171384Sjfv * You should have received a copy of the GNU General Public License version
18171384Sjfv * 2 along with this work; if not, write to the Free Software Foundation,
19171384Sjfv * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20171384Sjfv *
21171384Sjfv * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22171384Sjfv * or visit www.oracle.com if you need additional information or have any
23171384Sjfv * questions.
24171384Sjfv */
25171384Sjfv
26171384Sjfvpackage jdk.javadoc.internal.tool;
27171384Sjfv
28171384Sjfvimport java.io.File;
29171384Sjfvimport java.io.FileNotFoundException;
30171384Sjfvimport java.io.IOException;
31171384Sjfvimport java.io.PrintWriter;
32179055Sjfvimport java.lang.reflect.Method;
33179055Sjfvimport java.nio.file.Path;
34171384Sjfvimport java.text.BreakIterator;
35171384Sjfvimport java.util.ArrayList;
36171384Sjfvimport java.util.Arrays;
37171384Sjfvimport java.util.Collection;
38171384Sjfvimport java.util.Collections;
39190873Sjfvimport java.util.List;
40190873Sjfvimport java.util.Locale;
41190873Sjfvimport java.util.Objects;
42190873Sjfvimport java.util.Set;
43190873Sjfv
44171384Sjfvimport static javax.tools.DocumentationTool.Location.*;
45238263Sjfv
46200239Sjfvimport javax.tools.JavaFileManager;
47179055Sjfvimport javax.tools.JavaFileObject;
48171384Sjfvimport javax.tools.StandardJavaFileManager;
49171384Sjfvimport javax.tools.StandardLocation;
50215911Sjfv
51171384Sjfvimport com.sun.tools.javac.api.JavacTrees;
52179055Sjfvimport com.sun.tools.javac.file.BaseFileManager;
53215911Sjfvimport com.sun.tools.javac.file.JavacFileManager;
54235528Sjfvimport com.sun.tools.javac.main.CommandLine;
55171384Sjfvimport com.sun.tools.javac.main.OptionHelper;
56171384Sjfvimport com.sun.tools.javac.main.OptionHelper.GrumpyHelper;
57185352Sjfvimport com.sun.tools.javac.platform.PlatformDescription;
58171384Sjfvimport com.sun.tools.javac.platform.PlatformUtils;
59171384Sjfvimport com.sun.tools.javac.util.ClientCodeException;
60171384Sjfvimport com.sun.tools.javac.util.Context;
61171384Sjfvimport com.sun.tools.javac.util.Log;
62171384Sjfvimport com.sun.tools.javac.util.Options;
63171384Sjfv
64171384Sjfvimport jdk.javadoc.doclet.Doclet;
65235528Sjfvimport jdk.javadoc.doclet.Doclet.Option;
66235528Sjfvimport jdk.javadoc.doclet.DocletEnvironment;
67200239Sjfv
68235528Sjfvimport static com.sun.tools.javac.main.Option.*;
69235528Sjfv
70215911Sjfv/**
71235528Sjfv * Main program of Javadoc.
72235528Sjfv * Previously named "Main".
73171384Sjfv *
74235528Sjfv *  <p><b>This is NOT part of any supported API.
75235528Sjfv *  If you write code that depends on this, you do so at your own risk.
76235528Sjfv *  This code and its internal interfaces are subject to change or
77200239Sjfv *  deletion without notice.</b>
78171384Sjfv *
79235528Sjfv * @author Robert Field
80171384Sjfv * @author Neal Gafter (rewrite)
81200239Sjfv */
82171384Sjfvpublic class Start extends ToolOption.Helper {
83179055Sjfv
84235528Sjfv    @SuppressWarnings("deprecation")
85181003Sjfv    private static final Class<?> OldStdDoclet =
86171384Sjfv            com.sun.tools.doclets.standard.Standard.class;
87171384Sjfv
88235528Sjfv    private static final Class<?> StdDoclet =
89235528Sjfv            jdk.javadoc.doclets.StandardDoclet.class;
90179055Sjfv    /** Context for this invocation. */
91235528Sjfv    private final Context context;
92171384Sjfv
93171384Sjfv    private static final String ProgramName = "javadoc";
94190873Sjfv
95235528Sjfv    // meaning we allow all visibility of PROTECTED and PUBLIC
96235528Sjfv    private static final String defaultModifier = "protected";
97171384Sjfv
98238263Sjfv    private Messager messager;
99238263Sjfv
100185352Sjfv    private final String docletName;
101171384Sjfv
102171384Sjfv    private final ClassLoader classLoader;
103171384Sjfv
104171384Sjfv    private Class<?> docletClass;
105171384Sjfv
106190873Sjfv    private Doclet doclet;
107190873Sjfv
108190873Sjfv    // used to determine the locale for the messager
109200239Sjfv    private Locale locale;
110200239Sjfv
111200239Sjfv
112200239Sjfv    /**
113238263Sjfv     * In API mode, exceptions thrown while calling the doclet are
114200239Sjfv     * propagated using ClientCodeException.
115200239Sjfv     */
116200239Sjfv    private boolean apiMode;
117200239Sjfv
118235528Sjfv    private JavaFileManager fileManager;
119235528Sjfv
120235528Sjfv    Start() {
121200239Sjfv        this(null, null, null, null, null);
122235528Sjfv    }
123200239Sjfv
124200239Sjfv    Start(PrintWriter writer) {
125235528Sjfv        this(null, null, writer, null, null);
126235528Sjfv    }
127200239Sjfv
128200239Sjfv    Start(Context context, String programName, PrintWriter writer,
129235528Sjfv            String docletName, ClassLoader classLoader) {
130200239Sjfv        this.context = context == null ? new Context() : context;
131215911Sjfv        String pname = programName == null ? ProgramName : programName;
132215911Sjfv        this.messager = writer == null
133215911Sjfv                ? new Messager(this.context, pname)
134217593Sjfv                : new Messager(this.context, pname, writer, writer);
135235528Sjfv        this.docletName = docletName;
136235528Sjfv        this.classLoader = classLoader;
137217593Sjfv        this.docletClass = null;
138235528Sjfv        this.locale = Locale.getDefault();
139235528Sjfv    }
140235528Sjfv
141171384Sjfv    public Start(Context context) {
142        this.docletClass = null;
143        this.context = Objects.requireNonNull(context);
144        this.apiMode = true;
145        this.docletName = null;
146        this.classLoader = null;
147        this.locale = Locale.getDefault();
148    }
149
150    void initMessager() {
151        if (!apiMode)
152            return;
153        if (messager == null) {
154            Log log = context.get(Log.logKey);
155            if (log instanceof Messager) {
156                messager = (Messager) log;
157            } else {
158                PrintWriter out = context.get(Log.errKey);
159                messager = (out == null)
160                        ? new Messager(context, ProgramName)
161                        : new Messager(context, ProgramName, out, out);
162            }
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", "main.usage.foot", 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 option, String foot, boolean exit) {
188        messager.notice(main);
189        // let doclet print usage information (does nothing on error)
190        if (docletClass != null) {
191            String name = doclet.getName();
192            Set<Option> supportedOptions = doclet.getSupportedOptions();
193            messager.notice("main.doclet.usage.header", name);
194            Option.Kind myKind = option.equals("-X")
195                    ? Option.Kind.EXTENDED
196                    : Option.Kind.STANDARD;
197            supportedOptions.stream()
198                    .filter(opt -> opt.getKind() == myKind)
199                    .forEach(opt -> messager.printNotice(opt.toString()));
200        }
201        if (foot != null)
202            messager.notice(foot);
203
204        if (exit)
205            throw new Messager.ExitJavadoc();
206    }
207
208
209    /**
210     * Main program - external wrapper. In order to maintain backward
211     * CLI  compatibility, we dispatch to the old tool or the old doclet's
212     * Start mechanism, based on the options present on the command line
213     * with the following precedence:
214     *   1. presence of -Xold, dispatch to old tool
215     *   2. doclet variant, if old, dispatch to old Start
216     *   3. taglet variant, if old, dispatch to old Start
217     *
218     * Thus the presence of -Xold switches the tool, soon after command files
219     * if any, are expanded, this is performed here, noting that the messager
220     * is available at this point in time.
221     * The doclet/taglet tests are performed in the begin method, further on,
222     * this is to minimize argument processing and most importantly the impact
223     * of class loader creation, needed to detect the doclet/taglet class variants.
224     */
225    @SuppressWarnings("deprecation")
226    int begin(String... argv) {
227        // Preprocess @file arguments
228        try {
229            argv = CommandLine.parse(argv);
230        } catch (FileNotFoundException e) {
231            messager.error("main.cant.read", e.getMessage());
232            throw new Messager.ExitJavadoc();
233        } catch (IOException e) {
234            e.printStackTrace(System.err);
235            throw new Messager.ExitJavadoc();
236        }
237
238        if (argv.length > 0 && "-Xold".equals(argv[0])) {
239            messager.warning("main.legacy_api");
240            String[] nargv = Arrays.copyOfRange(argv, 1, argv.length);
241            return com.sun.tools.javadoc.Main.execute(nargv);
242        }
243        boolean ok = begin(Arrays.asList(argv), Collections.<JavaFileObject> emptySet());
244        return ok ? 0 : 1;
245    }
246
247    // Called by 199 API.
248    public boolean begin(Class<?> docletClass,
249            Iterable<String> options,
250            Iterable<? extends JavaFileObject> fileObjects) {
251        this.docletClass = docletClass;
252        List<String> opts = new ArrayList<>();
253        for (String opt: options)
254            opts.add(opt);
255
256        return begin(opts, fileObjects);
257    }
258
259    @SuppressWarnings("deprecation")
260    private boolean begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) {
261        fileManager = context.get(JavaFileManager.class);
262        if (fileManager == null) {
263            JavacFileManager.preRegister(context);
264            fileManager = context.get(JavaFileManager.class);
265            if (fileManager instanceof BaseFileManager) {
266                ((BaseFileManager) fileManager).autoClose = true;
267            }
268        }
269        // locale, doclet and maybe taglet, needs to be determined first
270        docletClass = preProcess(fileManager, options);
271        if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) {
272            // no need to dispatch to old, safe to init now
273            initMessager();
274            messager.setLocale(locale);
275            try {
276                Object o = docletClass.getConstructor().newInstance();
277                doclet = (Doclet) o;
278            } catch (ReflectiveOperationException exc) {
279                exc.printStackTrace();
280                if (!apiMode) {
281                    error("main.could_not_instantiate_class", docletClass);
282                    throw new Messager.ExitJavadoc();
283                }
284                throw new ClientCodeException(exc);
285            }
286        } else {
287            if (this.apiMode) {
288                com.sun.tools.javadoc.main.Start ostart
289                        = new com.sun.tools.javadoc.main.Start(context);
290                return ostart.begin(docletClass, options, fileObjects);
291            }
292            warn("main.legacy_api");
293            String[] array = options.toArray(new String[options.size()]);
294            return com.sun.tools.javadoc.Main.execute(array) == 0;
295        }
296
297        boolean failed = false;
298        try {
299            failed = !parseAndExecute(options, fileObjects);
300        } catch (Messager.ExitJavadoc exc) {
301            // ignore, we just exit this way
302        } catch (OutOfMemoryError ee) {
303            messager.error("main.out.of.memory");
304            failed = true;
305        } catch (ClientCodeException e) {
306            // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
307            throw e;
308        } catch (Error ee) {
309            ee.printStackTrace(System.err);
310            messager.error("main.fatal.error");
311            failed = true;
312        } catch (Exception ee) {
313            ee.printStackTrace(System.err);
314            messager.error("main.fatal.exception");
315            failed = true;
316        } finally {
317            if (fileManager != null
318                    && fileManager instanceof BaseFileManager
319                    && ((BaseFileManager) fileManager).autoClose) {
320                try {
321                    fileManager.close();
322                } catch (IOException ignore) {}
323            }
324            boolean haveErrorWarnings = messager.nerrors() > 0 ||
325                    (rejectWarnings && messager.nwarnings() > 0);
326            if (failed && !haveErrorWarnings) {
327                // the doclet failed, but nothing reported, flag it!.
328                messager.error("main.unknown.error");
329            }
330            failed |= haveErrorWarnings;
331            messager.exitNotice();
332            messager.flush();
333        }
334        return !failed;
335    }
336
337    /**
338     * Main program - internal
339     */
340    private boolean parseAndExecute(List<String> argList,
341            Iterable<? extends JavaFileObject> fileObjects) throws IOException {
342        long tm = System.currentTimeMillis();
343
344        List<String> javaNames = new ArrayList<>();
345
346        compOpts = Options.instance(context);
347
348        // Make sure no obsolete source/target messages are reported
349        com.sun.tools.javac.main.Option.XLINT.process(getOptionHelper(), "-Xlint:-options");
350
351        doclet.init(locale, messager);
352        parseArgs(argList, javaNames);
353
354        if (fileManager instanceof BaseFileManager) {
355            ((BaseFileManager) fileManager).handleOptions(fileManagerOpts);
356        }
357
358        String platformString = compOpts.get("--release");
359
360        if (platformString != null) {
361            if (compOpts.isSet("-source")) {
362                usageError("main.release.bootclasspath.conflict", "-source");
363            }
364            if (fileManagerOpts.containsKey(BOOT_CLASS_PATH)) {
365                usageError("main.release.bootclasspath.conflict", BOOT_CLASS_PATH.getPrimaryName());
366            }
367
368            PlatformDescription platformDescription =
369                    PlatformUtils.lookupPlatformDescription(platformString);
370
371            if (platformDescription == null) {
372                usageError("main.unsupported.release.version", platformString);
373            }
374
375            compOpts.put(SOURCE, platformDescription.getSourceVersion());
376
377            context.put(PlatformDescription.class, platformDescription);
378
379            Collection<Path> platformCP = platformDescription.getPlatformPath();
380
381            if (platformCP != null) {
382                if (fileManager instanceof StandardJavaFileManager) {
383                    StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
384
385                    sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP);
386                } else {
387                    usageError("main.release.not.standard.file.manager", platformString);
388                }
389            }
390        }
391
392        compOpts.notifyListeners();
393
394        if (javaNames.isEmpty() && subPackages.isEmpty() && isEmpty(fileObjects)) {
395            usageError("main.No_packages_or_classes_specified");
396        }
397
398        JavadocTool comp = JavadocTool.make0(context);
399        if (comp == null) return false;
400
401        if (showAccess == null) {
402            setFilter(defaultModifier);
403        }
404
405        DocletEnvironment root = comp.getEnvironment(
406                encoding,
407                showAccess,
408                overviewpath,
409                javaNames,
410                fileObjects,
411                subPackages,
412                excludedPackages,
413                docClasses,
414                quiet);
415
416        // release resources
417        comp = null;
418
419        if (breakiterator || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
420            JavacTrees trees = JavacTrees.instance(context);
421            trees.setBreakIterator(BreakIterator.getSentenceInstance(locale));
422        }
423        // pass off control to the doclet
424        boolean ok = root != null;
425        if (ok) ok = doclet.run(root);
426
427        // We're done.
428        if (compOpts.get("-verbose") != null) {
429            tm = System.currentTimeMillis() - tm;
430            messager.notice("main.done_in", Long.toString(tm));
431        }
432
433        return ok;
434    }
435
436    Set<Doclet.Option> docletOptions = null;
437    int handleDocletOptions(int idx, List<String> args, boolean isToolOption) {
438        if (docletOptions == null) {
439            docletOptions = doclet.getSupportedOptions();
440        }
441        String arg = args.get(idx);
442
443        for (Doclet.Option opt : docletOptions) {
444            if (opt.matches(arg)) {
445                if (args.size() - idx < opt.getArgumentCount()) {
446                    usageError("main.requires_argument", arg);
447                }
448                opt.process(arg, args.listIterator(idx + 1));
449                idx += opt.getArgumentCount();
450                return idx;
451            }
452        }
453        // check if arg is accepted by the tool before emitting error
454        if (!isToolOption)
455            usageError("main.invalid_flag", arg);
456        return idx;
457    }
458
459    private Class<?> preProcess(JavaFileManager jfm, List<String> argv) {
460        // doclet specifying arguments
461        String userDocletPath = null;
462        String userDocletName = null;
463
464        // taglet specifying arguments, since tagletpath is a doclet
465        // functionality, assume they are repeated and inspect all.
466        List<File> userTagletPath = new ArrayList<>();
467        List<String> userTagletNames = new ArrayList<>();
468
469        // Step 1: loop through the args, set locale early on, if found.
470        for (int i = 0 ; i < argv.size() ; i++) {
471            String arg = argv.get(i);
472            if (arg.equals(ToolOption.LOCALE.opt)) {
473                oneArg(argv, i++);
474                String lname = argv.get(i);
475                locale = getLocale(lname);
476            } else if (arg.equals(ToolOption.DOCLET.opt)) {
477                oneArg(argv, i++);
478                if (userDocletName != null) {
479                    usageError("main.more_than_one_doclet_specified_0_and_1",
480                            userDocletName, argv.get(i));
481                }
482                if (docletName != null) {
483                    usageError("main.more_than_one_doclet_specified_0_and_1",
484                            docletName, argv.get(i));
485                }
486                userDocletName = argv.get(i);
487            } else if (arg.equals(ToolOption.DOCLETPATH.opt)) {
488                oneArg(argv, i++);
489                if (userDocletPath == null) {
490                    userDocletPath = argv.get(i);
491                } else {
492                    userDocletPath += File.pathSeparator + argv.get(i);
493                }
494            } else if ("-taglet".equals(arg)) {
495                userTagletNames.add(argv.get(i + 1));
496            } else if ("-tagletpath".equals(arg)) {
497                for (String pathname : argv.get(i + 1).split(File.pathSeparator)) {
498                    userTagletPath.add(new File(pathname));
499                }
500            }
501        }
502
503        // Step 2: a doclet is provided, nothing more to do.
504        if (docletClass != null) {
505            return docletClass;
506        }
507
508        // Step 3: doclet name specified ? if so find a ClassLoader,
509        // and load it.
510        if (userDocletName != null) {
511            ClassLoader cl = classLoader;
512            if (cl == null) {
513                if (!fileManager.hasLocation(DOCLET_PATH)) {
514                    List<File> paths = new ArrayList<>();
515                    if (userDocletPath != null) {
516                        for (String pathname : userDocletPath.split(File.pathSeparator)) {
517                            paths.add(new File(pathname));
518                        }
519                    }
520                    try {
521                        ((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);
522                    } catch (IOException ioe) {
523                        error("main.doclet_could_not_set_location", paths);
524                        throw new Messager.ExitJavadoc();
525                    }
526                }
527                cl = fileManager.getClassLoader(DOCLET_PATH);
528                if (cl == null) {
529                    // despite doclet specified on cmdline no classloader found!
530                    error("main.doclet_no_classloader_found", userDocletName);
531                    throw new Messager.ExitJavadoc();
532                }
533            }
534            try {
535                Class<?> klass = cl.loadClass(userDocletName);
536                return klass;
537            } catch (ClassNotFoundException cnfe) {
538                error("main.doclet_class_not_found", userDocletName);
539                throw new Messager.ExitJavadoc();
540            }
541        }
542
543        // Step 4: we have a doclet, try loading it
544        if (docletName != null) {
545            try {
546                return Class.forName(docletName, true, getClass().getClassLoader());
547            } catch (ClassNotFoundException cnfe) {
548                error("main.doclet_class_not_found", userDocletName);
549                throw new Messager.ExitJavadoc();
550            }
551        }
552
553        // Step 5: we don't have a doclet specified, do we have taglets ?
554        if (!userTagletNames.isEmpty() && hasOldTaglet(userTagletNames, userTagletPath)) {
555            // found a bogey, return the old doclet
556            return OldStdDoclet;
557        }
558
559        // finally
560        return StdDoclet;
561    }
562
563    /*
564     * This method returns true iff it finds a legacy taglet, but for
565     * all other conditions including errors it returns false, allowing
566     * nature to take its own course.
567     */
568    @SuppressWarnings("deprecation")
569    private boolean hasOldTaglet(List<String> tagletNames, List<File> tagletPaths) {
570        if (!fileManager.hasLocation(TAGLET_PATH)) {
571            try {
572                ((StandardJavaFileManager) fileManager).setLocation(TAGLET_PATH, tagletPaths);
573            } catch (IOException ioe) {
574                error("main.doclet_could_not_set_location", tagletPaths);
575                throw new Messager.ExitJavadoc();
576            }
577        }
578        ClassLoader cl = fileManager.getClassLoader(TAGLET_PATH);
579        if (cl == null) {
580            // no classloader found!
581            error("main.doclet_no_classloader_found", tagletNames.get(0));
582            throw new Messager.ExitJavadoc();
583        }
584        for (String tagletName : tagletNames) {
585            try {
586                Class<?> klass = cl.loadClass(tagletName);
587                if (com.sun.tools.doclets.Taglet.class.isAssignableFrom(klass)) {
588                    return true;
589                }
590            } catch (ClassNotFoundException cnfe) {
591                error("main.doclet_class_not_found", tagletName);
592                throw new Messager.ExitJavadoc();
593            }
594        }
595        return false;
596    }
597
598    private void parseArgs(List<String> args, List<String> javaNames) {
599        for (int i = 0 ; i < args.size() ; i++) {
600            String arg = args.get(i);
601            ToolOption o = ToolOption.get(arg);
602            if (o != null) {
603                // handle a doclet argument that may be needed however
604                // don't increment the index, and allow the tool to consume args
605                handleDocletOptions(i, args, true);
606
607                if (o.hasArg) {
608                    oneArg(args, i++);
609                    o.process(this, args.get(i));
610                } else if (o.hasSuffix) {
611                    o.process(this, arg);
612                } else {
613                    setOption(arg);
614                    o.process(this);
615                }
616            } else if (arg.startsWith("-XD")) {
617                // hidden javac options
618                String s = arg.substring("-XD".length());
619                int eq = s.indexOf('=');
620                String key = (eq < 0) ? s : s.substring(0, eq);
621                String value = (eq < 0) ? s : s.substring(eq+1);
622                compOpts.put(key, value);
623            } else if (arg.startsWith("-")) {
624                i = handleDocletOptions(i, args, false);
625            } else {
626                javaNames.add(arg);
627            }
628        }
629    }
630
631    private <T> boolean isEmpty(Iterable<T> iter) {
632        return !iter.iterator().hasNext();
633    }
634
635    /**
636     * Set one arg option.
637     * Error and exit if one argument is not provided.
638     */
639    private void oneArg(List<String> args, int index) {
640        if ((index + 1) < args.size()) {
641            setOption(args.get(index), args.get(index+1));
642        } else {
643            usageError("main.requires_argument", args.get(index));
644        }
645    }
646
647    @Override
648    void usageError(String key, Object... args) {
649        error(key, args);
650        usage(true);
651    }
652
653    void error(String key, Object... args) {
654        messager.error(key, args);
655    }
656
657    void warn(String key, Object... args)  {
658        messager.warning(key, args);
659    }
660
661    /**
662     * indicate an option with no arguments was given.
663     */
664    private void setOption(String opt) {
665        String[] option = { opt };
666        options.add(Arrays.asList(option));
667    }
668
669    /**
670     * indicate an option with one argument was given.
671     */
672    private void setOption(String opt, String argument) {
673        String[] option = { opt, argument };
674        options.add(Arrays.asList(option));
675    }
676
677    /**
678     * indicate an option with the specified list of arguments was given.
679     */
680    private void setOption(String opt, List<String> arguments) {
681        List<String> args = new ArrayList<>(arguments.size() + 1);
682        args.add(opt);
683        args.addAll(arguments);
684        options.add(args);
685    }
686
687    /**
688     * Get the locale if specified on the command line
689     * else return null and if locale option is not used
690     * then return default locale.
691     */
692    private Locale getLocale(String localeName) {
693        Locale userlocale = null;
694        if (localeName == null || localeName.isEmpty()) {
695            return Locale.getDefault();
696        }
697        int firstuscore = localeName.indexOf('_');
698        int seconduscore = -1;
699        String language = null;
700        String country = null;
701        String variant = null;
702        if (firstuscore == 2) {
703            language = localeName.substring(0, firstuscore);
704            seconduscore = localeName.indexOf('_', firstuscore + 1);
705            if (seconduscore > 0) {
706                if (seconduscore != firstuscore + 3
707                        || localeName.length() <= seconduscore + 1) {
708                    usageError("main.malformed_locale_name", localeName);
709                    return null;
710                }
711                country = localeName.substring(firstuscore + 1,
712                        seconduscore);
713                variant = localeName.substring(seconduscore + 1);
714            } else if (localeName.length() == firstuscore + 3) {
715                country = localeName.substring(firstuscore + 1);
716            } else {
717                usageError("main.malformed_locale_name", localeName);
718                return null;
719            }
720        } else if (firstuscore == -1 && localeName.length() == 2) {
721            language = localeName;
722        } else {
723            usageError("main.malformed_locale_name", localeName);
724            return null;
725        }
726        userlocale = searchLocale(language, country, variant);
727        if (userlocale == null) {
728            usageError("main.illegal_locale_name", localeName);
729            return null;
730        } else {
731            return userlocale;
732        }
733    }
734
735    /**
736     * Search the locale for specified language, specified country and
737     * specified variant.
738     */
739    private Locale searchLocale(String language, String country,
740                                String variant) {
741        for (Locale loc : Locale.getAvailableLocales()) {
742            if (loc.getLanguage().equals(language) &&
743                (country == null || loc.getCountry().equals(country)) &&
744                (variant == null || loc.getVariant().equals(variant))) {
745                return loc;
746            }
747        }
748        return null;
749    }
750
751    @Override
752    OptionHelper getOptionHelper() {
753        return new GrumpyHelper(null) {
754            @Override
755            public String get(com.sun.tools.javac.main.Option option) {
756                return compOpts.get(option);
757            }
758
759            @Override
760            public void put(String name, String value) {
761                compOpts.put(name, value);
762            }
763        };
764    }
765}
766