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