Option.java revision 3261:527e819dbc95
1169689Skan/*
2169689Skan * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
3169689Skan * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4169689Skan *
5169689Skan * This code is free software; you can redistribute it and/or modify it
6169689Skan * under the terms of the GNU General Public License version 2 only, as
7169689Skan * published by the Free Software Foundation.  Oracle designates this
8169689Skan * particular file as subject to the "Classpath" exception as provided
9169689Skan * by Oracle in the LICENSE file that accompanied this code.
10169689Skan *
11169689Skan * This code is distributed in the hope that it will be useful, but WITHOUT
12169689Skan * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13169689Skan * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14169689Skan * version 2 for more details (a copy is included in the LICENSE file that
15169689Skan * accompanied this code).
16169689Skan *
17169689Skan * You should have received a copy of the GNU General Public License version
18169689Skan * 2 along with this work; if not, write to the Free Software Foundation,
19169689Skan * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20169689Skan *
21169689Skan * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22169689Skan * or visit www.oracle.com if you need additional information or have any
23169689Skan * questions.
24169689Skan */
25169689Skan
26169689Skanpackage com.sun.tools.javac.main;
27169689Skan
28169689Skanimport java.io.FileWriter;
29169689Skanimport java.io.PrintWriter;
30169689Skanimport java.nio.file.Files;
31169689Skanimport java.nio.file.Path;
32169689Skanimport java.nio.file.Paths;
33169689Skanimport java.util.Collections;
34169689Skanimport java.util.EnumSet;
35169689Skanimport java.util.LinkedHashMap;
36169689Skanimport java.util.Map;
37169689Skanimport java.util.ServiceLoader;
38169689Skanimport java.util.Set;
39169689Skanimport java.util.TreeSet;
40169689Skanimport java.util.stream.Collectors;
41169689Skanimport java.util.stream.StreamSupport;
42169689Skan
43169689Skanimport javax.lang.model.SourceVersion;
44169689Skan
45169689Skanimport com.sun.tools.doclint.DocLint;
46169689Skanimport com.sun.tools.javac.code.Lint;
47169689Skanimport com.sun.tools.javac.code.Lint.LintCategory;
48169689Skanimport com.sun.tools.javac.code.Source;
49169689Skanimport com.sun.tools.javac.code.Type;
50169689Skanimport com.sun.tools.javac.jvm.Profile;
51169689Skanimport com.sun.tools.javac.jvm.Target;
52169689Skanimport com.sun.tools.javac.platform.PlatformProvider;
53169689Skanimport com.sun.tools.javac.processing.JavacProcessingEnvironment;
54169689Skanimport com.sun.tools.javac.util.Log;
55169689Skanimport com.sun.tools.javac.util.Log.PrefixKind;
56169689Skanimport com.sun.tools.javac.util.Log.WriterKind;
57169689Skanimport com.sun.tools.javac.util.Options;
58169689Skanimport com.sun.tools.javac.util.StringUtils;
59169689Skan
60169689Skanimport static com.sun.tools.javac.main.Option.ChoiceKind.*;
61169689Skanimport static com.sun.tools.javac.main.Option.OptionGroup.*;
62169689Skanimport static com.sun.tools.javac.main.Option.OptionKind.*;
63169689Skan
64169689Skan/**
65169689Skan * Options for javac. The specific Option to handle a command-line option
66169689Skan * is identified by searching the members of this enum in order, looking for
67169689Skan * the first {@link #matches match}. The action for an Option is performed
68169689Skan * by calling {@link #process process}, and by providing a suitable
69169689Skan * {@link OptionHelper} to provide access the compiler state.
70169689Skan *
71169689Skan * <p><b>This is NOT part of any supported API.
72169689Skan * If you write code that depends on this, you do so at your own
73169689Skan * risk.  This code and its internal interfaces are subject to change
74169689Skan * or deletion without notice.</b></p>
75169689Skan */
76169689Skanpublic enum Option {
77169689Skan    G("-g", "opt.g", STANDARD, BASIC),
78169689Skan
79169689Skan    G_NONE("-g:none", "opt.g.none", STANDARD, BASIC) {
80169689Skan        @Override
81169689Skan        public boolean process(OptionHelper helper, String option) {
82169689Skan            helper.put("-g:", "none");
83169689Skan            return false;
84169689Skan        }
85169689Skan    },
86169689Skan
87169689Skan    G_CUSTOM("-g:",  "opt.g.lines.vars.source",
88169689Skan            STANDARD, BASIC, ANYOF, "lines", "vars", "source"),
89169689Skan
90169689Skan    XLINT("-Xlint", "opt.Xlint", EXTENDED, BASIC),
91169689Skan
92169689Skan    XLINT_CUSTOM("-Xlint:", EXTENDED, BASIC, ANYOF, getXLintChoices()) {
93169689Skan        private static final String LINT_KEY_FORMAT = "         %-19s %s";
94169689Skan        @Override
95169689Skan        void help(Log log, OptionKind kind) {
96169689Skan            if (this.kind != kind)
97169689Skan                return;
98169689Skan
99169689Skan            log.printRawLines(WriterKind.NOTICE,
100169689Skan                              String.format(HELP_LINE_FORMAT,
101169689Skan                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.subopts"),
102169689Skan                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.suboptlist")));
103169689Skan            log.printRawLines(WriterKind.NOTICE,
104169689Skan                              String.format(LINT_KEY_FORMAT,
105169689Skan                                            "all",
106169689Skan                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.all")));
107169689Skan            for (LintCategory lc : LintCategory.values()) {
108169689Skan                if (lc.hidden) continue;
109169689Skan                log.printRawLines(WriterKind.NOTICE,
110169689Skan                                  String.format(LINT_KEY_FORMAT,
111169689Skan                                                lc.option,
112169689Skan                                                log.localize(PrefixKind.JAVAC,
113169689Skan                                                             "opt.Xlint.desc." + lc.option)));
114169689Skan            }
115169689Skan            log.printRawLines(WriterKind.NOTICE,
116169689Skan                              String.format(LINT_KEY_FORMAT,
117169689Skan                                            "none",
118169689Skan                                            log.localize(PrefixKind.JAVAC, "opt.Xlint.none")));
119169689Skan        }
120169689Skan    },
121169689Skan
122169689Skan    XDOCLINT("-Xdoclint", "opt.Xdoclint", EXTENDED, BASIC),
123169689Skan
124169689Skan    XDOCLINT_CUSTOM("-Xdoclint:", "opt.Xdoclint.subopts", "opt.Xdoclint.custom", EXTENDED, BASIC) {
125169689Skan        @Override
126169689Skan        public boolean matches(String option) {
127169689Skan            return DocLint.isValidOption(
128169689Skan                    option.replace(XDOCLINT_CUSTOM.text, DocLint.XMSGS_CUSTOM_PREFIX));
129169689Skan        }
130169689Skan
131169689Skan        @Override
132169689Skan        public boolean process(OptionHelper helper, String option) {
133169689Skan            String prev = helper.get(XDOCLINT_CUSTOM);
134169689Skan            String next = (prev == null) ? option : (prev + " " + option);
135169689Skan            helper.put(XDOCLINT_CUSTOM.text, next);
136169689Skan            return false;
137169689Skan        }
138169689Skan    },
139169689Skan
140169689Skan    XDOCLINT_PACKAGE("-Xdoclint/package:", "opt.Xdoclint.package.args", "opt.Xdoclint.package.desc", EXTENDED, BASIC) {
141169689Skan        @Override
142169689Skan        public boolean matches(String option) {
143169689Skan            return DocLint.isValidOption(
144169689Skan                    option.replace(XDOCLINT_PACKAGE.text, DocLint.XCHECK_PACKAGE));
145169689Skan        }
146169689Skan
147169689Skan        @Override
148169689Skan        public boolean process(OptionHelper helper, String option) {
149169689Skan            String prev = helper.get(XDOCLINT_PACKAGE);
150169689Skan            String next = (prev == null) ? option : (prev + " " + option);
151169689Skan            helper.put(XDOCLINT_PACKAGE.text, next);
152169689Skan            return false;
153169689Skan        }
154169689Skan    },
155169689Skan
156169689Skan    // -nowarn is retained for command-line backward compatibility
157169689Skan    NOWARN("-nowarn", "opt.nowarn", STANDARD, BASIC) {
158169689Skan        @Override
159169689Skan        public boolean process(OptionHelper helper, String option) {
160169689Skan            helper.put("-Xlint:none", option);
161169689Skan            return false;
162169689Skan        }
163169689Skan    },
164169689Skan
165169689Skan    VERBOSE("-verbose", "opt.verbose", STANDARD, BASIC),
166169689Skan
167169689Skan    // -deprecation is retained for command-line backward compatibility
168169689Skan    DEPRECATION("-deprecation", "opt.deprecation", STANDARD, BASIC) {
169169689Skan        @Override
170169689Skan        public boolean process(OptionHelper helper, String option) {
171169689Skan            helper.put("-Xlint:deprecation", option);
172169689Skan            return false;
173169689Skan        }
174169689Skan    },
175169689Skan
176169689Skan    CLASSPATH("-classpath", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER),
177169689Skan
178169689Skan    CP("-cp", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER) {
179169689Skan        @Override
180169689Skan        public boolean process(OptionHelper helper, String option, String arg) {
181169689Skan            return super.process(helper, "-classpath", arg);
182169689Skan        }
183169689Skan    },
184169689Skan
185169689Skan    SOURCEPATH("-sourcepath", "opt.arg.path", "opt.sourcepath", STANDARD, FILEMANAGER),
186169689Skan
187169689Skan    BOOTCLASSPATH("-bootclasspath", "opt.arg.path", "opt.bootclasspath", STANDARD, FILEMANAGER) {
188169689Skan        @Override
189169689Skan        public boolean process(OptionHelper helper, String option, String arg) {
190169689Skan            helper.remove("-Xbootclasspath/p:");
191169689Skan            helper.remove("-Xbootclasspath/a:");
192169689Skan            return super.process(helper, option, arg);
193169689Skan        }
194169689Skan    },
195169689Skan
196169689Skan    XBOOTCLASSPATH_PREPEND("-Xbootclasspath/p:", "opt.arg.path", "opt.Xbootclasspath.p", EXTENDED, FILEMANAGER),
197169689Skan
198169689Skan    XBOOTCLASSPATH_APPEND("-Xbootclasspath/a:", "opt.arg.path", "opt.Xbootclasspath.a", EXTENDED, FILEMANAGER),
199169689Skan
200169689Skan    XBOOTCLASSPATH("-Xbootclasspath:", "opt.arg.path", "opt.bootclasspath", EXTENDED, FILEMANAGER) {
201169689Skan        @Override
202169689Skan        public boolean process(OptionHelper helper, String option, String arg) {
203169689Skan            helper.remove("-Xbootclasspath/p:");
204169689Skan            helper.remove("-Xbootclasspath/a:");
205169689Skan            return super.process(helper, "-bootclasspath", arg);
206169689Skan        }
207169689Skan    },
208169689Skan
209169689Skan    EXTDIRS("-extdirs", "opt.arg.dirs", "opt.extdirs", STANDARD, FILEMANAGER),
210169689Skan
211169689Skan    DJAVA_EXT_DIRS("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs", EXTENDED, FILEMANAGER) {
212169689Skan        @Override
213169689Skan        public boolean process(OptionHelper helper, String option, String arg) {
214169689Skan            return super.process(helper, "-extdirs", arg);
215169689Skan        }
216169689Skan    },
217169689Skan
218169689Skan    ENDORSEDDIRS("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs", STANDARD, FILEMANAGER),
219169689Skan
220169689Skan    DJAVA_ENDORSED_DIRS("-Djava.endorsed.dirs=", "opt.arg.dirs", "opt.endorseddirs", EXTENDED, FILEMANAGER) {
221169689Skan        @Override
222169689Skan        public boolean process(OptionHelper helper, String option, String arg) {
223169689Skan            return super.process(helper, "-endorseddirs", arg);
224169689Skan        }
225169689Skan    },
226169689Skan
227169689Skan    PROC("-proc:", "opt.proc.none.only", STANDARD, BASIC,  ONEOF, "none", "only"),
228169689Skan
229169689Skan    PROCESSOR("-processor", "opt.arg.class.list", "opt.processor", STANDARD, BASIC),
230169689Skan
231169689Skan    PROCESSORPATH("-processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER),
232169689Skan
233169689Skan    PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC),
234169689Skan
235169689Skan    D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
236169689Skan
237169689Skan    S("-s", "opt.arg.directory", "opt.sourceDest", STANDARD, FILEMANAGER),
238169689Skan
239169689Skan    H("-h", "opt.arg.directory", "opt.headerDest", STANDARD, FILEMANAGER),
240169689Skan
241169689Skan    IMPLICIT("-implicit:", "opt.implicit", STANDARD, BASIC, ONEOF, "none", "class"),
242169689Skan
243169689Skan    ENCODING("-encoding", "opt.arg.encoding", "opt.encoding", STANDARD, FILEMANAGER),
244169689Skan
245169689Skan    SOURCE("-source", "opt.arg.release", "opt.source", STANDARD, BASIC) {
246169689Skan        @Override
247169689Skan        public boolean process(OptionHelper helper, String option, String operand) {
248169689Skan            Source source = Source.lookup(operand);
249169689Skan            if (source == null) {
250169689Skan                helper.error("err.invalid.source", operand);
251169689Skan                return true;
252169689Skan            }
253169689Skan            return super.process(helper, option, operand);
254169689Skan        }
255169689Skan    },
256169689Skan
257169689Skan    TARGET("-target", "opt.arg.release", "opt.target", STANDARD, BASIC) {
258169689Skan        @Override
259169689Skan        public boolean process(OptionHelper helper, String option, String operand) {
260169689Skan            Target target = Target.lookup(operand);
261169689Skan            if (target == null) {
262169689Skan                helper.error("err.invalid.target", operand);
263169689Skan                return true;
264169689Skan            }
265169689Skan            return super.process(helper, option, operand);
266169689Skan        }
267169689Skan    },
268169689Skan
269169689Skan    RELEASE("-release", "opt.arg.release", "opt.release", STANDARD, BASIC) {
270169689Skan        @Override
271169689Skan        void help(Log log, OptionKind kind) {
272169689Skan            if (this.kind != kind)
273169689Skan                return;
274169689Skan
275169689Skan            Iterable<PlatformProvider> providers =
276169689Skan                    ServiceLoader.load(PlatformProvider.class, Arguments.class.getClassLoader());
277169689Skan            Set<String> platforms = StreamSupport.stream(providers.spliterator(), false)
278169689Skan                                                 .flatMap(provider -> StreamSupport.stream(provider.getSupportedPlatformNames()
279169689Skan                                                                                                   .spliterator(),
280169689Skan                                                                                           false))
281169689Skan                                                 .collect(Collectors.toCollection(TreeSet :: new));
282169689Skan
283169689Skan            StringBuilder targets = new StringBuilder();
284169689Skan            String delim = "";
285169689Skan            for (String platform : platforms) {
286169689Skan                targets.append(delim);
287169689Skan                targets.append(platform);
288169689Skan                delim = ", ";
289169689Skan            }
290169689Skan
291169689Skan            log.printRawLines(WriterKind.NOTICE,
292169689Skan                    String.format(HELP_LINE_FORMAT,
293169689Skan                        super.helpSynopsis(log),
294169689Skan                        log.localize(PrefixKind.JAVAC, descrKey, targets.toString())));
295169689Skan        }
296169689Skan    },
297169689Skan
298169689Skan    PROFILE("-profile", "opt.arg.profile", "opt.profile", STANDARD, BASIC) {
299169689Skan        @Override
300169689Skan        public boolean process(OptionHelper helper, String option, String operand) {
301169689Skan            Profile profile = Profile.lookup(operand);
302169689Skan            if (profile == null) {
303169689Skan                helper.error("err.invalid.profile", operand);
304169689Skan                return true;
305169689Skan            }
306169689Skan            return super.process(helper, option, operand);
307169689Skan        }
308169689Skan    },
309169689Skan
310169689Skan    VERSION("-version", "opt.version", STANDARD, INFO) {
311169689Skan        @Override
312169689Skan        public boolean process(OptionHelper helper, String option) {
313169689Skan            Log log = helper.getLog();
314169689Skan            String ownName = helper.getOwnName();
315169689Skan            log.printLines(PrefixKind.JAVAC, "version", ownName,  JavaCompiler.version());
316169689Skan            return super.process(helper, option);
317169689Skan        }
318169689Skan    },
319169689Skan
320169689Skan    FULLVERSION("-fullversion", null, HIDDEN, INFO) {
321169689Skan        @Override
322169689Skan        public boolean process(OptionHelper helper, String option) {
323169689Skan            Log log = helper.getLog();
324169689Skan            String ownName = helper.getOwnName();
325169689Skan            log.printLines(PrefixKind.JAVAC, "fullVersion", ownName,  JavaCompiler.fullVersion());
326169689Skan            return super.process(helper, option);
327169689Skan        }
328169689Skan    },
329169689Skan
330169689Skan    DIAGS("-XDdiags=", null, HIDDEN, INFO) {
331169689Skan        @Override
332169689Skan        public boolean process(OptionHelper helper, String option) {
333169689Skan            option = option.substring(option.indexOf('=') + 1);
334169689Skan            String diagsOption = option.contains("%") ?
335169689Skan                "-XDdiagsFormat=" :
336169689Skan                "-XDdiags=";
337169689Skan            diagsOption += option;
338169689Skan            if (XD.matches(diagsOption))
339169689Skan                return XD.process(helper, diagsOption);
340169689Skan            else
341169689Skan                return false;
342169689Skan        }
343169689Skan    },
344169689Skan
345169689Skan    HELP("-help", "opt.help", STANDARD, INFO) {
346169689Skan        @Override
347169689Skan        public boolean process(OptionHelper helper, String option) {
348169689Skan            Log log = helper.getLog();
349169689Skan            String ownName = helper.getOwnName();
350169689Skan            log.printLines(PrefixKind.JAVAC, "msg.usage.header", ownName);
351169689Skan            for (Option o: getJavaCompilerOptions()) {
352169689Skan                o.help(log, OptionKind.STANDARD);
353169689Skan            }
354169689Skan            log.printNewline();
355169689Skan            return super.process(helper, option);
356169689Skan        }
357169689Skan    },
358169689Skan
359169689Skan    A("-A", "opt.arg.key.equals.value", "opt.A", STANDARD, BASIC, true) {
360169689Skan        @Override
361169689Skan        public boolean matches(String arg) {
362169689Skan            return arg.startsWith("-A");
363169689Skan        }
364169689Skan
365169689Skan        @Override
366169689Skan        public boolean hasArg() {
367169689Skan            return false;
368169689Skan        }
369169689Skan        // Mapping for processor options created in
370169689Skan        // JavacProcessingEnvironment
371169689Skan        @Override
372169689Skan        public boolean process(OptionHelper helper, String option) {
373169689Skan            int argLength = option.length();
374169689Skan            if (argLength == 2) {
375169689Skan                helper.error("err.empty.A.argument");
376169689Skan                return true;
377169689Skan            }
378169689Skan            int sepIndex = option.indexOf('=');
379169689Skan            String key = option.substring(2, (sepIndex != -1 ? sepIndex : argLength) );
380169689Skan            if (!JavacProcessingEnvironment.isValidOptionName(key)) {
381169689Skan                helper.error("err.invalid.A.key", option);
382169689Skan                return true;
383169689Skan            }
384169689Skan            return process(helper, option, option);
385169689Skan        }
386169689Skan    },
387169689Skan
388169689Skan    X("-X", "opt.X", STANDARD, INFO) {
389169689Skan        @Override
390169689Skan        public boolean process(OptionHelper helper, String option) {
391169689Skan            Log log = helper.getLog();
392169689Skan            for (Option o: getJavaCompilerOptions()) {
393169689Skan                o.help(log, OptionKind.EXTENDED);
394169689Skan            }
395169689Skan            log.printNewline();
396169689Skan            log.printLines(PrefixKind.JAVAC, "msg.usage.nonstandard.footer");
397169689Skan            return super.process(helper, option);
398169689Skan        }
399169689Skan    },
400169689Skan
401169689Skan    // This option exists only for the purpose of documenting itself.
402169689Skan    // It's actually implemented by the launcher.
403169689Skan    J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, true) {
404169689Skan        @Override
405169689Skan        public boolean process(OptionHelper helper, String option) {
406169689Skan            throw new AssertionError
407169689Skan                ("the -J flag should be caught by the launcher.");
408169689Skan        }
409169689Skan    },
410169689Skan
411169689Skan    MOREINFO("-moreinfo", null, HIDDEN, BASIC) {
412169689Skan        @Override
413169689Skan        public boolean process(OptionHelper helper, String option) {
414169689Skan            Type.moreInfo = true;
415169689Skan            return super.process(helper, option);
416169689Skan        }
417169689Skan    },
418169689Skan
419169689Skan    // treat warnings as errors
420169689Skan    WERROR("-Werror", "opt.Werror", STANDARD, BASIC),
421169689Skan
422169689Skan    // prompt after each error
423169689Skan    // new Option("-prompt",                                        "opt.prompt"),
424169689Skan    PROMPT("-prompt", null, HIDDEN, BASIC),
425169689Skan
426169689Skan    // dump stack on error
427169689Skan    DOE("-doe", null, HIDDEN, BASIC),
428169689Skan
429169689Skan    // output source after type erasure
430169689Skan    PRINTSOURCE("-printsource", null, HIDDEN, BASIC),
431169689Skan
432169689Skan    // display warnings for generic unchecked operations
433169689Skan    WARNUNCHECKED("-warnunchecked", null, HIDDEN, BASIC) {
434169689Skan        @Override
435169689Skan        public boolean process(OptionHelper helper, String option) {
436169689Skan            helper.put("-Xlint:unchecked", option);
437169689Skan            return false;
438169689Skan        }
439169689Skan    },
440169689Skan
441169689Skan    XMAXERRS("-Xmaxerrs", "opt.arg.number", "opt.maxerrs", EXTENDED, BASIC),
442169689Skan
443169689Skan    XMAXWARNS("-Xmaxwarns", "opt.arg.number", "opt.maxwarns", EXTENDED, BASIC),
444169689Skan
445169689Skan    XSTDOUT("-Xstdout", "opt.arg.file", "opt.Xstdout", EXTENDED, INFO) {
446169689Skan        @Override
447169689Skan        public boolean process(OptionHelper helper, String option, String arg) {
448169689Skan            try {
449169689Skan                Log log = helper.getLog();
450169689Skan                log.setWriters(new PrintWriter(new FileWriter(arg), true));
451169689Skan            } catch (java.io.IOException e) {
452169689Skan                helper.error("err.error.writing.file", arg, e);
453169689Skan                return true;
454169689Skan            }
455169689Skan            return super.process(helper, option, arg);
456169689Skan        }
457169689Skan    },
458169689Skan
459169689Skan    XPRINT("-Xprint", "opt.print", EXTENDED, BASIC),
460169689Skan
461169689Skan    XPRINTROUNDS("-XprintRounds", "opt.printRounds", EXTENDED, BASIC),
462169689Skan
463169689Skan    XPRINTPROCESSORINFO("-XprintProcessorInfo", "opt.printProcessorInfo", EXTENDED, BASIC),
464169689Skan
465169689Skan    XPREFER("-Xprefer:", "opt.prefer", EXTENDED, BASIC, ONEOF, "source", "newer"),
466169689Skan
467169689Skan    XXUSERPATHSFIRST("-XXuserPathsFirst", "opt.userpathsfirst", HIDDEN, BASIC),
468169689Skan
469169689Skan    // see enum PkgInfo
470169689Skan    XPKGINFO("-Xpkginfo:", "opt.pkginfo", EXTENDED, BASIC, ONEOF, "always", "legacy", "nonempty"),
471169689Skan
472169689Skan    /* -O is a no-op, accepted for backward compatibility. */
473169689Skan    O("-O", null, HIDDEN, BASIC),
474169689Skan
475169689Skan    /* -Xjcov produces tables to support the code coverage tool jcov. */
476169689Skan    XJCOV("-Xjcov", null, HIDDEN, BASIC),
477169689Skan
478169689Skan    PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
479169689Skan        @Override
480169689Skan        public boolean process(OptionHelper helper, String option) {
481169689Skan            String p = option.substring(option.indexOf(':') + 1);
482169689Skan            String prev = helper.get(PLUGIN);
483169689Skan            helper.put(PLUGIN.text, (prev == null) ? p : prev + '\0' + p.trim());
484169689Skan            return false;
485169689Skan        }
486169689Skan    },
487169689Skan
488169689Skan    XDIAGS("-Xdiags:", "opt.diags", EXTENDED, BASIC, ONEOF, "compact", "verbose"),
489169689Skan
490169689Skan    /* This is a back door to the compiler's option table.
491169689Skan     * -XDx=y sets the option x to the value y.
492169689Skan     * -XDx sets the option x to the value x.
493169689Skan     */
494169689Skan    XD("-XD", null, HIDDEN, BASIC) {
495169689Skan        @Override
496169689Skan        public boolean matches(String s) {
497169689Skan            return s.startsWith(text);
498169689Skan        }
499169689Skan        @Override
500169689Skan        public boolean process(OptionHelper helper, String option) {
501169689Skan            option = option.substring(text.length());
502169689Skan            int eq = option.indexOf('=');
503169689Skan            String key = (eq < 0) ? option : option.substring(0, eq);
504169689Skan            String value = (eq < 0) ? option : option.substring(eq+1);
505169689Skan            helper.put(key, value);
506169689Skan            return false;
507169689Skan        }
508169689Skan    },
509169689Skan
510169689Skan    // This option exists only for the purpose of documenting itself.
511169689Skan    // It's actually implemented by the CommandLine class.
512169689Skan    AT("@", "opt.arg.file", "opt.AT", STANDARD, INFO, true) {
513169689Skan        @Override
514169689Skan        public boolean process(OptionHelper helper, String option) {
515169689Skan            throw new AssertionError("the @ flag should be caught by CommandLine.");
516169689Skan        }
517169689Skan    },
518169689Skan
519169689Skan    /*
520169689Skan     * TODO: With apt, the matches method accepts anything if
521169689Skan     * -XclassAsDecls is used; code elsewhere does the lookup to
522169689Skan     * see if the class name is both legal and found.
523169689Skan     *
524169689Skan     * In apt, the process method adds the candidate class file
525169689Skan     * name to a separate list.
526169689Skan     */
527169689Skan    SOURCEFILE("sourcefile", null, HIDDEN, INFO) {
528169689Skan        @Override
529169689Skan        public boolean matches(String s) {
530169689Skan            return s.endsWith(".java")  // Java source file
531169689Skan                || SourceVersion.isName(s);   // Legal type name
532169689Skan        }
533169689Skan        @Override
534169689Skan        public boolean process(OptionHelper helper, String option) {
535169689Skan            if (option.endsWith(".java") ) {
536169689Skan                Path p = Paths.get(option);
537169689Skan                if (!Files.exists(p)) {
538169689Skan                    helper.error("err.file.not.found", p);
539169689Skan                    return true;
540169689Skan                }
541169689Skan                if (!Files.isRegularFile(p)) {
542169689Skan                    helper.error("err.file.not.file", p);
543169689Skan                    return true;
544169689Skan                }
545169689Skan                helper.addFile(p);
546169689Skan            } else {
547169689Skan                helper.addClassName(option);
548169689Skan            }
549169689Skan            return false;
550169689Skan        }
551169689Skan    };
552169689Skan
553169689Skan    /** The kind of an Option. This is used by the -help and -X options. */
554169689Skan    public enum OptionKind {
555169689Skan        /** A standard option, documented by -help. */
556169689Skan        STANDARD,
557169689Skan        /** An extended option, documented by -X. */
558169689Skan        EXTENDED,
559169689Skan        /** A hidden option, not documented. */
560169689Skan        HIDDEN,
561169689Skan    }
562169689Skan
563169689Skan    /** The group for an Option. This determines the situations in which the
564169689Skan     *  option is applicable. */
565169689Skan    enum OptionGroup {
566169689Skan        /** A basic option, available for use on the command line or via the
567169689Skan         *  Compiler API. */
568169689Skan        BASIC,
569169689Skan        /** An option for javac's standard JavaFileManager. Other file managers
570169689Skan         *  may or may not support these options. */
571169689Skan        FILEMANAGER,
572169689Skan        /** A command-line option that requests information, such as -help. */
573169689Skan        INFO,
574169689Skan        /** A command-line "option" representing a file or class name. */
575169689Skan        OPERAND
576169689Skan    }
577169689Skan
578169689Skan    /** The kind of choice for "choice" options. */
579169689Skan    enum ChoiceKind {
580169689Skan        /** The expected value is exactly one of the set of choices. */
581169689Skan        ONEOF,
582169689Skan        /** The expected value is one of more of the set of choices. */
583169689Skan        ANYOF
584169689Skan    }
585169689Skan
586169689Skan    public final String text;
587169689Skan
588169689Skan    final OptionKind kind;
589169689Skan
590169689Skan    final OptionGroup group;
591169689Skan
592169689Skan    /** Documentation key for arguments.
593169689Skan     */
594169689Skan    final String argsNameKey;
595169689Skan
596169689Skan    /** Documentation key for description.
597169689Skan     */
598169689Skan    final String descrKey;
599169689Skan
600169689Skan    /** Suffix option (-foo=bar or -foo:bar)
601169689Skan     */
602169689Skan    final boolean hasSuffix;
603169689Skan
604169689Skan    /** The kind of choices for this option, if any.
605169689Skan     */
606169689Skan    final ChoiceKind choiceKind;
607169689Skan
608169689Skan    /** The choices for this option, if any, and whether or not the choices
609169689Skan     *  are hidden
610169689Skan     */
611169689Skan    final Map<String,Boolean> choices;
612169689Skan
613169689Skan
614169689Skan    Option(String text, String descrKey,
615169689Skan            OptionKind kind, OptionGroup group) {
616169689Skan        this(text, null, descrKey, kind, group, null, null, false);
617169689Skan    }
618169689Skan
619169689Skan    Option(String text, String argsNameKey, String descrKey,
620169689Skan            OptionKind kind, OptionGroup group) {
621169689Skan        this(text, argsNameKey, descrKey, kind, group, null, null, false);
622169689Skan    }
623169689Skan
624169689Skan    Option(String text, String argsNameKey, String descrKey,
625169689Skan            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