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 
  26 package com.sun.tools.javac.main;
  27 
  28 import java.io.FileWriter;
  29 import java.io.PrintWriter;
  30 import java.nio.file.Files;
  31 import java.nio.file.Path;
  32 import java.nio.file.Paths;
  33 import java.util.Collections;
  34 import java.util.EnumSet;
  35 import java.util.LinkedHashMap;
  36 import java.util.Map;
  37 import java.util.ServiceLoader;
  38 import java.util.Set;
  39 import java.util.TreeSet;
  40 import java.util.stream.Collectors;
  41 import java.util.stream.StreamSupport;
  42 
  43 import javax.lang.model.SourceVersion;
  44 
  45 import com.sun.tools.doclint.DocLint;
  46 import com.sun.tools.javac.code.Lint;
  47 import com.sun.tools.javac.code.Lint.LintCategory;
  48 import com.sun.tools.javac.code.Source;
  49 import com.sun.tools.javac.code.Type;
  50 import com.sun.tools.javac.jvm.Profile;
  51 import com.sun.tools.javac.jvm.Target;
  52 import com.sun.tools.javac.platform.PlatformProvider;
  53 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
  54 import com.sun.tools.javac.util.Log;
  55 import com.sun.tools.javac.util.Log.PrefixKind;
  56 import com.sun.tools.javac.util.Log.WriterKind;
  57 import com.sun.tools.javac.util.Options;
  58 import com.sun.tools.javac.util.StringUtils;
  59 
  60 import static com.sun.tools.javac.main.Option.ChoiceKind.*;
  61 import static com.sun.tools.javac.main.Option.OptionGroup.*;
  62 import 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  */
  76 public 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.STDOUT,
 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.STDOUT,
 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.STDOUT,
 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.STDOUT,
 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     MODULESOURCEPATH("-modulesourcepath", "opt.arg.mspath", "opt.modulesourcepath", STANDARD, FILEMANAGER),
 188 
 189     MODULEPATH("-modulepath", "opt.arg.path", "opt.modulepath", STANDARD, FILEMANAGER),
 190 
 191     MP("-mp", "opt.arg.path", "opt.modulepath", STANDARD, FILEMANAGER) {
 192         @Override
 193         public boolean process(OptionHelper helper, String option, String arg) {
 194             return super.process(helper, "-modulepath", arg);
 195         }
 196     },
 197 
 198     UPGRADEMODULEPATH("-upgrademodulepath", "opt.arg.path", "opt.upgrademodulepath", STANDARD, FILEMANAGER),
 199 
 200     SYSTEM("-system", "opt.arg.jdk", "opt.system", STANDARD, FILEMANAGER),
 201 
 202     XPATCH("-Xpatch:", "opt.arg.patch", "opt.patch", EXTENDED, FILEMANAGER),
 203 
 204     BOOTCLASSPATH("-bootclasspath", "opt.arg.path", "opt.bootclasspath", STANDARD, FILEMANAGER) {
 205         @Override
 206         public boolean process(OptionHelper helper, String option, String arg) {
 207             helper.remove("-Xbootclasspath/p:");
 208             helper.remove("-Xbootclasspath/a:");
 209             return super.process(helper, option, arg);
 210         }
 211     },
 212 
 213     XBOOTCLASSPATH_PREPEND("-Xbootclasspath/p:", "opt.arg.path", "opt.Xbootclasspath.p", EXTENDED, FILEMANAGER),
 214 
 215     XBOOTCLASSPATH_APPEND("-Xbootclasspath/a:", "opt.arg.path", "opt.Xbootclasspath.a", EXTENDED, FILEMANAGER),
 216 
 217     XBOOTCLASSPATH("-Xbootclasspath:", "opt.arg.path", "opt.bootclasspath", EXTENDED, FILEMANAGER) {
 218         @Override
 219         public boolean process(OptionHelper helper, String option, String arg) {
 220             helper.remove("-Xbootclasspath/p:");
 221             helper.remove("-Xbootclasspath/a:");
 222             return super.process(helper, "-bootclasspath", arg);
 223         }
 224     },
 225 
 226     EXTDIRS("-extdirs", "opt.arg.dirs", "opt.extdirs", STANDARD, FILEMANAGER),
 227 
 228     DJAVA_EXT_DIRS("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs", EXTENDED, FILEMANAGER) {
 229         @Override
 230         public boolean process(OptionHelper helper, String option, String arg) {
 231             return super.process(helper, "-extdirs", arg);
 232         }
 233     },
 234 
 235     ENDORSEDDIRS("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs", STANDARD, FILEMANAGER),
 236 
 237     DJAVA_ENDORSED_DIRS("-Djava.endorsed.dirs=", "opt.arg.dirs", "opt.endorseddirs", EXTENDED, FILEMANAGER) {
 238         @Override
 239         public boolean process(OptionHelper helper, String option, String arg) {
 240             return super.process(helper, "-endorseddirs", arg);
 241         }
 242     },
 243 
 244     PROC("-proc:", "opt.proc.none.only", STANDARD, BASIC,  ONEOF, "none", "only"),
 245 
 246     PROCESSOR("-processor", "opt.arg.class.list", "opt.processor", STANDARD, BASIC),
 247 
 248     PROCESSORPATH("-processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER),
 249 
 250     PROCESSORMODULEPATH("-processormodulepath", "opt.arg.path", "opt.processormodulepath", STANDARD, FILEMANAGER),
 251 
 252     PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC),
 253 
 254     D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
 255 
 256     S("-s", "opt.arg.directory", "opt.sourceDest", STANDARD, FILEMANAGER),
 257 
 258     H("-h", "opt.arg.directory", "opt.headerDest", STANDARD, FILEMANAGER),
 259 
 260     IMPLICIT("-implicit:", "opt.implicit", STANDARD, BASIC, ONEOF, "none", "class"),
 261 
 262     ENCODING("-encoding", "opt.arg.encoding", "opt.encoding", STANDARD, FILEMANAGER),
 263 
 264     SOURCE("-source", "opt.arg.release", "opt.source", STANDARD, BASIC) {
 265         @Override
 266         public boolean process(OptionHelper helper, String option, String operand) {
 267             Source source = Source.lookup(operand);
 268             if (source == null) {
 269                 helper.error("err.invalid.source", operand);
 270                 return true;
 271             }
 272             return super.process(helper, option, operand);
 273         }
 274     },
 275 
 276     TARGET("-target", "opt.arg.release", "opt.target", STANDARD, BASIC) {
 277         @Override
 278         public boolean process(OptionHelper helper, String option, String operand) {
 279             Target target = Target.lookup(operand);
 280             if (target == null) {
 281                 helper.error("err.invalid.target", operand);
 282                 return true;
 283             }
 284             return super.process(helper, option, operand);
 285         }
 286     },
 287 
 288     RELEASE("-release", "opt.arg.release", "opt.release", STANDARD, BASIC) {
 289         @Override
 290         void help(Log log, OptionKind kind) {
 291             if (this.kind != kind)
 292                 return;
 293 
 294             Iterable<PlatformProvider> providers =
 295                     ServiceLoader.load(PlatformProvider.class, Arguments.class.getClassLoader());
 296             Set<String> platforms = StreamSupport.stream(providers.spliterator(), false)
 297                                                  .flatMap(provider -> StreamSupport.stream(provider.getSupportedPlatformNames()
 298                                                                                                    .spliterator(),
 299                                                                                            false))
 300                                                  .collect(Collectors.toCollection(TreeSet :: new));
 301 
 302             StringBuilder targets = new StringBuilder();
 303             String delim = "";
 304             for (String platform : platforms) {
 305                 targets.append(delim);
 306                 targets.append(platform);
 307                 delim = ", ";
 308             }
 309 
 310             log.printRawLines(WriterKind.STDOUT,
 311                     String.format(HELP_LINE_FORMAT,
 312                         super.helpSynopsis(log),
 313                         log.localize(PrefixKind.JAVAC, descrKey, targets.toString())));
 314         }
 315     },
 316 
 317     PROFILE("-profile", "opt.arg.profile", "opt.profile", STANDARD, BASIC) {
 318         @Override
 319         public boolean process(OptionHelper helper, String option, String operand) {
 320             Profile profile = Profile.lookup(operand);
 321             if (profile == null) {
 322                 helper.error("err.invalid.profile", operand);
 323                 return true;
 324             }
 325             return super.process(helper, option, operand);
 326         }
 327     },
 328 
 329     VERSION("-version", "opt.version", STANDARD, INFO) {
 330         @Override
 331         public boolean process(OptionHelper helper, String option) {
 332             Log log = helper.getLog();
 333             String ownName = helper.getOwnName();
 334             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "version", ownName,  JavaCompiler.version());
 335             return super.process(helper, option);
 336         }
 337     },
 338 
 339     FULLVERSION("-fullversion", null, HIDDEN, INFO) {
 340         @Override
 341         public boolean process(OptionHelper helper, String option) {
 342             Log log = helper.getLog();
 343             String ownName = helper.getOwnName();
 344             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "fullVersion", ownName,  JavaCompiler.fullVersion());
 345             return super.process(helper, option);
 346         }
 347     },
 348 
 349     HELP("-help", "opt.help", STANDARD, INFO) {
 350         @Override
 351         public boolean process(OptionHelper helper, String option) {
 352             Log log = helper.getLog();
 353             String ownName = helper.getOwnName();
 354             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.header", ownName);
 355             for (Option o: getJavaCompilerOptions()) {
 356                 o.help(log, OptionKind.STANDARD);
 357             }
 358             log.printNewline(WriterKind.STDOUT);
 359             return super.process(helper, option);
 360         }
 361     },
 362 
 363     A("-A", "opt.arg.key.equals.value", "opt.A", STANDARD, BASIC, true) {
 364         @Override
 365         public boolean matches(String arg) {
 366             return arg.startsWith("-A");
 367         }
 368 
 369         @Override
 370         public boolean hasArg() {
 371             return false;
 372         }
 373         // Mapping for processor options created in
 374         // JavacProcessingEnvironment
 375         @Override
 376         public boolean process(OptionHelper helper, String option) {
 377             int argLength = option.length();
 378             if (argLength == 2) {
 379                 helper.error("err.empty.A.argument");
 380                 return true;
 381             }
 382             int sepIndex = option.indexOf('=');
 383             String key = option.substring(2, (sepIndex != -1 ? sepIndex : argLength) );
 384             if (!JavacProcessingEnvironment.isValidOptionName(key)) {
 385                 helper.error("err.invalid.A.key", option);
 386                 return true;
 387             }
 388             return process(helper, option, option);
 389         }
 390     },
 391 
 392     X("-X", "opt.X", STANDARD, INFO) {
 393         @Override
 394         public boolean process(OptionHelper helper, String option) {
 395             Log log = helper.getLog();
 396             for (Option o: getJavaCompilerOptions()) {
 397                 o.help(log, OptionKind.EXTENDED);
 398             }
 399             log.printNewline(WriterKind.STDOUT);
 400             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.nonstandard.footer");
 401             return super.process(helper, option);
 402         }
 403     },
 404 
 405     // This option exists only for the purpose of documenting itself.
 406     // It's actually implemented by the launcher.
 407     J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, true) {
 408         @Override
 409         public boolean process(OptionHelper helper, String option) {
 410             throw new AssertionError
 411                 ("the -J flag should be caught by the launcher.");
 412         }
 413     },
 414 
 415     MOREINFO("-moreinfo", null, HIDDEN, BASIC) {
 416         @Override
 417         public boolean process(OptionHelper helper, String option) {
 418             Type.moreInfo = true;
 419             return super.process(helper, option);
 420         }
 421     },
 422 
 423     // treat warnings as errors
 424     WERROR("-Werror", "opt.Werror", STANDARD, BASIC),
 425 
 426     // prompt after each error
 427     // new Option("-prompt",                                        "opt.prompt"),
 428     PROMPT("-prompt", null, HIDDEN, BASIC),
 429 
 430     // dump stack on error
 431     DOE("-doe", null, HIDDEN, BASIC),
 432 
 433     // output source after type erasure
 434     PRINTSOURCE("-printsource", null, HIDDEN, BASIC),
 435 
 436     // display warnings for generic unchecked operations
 437     WARNUNCHECKED("-warnunchecked", null, HIDDEN, BASIC) {
 438         @Override
 439         public boolean process(OptionHelper helper, String option) {
 440             helper.put("-Xlint:unchecked", option);
 441             return false;
 442         }
 443     },
 444 
 445     XMAXERRS("-Xmaxerrs", "opt.arg.number", "opt.maxerrs", EXTENDED, BASIC),
 446 
 447     XMAXWARNS("-Xmaxwarns", "opt.arg.number", "opt.maxwarns", EXTENDED, BASIC),
 448 
 449     XSTDOUT("-Xstdout", "opt.arg.file", "opt.Xstdout", EXTENDED, INFO) {
 450         @Override
 451         public boolean process(OptionHelper helper, String option, String arg) {
 452             try {
 453                 Log log = helper.getLog();
 454                 log.setWriters(new PrintWriter(new FileWriter(arg), true));
 455             } catch (java.io.IOException e) {
 456                 helper.error("err.error.writing.file", arg, e);
 457                 return true;
 458             }
 459             return super.process(helper, option, arg);
 460         }
 461     },
 462 
 463     XPRINT("-Xprint", "opt.print", EXTENDED, BASIC),
 464 
 465     XPRINTROUNDS("-XprintRounds", "opt.printRounds", EXTENDED, BASIC),
 466 
 467     XPRINTPROCESSORINFO("-XprintProcessorInfo", "opt.printProcessorInfo", EXTENDED, BASIC),
 468 
 469     XPREFER("-Xprefer:", "opt.prefer", EXTENDED, BASIC, ONEOF, "source", "newer"),
 470 
 471     XXUSERPATHSFIRST("-XXuserPathsFirst", "opt.userpathsfirst", HIDDEN, BASIC),
 472 
 473     // see enum PkgInfo
 474     XPKGINFO("-Xpkginfo:", "opt.pkginfo", EXTENDED, BASIC, ONEOF, "always", "legacy", "nonempty"),
 475 
 476     /* -O is a no-op, accepted for backward compatibility. */
 477     O("-O", null, HIDDEN, BASIC),
 478 
 479     /* -Xjcov produces tables to support the code coverage tool jcov. */
 480     XJCOV("-Xjcov", null, HIDDEN, BASIC),
 481 
 482     PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
 483         @Override
 484         public boolean process(OptionHelper helper, String option) {
 485             String p = option.substring(option.indexOf(':') + 1).trim();
 486             String prev = helper.get(PLUGIN);
 487             helper.put(PLUGIN.text, (prev == null) ? p : prev + '\0' + p);
 488             return false;
 489         }
 490     },
 491 
 492     XDIAGS("-Xdiags:", "opt.diags", EXTENDED, BASIC, ONEOF, "compact", "verbose"),
 493 
 494     XDEBUG("-Xdebug:", null, HIDDEN, BASIC) {
 495         @Override
 496         public boolean process(OptionHelper helper, String option) {
 497             String p = option.substring(option.indexOf(':') + 1).trim();
 498             String[] subOptions = p.split(";");
 499             for (String subOption : subOptions) {
 500                 subOption = "debug." + subOption.trim();
 501                 XD.process(helper, subOption, subOption);
 502             }
 503             return false;
 504         }
 505     },
 506 
 507     XSHOULDSTOP("-Xshouldstop:", null, HIDDEN, BASIC) {
 508         @Override
 509         public boolean process(OptionHelper helper, String option) {
 510             String p = option.substring(option.indexOf(':') + 1).trim();
 511             String[] subOptions = p.split(";");
 512             for (String subOption : subOptions) {
 513                 subOption = "shouldstop." + subOption.trim();
 514                 XD.process(helper, subOption, subOption);
 515             }
 516             return false;
 517         }
 518     },
 519 
 520     DIAGS("-diags:", null, HIDDEN, BASIC, true) {
 521         @Override
 522         public boolean process(OptionHelper helper, String option) {
 523             return HiddenGroup.DIAGS.process(helper, option);
 524         }
 525     },
 526 
 527     /* This is a back door to the compiler's option table.
 528      * -XDx=y sets the option x to the value y.
 529      * -XDx sets the option x to the value x.
 530      */
 531     XD("-XD", null, HIDDEN, BASIC) {
 532         @Override
 533         public boolean matches(String s) {
 534             return s.startsWith(text);
 535         }
 536         @Override
 537         public boolean process(OptionHelper helper, String option) {
 538             return process(helper, option, option.substring(text.length()));
 539         }
 540 
 541         @Override
 542         public boolean process(OptionHelper helper, String option, String arg) {
 543             int eq = arg.indexOf('=');
 544             String key = (eq < 0) ? arg : arg.substring(0, eq);
 545             String value = (eq < 0) ? arg : arg.substring(eq+1);
 546             helper.put(key, value);
 547             return false;
 548         }
 549     },
 550 
 551     XADDEXPORTS("-XaddExports:", "opt.arg.addExports", "opt.addExports", EXTENDED, BASIC) {
 552         @Override
 553         public boolean process(OptionHelper helper, String option) {
 554             String p = option.substring(option.indexOf(':') + 1).trim();
 555             String prev = helper.get(XADDEXPORTS);
 556             helper.put(XADDEXPORTS.text, (prev == null) ? p : prev + '\0' + p);
 557             return false;
 558         }
 559     },
 560 
 561     XADDREADS("-XaddReads:", "opt.arg.addReads", "opt.addReads", EXTENDED, BASIC) {
 562         @Override
 563         public boolean process(OptionHelper helper, String option) {
 564             String p = option.substring(option.indexOf(':') + 1).trim();
 565             String prev = helper.get(XADDREADS);
 566             helper.put(XADDREADS.text, (prev == null) ? p : prev + '\0' + p);
 567             return false;
 568         }
 569     },
 570 
 571     XMODULE("-Xmodule:", "opt.arg.module", "opt.module", EXTENDED, BASIC) {
 572         @Override
 573         public boolean process(OptionHelper helper, String option) {
 574             String prev = helper.get(XMODULE);
 575             if (prev != null) {
 576                 helper.error("err.option.too.many", XMODULE.text);
 577             }
 578             String p = option.substring(option.indexOf(':') + 1);
 579             helper.put(XMODULE.text, p);
 580             return false;
 581         }
 582     },
 583 
 584     M("-m", "opt.arg.m", "opt.m", STANDARD, BASIC),
 585 
 586     ADDMODS("-addmods", "opt.arg.addmods", "opt.addmods", STANDARD, BASIC),
 587     LIMITMODS("-limitmods", "opt.arg.limitmods", "opt.limitmods", STANDARD, BASIC),
 588 
 589     // This option exists only for the purpose of documenting itself.
 590     // It's actually implemented by the CommandLine class.
 591     AT("@", "opt.arg.file", "opt.AT", STANDARD, INFO, true) {
 592         @Override
 593         public boolean process(OptionHelper helper, String option) {
 594             throw new AssertionError("the @ flag should be caught by CommandLine.");
 595         }
 596     },
 597 
 598     // Standalone positional argument: source file or type name.
 599     SOURCEFILE("sourcefile", null, HIDDEN, INFO) {
 600         @Override
 601         public boolean matches(String s) {
 602             if (s.endsWith(".java"))  // Java source file
 603                 return true;
 604             int sep = s.indexOf('/');
 605             if (sep != -1) {
 606                 return SourceVersion.isName(s.substring(0, sep))
 607                         && SourceVersion.isName(s.substring(sep + 1));
 608             } else {
 609                 return SourceVersion.isName(s);   // Legal type name
 610             }
 611         }
 612         @Override
 613         public boolean process(OptionHelper helper, String option) {
 614             if (option.endsWith(".java") ) {
 615                 Path p = Paths.get(option);
 616                 if (!Files.exists(p)) {
 617                     helper.error("err.file.not.found", p);
 618                     return true;
 619                 }
 620                 if (!Files.isRegularFile(p)) {
 621                     helper.error("err.file.not.file", p);
 622                     return true;
 623                 }
 624                 helper.addFile(p);
 625             } else {
 626                 helper.addClassName(option);
 627             }
 628             return false;
 629         }
 630     },
 631 
 632     MULTIRELEASE("-multi-release", "opt.arg.multi-release", "opt.multi-release", HIDDEN, FILEMANAGER);
 633 
 634     /** The kind of an Option. This is used by the -help and -X options. */
 635     public enum OptionKind {
 636         /** A standard option, documented by -help. */
 637         STANDARD,
 638         /** An extended option, documented by -X. */
 639         EXTENDED,
 640         /** A hidden option, not documented. */
 641         HIDDEN,
 642     }
 643 
 644     /** The group for an Option. This determines the situations in which the
 645      *  option is applicable. */
 646     enum OptionGroup {
 647         /** A basic option, available for use on the command line or via the
 648          *  Compiler API. */
 649         BASIC,
 650         /** An option for javac's standard JavaFileManager. Other file managers
 651          *  may or may not support these options. */
 652         FILEMANAGER,
 653         /** A command-line option that requests information, such as -help. */
 654         INFO,
 655         /** A command-line "option" representing a file or class name. */
 656         OPERAND
 657     }
 658 
 659     /** The kind of choice for "choice" options. */
 660     enum ChoiceKind {
 661         /** The expected value is exactly one of the set of choices. */
 662         ONEOF,
 663         /** The expected value is one of more of the set of choices. */
 664         ANYOF
 665     }
 666 
 667     enum HiddenGroup {
 668         DIAGS("diags");
 669 
 670         final String text;
 671 
 672         HiddenGroup(String text) {
 673             this.text = text;
 674         }
 675 
 676         public boolean process(OptionHelper helper, String option) {
 677             String p = option.substring(option.indexOf(':') + 1).trim();
 678             String[] subOptions = p.split(";");
 679             for (String subOption : subOptions) {
 680                 subOption = text + "." + subOption.trim();
 681                 XD.process(helper, subOption, subOption);
 682             }
 683             return false;
 684         }
 685     }
 686 
 687     public final String text;
 688 
 689     final OptionKind kind;
 690 
 691     final OptionGroup group;
 692 
 693     /** Documentation key for arguments.
 694      */
 695     final String argsNameKey;
 696 
 697     /** Documentation key for description.
 698      */
 699     final String descrKey;
 700 
 701     /** Suffix option (-foo=bar or -foo:bar)
 702      */
 703     final boolean hasSuffix;
 704 
 705     /** The kind of choices for this option, if any.
 706      */
 707     final ChoiceKind choiceKind;
 708 
 709     /** The choices for this option, if any, and whether or not the choices
 710      *  are hidden
 711      */
 712     final Map<String,Boolean> choices;
 713 
 714 
 715     Option(String text, String descrKey,
 716             OptionKind kind, OptionGroup group) {
 717         this(text, null, descrKey, kind, group, null, null, false);
 718     }
 719 
 720     Option(String text, String descrKey,
 721             OptionKind kind, OptionGroup group,
 722             boolean doHasSuffix) {
 723         this(text, null, descrKey, kind, group, null, null, doHasSuffix);
 724     }
 725 
 726     Option(String text, String argsNameKey, String descrKey,
 727             OptionKind kind, OptionGroup group) {
 728         this(text, argsNameKey, descrKey, kind, group, null, null, false);
 729     }
 730 
 731     Option(String text, String argsNameKey, String descrKey,
 732             OptionKind kind, OptionGroup group, boolean doHasSuffix) {
 733         this(text, argsNameKey, descrKey, kind, group, null, null, doHasSuffix);
 734     }
 735 
 736     Option(String text, OptionKind kind, OptionGroup group,
 737             ChoiceKind choiceKind, Map<String,Boolean> choices) {
 738         this(text, null, null, kind, group, choiceKind, choices, false);
 739     }
 740 
 741     Option(String text, String descrKey,
 742             OptionKind kind, OptionGroup group,
 743             ChoiceKind choiceKind, String... choices) {
 744         this(text, null, descrKey, kind, group, choiceKind,
 745                 createChoices(choices), false);
 746     }
 747     // where
 748         private static Map<String,Boolean> createChoices(String... choices) {
 749             Map<String,Boolean> map = new LinkedHashMap<>();
 750             for (String c: choices)
 751                 map.put(c, false);
 752             return map;
 753         }
 754 
 755     private Option(String text, String argsNameKey, String descrKey,
 756             OptionKind kind, OptionGroup group,
 757             ChoiceKind choiceKind, Map<String,Boolean> choices,
 758             boolean doHasSuffix) {
 759         this.text = text;
 760         this.argsNameKey = argsNameKey;
 761         this.descrKey = descrKey;
 762         this.kind = kind;
 763         this.group = group;
 764         this.choiceKind = choiceKind;
 765         this.choices = choices;
 766         char lastChar = text.charAt(text.length()-1);
 767         this.hasSuffix = doHasSuffix || lastChar == ':' || lastChar == '=';
 768     }
 769 
 770     public String getText() {
 771         return text;
 772     }
 773 
 774     public OptionKind getKind() {
 775         return kind;
 776     }
 777 
 778     public boolean hasArg() {
 779         return argsNameKey != null && !hasSuffix;
 780     }
 781 
 782     public boolean matches(String option) {
 783         if (!hasSuffix)
 784             return option.equals(text);
 785 
 786         if (!option.startsWith(text))
 787             return false;
 788 
 789         if (choices != null) {
 790             String arg = option.substring(text.length());
 791             if (choiceKind == ChoiceKind.ONEOF)
 792                 return choices.keySet().contains(arg);
 793             else {
 794                 for (String a: arg.split(",+")) {
 795                     if (!choices.keySet().contains(a))
 796                         return false;
 797                 }
 798             }
 799         }
 800 
 801         return true;
 802     }
 803 
 804     public boolean process(OptionHelper helper, String option, String arg) {
 805         if (choices != null) {
 806             if (choiceKind == ChoiceKind.ONEOF) {
 807                 // some clients like to see just one of option+choice set
 808                 for (String s: choices.keySet())
 809                     helper.remove(option + s);
 810                 String opt = option + arg;
 811                 helper.put(opt, opt);
 812                 // some clients like to see option (without trailing ":")
 813                 // set to arg
 814                 String nm = option.substring(0, option.length() - 1);
 815                 helper.put(nm, arg);
 816             } else {
 817                 // set option+word for each word in arg
 818                 for (String a: arg.split(",+")) {
 819                     String opt = option + a;
 820                     helper.put(opt, opt);
 821                 }
 822             }
 823         }
 824         helper.put(option, arg);
 825         if (group == OptionGroup.FILEMANAGER)
 826             helper.handleFileManagerOption(this, arg);
 827         return false;
 828     }
 829 
 830     public boolean process(OptionHelper helper, String option) {
 831         if (hasSuffix)
 832             return process(helper, text, option.substring(text.length()));
 833         else
 834             return process(helper, option, option);
 835     }
 836 
 837     private static final String HELP_LINE_FORMAT = "  %-26s %s";
 838 
 839     void help(Log log, OptionKind kind) {
 840         if (this.kind != kind)
 841             return;
 842 
 843         log.printRawLines(WriterKind.STDOUT,
 844                 String.format(HELP_LINE_FORMAT,
 845                     helpSynopsis(log),
 846                     log.localize(PrefixKind.JAVAC, descrKey)));
 847 
 848     }
 849 
 850     private String helpSynopsis(Log log) {
 851         StringBuilder sb = new StringBuilder();
 852         sb.append(text);
 853         if (argsNameKey == null) {
 854             if (choices != null) {
 855                 String sep = "{";
 856                 for (Map.Entry<String,Boolean> e: choices.entrySet()) {
 857                     if (!e.getValue()) {
 858                         sb.append(sep);
 859                         sb.append(e.getKey());
 860                         sep = ",";
 861                     }
 862                 }
 863                 sb.append("}");
 864             }
 865         } else {
 866             if (!hasSuffix)
 867                 sb.append(" ");
 868             sb.append(log.localize(PrefixKind.JAVAC, argsNameKey));
 869 
 870         }
 871 
 872         return sb.toString();
 873     }
 874 
 875     // For -XpkgInfo:value
 876     public enum PkgInfo {
 877         /**
 878          * Always generate package-info.class for every package-info.java file.
 879          * The file may be empty if there annotations with a RetentionPolicy
 880          * of CLASS or RUNTIME.  This option may be useful in conjunction with
 881          * build systems (such as Ant) that expect javac to generate at least
 882          * one .class file for every .java file.
 883          */
 884         ALWAYS,
 885         /**
 886          * Generate a package-info.class file if package-info.java contains
 887          * annotations. The file may be empty if all the annotations have
 888          * a RetentionPolicy of SOURCE.
 889          * This value is just for backwards compatibility with earlier behavior.
 890          * Either of the other two values are to be preferred to using this one.
 891          */
 892         LEGACY,
 893         /**
 894          * Generate a package-info.class file if and only if there are annotations
 895          * in package-info.java to be written into it.
 896          */
 897         NONEMPTY;
 898 
 899         public static PkgInfo get(Options options) {
 900             String v = options.get(XPKGINFO);
 901             return (v == null
 902                     ? PkgInfo.LEGACY
 903                     : PkgInfo.valueOf(StringUtils.toUpperCase(v)));
 904         }
 905     }
 906 
 907     private static Map<String,Boolean> getXLintChoices() {
 908         Map<String,Boolean> choices = new LinkedHashMap<>();
 909         choices.put("all", false);
 910         for (Lint.LintCategory c : Lint.LintCategory.values())
 911             choices.put(c.option, c.hidden);
 912         for (Lint.LintCategory c : Lint.LintCategory.values())
 913             choices.put("-" + c.option, c.hidden);
 914         choices.put("none", false);
 915         return choices;
 916     }
 917 
 918     static Set<Option> getJavaCompilerOptions() {
 919         return EnumSet.allOf(Option.class);
 920     }
 921 
 922     public static Set<Option> getJavacFileManagerOptions() {
 923         return getOptions(EnumSet.of(FILEMANAGER));
 924     }
 925 
 926     public static Set<Option> getJavacToolOptions() {
 927         return getOptions(EnumSet.of(BASIC));
 928     }
 929 
 930     static Set<Option> getOptions(Set<OptionGroup> desired) {
 931         Set<Option> options = EnumSet.noneOf(Option.class);
 932         for (Option option : Option.values())
 933             if (desired.contains(option.group))
 934                 options.add(option);
 935         return Collections.unmodifiableSet(options);
 936     }
 937 
 938 }