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