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