langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java
changeset 40308 274367a99f98
parent 40232 4995ab1a4558
child 40762 f8883aa0053c
equal deleted inserted replaced
40306:1a0fcaf3f2ed 40308:274367a99f98
    28 import java.io.FileWriter;
    28 import java.io.FileWriter;
    29 import java.io.PrintWriter;
    29 import java.io.PrintWriter;
    30 import java.nio.file.Files;
    30 import java.nio.file.Files;
    31 import java.nio.file.Path;
    31 import java.nio.file.Path;
    32 import java.nio.file.Paths;
    32 import java.nio.file.Paths;
       
    33 import java.text.Collator;
       
    34 import java.util.Arrays;
    33 import java.util.Collections;
    35 import java.util.Collections;
       
    36 import java.util.Comparator;
    34 import java.util.EnumSet;
    37 import java.util.EnumSet;
       
    38 import java.util.Iterator;
    35 import java.util.LinkedHashMap;
    39 import java.util.LinkedHashMap;
       
    40 import java.util.Locale;
    36 import java.util.Map;
    41 import java.util.Map;
    37 import java.util.ServiceLoader;
    42 import java.util.ServiceLoader;
    38 import java.util.Set;
    43 import java.util.Set;
    39 import java.util.TreeSet;
    44 import java.util.TreeSet;
    40 import java.util.stream.Collectors;
    45 import java.util.stream.Collectors;
    49 import com.sun.tools.javac.code.Type;
    54 import com.sun.tools.javac.code.Type;
    50 import com.sun.tools.javac.jvm.Profile;
    55 import com.sun.tools.javac.jvm.Profile;
    51 import com.sun.tools.javac.jvm.Target;
    56 import com.sun.tools.javac.jvm.Target;
    52 import com.sun.tools.javac.platform.PlatformProvider;
    57 import com.sun.tools.javac.platform.PlatformProvider;
    53 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
    58 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
       
    59 import com.sun.tools.javac.resources.CompilerProperties.Errors;
       
    60 import com.sun.tools.javac.util.Assert;
       
    61 import com.sun.tools.javac.util.JDK9Wrappers;
    54 import com.sun.tools.javac.util.Log;
    62 import com.sun.tools.javac.util.Log;
    55 import com.sun.tools.javac.util.Log.PrefixKind;
    63 import com.sun.tools.javac.util.Log.PrefixKind;
    56 import com.sun.tools.javac.util.Log.WriterKind;
    64 import com.sun.tools.javac.util.Log.WriterKind;
    57 import com.sun.tools.javac.util.Options;
    65 import com.sun.tools.javac.util.Options;
    58 import com.sun.tools.javac.util.StringUtils;
    66 import com.sun.tools.javac.util.StringUtils;
    60 import static com.sun.tools.javac.main.Option.ChoiceKind.*;
    68 import static com.sun.tools.javac.main.Option.ChoiceKind.*;
    61 import static com.sun.tools.javac.main.Option.OptionGroup.*;
    69 import static com.sun.tools.javac.main.Option.OptionGroup.*;
    62 import static com.sun.tools.javac.main.Option.OptionKind.*;
    70 import static com.sun.tools.javac.main.Option.OptionKind.*;
    63 
    71 
    64 /**
    72 /**
    65  * Options for javac. The specific Option to handle a command-line option
    73  * Options for javac.
    66  * is identified by searching the members of this enum in order, looking for
    74  * The specific Option to handle a command-line option can be found by calling
    67  * the first {@link #matches match}. The action for an Option is performed
    75  * {@link #lookup}, which search some or all of the members of this enum in order,
    68  * by calling {@link #process process}, and by providing a suitable
    76  * looking for the first {@link #matches match}.
       
    77  * The action for an Option is performed {@link #handleOption}, which determines
       
    78  * whether an argument is needed and where to find it;
       
    79  * {@code handleOption} then calls {@link #process process} providing a suitable
    69  * {@link OptionHelper} to provide access the compiler state.
    80  * {@link OptionHelper} to provide access the compiler state.
    70  *
    81  *
    71  * <p><b>This is NOT part of any supported API.
    82  * <p><b>This is NOT part of any supported API.
    72  * If you write code that depends on this, you do so at your own
    83  * If you write code that depends on this, you do so at your own
    73  * risk.  This code and its internal interfaces are subject to change
    84  * risk.  This code and its internal interfaces are subject to change
    87     G_CUSTOM("-g:",  "opt.g.lines.vars.source",
    98     G_CUSTOM("-g:",  "opt.g.lines.vars.source",
    88             STANDARD, BASIC, ANYOF, "lines", "vars", "source"),
    99             STANDARD, BASIC, ANYOF, "lines", "vars", "source"),
    89 
   100 
    90     XLINT("-Xlint", "opt.Xlint", EXTENDED, BASIC),
   101     XLINT("-Xlint", "opt.Xlint", EXTENDED, BASIC),
    91 
   102 
    92     XLINT_CUSTOM("-Xlint:", EXTENDED, BASIC, ANYOF, getXLintChoices()) {
   103     XLINT_CUSTOM("-Xlint:", "opt.arg.Xlint", "opt.Xlint.custom", EXTENDED, BASIC, ANYOF, getXLintChoices()) {
    93         private static final String LINT_KEY_FORMAT = "         %-19s %s";
   104         private final String LINT_KEY_FORMAT = LARGE_INDENT + "  %-" +
    94         @Override
   105                 (DEFAULT_SYNOPSIS_WIDTH + SMALL_INDENT.length() - LARGE_INDENT.length() - 2) + "s %s";
    95         void help(Log log, OptionKind kind) {
   106         @Override
    96             if (this.kind != kind)
   107         protected void help(Log log) {
    97                 return;
   108             super.help(log);
    98 
       
    99             log.printRawLines(WriterKind.STDOUT,
       
   100                               String.format(HELP_LINE_FORMAT,
       
   101                                             log.localize(PrefixKind.JAVAC, "opt.Xlint.subopts"),
       
   102                                             log.localize(PrefixKind.JAVAC, "opt.Xlint.suboptlist")));
       
   103             log.printRawLines(WriterKind.STDOUT,
   109             log.printRawLines(WriterKind.STDOUT,
   104                               String.format(LINT_KEY_FORMAT,
   110                               String.format(LINT_KEY_FORMAT,
   105                                             "all",
   111                                             "all",
   106                                             log.localize(PrefixKind.JAVAC, "opt.Xlint.all")));
   112                                             log.localize(PrefixKind.JAVAC, "opt.Xlint.all")));
   107             for (LintCategory lc : LintCategory.values()) {
   113             for (LintCategory lc : LintCategory.values()) {
   123 
   129 
   124     XDOCLINT_CUSTOM("-Xdoclint:", "opt.Xdoclint.subopts", "opt.Xdoclint.custom", EXTENDED, BASIC) {
   130     XDOCLINT_CUSTOM("-Xdoclint:", "opt.Xdoclint.subopts", "opt.Xdoclint.custom", EXTENDED, BASIC) {
   125         @Override
   131         @Override
   126         public boolean matches(String option) {
   132         public boolean matches(String option) {
   127             return DocLint.isValidOption(
   133             return DocLint.isValidOption(
   128                     option.replace(XDOCLINT_CUSTOM.text, DocLint.XMSGS_CUSTOM_PREFIX));
   134                     option.replace(XDOCLINT_CUSTOM.primaryName, DocLint.XMSGS_CUSTOM_PREFIX));
   129         }
   135         }
   130 
   136 
   131         @Override
   137         @Override
   132         public boolean process(OptionHelper helper, String option) {
   138         public boolean process(OptionHelper helper, String option) {
   133             String prev = helper.get(XDOCLINT_CUSTOM);
   139             String prev = helper.get(XDOCLINT_CUSTOM);
   134             String next = (prev == null) ? option : (prev + " " + option);
   140             String next = (prev == null) ? option : (prev + " " + option);
   135             helper.put(XDOCLINT_CUSTOM.text, next);
   141             helper.put(XDOCLINT_CUSTOM.primaryName, next);
   136             return false;
   142             return false;
   137         }
   143         }
   138     },
   144     },
   139 
   145 
   140     XDOCLINT_PACKAGE("-Xdoclint/package:", "opt.Xdoclint.package.args", "opt.Xdoclint.package.desc", EXTENDED, BASIC) {
   146     XDOCLINT_PACKAGE("-Xdoclint/package:", "opt.Xdoclint.package.args", "opt.Xdoclint.package.desc", EXTENDED, BASIC) {
   141         @Override
   147         @Override
   142         public boolean matches(String option) {
   148         public boolean matches(String option) {
   143             return DocLint.isValidOption(
   149             return DocLint.isValidOption(
   144                     option.replace(XDOCLINT_PACKAGE.text, DocLint.XCHECK_PACKAGE));
   150                     option.replace(XDOCLINT_PACKAGE.primaryName, DocLint.XCHECK_PACKAGE));
   145         }
   151         }
   146 
   152 
   147         @Override
   153         @Override
   148         public boolean process(OptionHelper helper, String option) {
   154         public boolean process(OptionHelper helper, String option) {
   149             String prev = helper.get(XDOCLINT_PACKAGE);
   155             String prev = helper.get(XDOCLINT_PACKAGE);
   150             String next = (prev == null) ? option : (prev + " " + option);
   156             String next = (prev == null) ? option : (prev + " " + option);
   151             helper.put(XDOCLINT_PACKAGE.text, next);
   157             helper.put(XDOCLINT_PACKAGE.primaryName, next);
   152             return false;
   158             return false;
   153         }
   159         }
   154     },
   160     },
   155 
   161 
   156     // -nowarn is retained for command-line backward compatibility
   162     // -nowarn is retained for command-line backward compatibility
   171             helper.put("-Xlint:deprecation", option);
   177             helper.put("-Xlint:deprecation", option);
   172             return false;
   178             return false;
   173         }
   179         }
   174     },
   180     },
   175 
   181 
   176     CLASSPATH("-classpath", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER),
   182     CLASS_PATH("--class-path -classpath -cp", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER),
   177 
   183 
   178     CP("-cp", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER) {
   184     SOURCE_PATH("--source-path -sourcepath", "opt.arg.path", "opt.sourcepath", STANDARD, FILEMANAGER),
       
   185 
       
   186     MODULE_SOURCE_PATH("--module-source-path -modulesourcepath", "opt.arg.mspath", "opt.modulesourcepath", STANDARD, FILEMANAGER),
       
   187 
       
   188     MODULE_PATH("--module-path -p -modulepath -mp", "opt.arg.path", "opt.modulepath", STANDARD, FILEMANAGER),
       
   189 
       
   190     UPGRADE_MODULE_PATH("--upgrade-module-path -upgrademodulepath", "opt.arg.path", "opt.upgrademodulepath", STANDARD, FILEMANAGER),
       
   191 
       
   192     SYSTEM("--system -system", "opt.arg.jdk", "opt.system", STANDARD, FILEMANAGER),
       
   193 
       
   194     PATCH_MODULE("--patch-module -Xpatch:", "opt.arg.patch", "opt.patch", EXTENDED, FILEMANAGER) {
       
   195         // The deferred filemanager diagnostics mechanism assumes a single value per option,
       
   196         // but --patch-module can be used multiple times, once per module. Therefore we compose
       
   197         // a value for the option containing the last value specified for each module, and separate
       
   198         // the the module=path pairs by an invalid path character, NULL.
       
   199         // The standard file manager code knows to split apart the NULL-separated components.
   179         @Override
   200         @Override
   180         public boolean process(OptionHelper helper, String option, String arg) {
   201         public boolean process(OptionHelper helper, String option, String arg) {
   181             return super.process(helper, "-classpath", arg);
   202             if (!arg.contains("=")) { // could be more strict regeex, e.g. "(?i)[a-z0-9_.]+=.*"
   182         }
   203                 helper.error(Errors.LocnInvalidArgForXpatch(arg));
   183     },
   204             }
   184 
   205 
   185     SOURCEPATH("-sourcepath", "opt.arg.path", "opt.sourcepath", STANDARD, FILEMANAGER),
   206             String previous = helper.get(this);
   186 
   207             if (previous == null) {
   187     MODULESOURCEPATH("-modulesourcepath", "opt.arg.mspath", "opt.modulesourcepath", STANDARD, FILEMANAGER),
   208                 return super.process(helper, option, arg);
   188 
   209             }
   189     MODULEPATH("-modulepath", "opt.arg.path", "opt.modulepath", STANDARD, FILEMANAGER),
   210 
   190 
   211             Map<String,String> map = new LinkedHashMap<>();
   191     MP("-mp", "opt.arg.path", "opt.modulepath", STANDARD, FILEMANAGER) {
   212             for (String s : previous.split("\0")) {
   192         @Override
   213                 int sep = s.indexOf('=');
   193         public boolean process(OptionHelper helper, String option, String arg) {
   214                 map.put(s.substring(0, sep), s.substring(sep + 1));
   194             return super.process(helper, "-modulepath", arg);
   215             }
   195         }
   216 
   196     },
   217             int sep = arg.indexOf('=');
   197 
   218             map.put(arg.substring(0, sep), arg.substring(sep + 1));
   198     UPGRADEMODULEPATH("-upgrademodulepath", "opt.arg.path", "opt.upgrademodulepath", STANDARD, FILEMANAGER),
   219 
   199 
   220             StringBuilder sb = new StringBuilder();
   200     SYSTEM("-system", "opt.arg.jdk", "opt.system", STANDARD, FILEMANAGER),
   221             map.forEach((m, p) -> {
   201 
   222                 if (sb.length() > 0)
   202     XPATCH("-Xpatch:", "opt.arg.patch", "opt.patch", EXTENDED, FILEMANAGER),
   223                     sb.append('\0');
   203 
   224                 sb.append(m).append('=').append(p);
   204     BOOTCLASSPATH("-bootclasspath", "opt.arg.path", "opt.bootclasspath", STANDARD, FILEMANAGER) {
   225             });
       
   226             return super.process(helper, option, sb.toString());
       
   227         }
       
   228     },
       
   229 
       
   230     BOOT_CLASS_PATH("--boot-class-path -bootclasspath", "opt.arg.path", "opt.bootclasspath", STANDARD, FILEMANAGER) {
   205         @Override
   231         @Override
   206         public boolean process(OptionHelper helper, String option, String arg) {
   232         public boolean process(OptionHelper helper, String option, String arg) {
   207             helper.remove("-Xbootclasspath/p:");
   233             helper.remove("-Xbootclasspath/p:");
   208             helper.remove("-Xbootclasspath/a:");
   234             helper.remove("-Xbootclasspath/a:");
   209             return super.process(helper, option, arg);
   235             return super.process(helper, option, arg);
   226     EXTDIRS("-extdirs", "opt.arg.dirs", "opt.extdirs", STANDARD, FILEMANAGER),
   252     EXTDIRS("-extdirs", "opt.arg.dirs", "opt.extdirs", STANDARD, FILEMANAGER),
   227 
   253 
   228     DJAVA_EXT_DIRS("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs", EXTENDED, FILEMANAGER) {
   254     DJAVA_EXT_DIRS("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs", EXTENDED, FILEMANAGER) {
   229         @Override
   255         @Override
   230         public boolean process(OptionHelper helper, String option, String arg) {
   256         public boolean process(OptionHelper helper, String option, String arg) {
   231             return super.process(helper, "-extdirs", arg);
   257             return EXTDIRS.process(helper, "-extdirs", arg);
   232         }
   258         }
   233     },
   259     },
   234 
   260 
   235     ENDORSEDDIRS("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs", STANDARD, FILEMANAGER),
   261     ENDORSEDDIRS("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs", STANDARD, FILEMANAGER),
   236 
   262 
   237     DJAVA_ENDORSED_DIRS("-Djava.endorsed.dirs=", "opt.arg.dirs", "opt.endorseddirs", EXTENDED, FILEMANAGER) {
   263     DJAVA_ENDORSED_DIRS("-Djava.endorsed.dirs=", "opt.arg.dirs", "opt.endorseddirs", EXTENDED, FILEMANAGER) {
   238         @Override
   264         @Override
   239         public boolean process(OptionHelper helper, String option, String arg) {
   265         public boolean process(OptionHelper helper, String option, String arg) {
   240             return super.process(helper, "-endorseddirs", arg);
   266             return ENDORSEDDIRS.process(helper, "-endorseddirs", arg);
   241         }
   267         }
   242     },
   268     },
   243 
   269 
   244     PROC("-proc:", "opt.proc.none.only", STANDARD, BASIC,  ONEOF, "none", "only"),
   270     PROC("-proc:", "opt.proc.none.only", STANDARD, BASIC,  ONEOF, "none", "only"),
   245 
   271 
   246     PROCESSOR("-processor", "opt.arg.class.list", "opt.processor", STANDARD, BASIC),
   272     PROCESSOR("-processor", "opt.arg.class.list", "opt.processor", STANDARD, BASIC),
   247 
   273 
   248     PROCESSORPATH("-processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER),
   274     PROCESSOR_PATH("--processor-path -processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER),
   249 
   275 
   250     PROCESSORMODULEPATH("-processormodulepath", "opt.arg.path", "opt.processormodulepath", STANDARD, FILEMANAGER),
   276     PROCESSOR_MODULE_PATH("--processor-module-path -processormodulepath", "opt.arg.path", "opt.processormodulepath", STANDARD, FILEMANAGER),
   251 
   277 
   252     PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC),
   278     PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC),
   253 
   279 
   254     D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
   280     D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
   255 
   281 
   283             }
   309             }
   284             return super.process(helper, option, operand);
   310             return super.process(helper, option, operand);
   285         }
   311         }
   286     },
   312     },
   287 
   313 
   288     RELEASE("-release", "opt.arg.release", "opt.release", STANDARD, BASIC) {
   314     RELEASE("--release -release", "opt.arg.release", "opt.release", STANDARD, BASIC) {
   289         @Override
   315         @Override
   290         void help(Log log, OptionKind kind) {
   316         protected void help(Log log) {
   291             if (this.kind != kind)
       
   292                 return;
       
   293 
       
   294             Iterable<PlatformProvider> providers =
   317             Iterable<PlatformProvider> providers =
   295                     ServiceLoader.load(PlatformProvider.class, Arguments.class.getClassLoader());
   318                     ServiceLoader.load(PlatformProvider.class, Arguments.class.getClassLoader());
   296             Set<String> platforms = StreamSupport.stream(providers.spliterator(), false)
   319             Set<String> platforms = StreamSupport.stream(providers.spliterator(), false)
   297                                                  .flatMap(provider -> StreamSupport.stream(provider.getSupportedPlatformNames()
   320                                                  .flatMap(provider -> StreamSupport.stream(provider.getSupportedPlatformNames()
   298                                                                                                    .spliterator(),
   321                                                                                                    .spliterator(),
   305                 targets.append(delim);
   328                 targets.append(delim);
   306                 targets.append(platform);
   329                 targets.append(platform);
   307                 delim = ", ";
   330                 delim = ", ";
   308             }
   331             }
   309 
   332 
   310             log.printRawLines(WriterKind.STDOUT,
   333             super.help(log, log.localize(PrefixKind.JAVAC, descrKey, targets.toString()));
   311                     String.format(HELP_LINE_FORMAT,
       
   312                         super.helpSynopsis(log),
       
   313                         log.localize(PrefixKind.JAVAC, descrKey, targets.toString())));
       
   314         }
   334         }
   315     },
   335     },
   316 
   336 
   317     PROFILE("-profile", "opt.arg.profile", "opt.profile", STANDARD, BASIC) {
   337     PROFILE("-profile", "opt.arg.profile", "opt.profile", STANDARD, BASIC) {
   318         @Override
   338         @Override
   344             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "fullVersion", ownName,  JavaCompiler.fullVersion());
   364             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "fullVersion", ownName,  JavaCompiler.fullVersion());
   345             return super.process(helper, option);
   365             return super.process(helper, option);
   346         }
   366         }
   347     },
   367     },
   348 
   368 
   349     HELP("-help", "opt.help", STANDARD, INFO) {
   369     // Note: -h is already taken for "native header output directory".
       
   370     HELP("--help -help", "opt.help", STANDARD, INFO) {
   350         @Override
   371         @Override
   351         public boolean process(OptionHelper helper, String option) {
   372         public boolean process(OptionHelper helper, String option) {
   352             Log log = helper.getLog();
   373             Log log = helper.getLog();
   353             String ownName = helper.getOwnName();
   374             String ownName = helper.getOwnName();
   354             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.header", ownName);
   375             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.header", ownName);
   355             for (Option o: getJavaCompilerOptions()) {
   376             showHelp(log, OptionKind.STANDARD);
   356                 o.help(log, OptionKind.STANDARD);
       
   357             }
       
   358             log.printNewline(WriterKind.STDOUT);
   377             log.printNewline(WriterKind.STDOUT);
   359             return super.process(helper, option);
   378             return super.process(helper, option);
   360         }
   379         }
   361     },
   380     },
   362 
   381 
   363     A("-A", "opt.arg.key.equals.value", "opt.A", STANDARD, BASIC, true) {
   382     A("-A", "opt.arg.key.equals.value", "opt.A", STANDARD, BASIC, ArgKind.ADJACENT) {
   364         @Override
   383         @Override
   365         public boolean matches(String arg) {
   384         public boolean matches(String arg) {
   366             return arg.startsWith("-A");
   385             return arg.startsWith("-A");
   367         }
   386         }
   368 
   387 
   383             String key = option.substring(2, (sepIndex != -1 ? sepIndex : argLength) );
   402             String key = option.substring(2, (sepIndex != -1 ? sepIndex : argLength) );
   384             if (!JavacProcessingEnvironment.isValidOptionName(key)) {
   403             if (!JavacProcessingEnvironment.isValidOptionName(key)) {
   385                 helper.error("err.invalid.A.key", option);
   404                 helper.error("err.invalid.A.key", option);
   386                 return true;
   405                 return true;
   387             }
   406             }
   388             return process(helper, option, option);
   407             helper.put(option, option);
       
   408             return false;
   389         }
   409         }
   390     },
   410     },
   391 
   411 
   392     X("-X", "opt.X", STANDARD, INFO) {
   412     X("-X", "opt.X", STANDARD, INFO) {
   393         @Override
   413         @Override
   394         public boolean process(OptionHelper helper, String option) {
   414         public boolean process(OptionHelper helper, String option) {
   395             Log log = helper.getLog();
   415             Log log = helper.getLog();
   396             for (Option o: getJavaCompilerOptions()) {
   416             showHelp(log, OptionKind.EXTENDED);
   397                 o.help(log, OptionKind.EXTENDED);
       
   398             }
       
   399             log.printNewline(WriterKind.STDOUT);
   417             log.printNewline(WriterKind.STDOUT);
   400             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.nonstandard.footer");
   418             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.nonstandard.footer");
   401             return super.process(helper, option);
   419             return super.process(helper, option);
   402         }
   420         }
   403     },
   421     },
   404 
   422 
   405     // This option exists only for the purpose of documenting itself.
   423     // This option exists only for the purpose of documenting itself.
   406     // It's actually implemented by the launcher.
   424     // It's actually implemented by the launcher.
   407     J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, true) {
   425     J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, ArgKind.ADJACENT) {
   408         @Override
   426         @Override
   409         public boolean process(OptionHelper helper, String option) {
   427         public boolean process(OptionHelper helper, String option) {
   410             throw new AssertionError
   428             throw new AssertionError
   411                 ("the -J flag should be caught by the launcher.");
   429                 ("the -J flag should be caught by the launcher.");
   412         }
   430         }
   482     PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
   500     PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
   483         @Override
   501         @Override
   484         public boolean process(OptionHelper helper, String option) {
   502         public boolean process(OptionHelper helper, String option) {
   485             String p = option.substring(option.indexOf(':') + 1).trim();
   503             String p = option.substring(option.indexOf(':') + 1).trim();
   486             String prev = helper.get(PLUGIN);
   504             String prev = helper.get(PLUGIN);
   487             helper.put(PLUGIN.text, (prev == null) ? p : prev + '\0' + p);
   505             helper.put(PLUGIN.primaryName, (prev == null) ? p : prev + '\0' + p);
   488             return false;
   506             return false;
   489         }
   507         }
   490     },
   508     },
   491 
   509 
   492     XDIAGS("-Xdiags:", "opt.diags", EXTENDED, BASIC, ONEOF, "compact", "verbose"),
   510     XDIAGS("-Xdiags:", "opt.diags", EXTENDED, BASIC, ONEOF, "compact", "verbose"),
   515             }
   533             }
   516             return false;
   534             return false;
   517         }
   535         }
   518     },
   536     },
   519 
   537 
   520     DIAGS("-diags:", null, HIDDEN, BASIC, true) {
   538     DIAGS("-diags:", null, HIDDEN, BASIC) {
   521         @Override
   539         @Override
   522         public boolean process(OptionHelper helper, String option) {
   540         public boolean process(OptionHelper helper, String option) {
   523             return HiddenGroup.DIAGS.process(helper, option);
   541             return HiddenGroup.DIAGS.process(helper, option);
   524         }
   542         }
   525     },
   543     },
   529      * -XDx sets the option x to the value x.
   547      * -XDx sets the option x to the value x.
   530      */
   548      */
   531     XD("-XD", null, HIDDEN, BASIC) {
   549     XD("-XD", null, HIDDEN, BASIC) {
   532         @Override
   550         @Override
   533         public boolean matches(String s) {
   551         public boolean matches(String s) {
   534             return s.startsWith(text);
   552             return s.startsWith(primaryName);
   535         }
   553         }
   536         @Override
   554         @Override
   537         public boolean process(OptionHelper helper, String option) {
   555         public boolean process(OptionHelper helper, String option) {
   538             return process(helper, option, option.substring(text.length()));
   556             return process(helper, option, option.substring(primaryName.length()));
   539         }
   557         }
   540 
   558 
   541         @Override
   559         @Override
   542         public boolean process(OptionHelper helper, String option, String arg) {
   560         public boolean process(OptionHelper helper, String option, String arg) {
   543             int eq = arg.indexOf('=');
   561             int eq = arg.indexOf('=');
   546             helper.put(key, value);
   564             helper.put(key, value);
   547             return false;
   565             return false;
   548         }
   566         }
   549     },
   567     },
   550 
   568 
   551     XADDEXPORTS("-XaddExports:", "opt.arg.addExports", "opt.addExports", EXTENDED, BASIC) {
   569     ADD_EXPORTS("--add-exports -XaddExports:", "opt.arg.addExports", "opt.addExports", EXTENDED, BASIC) {
   552         @Override
   570         @Override
   553         public boolean process(OptionHelper helper, String option) {
   571         public boolean process(OptionHelper helper, String option, String arg) {
   554             String p = option.substring(option.indexOf(':') + 1).trim();
   572             String prev = helper.get(ADD_EXPORTS);
   555             String prev = helper.get(XADDEXPORTS);
   573             helper.put(ADD_EXPORTS.primaryName, (prev == null) ? arg : prev + '\0' + arg);
   556             helper.put(XADDEXPORTS.text, (prev == null) ? p : prev + '\0' + p);
   574             return false;
   557             return false;
   575         }
   558         }
   576     },
   559     },
   577 
   560 
   578     ADD_READS("--add-reads -XaddReads:", "opt.arg.addReads", "opt.addReads", EXTENDED, BASIC) {
   561     XADDREADS("-XaddReads:", "opt.arg.addReads", "opt.addReads", EXTENDED, BASIC) {
   579         @Override
   562         @Override
   580         public boolean process(OptionHelper helper, String option, String arg) {
   563         public boolean process(OptionHelper helper, String option) {
   581             String prev = helper.get(ADD_READS);
   564             String p = option.substring(option.indexOf(':') + 1).trim();
   582             helper.put(ADD_READS.primaryName, (prev == null) ? arg : prev + '\0' + arg);
   565             String prev = helper.get(XADDREADS);
       
   566             helper.put(XADDREADS.text, (prev == null) ? p : prev + '\0' + p);
       
   567             return false;
   583             return false;
   568         }
   584         }
   569     },
   585     },
   570 
   586 
   571     XMODULE("-Xmodule:", "opt.arg.module", "opt.module", EXTENDED, BASIC) {
   587     XMODULE("-Xmodule:", "opt.arg.module", "opt.module", EXTENDED, BASIC) {
   572         @Override
   588         @Override
   573         public boolean process(OptionHelper helper, String option) {
   589         public boolean process(OptionHelper helper, String option, String arg) {
   574             String prev = helper.get(XMODULE);
   590             String prev = helper.get(XMODULE);
   575             if (prev != null) {
   591             if (prev != null) {
   576                 helper.error("err.option.too.many", XMODULE.text);
   592                 helper.error("err.option.too.many", XMODULE.primaryName);
   577             }
   593             }
   578             String p = option.substring(option.indexOf(':') + 1);
   594             helper.put(XMODULE.primaryName, arg);
   579             helper.put(XMODULE.text, p);
   595             return false;
   580             return false;
   596         }
   581         }
   597     },
   582     },
   598 
   583 
   599     MODULE("--module -m", "opt.arg.m", "opt.m", STANDARD, BASIC),
   584     M("-m", "opt.arg.m", "opt.m", STANDARD, BASIC),
   600 
   585 
   601     ADD_MODULES("--add-modules -addmods", "opt.arg.addmods", "opt.addmods", STANDARD, BASIC),
   586     ADDMODS("-addmods", "opt.arg.addmods", "opt.addmods", STANDARD, BASIC),
   602 
   587     LIMITMODS("-limitmods", "opt.arg.limitmods", "opt.limitmods", STANDARD, BASIC),
   603     LIMIT_MODULES("--limit-modules -limitmods", "opt.arg.limitmods", "opt.limitmods", STANDARD, BASIC),
   588 
   604 
   589     // This option exists only for the purpose of documenting itself.
   605     // This option exists only for the purpose of documenting itself.
   590     // It's actually implemented by the CommandLine class.
   606     // It's actually implemented by the CommandLine class.
   591     AT("@", "opt.arg.file", "opt.AT", STANDARD, INFO, true) {
   607     AT("@", "opt.arg.file", "opt.AT", STANDARD, INFO, ArgKind.ADJACENT) {
   592         @Override
   608         @Override
   593         public boolean process(OptionHelper helper, String option) {
   609         public boolean process(OptionHelper helper, String option) {
   594             throw new AssertionError("the @ flag should be caught by CommandLine.");
   610             throw new AssertionError("the @ flag should be caught by CommandLine.");
   595         }
   611         }
   596     },
   612     },
   627             }
   643             }
   628             return false;
   644             return false;
   629         }
   645         }
   630     },
   646     },
   631 
   647 
   632     MULTIRELEASE("-multi-release", "opt.arg.multi-release", "opt.multi-release", HIDDEN, FILEMANAGER);
   648     MULTIRELEASE("--multi-release -multi-release", "opt.arg.multi-release", "opt.multi-release", HIDDEN, FILEMANAGER),
   633 
   649 
   634     /** The kind of an Option. This is used by the -help and -X options. */
   650     INHERIT_RUNTIME_ENVIRONMENT("--inherit-runtime-environment", "opt.inherit_runtime_environment",
       
   651             EXTENDED, BASIC) {
       
   652         @Override
       
   653         public boolean process(OptionHelper helper, String option) {
       
   654             try {
       
   655                 Class.forName(JDK9Wrappers.VMHelper.VM_CLASSNAME);
       
   656                 String[] runtimeArgs = JDK9Wrappers.VMHelper.getRuntimeArguments();
       
   657                 for (String arg : runtimeArgs) {
       
   658                     // Handle any supported runtime options; ignore all others.
       
   659                     // The runtime arguments always use the single token form, e.g. "--name=value".
       
   660                     for (Option o : getSupportedRuntimeOptions()) {
       
   661                         if (o.matches(arg)) {
       
   662                             o.handleOption(helper, arg, Collections.emptyIterator());
       
   663                             break;
       
   664                         }
       
   665                     }
       
   666                 }
       
   667             } catch (ClassNotFoundException | SecurityException e) {
       
   668                 helper.error("err.cannot.access.runtime.env");
       
   669             }
       
   670             return false;
       
   671         }
       
   672 
       
   673         private Option[] getSupportedRuntimeOptions() {
       
   674             Option[] supportedRuntimeOptions = {
       
   675                 ADD_EXPORTS,
       
   676                 ADD_MODULES,
       
   677                 LIMIT_MODULES,
       
   678                 MODULE_PATH,
       
   679                 UPGRADE_MODULE_PATH,
       
   680                 PATCH_MODULE
       
   681             };
       
   682             return supportedRuntimeOptions;
       
   683         }
       
   684     };
       
   685 
       
   686     /**
       
   687      * The kind of argument, if any, accepted by this option. The kind is augmented
       
   688      * by characters in the name of the option.
       
   689      */
       
   690     public enum ArgKind {
       
   691         /** This option does not take any argument. */
       
   692         NONE,
       
   693 
       
   694 // Not currently supported
       
   695 //        /**
       
   696 //         * This option takes an optional argument, which may be provided directly after an '='
       
   697 //         * separator, or in the following argument position if that word does not itself appear
       
   698 //         * to be the name of an option.
       
   699 //         */
       
   700 //        OPTIONAL,
       
   701 
       
   702         /**
       
   703          * This option takes an argument.
       
   704          * If the name of option ends with ':' or '=', the argument must be provided directly
       
   705          * after that separator.
       
   706          * Otherwise, if may appear after an '=' or in the following argument position.
       
   707          */
       
   708         REQUIRED,
       
   709 
       
   710         /**
       
   711          * This option takes an argument immediately after the option name, with no separator
       
   712          * character.
       
   713          */
       
   714         ADJACENT
       
   715     }
       
   716 
       
   717     /**
       
   718      * The kind of an Option. This is used by the -help and -X options.
       
   719      */
   635     public enum OptionKind {
   720     public enum OptionKind {
   636         /** A standard option, documented by -help. */
   721         /** A standard option, documented by -help. */
   637         STANDARD,
   722         STANDARD,
   638         /** An extended option, documented by -X. */
   723         /** An extended option, documented by -X. */
   639         EXTENDED,
   724         EXTENDED,
   640         /** A hidden option, not documented. */
   725         /** A hidden option, not documented. */
   641         HIDDEN,
   726         HIDDEN,
   642     }
   727     }
   643 
   728 
   644     /** The group for an Option. This determines the situations in which the
   729     /**
   645      *  option is applicable. */
   730      * The group for an Option. This determines the situations in which the
       
   731      * option is applicable.
       
   732      */
   646     enum OptionGroup {
   733     enum OptionGroup {
   647         /** A basic option, available for use on the command line or via the
   734         /** A basic option, available for use on the command line or via the
   648          *  Compiler API. */
   735          *  Compiler API. */
   649         BASIC,
   736         BASIC,
   650         /** An option for javac's standard JavaFileManager. Other file managers
   737         /** An option for javac's standard JavaFileManager. Other file managers
   654         INFO,
   741         INFO,
   655         /** A command-line "option" representing a file or class name. */
   742         /** A command-line "option" representing a file or class name. */
   656         OPERAND
   743         OPERAND
   657     }
   744     }
   658 
   745 
   659     /** The kind of choice for "choice" options. */
   746     /**
       
   747      * The kind of choice for "choice" options.
       
   748      */
   660     enum ChoiceKind {
   749     enum ChoiceKind {
   661         /** The expected value is exactly one of the set of choices. */
   750         /** The expected value is exactly one of the set of choices. */
   662         ONEOF,
   751         ONEOF,
   663         /** The expected value is one of more of the set of choices. */
   752         /** The expected value is one of more of the set of choices. */
   664         ANYOF
   753         ANYOF
   682             }
   771             }
   683             return false;
   772             return false;
   684         }
   773         }
   685     }
   774     }
   686 
   775 
   687     public final String text;
   776     /**
   688 
   777      * The "primary name" for this option.
   689     final OptionKind kind;
   778      * This is the name that is used to put values in the {@link Options} table.
   690 
   779      */
   691     final OptionGroup group;
   780     public final String primaryName;
   692 
   781 
   693     /** Documentation key for arguments.
   782     /**
   694      */
   783      * The set of names (primary name and aliases) for this option.
   695     final String argsNameKey;
   784      * Note that some names may end in a separator, to indicate that an argument must immediately
       
   785      * follow the separator (and cannot appear in the following argument position.
       
   786      */
       
   787     public final String[] names;
       
   788 
       
   789     /** Documentation key for arguments. */
       
   790     protected final String argsNameKey;
   696 
   791 
   697     /** Documentation key for description.
   792     /** Documentation key for description.
   698      */
   793      */
   699     final String descrKey;
   794     protected final String descrKey;
   700 
   795 
   701     /** Suffix option (-foo=bar or -foo:bar)
   796     /** The kind of this option. */
   702      */
   797     private final OptionKind kind;
   703     final boolean hasSuffix;
   798 
   704 
   799     /** The group for this option. */
   705     /** The kind of choices for this option, if any.
   800     private final OptionGroup group;
   706      */
   801 
   707     final ChoiceKind choiceKind;
   802     /** The kind of argument for this option. */
   708 
   803     private final ArgKind argKind;
   709     /** The choices for this option, if any, and whether or not the choices
   804 
   710      *  are hidden
   805     /** The kind of choices for this option, if any. */
   711      */
   806     private final ChoiceKind choiceKind;
   712     final Map<String,Boolean> choices;
   807 
   713 
   808     /** The choices for this option, if any, and whether or not the choices are hidden. */
       
   809     private final Map<String,Boolean> choices;
       
   810 
       
   811     /**
       
   812      * Looks up the first option matching the given argument in the full set of options.
       
   813      * @param arg the argument to be matches
       
   814      * @return the first option that matches, or null if none.
       
   815      */
       
   816     public static Option lookup(String arg) {
       
   817         return lookup(arg, EnumSet.allOf(Option.class));
       
   818     }
       
   819 
       
   820     /**
       
   821      * Looks up the first option matching the given argument within a set of options.
       
   822      * @param arg the argument to be matches
       
   823      * @return the first option that matches, or null if none.
       
   824      */
       
   825     public static Option lookup(String arg, Set<Option> options) {
       
   826         for (Option option: options) {
       
   827             if (option.matches(arg))
       
   828                 return option;
       
   829         }
       
   830         return null;
       
   831     }
       
   832 
       
   833     /**
       
   834      * Writes the "command line help" for given kind of option to the log.
       
   835      * @param log the log
       
   836      * @param kind  the kind of options to select
       
   837      */
       
   838     private static void showHelp(Log log, OptionKind kind) {
       
   839         Comparator<Option> comp = new Comparator<Option>() {
       
   840             final Collator collator = Collator.getInstance(Locale.US);
       
   841             { collator.setStrength(Collator.PRIMARY); }
       
   842 
       
   843             @Override
       
   844             public int compare(Option o1, Option o2) {
       
   845                 return collator.compare(o1.primaryName, o2.primaryName);
       
   846             }
       
   847         };
       
   848 
       
   849         getJavaCompilerOptions()
       
   850                 .stream()
       
   851                 .filter(o -> o.kind == kind)
       
   852                 .sorted(comp)
       
   853                 .forEach(o -> {
       
   854                     o.help(log);
       
   855                 });
       
   856     }
   714 
   857 
   715     Option(String text, String descrKey,
   858     Option(String text, String descrKey,
   716             OptionKind kind, OptionGroup group) {
   859             OptionKind kind, OptionGroup group) {
   717         this(text, null, descrKey, kind, group, null, null, false);
   860         this(text, null, descrKey, kind, group, null, null, ArgKind.NONE);
   718     }
       
   719 
       
   720     Option(String text, String descrKey,
       
   721             OptionKind kind, OptionGroup group,
       
   722             boolean doHasSuffix) {
       
   723         this(text, null, descrKey, kind, group, null, null, doHasSuffix);
       
   724     }
   861     }
   725 
   862 
   726     Option(String text, String argsNameKey, String descrKey,
   863     Option(String text, String argsNameKey, String descrKey,
   727             OptionKind kind, OptionGroup group) {
   864             OptionKind kind, OptionGroup group) {
   728         this(text, argsNameKey, descrKey, kind, group, null, null, false);
   865         this(text, argsNameKey, descrKey, kind, group, null, null, ArgKind.REQUIRED);
   729     }
   866     }
   730 
   867 
   731     Option(String text, String argsNameKey, String descrKey,
   868     Option(String text, String argsNameKey, String descrKey,
   732             OptionKind kind, OptionGroup group, boolean doHasSuffix) {
   869             OptionKind kind, OptionGroup group, ArgKind ak) {
   733         this(text, argsNameKey, descrKey, kind, group, null, null, doHasSuffix);
   870         this(text, argsNameKey, descrKey, kind, group, null, null, ak);
   734     }
   871     }
   735 
   872 
   736     Option(String text, OptionKind kind, OptionGroup group,
   873     Option(String text, String argsNameKey, String descrKey, OptionKind kind, OptionGroup group,
   737             ChoiceKind choiceKind, Map<String,Boolean> choices) {
   874             ChoiceKind choiceKind, Map<String,Boolean> choices) {
   738         this(text, null, null, kind, group, choiceKind, choices, false);
   875         this(text, argsNameKey, descrKey, kind, group, choiceKind, choices, ArgKind.REQUIRED);
   739     }
   876     }
   740 
   877 
   741     Option(String text, String descrKey,
   878     Option(String text, String descrKey,
   742             OptionKind kind, OptionGroup group,
   879             OptionKind kind, OptionGroup group,
   743             ChoiceKind choiceKind, String... choices) {
   880             ChoiceKind choiceKind, String... choices) {
   744         this(text, null, descrKey, kind, group, choiceKind,
   881         this(text, null, descrKey, kind, group, choiceKind,
   745                 createChoices(choices), false);
   882                 createChoices(choices), ArgKind.REQUIRED);
   746     }
   883     }
   747     // where
   884     // where
   748         private static Map<String,Boolean> createChoices(String... choices) {
   885         private static Map<String,Boolean> createChoices(String... choices) {
   749             Map<String,Boolean> map = new LinkedHashMap<>();
   886             Map<String,Boolean> map = new LinkedHashMap<>();
   750             for (String c: choices)
   887             for (String c: choices)
   753         }
   890         }
   754 
   891 
   755     private Option(String text, String argsNameKey, String descrKey,
   892     private Option(String text, String argsNameKey, String descrKey,
   756             OptionKind kind, OptionGroup group,
   893             OptionKind kind, OptionGroup group,
   757             ChoiceKind choiceKind, Map<String,Boolean> choices,
   894             ChoiceKind choiceKind, Map<String,Boolean> choices,
   758             boolean doHasSuffix) {
   895             ArgKind argKind) {
   759         this.text = text;
   896         this.names = text.trim().split("\\s+");
       
   897         Assert.check(names.length >= 1);
       
   898         this.primaryName = names[0];
   760         this.argsNameKey = argsNameKey;
   899         this.argsNameKey = argsNameKey;
   761         this.descrKey = descrKey;
   900         this.descrKey = descrKey;
   762         this.kind = kind;
   901         this.kind = kind;
   763         this.group = group;
   902         this.group = group;
   764         this.choiceKind = choiceKind;
   903         this.choiceKind = choiceKind;
   765         this.choices = choices;
   904         this.choices = choices;
   766         char lastChar = text.charAt(text.length()-1);
   905         this.argKind = argKind;
   767         this.hasSuffix = doHasSuffix || lastChar == ':' || lastChar == '=';
   906     }
   768     }
   907 
   769 
   908     public String getPrimaryName() {
   770     public String getText() {
   909         return primaryName;
   771         return text;
       
   772     }
   910     }
   773 
   911 
   774     public OptionKind getKind() {
   912     public OptionKind getKind() {
   775         return kind;
   913         return kind;
   776     }
   914     }
   777 
   915 
       
   916     public ArgKind getArgKind() {
       
   917         return argKind;
       
   918     }
       
   919 
   778     public boolean hasArg() {
   920     public boolean hasArg() {
   779         return argsNameKey != null && !hasSuffix;
   921         return (argKind != ArgKind.NONE);
   780     }
   922     }
   781 
   923 
   782     public boolean matches(String option) {
   924     public boolean matches(String option) {
       
   925         for (String name: names) {
       
   926             if (matches(option, name))
       
   927                 return true;
       
   928         }
       
   929         return false;
       
   930     }
       
   931 
       
   932     private boolean matches(String option, String name) {
       
   933         if (name.startsWith("--")) {
       
   934             return option.equals(name)
       
   935                     || hasArg() && option.startsWith(name + "=");
       
   936         }
       
   937 
       
   938         boolean hasSuffix = (argKind == ArgKind.ADJACENT)
       
   939                 || name.endsWith(":") || name.endsWith("=");
       
   940 
   783         if (!hasSuffix)
   941         if (!hasSuffix)
   784             return option.equals(text);
   942             return option.equals(name);
   785 
   943 
   786         if (!option.startsWith(text))
   944         if (!option.startsWith(name))
   787             return false;
   945             return false;
   788 
   946 
   789         if (choices != null) {
   947         if (choices != null) {
   790             String arg = option.substring(text.length());
   948             String arg = option.substring(name.length());
   791             if (choiceKind == ChoiceKind.ONEOF)
   949             if (choiceKind == ChoiceKind.ONEOF)
   792                 return choices.keySet().contains(arg);
   950                 return choices.keySet().contains(arg);
   793             else {
   951             else {
   794                 for (String a: arg.split(",+")) {
   952                 for (String a: arg.split(",+")) {
   795                     if (!choices.keySet().contains(a))
   953                     if (!choices.keySet().contains(a))
   799         }
   957         }
   800 
   958 
   801         return true;
   959         return true;
   802     }
   960     }
   803 
   961 
       
   962     /**
       
   963      * Handles an option.
       
   964      * If an argument for the option is required, depending on spec of the option, it will be found
       
   965      * as part of the current arg (following ':' or '=') or in the following argument.
       
   966      * This is the recommended way to handle an option directly, instead of calling the underlying
       
   967      * {@link #process process} methods.
       
   968      * @param helper a helper to provide access to the environment
       
   969      * @param arg the arg string that identified this option
       
   970      * @param rest the remaining strings to be analysed
       
   971      * @return true if the operation was successful, and false otherwise
       
   972      * @implNote The return value is the opposite of that used by {@link #process}.
       
   973      */
       
   974     public boolean handleOption(OptionHelper helper, String arg, Iterator<String> rest) {
       
   975         if (hasArg()) {
       
   976             String operand;
       
   977             int sep = findSeparator(arg);
       
   978             if (getArgKind() == Option.ArgKind.ADJACENT) {
       
   979                 operand = arg.substring(primaryName.length());
       
   980             } else if (sep > 0) {
       
   981                 operand = arg.substring(sep + 1);
       
   982             } else {
       
   983                 if (!rest.hasNext()) {
       
   984                     helper.error("err.req.arg", arg);
       
   985                     return false;
       
   986                 }
       
   987                 operand = rest.next();
       
   988             }
       
   989             return !process(helper, arg, operand);
       
   990         } else {
       
   991             return !process(helper, arg);
       
   992         }
       
   993     }
       
   994 
       
   995     /**
       
   996      * Processes an option that either does not need an argument,
       
   997      * or which contains an argument within it, following a separator.
       
   998      * @param helper a helper to provide access to the environment
       
   999      * @param option the option to be processed
       
  1000      * @return true if an error occurred
       
  1001      */
       
  1002     public boolean process(OptionHelper helper, String option) {
       
  1003         if (argKind == ArgKind.NONE) {
       
  1004             return process(helper, primaryName, option);
       
  1005         } else {
       
  1006             int sep = findSeparator(option);
       
  1007             return process(helper, primaryName, option.substring(sep + 1));
       
  1008         }
       
  1009     }
       
  1010 
       
  1011     /**
       
  1012      * Processes an option by updating the environment via a helper object.
       
  1013      * @param helper a helper to provide access to the environment
       
  1014      * @param option the option to be processed
       
  1015      * @param arg the value to associate with the option, or a default value
       
  1016      *  to be used if the option does not otherwise take an argument.
       
  1017      * @return true if an error occurred
       
  1018      */
   804     public boolean process(OptionHelper helper, String option, String arg) {
  1019     public boolean process(OptionHelper helper, String option, String arg) {
   805         if (choices != null) {
  1020         if (choices != null) {
   806             if (choiceKind == ChoiceKind.ONEOF) {
  1021             if (choiceKind == ChoiceKind.ONEOF) {
   807                 // some clients like to see just one of option+choice set
  1022                 // some clients like to see just one of option+choice set
   808                 for (String s: choices.keySet())
  1023                 for (String s: choices.keySet())
   809                     helper.remove(option + s);
  1024                     helper.remove(primaryName + s);
   810                 String opt = option + arg;
  1025                 String opt = primaryName + arg;
   811                 helper.put(opt, opt);
  1026                 helper.put(opt, opt);
   812                 // some clients like to see option (without trailing ":")
  1027                 // some clients like to see option (without trailing ":")
   813                 // set to arg
  1028                 // set to arg
   814                 String nm = option.substring(0, option.length() - 1);
  1029                 String nm = primaryName.substring(0, primaryName.length() - 1);
   815                 helper.put(nm, arg);
  1030                 helper.put(nm, arg);
   816             } else {
  1031             } else {
   817                 // set option+word for each word in arg
  1032                 // set option+word for each word in arg
   818                 for (String a: arg.split(",+")) {
  1033                 for (String a: arg.split(",+")) {
   819                     String opt = option + a;
  1034                     String opt = primaryName + a;
   820                     helper.put(opt, opt);
  1035                     helper.put(opt, opt);
   821                 }
  1036                 }
   822             }
  1037             }
   823         }
  1038         }
   824         helper.put(option, arg);
  1039         helper.put(primaryName, arg);
   825         if (group == OptionGroup.FILEMANAGER)
  1040         if (group == OptionGroup.FILEMANAGER)
   826             helper.handleFileManagerOption(this, arg);
  1041             helper.handleFileManagerOption(this, arg);
   827         return false;
  1042         return false;
   828     }
  1043     }
   829 
  1044 
   830     public boolean process(OptionHelper helper, String option) {
  1045     /**
   831         if (hasSuffix)
  1046      * Scans a word to find the first separator character, either colon or equals.
   832             return process(helper, text, option.substring(text.length()));
  1047      * @param word the word to be scanned
   833         else
  1048      * @return the position of the first':' or '=' character in the word,
   834             return process(helper, option, option);
  1049      *  or -1 if none found
   835     }
  1050      */
   836 
  1051     private static int findSeparator(String word) {
   837     private static final String HELP_LINE_FORMAT = "  %-26s %s";
  1052         for (int i = 0; i < word.length(); i++) {
   838 
  1053             switch (word.charAt(i)) {
   839     void help(Log log, OptionKind kind) {
  1054                 case ':': case '=':
   840         if (this.kind != kind)
  1055                     return i;
       
  1056             }
       
  1057         }
       
  1058         return -1;
       
  1059     }
       
  1060 
       
  1061     /** The indent for the option synopsis. */
       
  1062     private static final String SMALL_INDENT = "  ";
       
  1063     /** The automatic indent for the description. */
       
  1064     private static final String LARGE_INDENT = "        ";
       
  1065     /** The space allowed for the synopsis, if the description is to be shown on the same line. */
       
  1066     private static final int DEFAULT_SYNOPSIS_WIDTH = 28;
       
  1067     /** The nominal maximum line length, when seeing if text will fit on a line. */
       
  1068     private static final int DEFAULT_MAX_LINE_LENGTH = 80;
       
  1069     /** The format for a single-line help entry. */
       
  1070     private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s";
       
  1071 
       
  1072     /**
       
  1073      * Writes help text for this option to the log.
       
  1074      * @param log the log
       
  1075      */
       
  1076     protected void help(Log log) {
       
  1077         help(log, log.localize(PrefixKind.JAVAC, descrKey));
       
  1078     }
       
  1079 
       
  1080     protected void help(Log log, String descr) {
       
  1081         String synopses = Arrays.stream(names)
       
  1082                 .map(s -> helpSynopsis(s, log))
       
  1083                 .collect(Collectors.joining(", "));
       
  1084 
       
  1085         // If option synopses and description fit on a single line of reasonable length,
       
  1086         // display using COMPACT_FORMAT
       
  1087         if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH
       
  1088                 && !descr.contains("\n")
       
  1089                 && (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + descr.length() <= DEFAULT_MAX_LINE_LENGTH)) {
       
  1090             log.printRawLines(WriterKind.STDOUT, String.format(COMPACT_FORMAT, synopses, descr));
   841             return;
  1091             return;
   842 
  1092         }
   843         log.printRawLines(WriterKind.STDOUT,
  1093 
   844                 String.format(HELP_LINE_FORMAT,
  1094         // If option synopses fit on a single line of reasonable length, show that;
   845                     helpSynopsis(log),
  1095         // otherwise, show 1 per line
   846                     log.localize(PrefixKind.JAVAC, descrKey)));
  1096         if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) {
   847 
  1097             log.printRawLines(WriterKind.STDOUT, SMALL_INDENT + synopses);
   848     }
  1098         } else {
   849 
  1099             for (String name: names) {
   850     private String helpSynopsis(Log log) {
  1100                 log.printRawLines(WriterKind.STDOUT, SMALL_INDENT + helpSynopsis(name, log));
       
  1101             }
       
  1102         }
       
  1103 
       
  1104         // Finally, show the description
       
  1105         log.printRawLines(WriterKind.STDOUT, LARGE_INDENT + descr.replace("\n", "\n" + LARGE_INDENT));
       
  1106     }
       
  1107 
       
  1108     /**
       
  1109      * Composes the initial synopsis of one of the forms for this option.
       
  1110      * @param name the name of this form of the option
       
  1111      * @param log the log used to localize the description of the arguments
       
  1112      * @return  the synopsis
       
  1113      */
       
  1114     private String helpSynopsis(String name, Log log) {
   851         StringBuilder sb = new StringBuilder();
  1115         StringBuilder sb = new StringBuilder();
   852         sb.append(text);
  1116         sb.append(name);
   853         if (argsNameKey == null) {
  1117         if (argsNameKey == null) {
   854             if (choices != null) {
  1118             if (choices != null) {
   855                 String sep = "{";
  1119                 String sep = "{";
   856                 for (Map.Entry<String,Boolean> e: choices.entrySet()) {
  1120                 for (Map.Entry<String,Boolean> e: choices.entrySet()) {
   857                     if (!e.getValue()) {
  1121                     if (!e.getValue()) {
   861                     }
  1125                     }
   862                 }
  1126                 }
   863                 sb.append("}");
  1127                 sb.append("}");
   864             }
  1128             }
   865         } else {
  1129         } else {
   866             if (!hasSuffix)
  1130             if (!name.matches(".*[=:]$") && argKind != ArgKind.ADJACENT)
   867                 sb.append(" ");
  1131                 sb.append(" ");
   868             sb.append(log.localize(PrefixKind.JAVAC, argsNameKey));
  1132             sb.append(log.localize(PrefixKind.JAVAC, argsNameKey));
   869 
       
   870         }
  1133         }
   871 
  1134 
   872         return sb.toString();
  1135         return sb.toString();
   873     }
  1136     }
   874 
  1137 
   913             choices.put("-" + c.option, c.hidden);
  1176             choices.put("-" + c.option, c.hidden);
   914         choices.put("none", false);
  1177         choices.put("none", false);
   915         return choices;
  1178         return choices;
   916     }
  1179     }
   917 
  1180 
       
  1181     /**
       
  1182      * Returns the set of options supported by the command line tool.
       
  1183      * @return the set of options.
       
  1184      */
   918     static Set<Option> getJavaCompilerOptions() {
  1185     static Set<Option> getJavaCompilerOptions() {
   919         return EnumSet.allOf(Option.class);
  1186         return EnumSet.allOf(Option.class);
   920     }
  1187     }
   921 
  1188 
       
  1189     /**
       
  1190      * Returns the set of options supported by the built-in file manager.
       
  1191      * @return the set of options.
       
  1192      */
   922     public static Set<Option> getJavacFileManagerOptions() {
  1193     public static Set<Option> getJavacFileManagerOptions() {
   923         return getOptions(EnumSet.of(FILEMANAGER));
  1194         return getOptions(FILEMANAGER);
   924     }
  1195     }
   925 
  1196 
       
  1197     /**
       
  1198      * Returns the set of options supported by this implementation of
       
  1199      * the JavaCompiler API, via {@link JavaCompiler#getTask}.
       
  1200      * @return the set of options.
       
  1201      */
   926     public static Set<Option> getJavacToolOptions() {
  1202     public static Set<Option> getJavacToolOptions() {
   927         return getOptions(EnumSet.of(BASIC));
  1203         return getOptions(BASIC);
   928     }
  1204     }
   929 
  1205 
   930     static Set<Option> getOptions(Set<OptionGroup> desired) {
  1206     private static Set<Option> getOptions(OptionGroup group) {
   931         Set<Option> options = EnumSet.noneOf(Option.class);
  1207         return Arrays.stream(Option.values())
   932         for (Option option : Option.values())
  1208                 .filter(o -> o.group == group)
   933             if (desired.contains(option.group))
  1209                 .collect(Collectors.toCollection(() -> EnumSet.noneOf(Option.class)));
   934                 options.add(option);
       
   935         return Collections.unmodifiableSet(options);
       
   936     }
  1210     }
   937 
  1211 
   938 }
  1212 }