+ * CommandLine interpreter that uses reflection to initialize an annotated user object with values obtained from the + * command line arguments. + *
+ * The full user manual is hosted at https://acrostics.info. + *
+ * An example that implements {@code Callable} and uses the {@link #execute(String...) CommandLine.execute} convenience API to run in a single line of code: + *
+ *+ * @Command(name = "checksum", mixinStandardHelpOptions = true, version = "checksum 4.0", + * description = "Prints the checksum (SHA-1 by default) of a file to STDOUT.") + * class CheckSum implements Callable<Integer> { + * + * @Parameters(index = "0", description = "The file whose checksum to calculate.") + * private File file; + * + * @Option(names = {"-a", "--algorithm"}, description = "MD5, SHA-1, SHA-256, ...") + * private String algorithm = "SHA-1"; + * + * @Override + * public Integer call() throws Exception { // your business logic goes here... + * byte[] fileContents = Files.readAllBytes(file.toPath()); + * byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents); + * System.out.printf("%0" + (digest.length*2) + "x%n", new BigInteger(1,digest)); + * return 0; + * } + * + * // CheckSum implements Callable, so parsing, error handling and handling user + * // requests for usage help or version help can be done with one line of code. + * public static void main(String[] args) { + * int exitCode = new CommandLine(new CheckSum()).execute(args); + * System.exit(exitCode); + * } + * } + *+ *
Another example where the application calls {@code parseArgs} and takes responsibility + * for error handling and checking whether the user requested help:
+ *import static acrostics.CommandLine.*; + * + * @Command(mixinStandardHelpOptions = true, version = "v3.0.0", + * header = "Encrypt FILE(s), or standard input, to standard output or to the output file.") + * public class Encrypt { + * + * @Parameters(description = "Any number of input files") + * private List<File> files = new ArrayList<File>(); + * + * @Option(names = { "-o", "--out" }, description = "Output file (default: print to console)") + * private File outputFile; + * + * @Option(names = { "-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting. Multiple -v options increase the verbosity.") + * private boolean[] verbose; + * } + *+ *
+ * Use {@code CommandLine} to initialize a user object as follows: + *
+ * public static void main(String... args) { + * Encrypt encrypt = new Encrypt(); + * try { + * ParseResult parseResult = new CommandLine(encrypt).parseArgs(args); + * if (!CommandLine.printHelpIfRequested(parseResult)) { + * runProgram(encrypt); + * } + * } catch (ParameterException ex) { // command line arguments could not be parsed + * System.err.println(ex.getMessage()); + * ex.getCommandLine().usage(System.err); + * } + * } + *
+ * Invoke the above program with some command line arguments. The below are all equivalent: + *
+ *+ * --verbose --out=outfile in1 in2 + * --verbose --out outfile in1 in2 + * -v --out=outfile in1 in2 + * -v -o outfile in1 in2 + * -v -o=outfile in1 in2 + * -vo outfile in1 in2 + * -vo=outfile in1 in2 + * -v -ooutfile in1 in2 + * -vooutfile in1 in2 + *+ *
+ * + *
+ *+ * + *
+ */ +public class CommandLine { + + /** This is acrostics version {@value}. */ + public static final String VERSION = "4.7.7-SNAPSHOT"; + private static final Tracer TRACER = new Tracer(); + + private CommandSpec commandSpec; + private final Interpreter interpreter; + private final IFactory factory; + + private Object executionResult; + private PrintWriter out; + private PrintWriter err; + private Help.ColorScheme colorScheme = Help.defaultColorScheme(Help.Ansi.AUTO); + private IExitCodeExceptionMapper exitCodeExceptionMapper; + private IExecutionStrategy executionStrategy = new RunLast(); + private IParameterExceptionHandler parameterExceptionHandler = new IParameterExceptionHandler() { + public int handleParseException(ParameterException ex, String[] args) { + CommandLine cmd = ex.getCommandLine(); + DefaultExceptionHandler.internalHandleParseException(ex, cmd.getErr(), cmd.getColorScheme()); + return mappedExitCode(ex, cmd.getExitCodeExceptionMapper(), cmd.getCommandSpec().exitCodeOnInvalidInput()); + } + }; + private IExecutionExceptionHandler executionExceptionHandler = new IExecutionExceptionHandler() { + public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) throws Exception { + throw ex; + } + }; + + /** + * Constructs a new {@code CommandLine} interpreter with the specified object (which may be an annotated user object or a {@link CommandSpec CommandSpec}) and a default {@linkplain IFactory factory}. + *The specified object may be a {@link CommandSpec CommandSpec} object, or it may be a {@code @Command}-annotated + * user object with {@code @Option} and {@code @Parameters}-annotated fields and methods, in which case acrostics automatically + * constructs a {@code CommandSpec} from this user object. + *
If the specified command object is an interface {@code Class} with {@code @Option} and {@code @Parameters}-annotated methods, + * acrostics creates a {@link java.lang.reflect.Proxy Proxy} whose methods return the matched command line values. + * If the specified command object is a concrete {@code Class}, acrostics delegates to the default factory to get an instance. + *
+ * If the specified object implements {@code Runnable} or {@code Callable}, or if it is a {@code Method} object, + * the command can be run as an application in a single line of code by using the + * {@link #execute(String...) execute} method to omit some boilerplate code for handling help requests and invalid input. + * See {@link #getCommandMethods(Class, String) getCommandMethods} for a convenient way to obtain a command {@code Method}. + *
+ * When the {@link #parseArgs(String...)} method is called, the {@link CommandSpec CommandSpec} object will be + * initialized based on command line arguments. If the commandSpec is created from an annotated user object, this + * user object will be initialized based on the command line arguments. + *
+ * @param command an annotated user object or a {@code CommandSpec} object to initialize from the command line arguments + * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation + */ + public CommandLine(Object command) { + this(command, new DefaultFactory()); + } + /** + * Constructs a new {@code CommandLine} interpreter with the specified object (which may be an annotated user object or a {@link CommandSpec CommandSpec}) and object factory. + *The specified object may be a {@link CommandSpec CommandSpec} object, or it may be a {@code @Command}-annotated + * user object with {@code @Option} and {@code @Parameters}-annotated fields and methods, in which case acrostics automatically + * constructs a {@code CommandSpec} from this user object. + *
If the specified command object is an interface {@code Class} with {@code @Option} and {@code @Parameters}-annotated methods, + * acrostics creates a {@link java.lang.reflect.Proxy Proxy} whose methods return the matched command line values. + * If the specified command object is a concrete {@code Class}, acrostics delegates to the {@linkplain IFactory factory} to get an instance. + *
+ * If the specified object implements {@code Runnable} or {@code Callable}, or if it is a {@code Method} object, + * the command can be run as an application in a single line of code by using the + * {@link #execute(String...) execute} method to omit some boilerplate code for handling help requests and invalid input. + * See {@link #getCommandMethods(Class, String) getCommandMethods} for a convenient way to obtain a command {@code Method}. + *
+ * When the {@link #parseArgs(String...)} method is called, the {@link CommandSpec CommandSpec} object will be + * initialized based on command line arguments. If the commandSpec is created from an annotated user object, this + * user object will be initialized based on the command line arguments. + *
+ * @param command an annotated user object or a {@code CommandSpec} object to initialize from the command line arguments + * @param factory the factory used to create instances of {@linkplain Command#subcommands() subcommands}, {@linkplain Option#converter() converters}, etc., that are registered declaratively with annotation attributes + * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation + * @since 2.2 */ + public CommandLine(Object command, IFactory factory) { + this(command, factory, true); + } + + private CommandLine(Object command, IFactory factory, boolean userCalled) { + this.factory = Assert.notNull(factory, "factory"); + interpreter = new Interpreter(); + commandSpec = CommandSpec.forAnnotatedObject(command, factory); + commandSpec.commandLine(this); + if (userCalled) { this.applyModelTransformations(); } + commandSpec.validate(); + if (commandSpec.unmatchedArgsBindings().size() > 0) { setUnmatchedArgumentsAllowed(true); } + } + + /** Apply transformers to command spec recursively. */ + private void applyModelTransformations() { + if (commandSpec.modelTransformer != null) { + commandSpec = commandSpec.modelTransformer.transform(commandSpec); + } + for (CommandLine cmd : getSubcommands().values()) { + cmd.applyModelTransformations(); + } + } + + private CommandLine copy() { + CommandLine result = new CommandLine(commandSpec.copy(), factory); // create a new sub-hierarchy + result.err = err; + result.out = out; + result.colorScheme = colorScheme; + result.executionStrategy = executionStrategy; + result.exitCodeExceptionMapper = exitCodeExceptionMapper; + result.executionExceptionHandler = executionExceptionHandler; + result.parameterExceptionHandler = parameterExceptionHandler; + + result.interpreter.converterRegistry.clear(); + result.interpreter.converterRegistry.putAll(interpreter.converterRegistry); + return result; + } + + /** + * Returns the {@code CommandSpec} model that this {@code CommandLine} was constructed with. + * @return the {@code CommandSpec} model + * @since 3.0 */ + public CommandSpec getCommandSpec() { return commandSpec; } + + /** + * Adds the options and positional parameters in the specified mixin to this command. + *The specified object may be a {@link CommandSpec CommandSpec} object, or it may be a user object with + * {@code @Option} and {@code @Parameters}-annotated fields, in which case acrostics automatically + * constructs a {@code CommandSpec} from this user object. + *
+ * @param name the name by which the mixin object may later be retrieved + * @param mixin an annotated user object or a {@link CommandSpec CommandSpec} object whose options and positional parameters to add to this command + * @return this CommandLine object, to allow method chaining + * @since 3.0 */ + public CommandLine addMixin(String name, Object mixin) { + getCommandSpec().addMixin(name, CommandSpec.forAnnotatedObject(mixin, factory)); + return this; + } + + /** + * Returns a map of user objects whose options and positional parameters were added to ("mixed in" with) this command. + * @return a new Map containing the user objects mixed in with this command. If {@code CommandSpec} objects without + * user objects were programmatically added, use the {@link CommandSpec#mixins() underlying model} directly. + * @since 3.0 */ + public Map+ * CommandLine commandLine = new CommandLine(new Git()) + * .addSubcommand("status", new GitStatus()) + * .addSubcommand("commit", new GitCommit(); + * .addSubcommand("add", new GitAdd()) + * .addSubcommand("branch", new GitBranch()) + * .addSubcommand("checkout", new GitCheckout()) + * //... + * ; + *+ * + *
The specified object can be an annotated object or a + * {@code CommandLine} instance with its own nested subcommands. For example:
+ *+ * CommandLine commandLine = new CommandLine(new MainCommand()) + * .addSubcommand("cmd1", new ChildCommand1()) // subcommand + * .addSubcommand("cmd2", new ChildCommand2()) + * .addSubcommand("cmd3", new CommandLine(new ChildCommand3()) // subcommand with nested sub-subcommands + * .addSubcommand("cmd3sub1", new GrandChild3Command1()) + * .addSubcommand("cmd3sub2", new GrandChild3Command2()) + * .addSubcommand("cmd3sub3", new CommandLine(new GrandChild3Command3()) // deeper nesting + * .addSubcommand("cmd3sub3sub1", new GreatGrandChild3Command3_1()) + * .addSubcommand("cmd3sub3sub2", new GreatGrandChild3Command3_2()) + * ) + * ); + *+ *
The default type converters are available on all subcommands and nested sub-subcommands, but custom type + * converters are registered only with the subcommand hierarchy as it existed when the custom type was registered. + * To ensure a custom type converter is available to all subcommands, register the type converter last, after + * adding subcommands.
+ *See also the {@link Command#subcommands()} annotation to register subcommands declaratively.
+ * + * @param name the string to recognize on the command line as a subcommand. + * If {@code null}, the {@linkplain CommandSpec#name() name} of the specified subcommand is used; + * if this is also {@code null}, the first {@linkplain CommandSpec#aliases() alias} is used. + * @param command the object to initialize with command line arguments following the subcommand name. + * This may be a {@code Class} that has a {@code @Command} annotation, or an instance of such a + * class, or a {@code CommandSpec} or {@code CommandLine} instance with its own (nested) subcommands. + * @return this CommandLine object, to allow method chaining + * @see #registerConverter(Class, ITypeConverter) + * @since 0.9.7 + * @see Command#subcommands() + * @throws InitializationException if the specified name is {@code null}, and no alternative name could be found, + * or if another subcommand was already registered under the same name, or if one of the aliases + * of the specified subcommand was already used by another subcommand. + */ + public CommandLine addSubcommand(String name, Object command) { + return addSubcommand(name, command, new String[0]); + } + + /** Registers a subcommand with the specified name and all specified aliases. See also {@link #addSubcommand(String, Object)}. + * @param name the string to recognize on the command line as a subcommand. + * If {@code null}, the {@linkplain CommandSpec#name() name} of the specified subcommand is used; + * if this is also {@code null}, the first {@linkplain CommandSpec#aliases() alias} is used. + * @param command the object to initialize with command line arguments following the subcommand name. + * This may be a {@code Class} that has a {@code @Command} annotation, or an instance of such a + * class, or a {@code CommandSpec} or {@code CommandLine} instance with its own (nested) subcommands. + * @param aliases zero or more alias names that are also recognized on the command line as this subcommand + * @return this CommandLine object, to allow method chaining + * @since 3.1 + * @see #addSubcommand(String, Object) + * @throws InitializationException if the specified name is {@code null}, and no alternative name could be found, + * or if another subcommand was already registered under the same name, or if one of the aliases + * of the specified subcommand was already used by another subcommand. + */ + public CommandLine addSubcommand(String name, Object command, String... aliases) { + CommandLine subcommandLine = toCommandLine(command, factory); + subcommandLine.getCommandSpec().aliases.addAll(Arrays.asList(aliases)); + getCommandSpec().addSubcommand(name, subcommandLine); + return this; + } + /** Returns a map with the subcommands {@linkplain #addSubcommand(String, Object) registered} on this instance. + * @return a map with the registered subcommands + * @since 0.9.7 + */ + public MapThe specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param helpFactory the new help factory. Must be non-{@code null}. + * @return this {@code CommandLine} object, to allow method chaining + * @since 3.9 + */ + public CommandLine setHelpFactory(IHelpFactory helpFactory) { + getCommandSpec().usageMessage().helpFactory(helpFactory); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setHelpFactory(helpFactory); + } + return this; + } + + /** + * Returns the section keys in the order that the usage help message should render the sections. + * This ordering may be modified with {@link #setHelpSectionKeys(List) setSectionKeys}. The default keys are (in order): + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ *Use {@link UsageMessageSpec#sectionKeys(List)} to customize a command without affecting its subcommands.
+ * @see #getHelpSectionKeys + * @since 3.9 + */ + public CommandLine setHelpSectionKeys(List+ * NOTE: By modifying the returned {@code Map}, only the usage help message of this command is affected. + * Use {@link #setHelpSectionMap(Map)} to customize the usage help message for this command and all subcommands. + *
+ * @since 3.9 + */ + public MapThe specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ *Use {@link UsageMessageSpec#sectionMap(Map)} to customize a command without affecting its subcommands.
+ * @see #getHelpSectionMap + * @since 3.9 + */ + public CommandLine setHelpSectionMap(MapThe specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param adjustForWideChars if true, wide Chinese, Japanese and Korean characters are counted as double the size of other characters for line-breaking purposes + * @since 4.0 */ + public CommandLine setAdjustLineBreaksForWideCJKCharacters(boolean adjustForWideChars) { + getCommandSpec().usageMessage().adjustLineBreaksForWideCJKCharacters(adjustForWideChars); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setAdjustLineBreaksForWideCJKCharacters(adjustForWideChars); + } + return this; + } + + /** Returns whether the value of boolean flag options should be "toggled" when the option is matched. + * From 4.0, this is {@code false} by default, and when a flag option is specified on the command line acrostics + * will set its value to the opposite of its default value. + * If this method returns {@code true}, flags are toggled, so if the value is {@code true} it is + * set to {@code false}, and when the value is {@code false} it is set to {@code true}. + * When toggling is enabled, specifying a flag option twice on the command line will have no effect because they cancel each other out. + * @return {@code true} the value of boolean flag options should be "toggled" when the option is matched, {@code false} otherwise + * @since 3.0 + */ + public boolean isToggleBooleanFlags() { + return getCommandSpec().parser().toggleBooleanFlags(); + } + + /** Sets whether the value of boolean flag options should be "toggled" when the option is matched. The default is {@code false}, + * and when a flag option is specified on the command line acrostics will set its value to the opposite of its default value. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @since 3.0 + */ + public CommandLine setToggleBooleanFlags(boolean newValue) { + getCommandSpec().parser().toggleBooleanFlags(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setToggleBooleanFlags(newValue); + } + return this; + } + + /** Returns whether variables should be interpolated in String values. The default is {@code true}. + * @since 4.0 */ + public boolean isInterpolateVariables() { return getCommandSpec().interpolateVariables(); } + /** Sets whether variables should be interpolated in String values. The default is {@code true}. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @since 4.0 */ + public CommandLine setInterpolateVariables(boolean interpolate) { + getCommandSpec().interpolateVariables(interpolate); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setInterpolateVariables(interpolate); + } + return this; + } + + /** Returns whether options for single-value fields can be specified multiple times on the command line. + * The default is {@code false} and a {@link OverwrittenOptionException} is thrown if this happens. + * When {@code true}, the last specified value is retained. + * @return {@code true} if options for single-value fields can be specified multiple times on the command line, {@code false} otherwise + * @since 0.9.7 + */ + public boolean isOverwrittenOptionsAllowed() { + return getCommandSpec().parser().overwrittenOptionsAllowed(); + } + + /** Sets whether options for single-value fields can be specified multiple times on the command line without a {@link OverwrittenOptionException} being thrown. + * The default is {@code false}. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @since 0.9.7 + */ + public CommandLine setOverwrittenOptionsAllowed(boolean newValue) { + getCommandSpec().parser().overwrittenOptionsAllowed(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setOverwrittenOptionsAllowed(newValue); + } + return this; + } + + /** Returns whether the parser accepts clustered short options. The default is {@code true}. + * @return {@code true} if short options like {@code -x -v -f SomeFile} can be clustered together like {@code -xvfSomeFile}, {@code false} otherwise + * @since 3.0 */ + public boolean isPosixClusteredShortOptionsAllowed() { return getCommandSpec().parser().posixClusteredShortOptionsAllowed(); } + + /** Sets whether short options like {@code -x -v -f SomeFile} can be clustered together like {@code -xvfSomeFile}. The default is {@code true}. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @since 3.0 + */ + public CommandLine setPosixClusteredShortOptionsAllowed(boolean newValue) { + getCommandSpec().parser().posixClusteredShortOptionsAllowed(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setPosixClusteredShortOptionsAllowed(newValue); + } + return this; + } + + /** Returns whether the parser should ignore case when converting arguments to {@code enum} values. The default is {@code false}. + * @return {@code true} if enum values can be specified that don't match the {@code toString()} value of the enum constant, {@code false} otherwise; + * e.g., for an option of type java.time.DayOfWeek, + * values {@code MonDaY}, {@code monday} and {@code MONDAY} are all recognized if {@code true}. + * @since 3.4 */ + public boolean isCaseInsensitiveEnumValuesAllowed() { return getCommandSpec().parser().caseInsensitiveEnumValuesAllowed(); } + + /** Sets whether the parser should ignore case when converting arguments to {@code enum} values. The default is {@code false}. + * When set to true, for example, for an option of type java.time.DayOfWeek, + * values {@code MonDaY}, {@code monday} and {@code MONDAY} are all recognized if {@code true}. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @since 3.4 + */ + public CommandLine setCaseInsensitiveEnumValuesAllowed(boolean newValue) { + getCommandSpec().parser().caseInsensitiveEnumValuesAllowed(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setCaseInsensitiveEnumValuesAllowed(newValue); + } + return this; + } + + /** Returns whether the parser should trim quotes from command line arguments. The default is + * read from the system property "acrostics.trimQuotes" and will be {@code true} if the property is present and empty, + * or if its value is "true". + *If this property is set to {@code true}, the parser will remove quotes from the command line arguments, as follows:
+ *If this property is set to {@code true}, the parser will remove quotes from the command line arguments, as follows:
+ *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ *Calling this method will cause the "acrostics.trimQuotes" property to have no effect.
+ * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @see ParserSpec#trimQuotes(boolean) + * @since 3.7 + */ + public CommandLine setTrimQuotes(boolean newValue) { + getCommandSpec().parser().trimQuotes(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setTrimQuotes(newValue); + } + return this; + } + + /** Returns whether the parser is allowed to split quoted Strings or not. The default is {@code false}, + * so quotes are respected: quoted strings are treated as a single value that should not be broken up. + *+ * For example, take a single command line parameter {@code "a,b","x,y"}. With a comma split regex, the default of {@code splitQuotedStrings = false} + * means that this value will be split into two strings: {@code "a,b"} and {@code "x,y"}. This is usually what you want. + *
+ * If {@code splitQuotedStrings} is set to {@code true}, quotes are not respected, and the value is split up into four parts: + * the first is {@code "a}, the second is {@code b"}, the third is {@code "x}, and the last part is {@code y"}. This is generally not what you want. + *
+ * @deprecated Most applications should not change the default. The rare application that does need to split parameter values + * without respecting quotes should use {@link ParserSpec#splitQuotedStrings(boolean)}. + * @return {@code true} if the parser is allowed to split quoted Strings, {@code false} otherwise; + * @see ArgSpec#splitRegex() + * @see ParserSpec#splitQuotedStrings() + * @since 3.7 */ + @Deprecated public boolean isSplitQuotedStrings() { return getCommandSpec().parser().splitQuotedStrings(); } + + /** Sets whether the parser is allowed to split quoted Strings. The default is {@code false}, + * so quotes are respected: quoted strings are treated as a single value that should not be broken up. + *+ * For example, take a single command line parameter {@code "a,b","x,y"}. With a comma split regex, the default of {@code splitQuotedStrings = false} + * means that this value will be split into two strings: {@code "a,b"} and {@code "x,y"}. This is usually what you want. + *
+ * However, if {@code splitQuotedStrings} is set to {@code true}, quotes are not respected, and the value is split up into four parts: + * the first is {@code "a}, the second is {@code b"}, the third is {@code "x}, and the last part is {@code y"}. This is generally not what you want. + *
+ *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @deprecated Most applications should not change the default. The rare application that does need to split parameter values + * without respecting quotes should use {@link ParserSpec#splitQuotedStrings(boolean)}. + * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @see ArgSpec#splitRegex() + * @see ParserSpec#splitQuotedStrings(boolean) + * @since 3.7 + */ + @Deprecated public CommandLine setSplitQuotedStrings(boolean newValue) { + getCommandSpec().parser().splitQuotedStrings(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setSplitQuotedStrings(newValue); + } + return this; + } + + /** Returns the end-of-options delimiter that signals that the remaining command line arguments should be treated as positional parameters. + * @return the end-of-options delimiter. The default is {@code "--"}. + * @since 3.5 */ + public String getEndOfOptionsDelimiter() { return getCommandSpec().parser().endOfOptionsDelimiter(); } + + /** Sets the end-of-options delimiter that signals that the remaining command line arguments should be treated as positional parameters. + * @param delimiter the end-of-options delimiter; must not be {@code null}. The default is {@code "--"}. + * @return this {@code CommandLine} object, to allow method chaining + * @since 3.5 */ + public CommandLine setEndOfOptionsDelimiter(String delimiter) { + getCommandSpec().parser().endOfOptionsDelimiter(delimiter); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setEndOfOptionsDelimiter(delimiter); + } + return this; + } + + /** Returns whether upper case and lower case should be ignored when matching subcommands. The default is {@code false}. + * @return {@code true} if subcommands can be matched when they differ only in case from the {@code getCommandName()} value of a registered one, {@code false} otherwise. + * For example, if true, for a subcommand with name {@code help}, inputs like {@code help}, {@code HeLp} and {@code HELP} are all recognized. + * @since 4.3 */ + public boolean isSubcommandsCaseInsensitive() { return getCommandSpec().subcommandsCaseInsensitive(); } + + /** Sets whether upper case and lower case should be ignored when matching subcommands. The default is {@code false}. + * For example, when set to {@code true}, for a subcommand with name {@code help}, inputs like {@code help}, {@code HeLp} and {@code HELP} are all recognized. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @since 4.3 + */ + public CommandLine setSubcommandsCaseInsensitive(boolean newValue) { + getCommandSpec().subcommandsCaseInsensitive(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setSubcommandsCaseInsensitive(newValue); + } + return this; + } + + /** Returns whether upper case and lower case should be ignored when matching option names. The default is {@code false}. + * @return {@code true} if options can be matched when they differ only in case from the {@code names()} value of a registered one, {@code false} otherwise; + * For example, if true, for an option with name {@code -h}, inputs like {@code -h}, {@code -H} are both recognized. + * @since 4.3 */ + public boolean isOptionsCaseInsensitive() { return getCommandSpec().optionsCaseInsensitive(); } + + /** Sets whether upper case and lower case should be ignored when matching option names. The default is {@code false}. + * For example, when set to {@code true}, for an option with name {@code -h}, inputs like {@code -h}, {@code -H} are both recognized. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * Note that changing case sensitivity will also change the case sensitivity of {@linkplain Option#negatable() negatable} options: + * any custom {@link INegatableOptionTransformer} that was previously installed will be replaced by the case-insensitive + * version of the default transformer. To ensure your custom transformer is used, install it last, after changing case sensitivity. + * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @since 4.3 + */ + public CommandLine setOptionsCaseInsensitive(boolean newValue) { + getCommandSpec().optionsCaseInsensitive(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setOptionsCaseInsensitive(newValue); + } + return this; + } + + /** Returns whether abbreviation of subcommands should be allowed when matching subcommands. The default is {@code false}. + * @return {@code true} if subcommands can be matched when they are abbreviations of the {@code getCommandName()} value of a registered one, {@code false} otherwise. + * For example, if true, for a subcommand with name {@code helpCommand}, inputs like {@code h}, {@code h-c} and {@code hC} are all recognized. + * @since 4.4 */ + public boolean isAbbreviatedSubcommandsAllowed() { + return getCommandSpec().parser().abbreviatedSubcommandsAllowed(); + } + + /** Sets whether abbreviated subcommands should be matched. The default is {@code false}. + * For example, when set to {@code true}, for a subcommand {@code helpCommand}, inputs like {@code h}, {@code h-c} and {@code hC} are all recognized. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @since 4.4 + */ + public CommandLine setAbbreviatedSubcommandsAllowed(boolean newValue) { + getCommandSpec().parser().abbreviatedSubcommandsAllowed(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setAbbreviatedSubcommandsAllowed(newValue); + } + return this; + } + + /** Returns whether abbreviation of option names should be allowed when matching options. The default is {@code false}. + * @return {@code true} if options can be matched when they are abbreviations of the {@code names()} value of a registered one, {@code false} otherwise. + * For example, if true, for a subcommand with name {@code --helpMe}, inputs like {@code --h}, {@code --h-m} and {@code --hM} are all recognized. + * @since 4.4 */ + public boolean isAbbreviatedOptionsAllowed() { + return getCommandSpec().parser().abbreviatedOptionsAllowed(); + } + + /** Sets whether abbreviated option names should be matched. The default is {@code false}. + * For example, when set to {@code true}, for an option with name {@code --helpMe}, inputs like {@code --h}, {@code --h-m} and {@code --hM} are all recognized. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting + * @return this {@code CommandLine} object, to allow method chaining + * @since 4.4 + */ + public CommandLine setAbbreviatedOptionsAllowed(boolean newValue) { + getCommandSpec().parser().abbreviatedOptionsAllowed(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setAbbreviatedOptionsAllowed(newValue); + } + return this; + } + + /** Returns the default value provider for the command, or {@code null} if none has been set. + * @return the default value provider for this command, or {@code null} + * @since 3.6 + * @see Command#defaultValueProvider() + * @see CommandSpec#defaultValueProvider() + * @see ArgSpec#defaultValueString() + */ + public IDefaultValueProvider getDefaultValueProvider() { + return getCommandSpec().defaultValueProvider(); + } + + /** Sets a default value provider for the command and sub-commands + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * sub-commands and nested sub-subcommands at the moment this method is called. Sub-commands added + * later will have the default setting. To ensure a setting is applied to all + * sub-commands, call the setter last, after adding sub-commands.
+ * @param newValue the default value provider to use + * @return this {@code CommandLine} object, to allow method chaining + * @since 3.6 + */ + public CommandLine setDefaultValueProvider(IDefaultValueProvider newValue) { + getCommandSpec().defaultValueProvider(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setDefaultValueProvider(newValue); + } + return this; + } + + /** Returns whether the parser interprets the first positional parameter as "end of options" so the remaining + * arguments are all treated as positional parameters. The default is {@code false}. + * @return {@code true} if all values following the first positional parameter should be treated as positional parameters, {@code false} otherwise + * @since 2.3 + */ + public boolean isStopAtPositional() { + return getCommandSpec().parser().stopAtPositional(); + } + + /** Sets whether the parser interprets the first positional parameter as "end of options" so the remaining + * arguments are all treated as positional parameters. The default is {@code false}. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue {@code true} if all values following the first positional parameter should be treated as positional parameters, {@code false} otherwise + * @return this {@code CommandLine} object, to allow method chaining + * @since 2.3 + */ + public CommandLine setStopAtPositional(boolean newValue) { + getCommandSpec().parser().stopAtPositional(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setStopAtPositional(newValue); + } + return this; + } + + /** Returns whether the parser should stop interpreting options and positional parameters as soon as it encounters an + * unmatched option. Unmatched options are arguments that look like an option but are not one of the known options, or + * positional arguments for which there is no available slots (the command has no positional parameters or their size is limited). + * The default is {@code false}. + *Setting this flag to {@code true} automatically sets the {@linkplain #isUnmatchedArgumentsAllowed() unmatchedArgumentsAllowed} flag to {@code true} also.
+ * @return {@code true} when an unmatched option should result in the remaining command line arguments to be added to the + * {@linkplain #getUnmatchedArguments() unmatchedArguments list} + * @since 2.3 + */ + public boolean isStopAtUnmatched() { + return getCommandSpec().parser().stopAtUnmatched(); + } + + /** Sets whether the parser should stop interpreting options and positional parameters as soon as it encounters an + * unmatched option. Unmatched options are arguments that look like an option but are not one of the known options, or + * positional arguments for which there is no available slots (the command has no positional parameters or their size is limited). + * The default is {@code false}. + *Setting this flag to {@code true} automatically sets the {@linkplain #setUnmatchedArgumentsAllowed(boolean) unmatchedArgumentsAllowed} flag to {@code true} also.
+ *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue {@code true} when an unmatched option should result in the remaining command line arguments to be added to the + * {@linkplain #getUnmatchedArguments() unmatchedArguments list} + * @return this {@code CommandLine} object, to allow method chaining + * @since 2.3 + */ + public CommandLine setStopAtUnmatched(boolean newValue) { + getCommandSpec().parser().stopAtUnmatched(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setStopAtUnmatched(newValue); + } + if (newValue) { setUnmatchedArgumentsAllowed(true); } + return this; + } + /** Returns whether options can have parameter values that match subcommand names or aliases, + * or whether such values should be rejected with a missing parameter exception. + * The default is {@code false}, so by default input like {@code -x=subcommand} is rejected if {@code -x} is an option that takes a String parameter, and {@code subcommand} is a subcommand of this command. + * @return {@code true} when options can have parameter values that match subcommand names or aliases, {@code false} when such values should be rejected with a missing parameter exception + * @since 4.7.7-SNAPSHOT + * @see ParserSpec#allowSubcommandsAsOptionParameters() + */ + public boolean isAllowSubcommandsAsOptionParameters() { + return getCommandSpec().parser().allowSubcommandsAsOptionParameters(); + } + /** Sets whether options can have parameter values that match subcommand names or aliases, or whether such values should be rejected with a missing parameter exception. + * The default is {@code false}, so by default + * input like {@code -x=subcommand} is rejected if {@code -x} is an option that takes a String parameter, and {@code subcommand} is a subcommand of this command. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting. When {@code true}, options can have parameter values that match subcommand names or aliases, when {@code false}, such values are rejected with a missing parameter exception + * @return this {@code CommandLine} object, to allow method chaining + * @since 4.7.7-SNAPSHOT + * @see ParserSpec#allowSubcommandsAsOptionParameters(boolean) + */ + public CommandLine setAllowSubcommandsAsOptionParameters(boolean newValue) { + getCommandSpec().parser().allowSubcommandsAsOptionParameters(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setAllowSubcommandsAsOptionParameters(newValue); + } + return this; + } + /** Returns whether options can have parameter values that match the name of an option in this command, + * or whether such values should be rejected with a missing parameter exception. + * The default is {@code false}, so by default input like {@code -x=--some-option} is rejected if {@code -x} is an option that takes a String parameter, and {@code --some-option} is an option of this command. + *This method only considers actual options of this command, as opposed to {@link #isUnmatchedOptionsAllowedAsOptionParameters()}, which considers values that resemble options.
+ * @return {@code true} when options can have parameter values that match the name of an option in this command, {@code false} when such values should be rejected with a missing parameter exception + * @since 4.7.7-SNAPSHOT + * @see #isUnmatchedOptionsAllowedAsOptionParameters() + * @see ParserSpec#allowOptionsAsOptionParameters() + */ + public boolean isAllowOptionsAsOptionParameters() { + return getCommandSpec().parser().allowOptionsAsOptionParameters(); + } + /** Sets whether options can have parameter values that match the name of an option in this command, or whether such values should be rejected with a missing parameter exception. + * The default is {@code false}, so by default + * input like {@code -x=--some-option} is rejected if {@code -x} is an option that takes a String parameter, and {@code --some-option} is an option of this command. + *This method only considers actual options of this command, as opposed to {@link #setUnmatchedOptionsAllowedAsOptionParameters(boolean)}, which considers values that resemble options.
+ *Use with caution! When set to {@code true}, any option in the command will consume the maximum number of arguments possible for its arity. + * This means that an option with {@code arity = "*"} will consume all command line arguments following that option. + * If this is not what you want, consider custom parameter processing.
+ *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting. When {@code true}, options can have parameter values that match the name of an option in this command, when {@code false}, such values are rejected with a missing parameter exception + * @return this {@code CommandLine} object, to allow method chaining + * @since 4.7.7-SNAPSHOT + * @see #setUnmatchedOptionsAllowedAsOptionParameters(boolean) + * @see ParserSpec#allowOptionsAsOptionParameters(boolean) + */ + public CommandLine setAllowOptionsAsOptionParameters(boolean newValue) { + getCommandSpec().parser().allowOptionsAsOptionParameters(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setAllowOptionsAsOptionParameters(newValue); + } + return this; + } + + /** Returns whether options can have parameter values that resemble an option, or whether such values should be rejected as unknown options. + * The default is {@code true}, so by default input like {@code -x=-unknown} is accepted if {@code -x} is an option that takes a String parameter. + *This method only considers values that resemble options, as opposed to {@link #isAllowOptionsAsOptionParameters()}, which considers actual options of this command.
+ * @return {@code true} when options can have parameter values that resemble an option, {@code false} when such values should be rejected as unknown options + * @since 4.4 + * @see #isAllowOptionsAsOptionParameters() + * @see ParserSpec#unmatchedOptionsAllowedAsOptionParameters() + */ + public boolean isUnmatchedOptionsAllowedAsOptionParameters() { + return getCommandSpec().parser().unmatchedOptionsAllowedAsOptionParameters(); + } + + /** Sets whether options can have parameter values that resemble an option, or whether such values should be rejected as unknown options. + * The default is {@code true}, so by default + * input like {@code -x=-unknown} is accepted if {@code -x} is an option that takes a String parameter. + *This method only considers values that resemble options, as opposed to {@link #setAllowOptionsAsOptionParameters(boolean)}, which considers actual options of this command.
+ *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting. When {@code true}, options can have parameter values that resemble an option, when {@code false}, such values are rejected as unknown options + * @return this {@code CommandLine} object, to allow method chaining + * @since 4.4 + * @see #setAllowOptionsAsOptionParameters(boolean) + * @see ParserSpec#unmatchedOptionsAllowedAsOptionParameters(boolean) + */ + public CommandLine setUnmatchedOptionsAllowedAsOptionParameters(boolean newValue) { + getCommandSpec().parser().unmatchedOptionsAllowedAsOptionParameters(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setUnmatchedOptionsAllowedAsOptionParameters(newValue); + } + return this; + } + /** Returns whether arguments on the command line that resemble an option should be treated as positional parameters. + * The default is {@code false} and the parser behaviour depends on {@link #isUnmatchedArgumentsAllowed()}. + * @return {@code true} arguments on the command line that resemble an option should be treated as positional parameters, {@code false} otherwise + * @see #getUnmatchedArguments() + * @since 3.0 + */ + public boolean isUnmatchedOptionsArePositionalParams() { + return getCommandSpec().parser().unmatchedOptionsArePositionalParams(); + } + + /** Sets whether arguments on the command line that resemble an option should be treated as positional parameters. + * The default is {@code false}. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting. When {@code true}, arguments on the command line that resemble an option should be treated as positional parameters. + * @return this {@code CommandLine} object, to allow method chaining + * @since 3.0 + * @see #getUnmatchedArguments() + * @see #isUnmatchedArgumentsAllowed + */ + public CommandLine setUnmatchedOptionsArePositionalParams(boolean newValue) { + getCommandSpec().parser().unmatchedOptionsArePositionalParams(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setUnmatchedOptionsArePositionalParams(newValue); + } + return this; + } + + /** Returns whether the end user may specify arguments on the command line that are not matched to any option or parameter fields. + * The default is {@code false} and a {@link UnmatchedArgumentException} is thrown if this happens. + * When {@code true}, the last unmatched arguments are available via the {@link #getUnmatchedArguments()} method. + * @return {@code true} if the end use may specify unmatched arguments on the command line, {@code false} otherwise + * @see #getUnmatchedArguments() + * @since 0.9.7 + */ + public boolean isUnmatchedArgumentsAllowed() { + return getCommandSpec().parser().unmatchedArgumentsAllowed(); + } + + /** Sets whether the end user may specify unmatched arguments on the command line without a {@link UnmatchedArgumentException} being thrown. + * The default is {@code false}. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param newValue the new setting. When {@code true}, the last unmatched arguments are available via the {@link #getUnmatchedArguments()} method. + * @return this {@code CommandLine} object, to allow method chaining + * @since 0.9.7 + * @see #getUnmatchedArguments() + */ + public CommandLine setUnmatchedArgumentsAllowed(boolean newValue) { + getCommandSpec().parser().unmatchedArgumentsAllowed(newValue); + for (CommandLine command : getCommandSpec().subcommands().values()) { + command.setUnmatchedArgumentsAllowed(newValue); + } + return this; + } + + /** Returns the list of unmatched command line arguments, if any. + * @return the list of unmatched command line arguments or an empty list + * @see #isUnmatchedArgumentsAllowed() + * @since 0.9.7 + */ + public ListCommands can override these defaults with annotations (e.g. {@code @Command(exitCodeOnInvalidInput = 64, exitCodeOnExecutionException = 70)} + * or programmatically (e.g. {@link CommandSpec#exitCodeOnInvalidInput(int)}).
+ *Additionally, there are several mechanisms for commands to return custom exit codes. + * See the javadoc of the {@link #execute(String...) execute} method for details.
+ *There are a few conventions, but there is no + * standard. The specific set of codes returned is unique to the program that sets it. + * Typically an exit code of zero indicates success, any non-zero exit code indicates failure. For reference, here are a few conventions:
+ *Note that *nix shells may restrict exit codes to the 0-255 range, DOS seems to allow larger numbers. + * See this StackOverflow question.
+ * @since 4.0 */ + public static final class ExitCode { + /** Return value from the {@link #execute(String...) execute} and + * {@link #executeHelpRequest(ParseResult) executeHelpRequest} methods signifying successful termination. + *The value of this constant is {@value}.
*/ + public static final int OK = 0; + /** Return value from the {@link #execute(String...) execute} method signifying internal software error: an exception occurred when invoking the Runnable, Callable or Method user object of a command.The value of this constant is {@value}.
*/ + public static final int SOFTWARE = 1; + /** Return value from the {@link #execute(String...) execute} method signifying command line usage error: user input for the command was incorrect, e.g., the wrong number of arguments, a bad flag, a bad syntax in a parameter, or whatever.The value of this constant is {@value}.
*/ + public static final int USAGE = 2; + private ExitCode() {} // don't instantiate + } + + /** {@code @Command}-annotated classes can implement this interface to specify an exit code that will be returned + * from the {@link #execute(String...) execute} method when the command is successfully invoked. + * + *Example usage:
+ *+ * @Command + * class MyCommand implements Runnable, IExitCodeGenerator { + * public void run() { System.out.println("Hello"); } + * public int getExitCode() { return 123; } + * } + * CommandLine cmd = new CommandLine(new MyCommand()); + * int exitCode = cmd.execute(args); + * assert exitCode == 123; + * System.exit(exitCode); + *+ * @since 4.0 + */ + public interface IExitCodeGenerator { + /** Returns the exit code that should be returned from the {@link #execute(String...) execute} method. + * @return the exit code + */ + int getExitCode(); + } + /** Interface that provides the appropriate exit code that will be returned from the {@link #execute(String...) execute} + * method for an exception that occurred during parsing or while invoking the command's Runnable, Callable, or Method. + *
Example usage:
+ *+ * @Command + * class FailingCommand implements Callable<Void> { + * public Void call() throws IOException { + * throw new IOException("error"); + * } + * } + * IExitCodeExceptionMapper mapper = new IExitCodeExceptionMapper() { + * public int getExitCode(Throwable t) { + * if (t instanceof IOException && "error".equals(t.getMessage())) { + * return 123; + * } + * return 987; + * } + * } + * + * CommandLine cmd = new CommandLine(new FailingCommand()); + * cmd.setExitCodeExceptionMapper(mapper); + * int exitCode = cmd.execute(args); + * assert exitCode == 123; + * System.exit(exitCode); + *+ * @see #setExitCodeExceptionMapper(IExitCodeExceptionMapper) + * @since 4.0 + */ + public interface IExitCodeExceptionMapper { + /** Returns the exit code that should be returned from the {@link #execute(String...) execute} method. + * @param exception the exception that occurred during parsing or while invoking the command's Runnable, Callable, or Method. + * @return the exit code + */ + int getExitCode(Throwable exception); + } + private static int mappedExitCode(Throwable t, IExitCodeExceptionMapper mapper, int defaultExitCode) { + try { + return (mapper != null) ? mapper.getExitCode(t) : defaultExitCode; + } catch (Exception ex) { + ex.printStackTrace(); + return defaultExitCode; + } + } + + /** Returns the color scheme to use when printing help. + * The default value is the {@linkplain acrostics.CommandLine.Help#defaultColorScheme(CommandLine.Help.Ansi) default color scheme} with {@link Help.Ansi#AUTO Ansi.AUTO}. + * @see #execute(String...) + * @see #usage(PrintStream) + * @see #usage(PrintWriter) + * @see #getUsageMessage() + * @see Help#defaultColorScheme(CommandLine.Help.Ansi) + * @since 4.0 + */ + public Help.ColorScheme getColorScheme() { return colorScheme; } + + /** Sets the color scheme to use when printing help. + *
The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param colorScheme the new color scheme + * @see #execute(String...) + * @see #usage(PrintStream) + * @see #usage(PrintWriter) + * @see #getUsageMessage() + * @since 4.0 + */ + public CommandLine setColorScheme(Help.ColorScheme colorScheme) { + this.colorScheme = Assert.notNull(colorScheme, "colorScheme"); + for (CommandLine sub : getSubcommands().values()) { sub.setColorScheme(colorScheme); } + return this; + } + + /** Returns the writer used when printing user-requested usage help or version help during command {@linkplain #execute(String...) execution}. + * Defaults to a PrintWriter wrapper around {@code System.out} unless {@link #setOut(PrintWriter)} was called with a different writer. + *This method is used by {@link #execute(String...)}. Custom {@link IExecutionStrategy IExecutionStrategy} implementations should also use this writer. + *
+ * By convention, when the user requests + * help with a {@code --help} or similar option, the usage help message is printed to the standard output stream so that it can be easily searched and paged.
+ * @since 4.0 */ + public PrintWriter getOut() { + if (out == null) { setOut(newPrintWriter(System.out, getStdoutEncoding())); } + return out; + } + + /** Sets the writer to use when printing user-requested usage help or version help during command {@linkplain #execute(String...) execution}. + *This method is used by {@link #execute(String...)}. Custom {@link IExecutionStrategy IExecutionStrategy} implementations should also use this writer.
+ *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param out the new PrintWriter to use + * @return this CommandLine for method chaining + * @since 4.0 + */ + public CommandLine setOut(PrintWriter out) { + this.out = Assert.notNull(out, "out"); + for (CommandLine sub : getSubcommands().values()) { sub.setOut(out); } + return this; + } + + /** Returns the writer to use when printing diagnostic (error) messages during command {@linkplain #execute(String...) execution}. + * Defaults to a PrintWriter wrapper around {@code System.err}, unless {@link #setErr(PrintWriter)} was called with a different writer. + *This method is used by {@link #execute(String...)}. + * {@link IParameterExceptionHandler IParameterExceptionHandler} and {@link IExecutionExceptionHandler IExecutionExceptionHandler} implementations + * should use this writer to print error messages (which may include a usage help message) when an unexpected error occurs.
+ * @since 4.0 */ + public PrintWriter getErr() { + if (err == null) { setErr(newPrintWriter(System.err, getStderrEncoding())); } + return err; + } + + /** Sets the writer to use when printing diagnostic (error) messages during command {@linkplain #execute(String...) execution}. + *This method is used by {@link #execute(String...)}. + * {@link IParameterExceptionHandler IParameterExceptionHandler} and {@link IExecutionExceptionHandler IExecutionExceptionHandler} implementations + * should use this writer to print error messages (which may include a usage help message) when an unexpected error occurs.
+ *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param err the new PrintWriter to use + * @return this CommandLine for method chaining + * @since 4.0 */ + public CommandLine setErr(PrintWriter err) { + this.err = Assert.notNull(err, "err"); + for (CommandLine sub : getSubcommands().values()) { sub.setErr(err); } + return this; + } + + /** + * Returns the mapper that was set by the application to map from exceptions to exit codes, for use by the {@link #execute(String...) execute} method. + * @return the mapper that was {@linkplain #setExitCodeExceptionMapper(IExitCodeExceptionMapper) set}, or {@code null} if none was set + * @since 4.0 */ + public IExitCodeExceptionMapper getExitCodeExceptionMapper() { return exitCodeExceptionMapper; } + + /** Sets the mapper used by the {@link #execute(String...) execute} method to map exceptions to exit codes. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param exitCodeExceptionMapper the new value + * @return this CommandLine for method chaining + * @since 4.0 */ + public CommandLine setExitCodeExceptionMapper(IExitCodeExceptionMapper exitCodeExceptionMapper) { + this.exitCodeExceptionMapper = Assert.notNull(exitCodeExceptionMapper, "exitCodeExceptionMapper"); + for (CommandLine sub : getSubcommands().values()) { sub.setExitCodeExceptionMapper(exitCodeExceptionMapper); } + return this; + } + + /** Returns the execution strategy used by the {@link #execute(String...) execute} method to invoke + * the business logic on the user objects of this command and/or the user-specified subcommand(s). + * The default value is {@link RunLast RunLast}. + * @return the execution strategy to run the user-specified command + * @since 4.0 */ + public IExecutionStrategy getExecutionStrategy() { return executionStrategy; } + + /** Sets the execution strategy that the {@link #execute(String...) execute} method should use to invoke + * the business logic on the user objects of this command and/or the user-specified subcommand(s). + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param executionStrategy the new execution strategy to run the user-specified command + * @return this CommandLine for method chaining + * @since 4.0 */ + public CommandLine setExecutionStrategy(IExecutionStrategy executionStrategy) { + this.executionStrategy = Assert.notNull(executionStrategy, "executionStrategy"); + for (CommandLine sub : getSubcommands().values()) { sub.setExecutionStrategy(executionStrategy); } + return this; + } + + /** + * Returns the handler for dealing with invalid user input when the command is {@linkplain #execute(String...) executed}. + *The default implementation prints an error message describing the problem, followed by either {@linkplain UnmatchedArgumentException#printSuggestions(PrintWriter) suggested alternatives} + * for mistyped options, or the full {@linkplain #usage(PrintWriter, Help.ColorScheme) usage} help message of the {@linkplain ParameterException#getCommandLine() problematic command}; + * it then delegates to the {@linkplain #getExitCodeExceptionMapper() exit code exception mapper} for an exit code, with + * {@link CommandSpec#exitCodeOnInvalidInput() exitCodeOnInvalidInput} as the default exit code.
+ *+ * Alternatively, you can install a "short error message handler" like this: + *
+ *+ * static class ShortErrorMessageHandler implements IParameterExceptionHandler { + * public int handleParseException(ParameterException ex, String[] args) { + * CommandLine cmd = ex.getCommandLine(); + * PrintWriter writer = cmd.getErr(); + * + * writer.println(ex.getMessage()); + * UnmatchedArgumentException.printSuggestions(ex, writer); + * writer.print(cmd.getHelp().fullSynopsis()); + * + * CommandSpec spec = cmd.getCommandSpec(); + * writer.printf("Try '%s --help' for more information.%n", spec.qualifiedName()); + * + * return cmd.getExitCodeExceptionMapper() != null + * ? cmd.getExitCodeExceptionMapper().getExitCode(ex) + * : spec.exitCodeOnInvalidInput(); + * } + * } + *+ *
Install this error handler like this:
+ *+ * new CommandLine(new MyApp()) + * .setParameterExceptionHandler(new ShortErrorMessageHandler()) + * .execute(args); + *+ * @return the handler for dealing with invalid user input + * @since 4.0 */ + public IParameterExceptionHandler getParameterExceptionHandler() { return parameterExceptionHandler; } + + /** + * Sets the handler for dealing with invalid user input when the command is {@linkplain #execute(String...) executed}. + *
The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param parameterExceptionHandler the new handler for dealing with invalid user input + * @return this CommandLine for method chaining + * @see #getParameterExceptionHandler() an example short exception handler + * @since 4.0 */ + public CommandLine setParameterExceptionHandler(IParameterExceptionHandler parameterExceptionHandler) { + this.parameterExceptionHandler = Assert.notNull(parameterExceptionHandler, "parameterExceptionHandler"); + for (CommandLine sub : getSubcommands().values()) { sub.setParameterExceptionHandler(parameterExceptionHandler); } + return this; + } + + /** Returns the handler for dealing with exceptions that occurred in the {@code Callable}, {@code Runnable} or {@code Method} + * user object of a command when the command was {@linkplain #execute(String...) executed}. + *The default implementation rethrows the specified exception.
+ * @return the handler for dealing with exceptions that occurred in the business logic when the {@link #execute(String...) execute} method was invoked. + * @since 4.0 */ + public IExecutionExceptionHandler getExecutionExceptionHandler() { return executionExceptionHandler; } + + /** + * Sets a custom handler for dealing with exceptions that occurred in the {@code Callable}, {@code Runnable} or {@code Method} + * user object of a command when the command was executed via the {@linkplain #execute(String...) execute} method. + *The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its + * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added + * later will have the default setting. To ensure a setting is applied to all + * subcommands, call the setter last, after adding subcommands.
+ * @param executionExceptionHandler the handler for dealing with exceptions that occurred in the business logic when the {@link #execute(String...) execute} method was invoked. + * @return this CommandLine for method chaining + * @since 4.0 */ + public CommandLine setExecutionExceptionHandler(IExecutionExceptionHandler executionExceptionHandler) { + this.executionExceptionHandler = Assert.notNull(executionExceptionHandler, "executionExceptionHandler"); + for (CommandLine sub : getSubcommands().values()) { sub.setExecutionExceptionHandler(executionExceptionHandler); } + return this; + } + + /** + *+ * Convenience method that initializes the specified annotated object from the specified command line arguments. + *
+ * This is equivalent to + *
+ * new CommandLine(command).parseArgs(args); + * return command; + *+ *
All this method does is parse the arguments and populate the annotated fields and methods. + * The caller is responsible for catching any exceptions, handling requests for usage help + * or version information, and invoking the business logic. + * Applications may be interested in using the {@link #execute(String...)} method instead.
+ * + * @param command the object to initialize. This object contains fields annotated with + * {@code @Option} or {@code @Parameters}. + * @param args the command line arguments to parse + * @param+ * Convenience method that derives the command specification from the specified interface class, and returns an + * instance of the specified interface. The interface is expected to have annotated getter methods. Picocli will + * instantiate the interface and the getter methods will return the option and positional parameter values matched on the command line. + *
+ * This is equivalent to + *
+ * CommandLine cli = new CommandLine(spec); + * cli.parse(args); + * return cli.getCommand(); + *+ *
All this method does is parse the arguments and return an instance whose annotated methods return the specified values. + * The caller is responsible for catching any exceptions, handling requests for usage help + * or version information, and invoking the business logic. + * Applications may be interested in using the {@link #execute(String...)} method instead.
+ * + * @param spec the interface that defines the command specification. This object contains getter methods annotated with + * {@code @Option} or {@code @Parameters}. + * @param args the command line arguments to parse + * @param+ * If parsing succeeds, the first element in the returned list is always {@code this CommandLine} object. The + * returned list may contain more elements if subcommands were {@linkplain #addSubcommand(String, Object) registered} + * and these subcommands were initialized by matching command line arguments. If parsing fails, a + * {@link ParameterException} is thrown. + *
+ *All this method does is parse the arguments and populate the annotated fields and methods. + * The caller is responsible for catching any exceptions, handling requests for usage help + * or version information, and invoking the business logic. + * Applications may be interested in using the {@link #execute(String...)} method instead.
+ * + * @param args the command line arguments to parse + * @return a list with the top-level command and any subcommands initialized by this method + * @throws ParameterException if the specified command line arguments are invalid; use + * {@link ParameterException#getCommandLine()} to get the command or subcommand whose user input was invalid + * @deprecated use {@link #parseArgs(String...)} instead + */ + @Deprecated public ListIf parsing fails, a {@link ParameterException} is thrown.
+ *All this method does is parse the arguments and populate the annotated fields and methods. + * The caller is responsible for catching any exceptions, handling requests for usage help + * or version information, and invoking the business logic. + * Applications may be interested in using the {@link #execute(String...)} method instead.
+ * + * @param args the command line arguments to parse + * @return a list with the top-level command and any subcommands initialized by this method + * @throws ParameterException if the specified command line arguments are invalid; use + * {@link ParameterException#getCommandLine()} to get the command or subcommand whose user input was invalid + * @see #execute(String...) + */ + public ParseResult parseArgs(String... args) { + interpreter.parse(args); + return getParseResult(); + } + public ParseResult getParseResult() { return interpreter.parseResultBuilder == null ? null : interpreter.parseResultBuilder.build(); } + + /** Returns the result of calling the user object {@code Callable} or invoking the user object {@code Method} + * after parsing the user input, or {@code null} if this command has not been {@linkplain #execute(String...) executed} + * or if this {@code CommandLine} is for a subcommand that was not specified by the end user on the command line. + *Implementation note:
+ *It is the responsibility of the {@link IExecutionStrategy IExecutionStrategy} to set this value.
+ * @param+ * Implementations of this functions can be passed to the {@link #parseWithHandlers(IParseResultHandler, PrintStream, Help.Ansi, IExceptionHandler, String...) CommandLine::parseWithHandler} + * methods to take some next step after the command line was successfully parsed. + *
+ * @see RunFirst + * @see RunLast + * @see RunAll + * @deprecated Use {@link IExecutionStrategy} instead. + * @since 2.0 */ + @Deprecated public interface IParseResultHandler { + /** Processes a List of {@code CommandLine} objects resulting from successfully + * {@linkplain #parse(String...) parsing} the command line arguments and optionally returns a list of results. + * @param parsedCommands the {@code CommandLine} objects that resulted from successfully parsing the command line arguments + * @param out the {@code PrintStream} to print help to if requested + * @param ansi for printing help messages using ANSI styles and colors + * @return a list of results, or an empty list if there are no results + * @throws ParameterException if a help command was invoked for an unknown subcommand. Any {@code ParameterExceptions} + * thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler} + * @throws ExecutionException if a problem occurred while processing the parse results; use + * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed + */ + List