Option.java revision 3261:527e819dbc95
1/*
2 * Copyright (c) 2006, 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
26package com.sun.tools.javac.main;
27
28import java.io.FileWriter;
29import java.io.PrintWriter;
30import java.nio.file.Files;
31import java.nio.file.Path;
32import java.nio.file.Paths;
33import java.util.Collections;
34import java.util.EnumSet;
35import java.util.LinkedHashMap;
36import java.util.Map;
37import java.util.ServiceLoader;
38import java.util.Set;
39import java.util.TreeSet;
40import java.util.stream.Collectors;
41import java.util.stream.StreamSupport;
42
43import javax.lang.model.SourceVersion;
44
45import com.sun.tools.doclint.DocLint;
46import com.sun.tools.javac.code.Lint;
47import com.sun.tools.javac.code.Lint.LintCategory;
48import com.sun.tools.javac.code.Source;
49import com.sun.tools.javac.code.Type;
50import com.sun.tools.javac.jvm.Profile;
51import com.sun.tools.javac.jvm.Target;
52import com.sun.tools.javac.platform.PlatformProvider;
53import com.sun.tools.javac.processing.JavacProcessingEnvironment;
54import com.sun.tools.javac.util.Log;
55import com.sun.tools.javac.util.Log.PrefixKind;
56import com.sun.tools.javac.util.Log.WriterKind;
57import com.sun.tools.javac.util.Options;
58import com.sun.tools.javac.util.StringUtils;
59
60import static com.sun.tools.javac.main.Option.ChoiceKind.*;
61import static com.sun.tools.javac.main.Option.OptionGroup.*;
62import static com.sun.tools.javac.main.Option.OptionKind.*;
63
64/**
65 * Options for javac. The specific Option to handle a command-line option
66 * is identified by searching the members of this enum in order, looking for
67 * the first {@link #matches match}. The action for an Option is performed
68 * by calling {@link #process process}, and by providing a suitable
69 * {@link OptionHelper} to provide access the compiler state.
70 *
71 * <p><b>This is NOT part of any supported API.
72 * If you write code that depends on this, you do so at your own
73 * risk.  This code and its internal interfaces are subject to change
74 * or deletion without notice.</b></p>
75 */
76public enum Option {
77    G("-g", "opt.g", STANDARD, BASIC),
78
79    G_NONE("-g:none", "opt.g.none", STANDARD, BASIC) {
80        @Override
81        public boolean process(OptionHelper helper, String option) {
82            helper.put("-g:", "none");
83            return false;
84        }
85    },
86
87    G_CUSTOM("-g:",  "opt.g.lines.vars.source",
88            STANDARD, BASIC, ANYOF, "lines", "vars", "source"),
89
90    XLINT("-Xlint", "opt.Xlint", EXTENDED, BASIC),
91
92    XLINT_CUSTOM("-Xlint:", EXTENDED, BASIC, ANYOF, getXLintChoices()) {
93        private static final String LINT_KEY_FORMAT = "         %-19s %s";
94        @Override
95        void help(Log log, OptionKind kind) {
96            if (this.kind != kind)
97                return;
98
99            log.printRawLines(WriterKind.NOTICE,
100                              String.format(HELP_LINE_FORMAT,
101                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.subopts"),
102                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.suboptlist")));
103            log.printRawLines(WriterKind.NOTICE,
104                              String.format(LINT_KEY_FORMAT,
105                                            "all",
106                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.all")));
107            for (LintCategory lc : LintCategory.values()) {
108                if (lc.hidden) continue;
109                log.printRawLines(WriterKind.NOTICE,
110                                  String.format(LINT_KEY_FORMAT,
111                                                lc.option,
112                                                log.localize(PrefixKind.JAVAC,
113                                                             "opt.Xlint.desc." + lc.option)));
114            }
115            log.printRawLines(WriterKind.NOTICE,
116                              String.format(LINT_KEY_FORMAT,
117                                            "none",
118                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.none")));
119        }
120    },
121
122    XDOCLINT("-Xdoclint", "opt.Xdoclint", EXTENDED, BASIC),
123
124    XDOCLINT_CUSTOM("-Xdoclint:", "opt.Xdoclint.subopts", "opt.Xdoclint.custom", EXTENDED, BASIC) {
125        @Override
126        public boolean matches(String option) {
127            return DocLint.isValidOption(
128                    option.replace(XDOCLINT_CUSTOM.text, DocLint.XMSGS_CUSTOM_PREFIX));
129        }
130
131        @Override
132        public boolean process(OptionHelper helper, String option) {
133            String prev = helper.get(XDOCLINT_CUSTOM);
134            String next = (prev == null) ? option : (prev + " " + option);
135            helper.put(XDOCLINT_CUSTOM.text, next);
136            return false;
137        }
138    },
139
140    XDOCLINT_PACKAGE("-Xdoclint/package:", "opt.Xdoclint.package.args", "opt.Xdoclint.package.desc", EXTENDED, BASIC) {
141        @Override
142        public boolean matches(String option) {
143            return DocLint.isValidOption(
144                    option.replace(XDOCLINT_PACKAGE.text, DocLint.XCHECK_PACKAGE));
145        }
146
147        @Override
148        public boolean process(OptionHelper helper, String option) {
149            String prev = helper.get(XDOCLINT_PACKAGE);
150            String next = (prev == null) ? option : (prev + " " + option);
151            helper.put(XDOCLINT_PACKAGE.text, next);
152            return false;
153        }
154    },
155
156    // -nowarn is retained for command-line backward compatibility
157    NOWARN("-nowarn", "opt.nowarn", STANDARD, BASIC) {
158        @Override
159        public boolean process(OptionHelper helper, String option) {
160            helper.put("-Xlint:none", option);
161            return false;
162        }
163    },
164
165    VERBOSE("-verbose", "opt.verbose", STANDARD, BASIC),
166
167    // -deprecation is retained for command-line backward compatibility
168    DEPRECATION("-deprecation", "opt.deprecation", STANDARD, BASIC) {
169        @Override
170        public boolean process(OptionHelper helper, String option) {
171            helper.put("-Xlint:deprecation", option);
172            return false;
173        }
174    },
175
176    CLASSPATH("-classpath", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER),
177
178    CP("-cp", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER) {
179        @Override
180        public boolean process(OptionHelper helper, String option, String arg) {
181            return super.process(helper, "-classpath", arg);
182        }
183    },
184
185    SOURCEPATH("-sourcepath", "opt.arg.path", "opt.sourcepath", STANDARD, FILEMANAGER),
186
187    BOOTCLASSPATH("-bootclasspath", "opt.arg.path", "opt.bootclasspath", STANDARD, FILEMANAGER) {
188        @Override
189        public boolean process(OptionHelper helper, String option, String arg) {
190            helper.remove("-Xbootclasspath/p:");
191            helper.remove("-Xbootclasspath/a:");
192            return super.process(helper, option, arg);
193        }
194    },
195
196    XBOOTCLASSPATH_PREPEND("-Xbootclasspath/p:", "opt.arg.path", "opt.Xbootclasspath.p", EXTENDED, FILEMANAGER),
197
198    XBOOTCLASSPATH_APPEND("-Xbootclasspath/a:", "opt.arg.path", "opt.Xbootclasspath.a", EXTENDED, FILEMANAGER),
199
200    XBOOTCLASSPATH("-Xbootclasspath:", "opt.arg.path", "opt.bootclasspath", EXTENDED, FILEMANAGER) {
201        @Override
202        public boolean process(OptionHelper helper, String option, String arg) {
203            helper.remove("-Xbootclasspath/p:");
204            helper.remove("-Xbootclasspath/a:");
205            return super.process(helper, "-bootclasspath", arg);
206        }
207    },
208
209    EXTDIRS("-extdirs", "opt.arg.dirs", "opt.extdirs", STANDARD, FILEMANAGER),
210
211    DJAVA_EXT_DIRS("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs", EXTENDED, FILEMANAGER) {
212        @Override
213        public boolean process(OptionHelper helper, String option, String arg) {
214            return super.process(helper, "-extdirs", arg);
215        }
216    },
217
218    ENDORSEDDIRS("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs", STANDARD, FILEMANAGER),
219
220    DJAVA_ENDORSED_DIRS("-Djava.endorsed.dirs=", "opt.arg.dirs", "opt.endorseddirs", EXTENDED, FILEMANAGER) {
221        @Override
222        public boolean process(OptionHelper helper, String option, String arg) {
223            return super.process(helper, "-endorseddirs", arg);
224        }
225    },
226
227    PROC("-proc:", "opt.proc.none.only", STANDARD, BASIC,  ONEOF, "none", "only"),
228
229    PROCESSOR("-processor", "opt.arg.class.list", "opt.processor", STANDARD, BASIC),
230
231    PROCESSORPATH("-processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER),
232
233    PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC),
234
235    D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
236
237    S("-s", "opt.arg.directory", "opt.sourceDest", STANDARD, FILEMANAGER),
238
239    H("-h", "opt.arg.directory", "opt.headerDest", STANDARD, FILEMANAGER),
240
241    IMPLICIT("-implicit:", "opt.implicit", STANDARD, BASIC, ONEOF, "none", "class"),
242
243    ENCODING("-encoding", "opt.arg.encoding", "opt.encoding", STANDARD, FILEMANAGER),
244
245    SOURCE("-source", "opt.arg.release", "opt.source", STANDARD, BASIC) {
246        @Override
247        public boolean process(OptionHelper helper, String option, String operand) {
248            Source source = Source.lookup(operand);
249            if (source == null) {
250                helper.error("err.invalid.source", operand);
251                return true;
252            }
253            return super.process(helper, option, operand);
254        }
255    },
256
257    TARGET("-target", "opt.arg.release", "opt.target", STANDARD, BASIC) {
258        @Override
259        public boolean process(OptionHelper helper, String option, String operand) {
260            Target target = Target.lookup(operand);
261            if (target == null) {
262                helper.error("err.invalid.target", operand);
263                return true;
264            }
265            return super.process(helper, option, operand);
266        }
267    },
268
269    RELEASE("-release", "opt.arg.release", "opt.release", STANDARD, BASIC) {
270        @Override
271        void help(Log log, OptionKind kind) {
272            if (this.kind != kind)
273                return;
274
275            Iterable<PlatformProvider> providers =
276                    ServiceLoader.load(PlatformProvider.class, Arguments.class.getClassLoader());
277            Set<String> platforms = StreamSupport.stream(providers.spliterator(), false)
278                                                 .flatMap(provider -> StreamSupport.stream(provider.getSupportedPlatformNames()
279                                                                                                   .spliterator(),
280                                                                                           false))
281                                                 .collect(Collectors.toCollection(TreeSet :: new));
282
283            StringBuilder targets = new StringBuilder();
284            String delim = "";
285            for (String platform : platforms) {
286                targets.append(delim);
287                targets.append(platform);
288                delim = ", ";
289            }
290
291            log.printRawLines(WriterKind.NOTICE,
292                    String.format(HELP_LINE_FORMAT,
293                        super.helpSynopsis(log),
294                        log.localize(PrefixKind.JAVAC, descrKey, targets.toString())));
295        }
296    },
297
298    PROFILE("-profile", "opt.arg.profile", "opt.profile", STANDARD, BASIC) {
299        @Override
300        public boolean process(OptionHelper helper, String option, String operand) {
301            Profile profile = Profile.lookup(operand);
302            if (profile == null) {
303                helper.error("err.invalid.profile", operand);
304                return true;
305            }
306            return super.process(helper, option, operand);
307        }
308    },
309
310    VERSION("-version", "opt.version", STANDARD, INFO) {
311        @Override
312        public boolean process(OptionHelper helper, String option) {
313            Log log = helper.getLog();
314            String ownName = helper.getOwnName();
315            log.printLines(PrefixKind.JAVAC, "version", ownName,  JavaCompiler.version());
316            return super.process(helper, option);
317        }
318    },
319
320    FULLVERSION("-fullversion", null, HIDDEN, INFO) {
321        @Override
322        public boolean process(OptionHelper helper, String option) {
323            Log log = helper.getLog();
324            String ownName = helper.getOwnName();
325            log.printLines(PrefixKind.JAVAC, "fullVersion", ownName,  JavaCompiler.fullVersion());
326            return super.process(helper, option);
327        }
328    },
329
330    DIAGS("-XDdiags=", null, HIDDEN, INFO) {
331        @Override
332        public boolean process(OptionHelper helper, String option) {
333            option = option.substring(option.indexOf('=') + 1);
334            String diagsOption = option.contains("%") ?
335                "-XDdiagsFormat=" :
336                "-XDdiags=";
337            diagsOption += option;
338            if (XD.matches(diagsOption))
339                return XD.process(helper, diagsOption);
340            else
341                return false;
342        }
343    },
344
345    HELP("-help", "opt.help", STANDARD, INFO) {
346        @Override
347        public boolean process(OptionHelper helper, String option) {
348            Log log = helper.getLog();
349            String ownName = helper.getOwnName();
350            log.printLines(PrefixKind.JAVAC, "msg.usage.header", ownName);
351            for (Option o: getJavaCompilerOptions()) {
352                o.help(log, OptionKind.STANDARD);
353            }
354            log.printNewline();
355            return super.process(helper, option);
356        }
357    },
358
359    A("-A", "opt.arg.key.equals.value", "opt.A", STANDARD, BASIC, true) {
360        @Override
361        public boolean matches(String arg) {
362            return arg.startsWith("-A");
363        }
364
365        @Override
366        public boolean hasArg() {
367            return false;
368        }
369        // Mapping for processor options created in
370        // JavacProcessingEnvironment
371        @Override
372        public boolean process(OptionHelper helper, String option) {
373            int argLength = option.length();
374            if (argLength == 2) {
375                helper.error("err.empty.A.argument");
376                return true;
377            }
378            int sepIndex = option.indexOf('=');
379            String key = option.substring(2, (sepIndex != -1 ? sepIndex : argLength) );
380            if (!JavacProcessingEnvironment.isValidOptionName(key)) {
381                helper.error("err.invalid.A.key", option);
382                return true;
383            }
384            return process(helper, option, option);
385        }
386    },
387
388    X("-X", "opt.X", STANDARD, INFO) {
389        @Override
390        public boolean process(OptionHelper helper, String option) {
391            Log log = helper.getLog();
392            for (Option o: getJavaCompilerOptions()) {
393                o.help(log, OptionKind.EXTENDED);
394            }
395            log.printNewline();
396            log.printLines(PrefixKind.JAVAC, "msg.usage.nonstandard.footer");
397            return super.process(helper, option);
398        }
399    },
400
401    // This option exists only for the purpose of documenting itself.
402    // It's actually implemented by the launcher.
403    J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, true) {
404        @Override
405        public boolean process(OptionHelper helper, String option) {
406            throw new AssertionError
407                ("the -J flag should be caught by the launcher.");
408        }
409    },
410
411    MOREINFO("-moreinfo", null, HIDDEN, BASIC) {
412        @Override
413        public boolean process(OptionHelper helper, String option) {
414            Type.moreInfo = true;
415            return super.process(helper, option);
416        }
417    },
418
419    // treat warnings as errors
420    WERROR("-Werror", "opt.Werror", STANDARD, BASIC),
421
422    // prompt after each error
423    // new Option("-prompt",                                        "opt.prompt"),
424    PROMPT("-prompt", null, HIDDEN, BASIC),
425
426    // dump stack on error
427    DOE("-doe", null, HIDDEN, BASIC),
428
429    // output source after type erasure
430    PRINTSOURCE("-printsource", null, HIDDEN, BASIC),
431
432    // display warnings for generic unchecked operations
433    WARNUNCHECKED("-warnunchecked", null, HIDDEN, BASIC) {
434        @Override
435        public boolean process(OptionHelper helper, String option) {
436            helper.put("-Xlint:unchecked", option);
437            return false;
438        }
439    },
440
441    XMAXERRS("-Xmaxerrs", "opt.arg.number", "opt.maxerrs", EXTENDED, BASIC),
442
443    XMAXWARNS("-Xmaxwarns", "opt.arg.number", "opt.maxwarns", EXTENDED, BASIC),
444
445    XSTDOUT("-Xstdout", "opt.arg.file", "opt.Xstdout", EXTENDED, INFO) {
446        @Override
447        public boolean process(OptionHelper helper, String option, String arg) {
448            try {
449                Log log = helper.getLog();
450                log.setWriters(new PrintWriter(new FileWriter(arg), true));
451            } catch (java.io.IOException e) {
452                helper.error("err.error.writing.file", arg, e);
453                return true;
454            }
455            return super.process(helper, option, arg);
456        }
457    },
458
459    XPRINT("-Xprint", "opt.print", EXTENDED, BASIC),
460
461    XPRINTROUNDS("-XprintRounds", "opt.printRounds", EXTENDED, BASIC),
462
463    XPRINTPROCESSORINFO("-XprintProcessorInfo", "opt.printProcessorInfo", EXTENDED, BASIC),
464
465    XPREFER("-Xprefer:", "opt.prefer", EXTENDED, BASIC, ONEOF, "source", "newer"),
466
467    XXUSERPATHSFIRST("-XXuserPathsFirst", "opt.userpathsfirst", HIDDEN, BASIC),
468
469    // see enum PkgInfo
470    XPKGINFO("-Xpkginfo:", "opt.pkginfo", EXTENDED, BASIC, ONEOF, "always", "legacy", "nonempty"),
471
472    /* -O is a no-op, accepted for backward compatibility. */
473    O("-O", null, HIDDEN, BASIC),
474
475    /* -Xjcov produces tables to support the code coverage tool jcov. */
476    XJCOV("-Xjcov", null, HIDDEN, BASIC),
477
478    PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
479        @Override
480        public boolean process(OptionHelper helper, String option) {
481            String p = option.substring(option.indexOf(':') + 1);
482            String prev = helper.get(PLUGIN);
483            helper.put(PLUGIN.text, (prev == null) ? p : prev + '\0' + p.trim());
484            return false;
485        }
486    },
487
488    XDIAGS("-Xdiags:", "opt.diags", EXTENDED, BASIC, ONEOF, "compact", "verbose"),
489
490    /* This is a back door to the compiler's option table.
491     * -XDx=y sets the option x to the value y.
492     * -XDx sets the option x to the value x.
493     */
494    XD("-XD", null, HIDDEN, BASIC) {
495        @Override
496        public boolean matches(String s) {
497            return s.startsWith(text);
498        }
499        @Override
500        public boolean process(OptionHelper helper, String option) {
501            option = option.substring(text.length());
502            int eq = option.indexOf('=');
503            String key = (eq < 0) ? option : option.substring(0, eq);
504            String value = (eq < 0) ? option : option.substring(eq+1);
505            helper.put(key, value);
506            return false;
507        }
508    },
509
510    // This option exists only for the purpose of documenting itself.
511    // It's actually implemented by the CommandLine class.
512    AT("@", "opt.arg.file", "opt.AT", STANDARD, INFO, true) {
513        @Override
514        public boolean process(OptionHelper helper, String option) {
515            throw new AssertionError("the @ flag should be caught by CommandLine.");
516        }
517    },
518
519    /*
520     * TODO: With apt, the matches method accepts anything if
521     * -XclassAsDecls is used; code elsewhere does the lookup to
522     * see if the class name is both legal and found.
523     *
524     * In apt, the process method adds the candidate class file
525     * name to a separate list.
526     */
527    SOURCEFILE("sourcefile", null, HIDDEN, INFO) {
528        @Override
529        public boolean matches(String s) {
530            return s.endsWith(".java")  // Java source file
531                || SourceVersion.isName(s);   // Legal type name
532        }
533        @Override
534        public boolean process(OptionHelper helper, String option) {
535            if (option.endsWith(".java") ) {
536                Path p = Paths.get(option);
537                if (!Files.exists(p)) {
538                    helper.error("err.file.not.found", p);
539                    return true;
540                }
541                if (!Files.isRegularFile(p)) {
542                    helper.error("err.file.not.file", p);
543                    return true;
544                }
545                helper.addFile(p);
546            } else {
547                helper.addClassName(option);
548            }
549            return false;
550        }
551    };
552
553    /** The kind of an Option. This is used by the -help and -X options. */
554    public enum OptionKind {
555        /** A standard option, documented by -help. */
556        STANDARD,
557        /** An extended option, documented by -X. */
558        EXTENDED,
559        /** A hidden option, not documented. */
560        HIDDEN,
561    }
562
563    /** The group for an Option. This determines the situations in which the
564     *  option is applicable. */
565    enum OptionGroup {
566        /** A basic option, available for use on the command line or via the
567         *  Compiler API. */
568        BASIC,
569        /** An option for javac's standard JavaFileManager. Other file managers
570         *  may or may not support these options. */
571        FILEMANAGER,
572        /** A command-line option that requests information, such as -help. */
573        INFO,
574        /** A command-line "option" representing a file or class name. */
575        OPERAND
576    }
577
578    /** The kind of choice for "choice" options. */
579    enum ChoiceKind {
580        /** The expected value is exactly one of the set of choices. */
581        ONEOF,
582        /** The expected value is one of more of the set of choices. */
583        ANYOF
584    }
585
586    public final String text;
587
588    final OptionKind kind;
589
590    final OptionGroup group;
591
592    /** Documentation key for arguments.
593     */
594    final String argsNameKey;
595
596    /** Documentation key for description.
597     */
598    final String descrKey;
599
600    /** Suffix option (-foo=bar or -foo:bar)
601     */
602    final boolean hasSuffix;
603
604    /** The kind of choices for this option, if any.
605     */
606    final ChoiceKind choiceKind;
607
608    /** The choices for this option, if any, and whether or not the choices
609     *  are hidden
610     */
611    final Map<String,Boolean> choices;
612
613
614    Option(String text, String descrKey,
615            OptionKind kind, OptionGroup group) {
616        this(text, null, descrKey, kind, group, null, null, false);
617    }
618
619    Option(String text, String argsNameKey, String descrKey,
620            OptionKind kind, OptionGroup group) {
621        this(text, argsNameKey, descrKey, kind, group, null, null, false);
622    }
623
624    Option(String text, String argsNameKey, String descrKey,
625            OptionKind kind, OptionGroup group, boolean doHasSuffix) {
626        this(text, argsNameKey, descrKey, kind, group, null, null, doHasSuffix);
627    }
628
629    Option(String text, OptionKind kind, OptionGroup group,
630            ChoiceKind choiceKind, Map<String,Boolean> choices) {
631        this(text, null, null, kind, group, choiceKind, choices, false);
632    }
633
634    Option(String text, String descrKey,
635            OptionKind kind, OptionGroup group,
636            ChoiceKind choiceKind, String... choices) {
637        this(text, null, descrKey, kind, group, choiceKind,
638                createChoices(choices), false);
639    }
640    // where
641        private static Map<String,Boolean> createChoices(String... choices) {
642            Map<String,Boolean> map = new LinkedHashMap<>();
643            for (String c: choices)
644                map.put(c, false);
645            return map;
646        }
647
648    private Option(String text, String argsNameKey, String descrKey,
649            OptionKind kind, OptionGroup group,
650            ChoiceKind choiceKind, Map<String,Boolean> choices,
651            boolean doHasSuffix) {
652        this.text = text;
653        this.argsNameKey = argsNameKey;
654        this.descrKey = descrKey;
655        this.kind = kind;
656        this.group = group;
657        this.choiceKind = choiceKind;
658        this.choices = choices;
659        char lastChar = text.charAt(text.length()-1);
660        this.hasSuffix = doHasSuffix || lastChar == ':' || lastChar == '=';
661    }
662
663    public String getText() {
664        return text;
665    }
666
667    public OptionKind getKind() {
668        return kind;
669    }
670
671    public boolean hasArg() {
672        return argsNameKey != null && !hasSuffix;
673    }
674
675    public boolean matches(String option) {
676        if (!hasSuffix)
677            return option.equals(text);
678
679        if (!option.startsWith(text))
680            return false;
681
682        if (choices != null) {
683            String arg = option.substring(text.length());
684            if (choiceKind == ChoiceKind.ONEOF)
685                return choices.keySet().contains(arg);
686            else {
687                for (String a: arg.split(",+")) {
688                    if (!choices.keySet().contains(a))
689                        return false;
690                }
691            }
692        }
693
694        return true;
695    }
696
697    public boolean process(OptionHelper helper, String option, String arg) {
698        if (choices != null) {
699            if (choiceKind == ChoiceKind.ONEOF) {
700                // some clients like to see just one of option+choice set
701                for (String s: choices.keySet())
702                    helper.remove(option + s);
703                String opt = option + arg;
704                helper.put(opt, opt);
705                // some clients like to see option (without trailing ":")
706                // set to arg
707                String nm = option.substring(0, option.length() - 1);
708                helper.put(nm, arg);
709            } else {
710                // set option+word for each word in arg
711                for (String a: arg.split(",+")) {
712                    String opt = option + a;
713                    helper.put(opt, opt);
714                }
715            }
716        }
717        helper.put(option, arg);
718        if (group == OptionGroup.FILEMANAGER)
719            helper.handleFileManagerOption(this, arg);
720        return false;
721    }
722
723    public boolean process(OptionHelper helper, String option) {
724        if (hasSuffix)
725            return process(helper, text, option.substring(text.length()));
726        else
727            return process(helper, option, option);
728    }
729
730    private static final String HELP_LINE_FORMAT = "  %-26s %s";
731
732    void help(Log log, OptionKind kind) {
733        if (this.kind != kind)
734            return;
735
736        log.printRawLines(WriterKind.NOTICE,
737                String.format(HELP_LINE_FORMAT,
738                    helpSynopsis(log),
739                    log.localize(PrefixKind.JAVAC, descrKey)));
740
741    }
742
743    private String helpSynopsis(Log log) {
744        StringBuilder sb = new StringBuilder();
745        sb.append(text);
746        if (argsNameKey == null) {
747            if (choices != null) {
748                String sep = "{";
749                for (Map.Entry<String,Boolean> e: choices.entrySet()) {
750                    if (!e.getValue()) {
751                        sb.append(sep);
752                        sb.append(e.getKey());
753                        sep = ",";
754                    }
755                }
756                sb.append("}");
757            }
758        } else {
759            if (!hasSuffix)
760                sb.append(" ");
761            sb.append(log.localize(PrefixKind.JAVAC, argsNameKey));
762
763        }
764
765        return sb.toString();
766    }
767
768    // For -XpkgInfo:value
769    public enum PkgInfo {
770        /**
771         * Always generate package-info.class for every package-info.java file.
772         * The file may be empty if there annotations with a RetentionPolicy
773         * of CLASS or RUNTIME.  This option may be useful in conjunction with
774         * build systems (such as Ant) that expect javac to generate at least
775         * one .class file for every .java file.
776         */
777        ALWAYS,
778        /**
779         * Generate a package-info.class file if package-info.java contains
780         * annotations. The file may be empty if all the annotations have
781         * a RetentionPolicy of SOURCE.
782         * This value is just for backwards compatibility with earlier behavior.
783         * Either of the other two values are to be preferred to using this one.
784         */
785        LEGACY,
786        /**
787         * Generate a package-info.class file if and only if there are annotations
788         * in package-info.java to be written into it.
789         */
790        NONEMPTY;
791
792        public static PkgInfo get(Options options) {
793            String v = options.get(XPKGINFO);
794            return (v == null
795                    ? PkgInfo.LEGACY
796                    : PkgInfo.valueOf(StringUtils.toUpperCase(v)));
797        }
798    }
799
800    private static Map<String,Boolean> getXLintChoices() {
801        Map<String,Boolean> choices = new LinkedHashMap<>();
802        choices.put("all", false);
803        for (Lint.LintCategory c : Lint.LintCategory.values())
804            choices.put(c.option, c.hidden);
805        for (Lint.LintCategory c : Lint.LintCategory.values())
806            choices.put("-" + c.option, c.hidden);
807        choices.put("none", false);
808        return choices;
809    }
810
811    static Set<Option> getJavaCompilerOptions() {
812        return EnumSet.allOf(Option.class);
813    }
814
815    public static Set<Option> getJavacFileManagerOptions() {
816        return getOptions(EnumSet.of(FILEMANAGER));
817    }
818
819    public static Set<Option> getJavacToolOptions() {
820        return getOptions(EnumSet.of(BASIC));
821    }
822
823    static Set<Option> getOptions(Set<OptionGroup> desired) {
824        Set<Option> options = EnumSet.noneOf(Option.class);
825        for (Option option : Option.values())
826            if (desired.contains(option.group))
827                options.add(option);
828        return Collections.unmodifiableSet(options);
829    }
830
831}
832