diff --git a/src/main/java/picocli/CommandLine.java b/src/main/java/picocli/CommandLine.java index 9787f9ad5..92bca1ad5 100644 --- a/src/main/java/picocli/CommandLine.java +++ b/src/main/java/picocli/CommandLine.java @@ -1,8243 +1,8382 @@ -/* - Copyright 2017 Remko Popma - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ -package picocli; - -import java.io.*; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.WildcardType; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.NetworkInterface; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.ByteOrder; -import java.nio.charset.Charset; -import java.text.BreakIterator; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.Callable; -import java.util.regex.Pattern; - -import picocli.CommandLine.Help.Ansi.IStyle; -import picocli.CommandLine.Help.Ansi.Style; -import picocli.CommandLine.Help.Ansi.Text; -import picocli.CommandLine.Model.*; - -import static java.util.Locale.ENGLISH; -import static picocli.CommandLine.Model.ArgsReflection.abbreviate; -import static picocli.CommandLine.Help.Column.Overflow.SPAN; -import static picocli.CommandLine.Help.Column.Overflow.TRUNCATE; -import static picocli.CommandLine.Help.Column.Overflow.WRAP; - -/** - *

- * CommandLine interpreter that uses reflection to initialize an annotated domain object with values obtained from the - * command line arguments. - *

Example

- *
import static picocli.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(type = File.class, 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 domain 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 picocli version {@value}. */ - public static final String VERSION = "3.1.1-SNAPSHOT"; - - private final Tracer tracer = new Tracer(); - private final CommandSpec commandSpec; - private final Interpreter interpreter; - private final IFactory factory; - - /** - * 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 subcommand 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, in which case picocli automatically - * constructs a {@code CommandSpec} from this user object. - *

- * When the {@link #parse(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, in which case picocli automatically - * constructs a {@code CommandSpec} from this user object. - *

- * When the {@link #parse(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.factory = Assert.notNull(factory, "factory"); - interpreter = new Interpreter(); - commandSpec = CommandSpec.forAnnotatedObject(command, factory); - commandSpec.commandLine(this); - commandSpec.validate(); - if (commandSpec.unmatchedArgsBindings().size() > 0) { setUnmatchedArgumentsAllowed(true); } - } - - /** - * 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 picocli 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 getMixins() { - Map mixins = getCommandSpec().mixins(); - Map result = new LinkedHashMap(); - for (String name : mixins.keySet()) { result.put(name, mixins.get(name).userObject); } - return result; - } - - /** Registers a subcommand with the specified name. For example: - *
-     * 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 - * @param command the object to initialize with command line arguments following the subcommand name. - * This may be a {@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() - */ - 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 - * @param command the object to initialize with command line arguments following the subcommand name. - * This may be a {@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) - */ - public CommandLine addSubcommand(String name, Object command, String... aliases) { - CommandLine subcommandLine = toCommandLine(command, factory); - subcommandLine.getCommandSpec().aliases(aliases); - getCommandSpec().addSubcommand(name, subcommandLine); - CommandLine.Model.CommandReflection.initParentCommand(subcommandLine.getCommandSpec().userObject(), getCommandSpec().userObject()); - 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 Map getSubcommands() { - return new LinkedHashMap(getCommandSpec().subcommands()); - } - /** - * Returns the command that this is a subcommand of, or {@code null} if this is a top-level command. - * @return the command that this is a subcommand of, or {@code null} if this is a top-level command - * @see #addSubcommand(String, Object) - * @see Command#subcommands() - * @since 0.9.8 - */ - public CommandLine getParent() { - CommandSpec parent = getCommandSpec().parent(); - return parent == null ? null : parent.commandLine(); - } - - /** Returns the annotated user object that this {@code CommandLine} instance was constructed with. - * @param the type of the variable that the return value is being assigned to - * @return the annotated object that this {@code CommandLine} instance was constructed with - * @since 0.9.7 - */ - @SuppressWarnings("unchecked") - public T getCommand() { - return (T) getCommandSpec().userObject(); - } - - /** Returns {@code true} if an option annotated with {@link Option#usageHelp()} was specified on the command line. - * @return whether the parser encountered an option annotated with {@link Option#usageHelp()}. - * @since 0.9.8 */ - public boolean isUsageHelpRequested() { return interpreter.parseResult != null && interpreter.parseResult.usageHelpRequested; } - - /** Returns {@code true} if an option annotated with {@link Option#versionHelp()} was specified on the command line. - * @return whether the parser encountered an option annotated with {@link Option#versionHelp()}. - * @since 0.9.8 */ - public boolean isVersionHelpRequested() { return interpreter.parseResult != null && interpreter.parseResult.versionHelpRequested; } - - /** Returns whether the value of boolean flag options should be "toggled" when the option is matched. - * By default, 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}. If toggling is off, flags are simply set to {@code true}. - * @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 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 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 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 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 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 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 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 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 List getUnmatchedArguments() { - return interpreter.parseResult == null ? Collections.emptyList() : Collections.unmodifiableList(interpreter.parseResult.unmatched); - } - - /** - *

- * Convenience method that initializes the specified annotated object from the specified command line arguments. - *

- * This is equivalent to - *

-     * CommandLine cli = new CommandLine(command);
-     * cli.parse(args);
-     * return command;
-     * 
- * - * @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 the type of the annotated object - * @return the specified annotated object - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ParameterException if the specified command line arguments are invalid - * @since 0.9.7 - */ - public static T populateCommand(T command, String... args) { - CommandLine cli = toCommandLine(command, new DefaultFactory()); - cli.parse(args); - return command; - } - - /** Parses the specified command line arguments and returns a list of {@code CommandLine} objects representing the - * top-level command and any subcommands (if any) that were recognized and initialized during the parsing process. - *

- * 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. - *

- * - * @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 - */ - public List parse(String... args) { - return interpreter.parse(args); - } - /** Parses the specified command line arguments and returns a list of {@code ParseResult} with the options, positional - * parameters, and subcommands (if any) that were recognized and initialized during the parsing process. - *

If parsing fails, a {@link ParameterException} is thrown.

- * - * @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 - */ - public ParseResult parseArgs(String... args) { - interpreter.parse(args); - return interpreter.parseResult.build(); - } - public ParseResult getParseResult() { return interpreter.parseResult == null ? null : interpreter.parseResult.build(); } - /** - * Represents a function that can process a List of {@code CommandLine} objects resulting from successfully - * {@linkplain #parse(String...) parsing} the command line arguments. This is a - * functional interface - * whose functional method is {@link #handleParseResult(List, PrintStream, CommandLine.Help.Ansi)}. - *

- * 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 IParseResultHandler2} instead. - * @since 2.0 */ - @Deprecated public static 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 handleParseResult(List parsedCommands, PrintStream out, Help.Ansi ansi) throws ExecutionException; - } - - /** - * Represents a function that can process the {@code ParseResult} object resulting from successfully - * {@linkplain #parseArgs(String...) parsing} the command line arguments. This is a - * functional interface - * whose functional method is {@link IParseResultHandler2#handleParseResult(CommandLine.ParseResult)}. - *

- * Implementations of this function can be passed to the {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) CommandLine::parseWithHandlers} - * methods to take some next step after the command line was successfully parsed. - *

- * This interface replaces the {@link IParseResultHandler} interface; it takes the parse result as a {@code ParseResult} - * object instead of a List of {@code CommandLine} objects, and it has the freedom to select the {@link Help.Ansi} style - * to use and what {@code PrintStreams} to print to. - *

- * @param the return type of this handler - * @see RunFirst - * @see RunLast - * @see RunAll - * @since 3.0 */ - public static interface IParseResultHandler2 { - /** Processes the {@code ParseResult} object resulting from successfully - * {@linkplain CommandLine#parseArgs(String...) parsing} the command line arguments and returns a return value. - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @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 IExceptionHandler2} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - R handleParseResult(ParseResult parseResult) throws ExecutionException; - } - /** - * Represents a function that can handle a {@code ParameterException} that occurred while - * {@linkplain #parse(String...) parsing} the command line arguments. This is a - * functional interface - * whose functional method is {@link #handleException(CommandLine.ParameterException, PrintStream, CommandLine.Help.Ansi, String...)}. - *

- * Implementations of this function can be passed to the {@link #parseWithHandlers(IParseResultHandler, PrintStream, Help.Ansi, IExceptionHandler, String...) CommandLine::parseWithHandlers} - * methods to handle situations when the command line could not be parsed. - *

- * @deprecated Use {@link IExceptionHandler2} instead. - * @see DefaultExceptionHandler - * @since 2.0 */ - @Deprecated public static interface IExceptionHandler { - /** Handles a {@code ParameterException} that occurred while {@linkplain #parse(String...) parsing} the command - * line arguments and optionally returns a list of results. - * @param ex the ParameterException describing the problem that occurred while parsing the command line arguments, - * and the CommandLine representing the command or subcommand whose input was invalid - * @param out the {@code PrintStream} to print help to if requested - * @param ansi for printing help messages using ANSI styles and colors - * @param args the command line arguments that could not be parsed - * @return a list of results, or an empty list if there are no results - */ - List handleException(ParameterException ex, PrintStream out, Help.Ansi ansi, String... args); - } - /** - * Classes implementing this interface know how to handle {@code ParameterExceptions} (usually from invalid user input) - * and {@code ExecutionExceptions} that occurred while executing the {@code Runnable} or {@code Callable} command. - *

- * Implementations of this interface can be passed to the - * {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) CommandLine::parseWithHandlers} method. - *

- * This interface replaces the {@link IParseResultHandler} interface. - *

- * @param the return type of this handler - * @see DefaultExceptionHandler - * @since 3.0 */ - public static interface IExceptionHandler2 { - /** Handles a {@code ParameterException} that occurred while {@linkplain #parseArgs(String...) parsing} the command - * line arguments and optionally returns a list of results. - * @param ex the ParameterException describing the problem that occurred while parsing the command line arguments, - * and the CommandLine representing the command or subcommand whose input was invalid - * @param args the command line arguments that could not be parsed - * @return an object resulting from handling the exception - */ - R handleParseException(ParameterException ex, String[] args); - /** Handles a {@code ExecutionException} that occurred while executing the {@code Runnable} or - * {@code Callable} command and optionally returns a list of results. - * @param ex the ExecutionException describing the problem that occurred while executing the {@code Runnable} or - * {@code Callable} command, and the CommandLine representing the command or subcommand that was being executed - * @param parseResult the result of parsing the command line arguments - * @return an object resulting from handling the exception - */ - R handleExecutionException(ExecutionException ex, ParseResult parseResult); - } - - /** Abstract superclass for {@link IParseResultHandler2} and {@link IExceptionHandler2} implementations. - *

Note that {@code AbstractHandler} is a generic type. This, along with the abstract {@code self} method, - * allows method chaining to work properly in subclasses, without the need for casts. An example subclass can look like this:

- *
{@code
-     * class MyResultHandler extends AbstractHandler implements IParseResultHandler2 {
-     *
-     *     public MyReturnType handleParseResult(ParseResult parseResult) { ... }
-     *
-     *     protected MyResultHandler self() { return this; }
-     * }
-     * }
- * @param the return type of this handler - * @param The type of the handler subclass; for fluent API method chaining - * @since 3.0 */ - public static abstract class AbstractHandler> { - private Help.Ansi ansi = Help.Ansi.AUTO; - private Integer exitCode; - private PrintStream out = System.out; - private PrintStream err = System.err; - - /** Returns the stream to print command output to. Defaults to {@code System.out}, unless {@link #useOut(PrintStream)} - * was called with a different stream. - *

{@code IParseResultHandler2} implementations should use this stream. - * 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.

*/ - public PrintStream out() { return out; } - /** Returns the stream to print diagnostic messages to. Defaults to {@code System.err}, unless {@link #useErr(PrintStream)} - * was called with a different stream.

{@code IExceptionHandler2} implementations should use this stream to print error - * messages (which may include a usage help message) when an unexpected error occurs.

*/ - public PrintStream err() { return err; } - /** Returns the ANSI style to use. Defaults to {@code Help.Ansi.AUTO}, unless {@link #useAnsi(CommandLine.Help.Ansi)} was called with a different setting. */ - public Help.Ansi ansi() { return ansi; } - /** Returns the exit code to use as the termination status, or {@code null} (the default) if the handler should - * not call {@link System#exit(int)} after processing completes. - * @see #andExit(int) */ - public Integer exitCode() { return exitCode; } - /** Returns {@code true} if an exit code was set with {@link #andExit(int)}, or {@code false} (the default) if - * the handler should not call {@link System#exit(int)} after processing completes. */ - public boolean hasExitCode() { return exitCode != null; } - - /** Convenience method for subclasses that returns the specified result object if no exit code was set, - * or otherwise, if an exit code {@linkplain #andExit(int) was set}, calls {@code System.exit} with the configured - * exit code to terminate the currently running Java virtual machine. */ - protected R returnResultOrExit(R result) { - if (hasExitCode()) { System.exit(exitCode()); } - return result; - } - - /** Returns {@code this} to allow method chaining when calling the setters for a fluent API. */ - protected abstract T self(); - - /** Sets the stream to print command output to. For use by {@code IParseResultHandler2} implementations. - * @see #out() */ - public T useOut(PrintStream out) { this.out = Assert.notNull(out, "out"); return self(); } - /** Sets the stream to print diagnostic messages to. For use by {@code IExceptionHandler2} implementations. - * @see #err()*/ - public T useErr(PrintStream err) { this.err = Assert.notNull(err, "err"); return self(); } - /** Sets the ANSI style to use. - * @see #ansi() */ - public T useAnsi(Help.Ansi ansi) { this.ansi = Assert.notNull(ansi, "ansi"); return self(); } - /** Indicates that the handler should call {@link System#exit(int)} after processing completes and sets the exit code to use as the termination status. */ - public T andExit(int exitCode) { this.exitCode = exitCode; return self(); } - } - - /** - * Default exception handler that handles invalid user input by printing the exception message, followed by the usage - * message for the command or subcommand whose input was invalid. - *

{@code ParameterExceptions} (invalid user input) is handled like this:

- *
-     *     err().println(paramException.getMessage());
-     *     paramException.getCommandLine().usage(err(), ansi());
-     *     if (hasExitCode()) System.exit(exitCode()); else return prototypeReturnValue;
-     * 
- *

{@code ExecutionExceptions} that occurred while executing the {@code Runnable} or {@code Callable} command are simply rethrown and not handled.

- * @since 2.0 */ - @SuppressWarnings("deprecation") - public static class DefaultExceptionHandler extends AbstractHandler> implements IExceptionHandler, IExceptionHandler2 { - public List handleException(ParameterException ex, PrintStream out, Help.Ansi ansi, String... args) { - internalHandleParseException(ex, out, ansi, args); return Collections.emptyList(); } - - /** Prints the message of the specified exception, followed by the usage message for the command or subcommand - * whose input was invalid, to the stream returned by {@link #err()}. - * @param ex the ParameterException describing the problem that occurred while parsing the command line arguments, - * and the CommandLine representing the command or subcommand whose input was invalid - * @param args the command line arguments that could not be parsed - * @return the empty list - * @since 3.0 */ - public R handleParseException(ParameterException ex, String[] args) { - internalHandleParseException(ex, err(), ansi(), args); return returnResultOrExit(null); } - - private void internalHandleParseException(ParameterException ex, PrintStream out, Help.Ansi ansi, String[] args) { - out.println(ex.getMessage()); - ex.getCommandLine().usage(out, ansi); - } - - /** This implementation always simply rethrows the specified exception. - * @param ex the ExecutionException describing the problem that occurred while executing the {@code Runnable} or {@code Callable} command - * @param parseResult the result of parsing the command line arguments - * @return nothing: this method always rethrows the specified exception - * @throws ExecutionException always rethrows the specified exception - * @since 3.0 */ - public R handleExecutionException(ExecutionException ex, ParseResult parseResult) { throw ex; } - @Override protected DefaultExceptionHandler self() { return this; } - } - /** Convenience method that returns {@code new DefaultExceptionHandler>()}. */ - public static DefaultExceptionHandler> defaultExceptionHandler() { return new DefaultExceptionHandler>(); } - - /** @deprecated use {@link #printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi)} instead - * @since 2.0 */ - @Deprecated public static boolean printHelpIfRequested(List parsedCommands, PrintStream out, Help.Ansi ansi) { - return printHelpIfRequested(parsedCommands, out, out, ansi); - } - - /** Delegates to {@link #printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi)} with - * {@code parseResult.asCommandLineList(), System.out, System.err, Help.Ansi.AUTO}. - * @since 3.0 */ - public static boolean printHelpIfRequested(ParseResult parseResult) { - return printHelpIfRequested(parseResult.asCommandLineList(), System.out, System.err, Help.Ansi.AUTO); - } - /** - * Helper method that may be useful when processing the list of {@code CommandLine} objects that result from successfully - * {@linkplain #parse(String...) parsing} command line arguments. This method prints out - * {@linkplain #usage(PrintStream, Help.Ansi) usage help} if {@linkplain #isUsageHelpRequested() requested} - * or {@linkplain #printVersionHelp(PrintStream, Help.Ansi) version help} if {@linkplain #isVersionHelpRequested() requested} - * and returns {@code true}. If the command is a {@link Command#helpCommand()} and {@code runnable} or {@code callable}, - * that command is executed and this method returns {@code true}. - * Otherwise, if none of the specified {@code CommandLine} objects have help requested, - * this method returns {@code false}.

- * Note that this method only looks at the {@link Option#usageHelp() usageHelp} and - * {@link Option#versionHelp() versionHelp} attributes. The {@link Option#help() help} attribute is ignored. - *

Implementation note:

- * When an error occurs while processing the help request, it is recommended custom Help commands throw a - * {@link ParameterException} with a reference to the parent command. This will print the error message and the - * usage for the parent command, and will use the exit code of the exception handler if one was set. - *

- * @param parsedCommands the list of {@code CommandLine} objects to check if help was requested - * @param out the {@code PrintStream} to print help to if requested - * @param err the error string to print diagnostic messages to, in addition to the output from the exception handler - * @param ansi for printing help messages using ANSI styles and colors - * @return {@code true} if help was printed, {@code false} otherwise - * @see IHelpCommandInitializable - * @since 3.0 */ - public static boolean printHelpIfRequested(List parsedCommands, PrintStream out, PrintStream err, Help.Ansi ansi) { - for (int i = 0; i < parsedCommands.size(); i++) { - CommandLine parsed = parsedCommands.get(i); - if (parsed.isUsageHelpRequested()) { - parsed.usage(out, ansi); - return true; - } else if (parsed.isVersionHelpRequested()) { - parsed.printVersionHelp(out, ansi); - return true; - } else if (parsed.getCommandSpec().helpCommand()) { - if (parsed.getCommand() instanceof IHelpCommandInitializable) { - ((IHelpCommandInitializable) parsed.getCommand()).init(parsed, ansi, out, err); - } - execute(parsed, new ArrayList()); - return true; - } - } - return false; - } - private static List execute(CommandLine parsed, List executionResult) { - Object command = parsed.getCommand(); - if (command instanceof Runnable) { - try { - ((Runnable) command).run(); - executionResult.add(null); // for compatibility with picocli 2.x - return executionResult; - } catch (ParameterException ex) { - throw ex; - } catch (ExecutionException ex) { - throw ex; - } catch (Exception ex) { - throw new ExecutionException(parsed, "Error while running command (" + command + "): " + ex, ex); - } - } else if (command instanceof Callable) { - try { - @SuppressWarnings("unchecked") Callable callable = (Callable) command; - executionResult.add(callable.call()); - return executionResult; - } catch (ParameterException ex) { - throw ex; - } catch (ExecutionException ex) { - throw ex; - } catch (Exception ex) { - throw new ExecutionException(parsed, "Error while calling command (" + command + "): " + ex, ex); - } - } - throw new ExecutionException(parsed, "Parsed command (" + command + ") is not Runnable or Callable"); - } - /** Command line parse result handler that returns a value. This handler prints help if requested, and otherwise calls - * {@link #handle(CommandLine.ParseResult)} with the parse result. Facilitates implementation of the {@link IParseResultHandler2} interface. - *

Note that {@code AbstractParseResultHandler} is a generic type. This, along with the abstract {@code self} method, - * allows method chaining to work properly in subclasses, without the need for casts. An example subclass can look like this:

- *
{@code
-     * class MyResultHandler extends AbstractParseResultHandler {
-     *
-     *     protected MyReturnType handle(ParseResult parseResult) throws ExecutionException { ... }
-     *
-     *     protected MyResultHandler self() { return this; }
-     * }
-     * }
- * @since 3.0 */ - public abstract static class AbstractParseResultHandler extends AbstractHandler> implements IParseResultHandler2 { - /** Prints help if requested, and otherwise calls {@link #handle(CommandLine.ParseResult)}. - * Finally, either a list of result objects is returned, or the JVM is terminated if an exit code {@linkplain #andExit(int) was set}. - * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return the result of processing parse results, may be the specified prototype or some other object - * @throws ParameterException if the {@link HelpCommand HelpCommand} 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 IExceptionHandler2} - * @throws ExecutionException if a problem occurred while processing the parse results; client code can use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - public R handleParseResult(ParseResult parseResult) throws ExecutionException { - if (printHelpIfRequested(parseResult.asCommandLineList(), out(), err(), ansi())) { - return returnResultOrExit(null); - } - return returnResultOrExit(handle(parseResult)); - } - - /** Processes the specified {@code ParseResult} and returns the result as a list of objects. - * Implementations are responsible for catching any exceptions thrown in the {@code handle} method, and - * rethrowing an {@code ExecutionException} that details the problem and captures the offending {@code CommandLine} object. - * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return the result of processing parse results, may be the specified prototype or some other object - * @throws ExecutionException if a problem occurred while processing the parse results; client code can use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - */ - protected abstract R handle(ParseResult parseResult) throws ExecutionException; - } - /** - * Command line parse result handler that prints help if requested, and otherwise executes the top-level - * {@code Runnable} or {@code Callable} command. - * For use in the {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) parseWithHandler} methods. - * @since 2.0 */ - public static class RunFirst extends AbstractParseResultHandler> implements IParseResultHandler { - /** Prints help if requested, and otherwise executes the top-level {@code Runnable} or {@code Callable} command. - * Finally, either a list of result objects is returned, or the JVM is terminated if an exit code {@linkplain #andExit(int) was set}. - * If the top-level command does not implement either {@code Runnable} or {@code Callable}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @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 an empty list if help was requested, or a list containing a single element: the result of calling the - * {@code Callable}, or a {@code null} element if the top-level command was a {@code Runnable} - * @throws ParameterException if the {@link HelpCommand HelpCommand} 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 - */ - public List handleParseResult(List parsedCommands, PrintStream out, Help.Ansi ansi) { - if (printHelpIfRequested(parsedCommands, out, err(), ansi)) { return returnResultOrExit(Collections.emptyList()); } - return returnResultOrExit(execute(parsedCommands.get(0), new ArrayList())); - } - /** Executes the top-level {@code Runnable} or {@code Callable} subcommand. - * If the top-level command does not implement either {@code Runnable} or {@code Callable}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return an empty list if help was requested, or a list containing a single element: the result of calling the - * {@code Callable}, or a {@code null} element if the last (sub)command was a {@code Runnable} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @since 3.0 */ - protected List handle(ParseResult parseResult) throws ExecutionException { - return execute(parseResult.commandSpec().commandLine(), new ArrayList()); // first - } - @Override protected RunFirst self() { return this; } - } - /** - * Command line parse result handler that prints help if requested, and otherwise executes the most specific - * {@code Runnable} or {@code Callable} subcommand. - * For use in the {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) parseWithHandler} methods. - *

- * Something like this:

- *
{@code
-     *     // RunLast implementation: print help if requested, otherwise execute the most specific subcommand
-     *     List parsedCommands = parseResult.asCommandLineList();
-     *     if (CommandLine.printHelpIfRequested(parsedCommands, out(), err(), ansi())) {
-     *         return emptyList();
-     *     }
-     *     CommandLine last = parsedCommands.get(parsedCommands.size() - 1);
-     *     Object command = last.getCommand();
-     *     Object result = null;
-     *     if (command instanceof Runnable) {
-     *         try {
-     *             ((Runnable) command).run();
-     *         } catch (Exception ex) {
-     *             throw new ExecutionException(last, "Error in runnable " + command, ex);
-     *         }
-     *     } else if (command instanceof Callable) {
-     *         try {
-     *             result = ((Callable) command).call();
-     *         } catch (Exception ex) {
-     *             throw new ExecutionException(last, "Error in callable " + command, ex);
-     *         }
-     *     } else {
-     *         throw new ExecutionException(last, "Parsed command (" + command + ") is not Runnable or Callable");
-     *     }
-     *     if (hasExitCode()) { System.exit(exitCode()); }
-     *     return Arrays.asList(result);
-     * }
- *

- * From picocli v2.0, {@code RunLast} is used to implement the {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) run} - * and {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) call} convenience methods. - *

- * @since 2.0 */ - public static class RunLast extends AbstractParseResultHandler> implements IParseResultHandler { - /** Prints help if requested, and otherwise executes the most specific {@code Runnable} or {@code Callable} subcommand. - * Finally, either a list of result objects is returned, or the JVM is terminated if an exit code {@linkplain #andExit(int) was set}. - * If the last (sub)command does not implement either {@code Runnable} or {@code Callable}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @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 an empty list if help was requested, or a list containing a single element: the result of calling the - * {@code Callable}, or a {@code null} element if the last (sub)command was a {@code Runnable} - * @throws ParameterException if the {@link HelpCommand HelpCommand} 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 - */ - public List handleParseResult(List parsedCommands, PrintStream out, Help.Ansi ansi) { - if (printHelpIfRequested(parsedCommands, out, err(), ansi)) { return returnResultOrExit(Collections.emptyList()); } - return returnResultOrExit(execute(parsedCommands.get(parsedCommands.size() - 1), new ArrayList())); - } - /** Executes the most specific {@code Runnable} or {@code Callable} subcommand. - * If the last (sub)command does not implement either {@code Runnable} or {@code Callable}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return an empty list if help was requested, or a list containing a single element: the result of calling the - * {@code Callable}, or a {@code null} element if the last (sub)command was a {@code Runnable} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @since 3.0 */ - protected List handle(ParseResult parseResult) throws ExecutionException { - List parsedCommands = parseResult.asCommandLineList(); - return execute(parsedCommands.get(parsedCommands.size() - 1), new ArrayList()); - } - @Override protected RunLast self() { return this; } - } - /** - * Command line parse result handler that prints help if requested, and otherwise executes the top-level command and - * all subcommands as {@code Runnable} or {@code Callable}. - * For use in the {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) parseWithHandler} methods. - * @since 2.0 */ - public static class RunAll extends AbstractParseResultHandler> implements IParseResultHandler { - /** Prints help if requested, and otherwise executes the top-level command and all subcommands as {@code Runnable} - * or {@code Callable}. Finally, either a list of result objects is returned, or the JVM is terminated if an exit - * code {@linkplain #andExit(int) was set}. If any of the {@code CommandLine} commands does not implement either - * {@code Runnable} or {@code Callable}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @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 an empty list if help was requested, or a list containing the result of executing all commands: - * the return values from calling the {@code Callable} commands, {@code null} elements for commands that implement {@code Runnable} - * @throws ParameterException if the {@link HelpCommand HelpCommand} 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 - */ - public List handleParseResult(List parsedCommands, PrintStream out, Help.Ansi ansi) { - if (printHelpIfRequested(parsedCommands, out, err(), ansi)) { return returnResultOrExit(Collections.emptyList()); } - List result = new ArrayList(); - for (CommandLine parsed : parsedCommands) { - execute(parsed, result); - } - return returnResultOrExit(result); - } - /** Executes the top-level command and all subcommands as {@code Runnable} or {@code Callable}. - * If any of the {@code CommandLine} commands does not implement either {@code Runnable} or {@code Callable}, an {@code ExecutionException} - * is thrown detailing the problem and capturing the offending {@code CommandLine} object. - * - * @param parseResult the {@code ParseResult} that resulted from successfully parsing the command line arguments - * @return an empty list if help was requested, or a list containing the result of executing all commands: - * the return values from calling the {@code Callable} commands, {@code null} elements for commands that implement {@code Runnable} - * @throws ExecutionException if a problem occurred while processing the parse results; use - * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @since 3.0 */ - protected List handle(ParseResult parseResult) throws ExecutionException { - List prototypeResult = new ArrayList(); - execute(parseResult.commandSpec().commandLine(), prototypeResult); - while (parseResult.hasSubcommand()) { - parseResult = parseResult.subcommand(); - execute(parseResult.commandSpec().commandLine(), prototypeResult); - } - return returnResultOrExit(prototypeResult); - } - @Override protected RunAll self() { return this; } - } - - /** @deprecated use {@link #parseWithHandler(IParseResultHandler2, String[])} instead - * @since 2.0 */ - @Deprecated public List parseWithHandler(IParseResultHandler handler, PrintStream out, String... args) { - return parseWithHandlers(handler, out, Help.Ansi.AUTO, new DefaultExceptionHandler(), args); - } - /** - * Returns the result of calling {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...)} with - * a new {@link DefaultExceptionHandler} in addition to the specified parse result handler and the specified command line arguments. - *

- * This is a convenience method intended to offer the same ease of use as the {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) run} - * and {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) call} methods, but with more flexibility and better - * support for nested subcommands. - *

- *

Calling this method roughly expands to:

- *
{@code
-     * try {
-     *     ParseResult parseResult = parseArgs(args);
-     *     return handler.handleParseResult(parseResult);
-     * } catch (ParameterException ex) {
-     *     return new DefaultExceptionHandler().handleParseException(ex, args);
-     * }
-     * }
- *

- * Picocli provides some default handlers that allow you to accomplish some common tasks with very little code. - * The following handlers are available:

- *
    - *
  • {@link RunLast} handler prints help if requested, and otherwise gets the last specified command or subcommand - * and tries to execute it as a {@code Runnable} or {@code Callable}.
  • - *
  • {@link RunFirst} handler prints help if requested, and otherwise executes the top-level command as a {@code Runnable} or {@code Callable}.
  • - *
  • {@link RunAll} handler prints help if requested, and otherwise executes all recognized commands and subcommands as {@code Runnable} or {@code Callable} tasks.
  • - *
  • {@link DefaultExceptionHandler} prints the error message followed by usage help
  • - *
- * @param the return type of this handler - * @param handler the function that will handle the result of successfully parsing the command line arguments - * @param args the command line arguments - * @return an object resulting from handling the parse result or the exception that occurred while parsing the input - * @throws ExecutionException if the command line arguments were parsed successfully but a problem occurred while processing the - * parse results; use {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @see RunLast - * @see RunAll - * @since 3.0 */ - public R parseWithHandler(IParseResultHandler2 handler, String[] args) { - return parseWithHandlers(handler, new DefaultExceptionHandler(), args); - } - - /** @deprecated use {@link #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...)} instead - * @since 2.0 */ - @Deprecated public List parseWithHandlers(IParseResultHandler handler, PrintStream out, Help.Ansi ansi, IExceptionHandler exceptionHandler, String... args) { - try { - List result = parse(args); - return handler.handleParseResult(result, out, ansi); - } catch (ParameterException ex) { - return exceptionHandler.handleException(ex, out, ansi, args); - } - } - /** - * Tries to {@linkplain #parseArgs(String...) parse} the specified command line arguments, and if successful, delegates - * the processing of the resulting {@code ParseResult} object to the specified {@linkplain IParseResultHandler2 handler}. - * If the command line arguments were invalid, the {@code ParameterException} thrown from the {@code parse} method - * is caught and passed to the specified {@link IExceptionHandler2}. - *

- * This is a convenience method intended to offer the same ease of use as the {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) run} - * and {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) call} methods, but with more flexibility and better - * support for nested subcommands. - *

- *

Calling this method roughly expands to:

- *
-     * ParseResult parseResult = null;
-     * try {
-     *     parseResult = parseArgs(args);
-     *     return handler.handleParseResult(parseResult, prototypeReturnValue);
-     * } catch (ParameterException ex) {
-     *     return exceptionHandler.handleParseException(ex, prototypeReturnValue, (String[]) args);
-     * } catch (ExecutionException ex) {
-     *     return exceptionHandler.handleExecutionException(ex, prototypeReturnValue, parseResult);
-     * }
-     * 
- *

- * Picocli provides some default handlers that allow you to accomplish some common tasks with very little code. - * The following handlers are available:

- *
    - *
  • {@link RunLast} handler prints help if requested, and otherwise gets the last specified command or subcommand - * and tries to execute it as a {@code Runnable} or {@code Callable}.
  • - *
  • {@link RunFirst} handler prints help if requested, and otherwise executes the top-level command as a {@code Runnable} or {@code Callable}.
  • - *
  • {@link RunAll} handler prints help if requested, and otherwise executes all recognized commands and subcommands as {@code Runnable} or {@code Callable} tasks.
  • - *
  • {@link DefaultExceptionHandler} prints the error message followed by usage help
  • - *
- * - * @param handler the function that will handle the result of successfully parsing the command line arguments - * @param exceptionHandler the function that can handle the {@code ParameterException} thrown when the command line arguments are invalid - * @param args the command line arguments - * @return an object resulting from handling the parse result or the exception that occurred while parsing the input - * @throws ExecutionException if the command line arguments were parsed successfully but a problem occurred while processing the parse - * result {@code ParseResult} object; use {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed - * @param the return type of the result handler and exception handler - * @see RunLast - * @see RunAll - * @see DefaultExceptionHandler - * @since 3.0 */ - public R parseWithHandlers(IParseResultHandler2 handler, IExceptionHandler2 exceptionHandler, String... args) { - ParseResult parseResult = null; - try { - parseResult = parseArgs(args); - return handler.handleParseResult(parseResult); - } catch (ParameterException ex) { - return exceptionHandler.handleParseException(ex, (String[]) args); - } catch (ExecutionException ex) { - return exceptionHandler.handleExecutionException(ex, parseResult); - } - } - /** - * Equivalent to {@code new CommandLine(command).usage(out)}. See {@link #usage(PrintStream)} for details. - * @param command the object annotated with {@link Command}, {@link Option} and {@link Parameters} - * @param out the print stream to print the help message to - * @throws IllegalArgumentException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - */ - public static void usage(Object command, PrintStream out) { - toCommandLine(command, new DefaultFactory()).usage(out); - } - - /** - * Equivalent to {@code new CommandLine(command).usage(out, ansi)}. - * See {@link #usage(PrintStream, Help.Ansi)} for details. - * @param command the object annotated with {@link Command}, {@link Option} and {@link Parameters} - * @param out the print stream to print the help message to - * @param ansi whether the usage message should contain ANSI escape codes or not - * @throws IllegalArgumentException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - */ - public static void usage(Object command, PrintStream out, Help.Ansi ansi) { - toCommandLine(command, new DefaultFactory()).usage(out, ansi); - } - - /** - * Equivalent to {@code new CommandLine(command).usage(out, colorScheme)}. - * See {@link #usage(PrintStream, Help.ColorScheme)} for details. - * @param command the object annotated with {@link Command}, {@link Option} and {@link Parameters} - * @param out the print stream to print the help message to - * @param colorScheme the {@code ColorScheme} defining the styles for options, parameters and commands when ANSI is enabled - * @throws IllegalArgumentException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - */ - public static void usage(Object command, PrintStream out, Help.ColorScheme colorScheme) { - toCommandLine(command, new DefaultFactory()).usage(out, colorScheme); - } - - /** - * Delegates to {@link #usage(PrintStream, Help.Ansi)} with the {@linkplain Help.Ansi#AUTO platform default}. - * @param out the printStream to print to - * @see #usage(PrintStream, Help.ColorScheme) - */ - public void usage(PrintStream out) { usage(out, Help.Ansi.AUTO); } - /** - * Delegates to {@link #usage(PrintWriter, Help.Ansi)} with the {@linkplain Help.Ansi#AUTO platform default}. - * @param writer the PrintWriter to print to - * @see #usage(PrintWriter, Help.ColorScheme) - * @since 3.0 */ - public void usage(PrintWriter writer) { usage(writer, Help.Ansi.AUTO); } - - /** - * Delegates to {@link #usage(PrintStream, Help.ColorScheme)} with the {@linkplain Help#defaultColorScheme(CommandLine.Help.Ansi) default color scheme}. - * @param out the printStream to print to - * @param ansi whether the usage message should include ANSI escape codes or not - * @see #usage(PrintStream, Help.ColorScheme) - */ - public void usage(PrintStream out, Help.Ansi ansi) { usage(out, Help.defaultColorScheme(ansi)); } - /** Similar to {@link #usage(PrintStream, Help.Ansi)} but with the specified {@code PrintWriter} instead of a {@code PrintStream}. - * @since 3.0 */ - public void usage(PrintWriter writer, Help.Ansi ansi) { usage(writer, Help.defaultColorScheme(ansi)); } - - /** - * Prints a usage help message for the annotated command class to the specified {@code PrintStream}. - * Delegates construction of the usage help message to the {@link Help} inner class and is equivalent to: - *
-     * Help help = new Help(command).addAllSubcommands(getSubcommands());
-     * StringBuilder sb = new StringBuilder()
-     *         .append(help.headerHeading())
-     *         .append(help.header())
-     *         .append(help.synopsisHeading())      //e.g. Usage:
-     *         .append(help.synopsis())             //e.g. <main class> [OPTIONS] <command> [COMMAND-OPTIONS] [ARGUMENTS]
-     *         .append(help.descriptionHeading())   //e.g. %nDescription:%n%n
-     *         .append(help.description())          //e.g. {"Converts foos to bars.", "Use options to control conversion mode."}
-     *         .append(help.parameterListHeading()) //e.g. %nPositional parameters:%n%n
-     *         .append(help.parameterList())        //e.g. [FILE...] the files to convert
-     *         .append(help.optionListHeading())    //e.g. %nOptions:%n%n
-     *         .append(help.optionList())           //e.g. -h, --help   displays this help and exits
-     *         .append(help.commandListHeading())   //e.g. %nCommands:%n%n
-     *         .append(help.commandList())          //e.g.    add       adds the frup to the frooble
-     *         .append(help.footerHeading())
-     *         .append(help.footer());
-     * out.print(sb);
-     * 
- *

Annotate your class with {@link Command} to control many aspects of the usage help message, including - * the program name, text of section headings and section contents, and some aspects of the auto-generated sections - * of the usage help message. - *

To customize the auto-generated sections of the usage help message, like how option details are displayed, - * instantiate a {@link Help} object and use a {@link Help.TextTable} with more of fewer columns, a custom - * {@linkplain Help.Layout layout}, and/or a custom option {@linkplain Help.IOptionRenderer renderer} - * for ultimate control over which aspects of an Option or Field are displayed where.

- * @param out the {@code PrintStream} to print the usage help message to - * @param colorScheme the {@code ColorScheme} defining the styles for options, parameters and commands when ANSI is enabled - */ - public void usage(PrintStream out, Help.ColorScheme colorScheme) { - out.print(usage(new StringBuilder(), new Help(getCommandSpec(), colorScheme))); - } - /** Similar to {@link #usage(PrintStream, Help.ColorScheme)}, but with the specified {@code PrintWriter} instead of a {@code PrintStream}. - * @since 3.0 */ - public void usage(PrintWriter writer, Help.ColorScheme colorScheme) { - writer.print(usage(new StringBuilder(), new Help(getCommandSpec(), colorScheme))); - } - private static StringBuilder usage(StringBuilder sb, Help help) { - return sb.append(help.headerHeading()) - .append(help.header()) - .append(help.synopsisHeading()) //e.g. Usage: - .append(help.synopsis(help.synopsisHeadingLength())) //e.g. <main class> [OPTIONS] <command> [COMMAND-OPTIONS] [ARGUMENTS] - .append(help.descriptionHeading()) //e.g. %nDescription:%n%n - .append(help.description()) //e.g. {"Converts foos to bars.", "Use options to control conversion mode."} - .append(help.parameterListHeading()) //e.g. %nPositional parameters:%n%n - .append(help.parameterList()) //e.g. [FILE...] the files to convert - .append(help.optionListHeading()) //e.g. %nOptions:%n%n - .append(help.optionList()) //e.g. -h, --help displays this help and exits - .append(help.commandListHeading()) //e.g. %nCommands:%n%n - .append(help.commandList()) //e.g. add adds the frup to the frooble - .append(help.footerHeading()) - .append(help.footer()); - } - - /** - * Delegates to {@link #printVersionHelp(PrintStream, Help.Ansi)} with the {@linkplain Help.Ansi#AUTO platform default}. - * @param out the printStream to print to - * @see #printVersionHelp(PrintStream, Help.Ansi) - * @since 0.9.8 - */ - public void printVersionHelp(PrintStream out) { printVersionHelp(out, Help.Ansi.AUTO); } - - /** - * Prints version information from the {@link Command#version()} annotation to the specified {@code PrintStream}. - * Each element of the array of version strings is printed on a separate line. Version strings may contain - * markup for colors and style. - * @param out the printStream to print to - * @param ansi whether the usage message should include ANSI escape codes or not - * @see Command#version() - * @see Option#versionHelp() - * @see #isVersionHelpRequested() - * @since 0.9.8 - */ - public void printVersionHelp(PrintStream out, Help.Ansi ansi) { - for (String versionInfo : getCommandSpec().version()) { - out.println(ansi.new Text(versionInfo)); - } - } - /** - * Prints version information from the {@link Command#version()} annotation to the specified {@code PrintStream}. - * Each element of the array of version strings is {@linkplain String#format(String, Object...) formatted} with the - * specified parameters, and printed on a separate line. Both version strings and parameters may contain - * markup for colors and style. - * @param out the printStream to print to - * @param ansi whether the usage message should include ANSI escape codes or not - * @param params Arguments referenced by the format specifiers in the version strings - * @see Command#version() - * @see Option#versionHelp() - * @see #isVersionHelpRequested() - * @since 1.0.0 - */ - public void printVersionHelp(PrintStream out, Help.Ansi ansi, Object... params) { - for (String versionInfo : getCommandSpec().version()) { - out.println(ansi.new Text(String.format(versionInfo, params))); - } - } - - /** - * Delegates to {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.out} for - * requested usage help messages, {@code System.err} for diagnostic error messages, and {@link Help.Ansi#AUTO}. - * @param callable the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param args the command line arguments to parse - * @param the annotated object must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @see #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, otherwise returns the result of calling the Callable - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @since 3.0 - */ - public static , T> T call(C callable, String... args) { - return call(callable, System.out, System.err, Help.Ansi.AUTO, args); - } - - /** - * Delegates to {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.err} for - * diagnostic error messages and {@link Help.Ansi#AUTO}. - * @param callable the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param args the command line arguments to parse - * @param the annotated object must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @see #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, otherwise returns the result of calling the Callable - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @see RunLast - */ - public static , T> T call(C callable, PrintStream out, String... args) { - return call(callable, out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #call(Callable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.err} for diagnostic error messages. - * @param callable the command to call when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param ansi the ANSI style to use - * @param args the command line arguments to parse - * @param the annotated object must implement Callable - * @param the return type of the most specific command (must implement {@code Callable}) - * @see #call(Callable, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, otherwise returns the result of calling the Callable - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @see RunLast - */ - public static , T> T call(C callable, PrintStream out, Help.Ansi ansi, String... args) { - return call(callable, out, System.err, ansi, args); - } - /** - * Convenience method to allow command line application authors to avoid some boilerplate code in their application. - * The annotated object needs to implement {@link Callable}. Calling this method is equivalent to: - *
{@code
-     * CommandLine cmd = new CommandLine(callable);
-     * List results = cmd.parseWithHandlers(new RunLast().useOut(out).useAnsi(ansi),
-     *                                              new DefaultExceptionHandler().useErr(err).useAnsi(ansi),
-     *                                              args);
-     * T result = results == null || results.isEmpty() ? null : (T) results.get(0);
-     * return result;
-     * }
-     * 

- * If the specified Callable command has subcommands, the {@linkplain RunLast last} subcommand specified on the - * command line is executed. - * Commands with subcommands may be interested in calling the {@link #parseWithHandler(IParseResultHandler2, String[]) parseWithHandler} - * method with the {@link RunAll} handler or a custom handler. - *

- * @param callable the command to call when {@linkplain #parse(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param err the printStream to print diagnostic messages to - * @param ansi whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @param the annotated object must implement Callable - * @param the return type of the specified {@code Callable} - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Callable throws an exception - * @return {@code null} if an error occurred while parsing the command line options, or if help was requested and printed. Otherwise returns the result of calling the Callable - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @see RunLast - * @since 3.0 - */ - public static , T> T call(C callable, PrintStream out, PrintStream err, Help.Ansi ansi, String... args) { - CommandLine cmd = new CommandLine(callable); - List results = cmd.parseWithHandlers(new RunLast().useOut(out).useAnsi(ansi), new DefaultExceptionHandler>().useErr(err).useAnsi(ansi), args); - @SuppressWarnings("unchecked") T result = results == null || results.isEmpty() ? null : (T) results.get(0); - return result; - } - - /** - * Delegates to {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.out} for - * requested usage help messages, {@code System.err} for diagnostic error messages, and {@link Help.Ansi#AUTO}. - * @param runnable the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param args the command line arguments to parse - * @param the annotated object must implement Runnable - * @see #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @see RunLast - * @since 3.0 - */ - public static void run(R runnable, String... args) { - run(runnable, System.out, System.err, Help.Ansi.AUTO, args); - } - - /** - * Delegates to {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.err} for diagnostic error messages and {@link Help.Ansi#AUTO}. - * @param runnable the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param args the command line arguments to parse - * @param the annotated object must implement Runnable - * @see #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @see #parseWithHandler(IParseResultHandler2, String[]) - * @see RunLast - */ - public static void run(R runnable, PrintStream out, String... args) { - run(runnable, out, System.err, Help.Ansi.AUTO, args); - } - /** - * Delegates to {@link #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...)} with {@code System.err} for diagnostic error messages. - * @param runnable the command to run when {@linkplain #parseArgs(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param ansi whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @param the annotated object must implement Runnable - * @see #run(Runnable, PrintStream, PrintStream, Help.Ansi, String...) - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @see RunLast - */ - public static void run(R runnable, PrintStream out, Help.Ansi ansi, String... args) { - run(runnable, out, System.err, ansi, args); - } - /** - * Convenience method to allow command line application authors to avoid some boilerplate code in their application. - * The annotated object needs to implement {@link Runnable}. Calling this method is equivalent to: - *
{@code
-     * CommandLine cmd = new CommandLine(runnable);
-     * cmd.parseWithHandlers(new RunLast().useOut(out).useAnsi(ansi),
-     *                       new DefaultExceptionHandler().useErr(err).useAnsi(ansi),
-     *                       args);
-     * }
- *

- * If the specified Runnable command has subcommands, the {@linkplain RunLast last} subcommand specified on the - * command line is executed. - * Commands with subcommands may be interested in calling the {@link #parseWithHandler(IParseResultHandler2, String[]) parseWithHandler} - * method with the {@link RunAll} handler or a custom handler. - *

- * From picocli v2.0, this method prints usage help or version help if {@linkplain #printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi) requested}, - * and any exceptions thrown by the {@code Runnable} are caught and rethrown wrapped in an {@code ExecutionException}. - *

- * @param runnable the command to run when {@linkplain #parse(String...) parsing} succeeds. - * @param out the printStream to print the usage help message to when the user requested help - * @param err the printStream to print diagnostic messages to - * @param ansi whether the usage message should include ANSI escape codes or not - * @param args the command line arguments to parse - * @param the annotated object must implement Runnable - * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation - * @throws ExecutionException if the Runnable throws an exception - * @see #parseWithHandlers(IParseResultHandler2, IExceptionHandler2, String...) - * @see RunLast - * @since 3.0 - */ - public static void run(R runnable, PrintStream out, PrintStream err, Help.Ansi ansi, String... args) { - CommandLine cmd = new CommandLine(runnable); - cmd.parseWithHandlers(new RunLast().useOut(out).useAnsi(ansi), new DefaultExceptionHandler>().useErr(err).useAnsi(ansi), args); - } - - /** - * Registers the specified type converter for the specified class. When initializing fields annotated with - * {@link Option}, the field's type is used as a lookup key to find the associated type converter, and this - * type converter converts the original command line argument string value to the correct type. - *

- * Java 8 lambdas make it easy to register custom type converters: - *

- *
-     * commandLine.registerConverter(java.nio.file.Path.class, s -> java.nio.file.Paths.get(s));
-     * commandLine.registerConverter(java.time.Duration.class, s -> java.time.Duration.parse(s));
- *

- * Built-in type converters are pre-registered for the following java 1.5 types: - *

- *
    - *
  • all primitive types
  • - *
  • all primitive wrapper types: Boolean, Byte, Character, Double, Float, Integer, Long, Short
  • - *
  • any enum
  • - *
  • java.io.File
  • - *
  • java.math.BigDecimal
  • - *
  • java.math.BigInteger
  • - *
  • java.net.InetAddress
  • - *
  • java.net.URI
  • - *
  • java.net.URL
  • - *
  • java.nio.charset.Charset
  • - *
  • java.sql.Time
  • - *
  • java.util.Date
  • - *
  • java.util.UUID
  • - *
  • java.util.regex.Pattern
  • - *
  • StringBuilder
  • - *
  • CharSequence
  • - *
  • String
  • - *
- *

The specified converter will be registered with this {@code CommandLine} and the full hierarchy of its - * subcommands and nested sub-subcommands at the moment the converter is registered. Subcommands added - * later will not have this converter added automatically. To ensure a custom type converter is available to all - * subcommands, register the type converter last, after adding subcommands.

- * - * @param cls the target class to convert parameter string values to - * @param converter the class capable of converting string values to the specified target type - * @param the target type - * @return this CommandLine object, to allow method chaining - * @see #addSubcommand(String, Object) - */ - public CommandLine registerConverter(Class cls, ITypeConverter converter) { - interpreter.converterRegistry.put(Assert.notNull(cls, "class"), Assert.notNull(converter, "converter")); - for (CommandLine command : getCommandSpec().commands.values()) { - command.registerConverter(cls, converter); - } - return this; - } - - /** Returns the String that separates option names from option values when parsing command line options. - * @return the String the parser uses to separate option names from option values - * @see ParserSpec#separator() */ - public String getSeparator() { return getCommandSpec().parser().separator(); } - - /** Sets the String the parser uses to separate option names from option values to the specified value. - * The separator may also be set declaratively with the {@link CommandLine.Command#separator()} annotation attribute. - *

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 separator the String that separates option names from option values - * @see ParserSpec#separator(String) - * @return this {@code CommandLine} object, to allow method chaining */ - public CommandLine setSeparator(String separator) { - getCommandSpec().parser().separator(Assert.notNull(separator, "separator")); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setSeparator(separator); - } - return this; - } - - /** Returns the maximum width of the usage help message. - * @see UsageMessageSpec#width() */ - public int getUsageHelpWidth() { return getCommandSpec().usageMessage().width(); } - - /** Sets the maximum width of the usage help message. Longer lines are wrapped. - *

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 width the maximum width of the usage help message - * @see UsageMessageSpec#width(int) - * @return this {@code CommandLine} object, to allow method chaining */ - public CommandLine setUsageHelpWidth(int width) { - getCommandSpec().usageMessage().width(width); - for (CommandLine command : getCommandSpec().subcommands().values()) { - command.setUsageHelpWidth(width); - } - return this; - } - - /** Returns the command name (also called program name) displayed in the usage help synopsis. - * @return the command name (also called program name) displayed in the usage - * @see CommandSpec#name() - * @since 2.0 */ - public String getCommandName() { return getCommandSpec().name(); } - - /** Sets the command name (also called program name) displayed in the usage help synopsis to the specified value. - * Note that this method only modifies the usage help message, it does not impact parsing behaviour. - * The command name may also be set declaratively with the {@link CommandLine.Command#name()} annotation attribute. - * @param commandName command name (also called program name) displayed in the usage help synopsis - * @return this {@code CommandLine} object, to allow method chaining - * @see CommandSpec#name(String) - * @since 2.0 */ - public CommandLine setCommandName(String commandName) { - getCommandSpec().name(Assert.notNull(commandName, "commandName")); - return this; - } - - /** Returns whether arguments starting with {@code '@'} should be treated as the path to an argument file and its - * contents should be expanded into separate arguments for each line in the specified file. - * This property is {@code true} by default. - * @return whether "argument files" or {@code @files} should be expanded into their content - * @since 2.1 */ - public boolean isExpandAtFiles() { return getCommandSpec().parser().expandAtFiles(); } - - /** Sets whether arguments starting with {@code '@'} should be treated as the path to an argument file and its - * contents should be expanded into separate arguments for each line in the specified file. ({@code true} by default.) - * @param expandAtFiles whether "argument files" or {@code @files} should be expanded into their content - * @return this {@code CommandLine} object, to allow method chaining - * @since 2.1 */ - public CommandLine setExpandAtFiles(boolean expandAtFiles) { - getCommandSpec().parser().expandAtFiles(expandAtFiles); - return this; - } - private static boolean empty(String str) { return str == null || str.trim().length() == 0; } - private static boolean empty(Object[] array) { return array == null || array.length == 0; } - private static String str(String[] arr, int i) { return (arr == null || arr.length == 0) ? "" : arr[i]; } - private static boolean isBoolean(Class type) { return type == Boolean.class || type == Boolean.TYPE; } - private static CommandLine toCommandLine(Object obj, IFactory factory) { return obj instanceof CommandLine ? (CommandLine) obj : new CommandLine(obj, factory);} - private static boolean isMultiValue(Field field) { return isMultiValue(field.getType()); } - private static boolean isMultiValue(Class cls) { return cls.isArray() || Collection.class.isAssignableFrom(cls) || Map.class.isAssignableFrom(cls); } - - private static class NoCompletionCandidates implements Iterable { - public Iterator iterator() { return Collections.emptyList().iterator(); } - } - /** - *

- * Annotate fields in your class with {@code @Option} and picocli will initialize these fields when matching - * arguments are specified on the command line. - *

- * For example: - *

- *
-     * import static picocli.CommandLine.*;
-     *
-     * public class MyClass {
-     *     @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;
-     *
-     *     @Option(names = { "-h", "--help", "-?", "-help"}, usageHelp = true, description = "Display this help and exit")
-     *     private boolean help;
-     * }
-     * 
- *

- * A field cannot be annotated with both {@code @Parameters} and {@code @Option} or a - * {@code ParameterException} is thrown. - *

- */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Option { - /** - * One or more option names. At least one option name is required. - *

- * Different environments have different conventions for naming options, but usually options have a prefix - * that sets them apart from parameters. - * Picocli supports all of the below styles. The default separator is {@code '='}, but this can be configured. - *

- * *nix - *

- * In Unix and Linux, options have a short (single-character) name, a long name or both. - * Short options - * (POSIX - * style are single-character and are preceded by the {@code '-'} character, e.g., {@code `-v'}. - * GNU-style long - * (or mnemonic) options start with two dashes in a row, e.g., {@code `--file'}. - *

Picocli supports the POSIX convention that short options can be grouped, with the last option - * optionally taking a parameter, which may be attached to the option name or separated by a space or - * a {@code '='} character. The below examples are all equivalent: - *

-         * -xvfFILE
-         * -xvf FILE
-         * -xvf=FILE
-         * -xv --file FILE
-         * -xv --file=FILE
-         * -x -v --file FILE
-         * -x -v --file=FILE
-         * 

- * DOS - *

- * DOS options mostly have upper case single-character names and start with a single slash {@code '/'} character. - * Option parameters are separated by a {@code ':'} character. Options cannot be grouped together but - * must be specified separately. For example: - *

-         * DIR /S /A:D /T:C
-         * 

- * PowerShell - *

- * Windows PowerShell options generally are a word preceded by a single {@code '-'} character, e.g., {@code `-Help'}. - * Option parameters are separated by a space or by a {@code ':'} character. - *

- * @return one or more option names - */ - String[] names(); - - /** - * Indicates whether this option is required. By default this is false. - * If an option is required, but a user invokes the program without specifying the required option, - * a {@link MissingParameterException} is thrown from the {@link #parse(String...)} method. - * @return whether this option is required - */ - boolean required() default false; - - /** - * Set {@code help=true} if this option should disable validation of the remaining arguments: - * If the {@code help} option is specified, no error message is generated for missing required options. - *

- * This attribute is useful for special options like help ({@code -h} and {@code --help} on unix, - * {@code -?} and {@code -Help} on Windows) or version ({@code -V} and {@code --version} on unix, - * {@code -Version} on Windows). - *

- *

- * Note that the {@link #parse(String...)} method will not print help documentation. It will only set - * the value of the annotated field. It is the responsibility of the caller to inspect the annotated fields - * and take the appropriate action. - *

- * @return whether this option disables validation of the other arguments - * @deprecated Use {@link #usageHelp()} and {@link #versionHelp()} instead. See {@link #printHelpIfRequested(List, PrintStream, CommandLine.Help.Ansi)} - */ - @Deprecated boolean help() default false; - - /** - * Set {@code usageHelp=true} for the {@code --help} option that triggers display of the usage help message. - * The convenience methods {@code Commandline.call}, - * {@code Commandline.run}, and {@code Commandline.parseWithHandler(s)} will automatically print usage help - * when an option with {@code usageHelp=true} was specified on the command line. - *

- * By default, all options and positional parameters are included in the usage help message - * except when explicitly marked {@linkplain #hidden() hidden}. - *

- * If this option is specified on the command line, picocli will not validate the remaining arguments (so no "missing required - * option" errors) and the {@link CommandLine#isUsageHelpRequested()} method will return {@code true}. - *

- * Alternatively, consider annotating your command with {@linkplain Command#mixinStandardHelpOptions() @Command(mixinStandardHelpOptions = true)}. - *

- * @return whether this option allows the user to request usage help - * @since 0.9.8 - * @see #hidden() - * @see #run(Runnable, String...) - * @see #call(Callable, String...) - * @see #parseWithHandler(IParseResultHandler2, String[]) - * @see #printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi) - */ - boolean usageHelp() default false; - - /** - * Set {@code versionHelp=true} for the {@code --version} option that triggers display of the version information. - * The convenience methods {@code Commandline.call}, - * {@code Commandline.run}, and {@code Commandline.parseWithHandler(s)} will automatically print version information - * when an option with {@code versionHelp=true} was specified on the command line. - *

- * The version information string is obtained from the command's {@linkplain Command#version() version} annotation - * or from the {@linkplain Command#versionProvider() version provider}. - *

- * If this option is specified on the command line, picocli will not validate the remaining arguments (so no "missing required - * option" errors) and the {@link CommandLine#isUsageHelpRequested()} method will return {@code true}. - *

- * Alternatively, consider annotating your command with {@linkplain Command#mixinStandardHelpOptions() @Command(mixinStandardHelpOptions = true)}. - *

- * @return whether this option allows the user to request version information - * @since 0.9.8 - * @see #hidden() - * @see #run(Runnable, String...) - * @see #call(Callable, String...) - * @see #parseWithHandler(IParseResultHandler2, String[]) - * @see #printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi) - */ - boolean versionHelp() default false; - - /** - * Description of this option, used when generating the usage documentation. - *

- * From picocli 3.2, the usage string may contain variables that are rendered when help is requested. - * The string {@code ${DEFAULT-VALUE}} is replaced with the default value of the option. This is regardless of - * the command's {@link Command#showDefaultValues() showDefaultValues} setting or the option's {@link #showDefaultValue() showDefaultValue} setting. - * The string {@code ${COMPLETION-CANDIDATES}} is replaced with the completion candidates generated by - * {@link #completionCandidates()} in the description for this option. - * Also, embedded {@code %n} newline markers are converted to actual newlines. - *

- * @return the description of this option - */ - String[] description() default {}; - - /** - * Specifies the minimum number of required parameters and the maximum number of accepted parameters. - * If an option declares a positive arity, and the user specifies an insufficient number of parameters on the - * command line, a {@link MissingParameterException} is thrown by the {@link #parse(String...)} method. - *

- * In many cases picocli can deduce the number of required parameters from the field's type. - * By default, flags (boolean options) have arity zero, - * and single-valued type fields (String, int, Integer, double, Double, File, Date, etc) have arity one. - * Generally, fields with types that cannot hold multiple values can omit the {@code arity} attribute. - *

- * Fields used to capture options with arity two or higher should have a type that can hold multiple values, - * like arrays or Collections. See {@link #type()} for strongly-typed Collection fields. - *

- * For example, if an option has 2 required parameters and any number of optional parameters, - * specify {@code @Option(names = "-example", arity = "2..*")}. - *

- * A note on boolean options - *

- * By default picocli does not expect boolean options (also called "flags" or "switches") to have a parameter. - * You can make a boolean option take a required parameter by annotating your field with {@code arity="1"}. - * For example:

- *
@Option(names = "-v", arity = "1") boolean verbose;
- *

- * Because this boolean field is defined with arity 1, the user must specify either {@code -v false} - * or {@code -v true} - * on the command line, or a {@link MissingParameterException} is thrown by the {@link #parse(String...)} - * method. - *

- * To make the boolean parameter possible but optional, define the field with {@code arity = "0..1"}. - * For example:

- *
@Option(names="-v", arity="0..1") boolean verbose;
- *

This will accept any of the below without throwing an exception:

- *
-         * -v
-         * -v true
-         * -v false
-         * 
- * @return how many arguments this option requires - */ - String arity() default ""; - - /** - * Specify a {@code paramLabel} for the option parameter to be used in the usage help message. If omitted, - * picocli uses the field name in fish brackets ({@code '<'} and {@code '>'}) by default. Example: - *
class Example {
-         *     @Option(names = {"-o", "--output"}, paramLabel="FILE", description="path of the output file")
-         *     private File out;
-         *     @Option(names = {"-j", "--jobs"}, arity="0..1", description="Allow N jobs at once; infinite jobs with no arg.")
-         *     private int maxJobs = -1;
-         * }
- *

By default, the above gives a usage help message like the following:

-         * Usage: <main class> [OPTIONS]
-         * -o, --output FILE       path of the output file
-         * -j, --jobs [<maxJobs>]  Allow N jobs at once; infinite jobs with no arg.
-         * 
- * @return name of the option parameter used in the usage help message - */ - String paramLabel() default ""; - - /**

- * Optionally specify a {@code type} to control exactly what Class the option parameter should be converted - * to. This may be useful when the field type is an interface or an abstract class. For example, a field can - * be declared to have type {@code java.lang.Number}, and annotating {@code @Option(type=Short.class)} - * ensures that the option parameter value is converted to a {@code Short} before setting the field value. - *

- * For array fields whose component type is an interface or abstract class, specify the concrete component type. - * For example, a field with type {@code Number[]} may be annotated with {@code @Option(type=Short.class)} - * to ensure that option parameter values are converted to {@code Short} before adding an element to the array. - *

- * Picocli will use the {@link ITypeConverter} that is - * {@linkplain #registerConverter(Class, ITypeConverter) registered} for the specified type to convert - * the raw String values before modifying the field value. - *

- * Prior to 2.0, the {@code type} attribute was necessary for {@code Collection} and {@code Map} fields, - * but starting from 2.0 picocli will infer the component type from the generic type's type arguments. - * For example, for a field of type {@code Map} picocli will know the option parameter - * should be split up in key=value pairs, where the key should be converted to a {@code java.util.concurrent.TimeUnit} - * enum value, and the value should be converted to a {@code Long}. No {@code @Option(type=...)} type attribute - * is required for this. For generic types with wildcards, picocli will take the specified upper or lower bound - * as the Class to convert to, unless the {@code @Option} annotation specifies an explicit {@code type} attribute. - *

- * If the field type is a raw collection or a raw map, and you want it to contain other values than Strings, - * or if the generic type's type arguments are interfaces or abstract classes, you may - * specify a {@code type} attribute to control the Class that the option parameter should be converted to. - * @return the type(s) to convert the raw String values - */ - Class[] type() default {}; - - /** - * Optionally specify one or more {@link ITypeConverter} classes to use to convert the command line argument into - * a strongly typed value (or key-value pair for map fields). This is useful when a particular field should - * use a custom conversion that is different from the normal conversion for the field's type. - *

For example, for a specific field you may want to use a converter that maps the constant names defined - * in {@link java.sql.Types java.sql.Types} to the {@code int} value of these constants, but any other {@code int} fields should - * not be affected by this and should continue to use the standard int converter that parses numeric values.

- * @return the type converter(s) to use to convert String values to strongly typed values for this field - * @see CommandLine#registerConverter(Class, ITypeConverter) - */ - Class>[] converter() default {}; - - /** - * Specify a regular expression to use to split option parameter values before applying them to the field. - * All elements resulting from the split are added to the array or Collection. Ignored for single-value fields. - * @return a regular expression to split option parameter values or {@code ""} if the value should not be split - * @see String#split(String) - */ - String split() default ""; - - /** - * Set {@code hidden=true} if this option should not be included in the usage help message. - * @return whether this option should be excluded from the usage documentation - */ - boolean hidden() default false; - - /** Use this attribute to control for a specific option whether its default value should be shown in the usage - * help message. If not specified, the default value is only shown when the {@link Command#showDefaultValues()} - * is set {@code true} on the command. Use this attribute to specify whether the default value - * for this specific option should always be shown or never be shown, regardless of the command setting. - *

Note that picocli 3.2 allows {@linkplain #description() embedding default values} anywhere in the description that ignores this setting.

- * @return whether this option's default value should be shown in the usage help message - */ - Help.Visibility showDefaultValue() default Help.Visibility.ON_DEMAND; - - /** Use this attribute to specify an {@code Iterable} class that generates completion candidates for this option. - * For map fields, completion candidates should be in {@code key=value} form. - *

- * Completion candidates are used in bash completion scripts generated by the {@link AutoComplete} class. - * Bash has special completion options to generate file names and host names, and the bash completion scripts - * generated by {@code AutoComplete} delegate to these bash built-ins for {@code @Options} whose {@code type} is - * {@code java.io.File}, {@code java.nio.file.Path} or {@code java.net.InetAddress}. - *

- * For {@code @Options} whose {@code type} is a Java {@code enum}, {@code AutoComplete} can generate completion - * candidates from the type. For other types, use this attribute to specify completion candidates. - *

- * - * @return a class whose instances can iterate over the completion candidates for this option - * @see picocli.CommandLine.IFactory - * @since 3.2 */ - Class> completionCandidates() default NoCompletionCandidates.class; - } - /** - *

- * Fields annotated with {@code @Parameters} will be initialized with positional parameters. By specifying the - * {@link #index()} attribute you can pick the exact position or a range of positional parameters to apply. If no - * index is specified, the field will get all positional parameters (and so it should be an array or a collection). - *

- * For example: - *

- *
-     * import static picocli.CommandLine.*;
-     *
-     * public class MyCalcParameters {
-     *     @Parameters(description = "Any number of input numbers")
-     *     private List<BigDecimal> files = new ArrayList<BigDecimal>();
-     *
-     *     @Option(names = { "-h", "--help" }, usageHelp = true, description = "Display this help and exit")
-     *     private boolean help;
-     * }
-     * 

- * A field cannot be annotated with both {@code @Parameters} and {@code @Option} or a {@code ParameterException} - * is thrown.

- */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Parameters { - /** Specify an index ("0", or "1", etc.) to pick which of the command line arguments should be assigned to this - * field. For array or Collection fields, you can also specify an index range ("0..3", or "2..*", etc.) to assign - * a subset of the command line arguments to this field. The default is "*", meaning all command line arguments. - * @return an index or range specifying which of the command line arguments should be assigned to this field - */ - String index() default "*"; - - /** Description of the parameter(s), used when generating the usage documentation. - *

- * From picocli 3.2, the usage string may contain variables that are rendered when help is requested. - * The string {@code ${DEFAULT-VALUE}} is replaced with the default value of the positional parameter. This is regardless of - * the command's {@link Command#showDefaultValues() showDefaultValues} setting or the positional parameter's {@link #showDefaultValue() showDefaultValue} setting. - * The string {@code ${COMPLETION-CANDIDATES}} is replaced with the completion candidates generated by - * {@link #completionCandidates()} in the description for this positional parameter. - * Also, embedded {@code %n} newline markers are converted to actual newlines. - *

- * @return the description of the parameter(s) - */ - String[] description() default {}; - - /** - * Specifies the minimum number of required parameters and the maximum number of accepted parameters. If a - * positive arity is declared, and the user specifies an insufficient number of parameters on the command line, - * {@link MissingParameterException} is thrown by the {@link #parse(String...)} method. - *

The default depends on the type of the parameter: booleans require no parameters, arrays and Collections - * accept zero to any number of parameters, and any other type accepts one parameter.

- * @return the range of minimum and maximum parameters accepted by this command - */ - String arity() default ""; - - /** - * Specify a {@code paramLabel} for the parameter to be used in the usage help message. If omitted, - * picocli uses the field name in fish brackets ({@code '<'} and {@code '>'}) by default. Example: - *
class Example {
-         *     @Parameters(paramLabel="FILE", description="path of the input FILE(s)")
-         *     private File[] inputFiles;
-         * }
- *

By default, the above gives a usage help message like the following:

-         * Usage: <main class> [FILE...]
-         * [FILE...]       path of the input FILE(s)
-         * 
- * @return name of the positional parameter used in the usage help message - */ - String paramLabel() default ""; - - /** - *

- * Optionally specify a {@code type} to control exactly what Class the positional parameter should be converted - * to. This may be useful when the field type is an interface or an abstract class. For example, a field can - * be declared to have type {@code java.lang.Number}, and annotating {@code @Parameters(type=Short.class)} - * ensures that the positional parameter value is converted to a {@code Short} before setting the field value. - *

- * For array fields whose component type is an interface or abstract class, specify the concrete component type. - * For example, a field with type {@code Number[]} may be annotated with {@code @Parameters(type=Short.class)} - * to ensure that positional parameter values are converted to {@code Short} before adding an element to the array. - *

- * Picocli will use the {@link ITypeConverter} that is - * {@linkplain #registerConverter(Class, ITypeConverter) registered} for the specified type to convert - * the raw String values before modifying the field value. - *

- * Prior to 2.0, the {@code type} attribute was necessary for {@code Collection} and {@code Map} fields, - * but starting from 2.0 picocli will infer the component type from the generic type's type arguments. - * For example, for a field of type {@code Map} picocli will know the positional parameter - * should be split up in key=value pairs, where the key should be converted to a {@code java.util.concurrent.TimeUnit} - * enum value, and the value should be converted to a {@code Long}. No {@code @Parameters(type=...)} type attribute - * is required for this. For generic types with wildcards, picocli will take the specified upper or lower bound - * as the Class to convert to, unless the {@code @Parameters} annotation specifies an explicit {@code type} attribute. - *

- * If the field type is a raw collection or a raw map, and you want it to contain other values than Strings, - * or if the generic type's type arguments are interfaces or abstract classes, you may - * specify a {@code type} attribute to control the Class that the positional parameter should be converted to. - * @return the type(s) to convert the raw String values - */ - Class[] type() default {}; - - /** - * Optionally specify one or more {@link ITypeConverter} classes to use to convert the command line argument into - * a strongly typed value (or key-value pair for map fields). This is useful when a particular field should - * use a custom conversion that is different from the normal conversion for the field's type. - *

For example, for a specific field you may want to use a converter that maps the constant names defined - * in {@link java.sql.Types java.sql.Types} to the {@code int} value of these constants, but any other {@code int} fields should - * not be affected by this and should continue to use the standard int converter that parses numeric values.

- * @return the type converter(s) to use to convert String values to strongly typed values for this field - * @see CommandLine#registerConverter(Class, ITypeConverter) - */ - Class>[] converter() default {}; - - /** - * Specify a regular expression to use to split positional parameter values before applying them to the field. - * All elements resulting from the split are added to the array or Collection. Ignored for single-value fields. - * @return a regular expression to split operand values or {@code ""} if the value should not be split - * @see String#split(String) - */ - String split() default ""; - - /** - * Set {@code hidden=true} if this parameter should not be included in the usage message. - * @return whether this parameter should be excluded from the usage message - */ - boolean hidden() default false; - - /** Use this attribute to control for a specific positional parameter whether its default value should be shown in the usage - * help message. If not specified, the default value is only shown when the {@link Command#showDefaultValues()} - * is set {@code true} on the command. Use this attribute to specify whether the default value - * for this specific positional parameter should always be shown or never be shown, regardless of the command setting. - *

Note that picocli 3.2 allows {@linkplain #description() embedding default values} anywhere in the description that ignores this setting.

- * @return whether this positional parameter's default value should be shown in the usage help message - */ - Help.Visibility showDefaultValue() default Help.Visibility.ON_DEMAND; - - /** Use this attribute to specify an {@code Iterable} class that generates completion candidates for - * this positional parameter. For map fields, completion candidates should be in {@code key=value} form. - *

- * Completion candidates are used in bash completion scripts generated by the {@link AutoComplete} class. - * Unfortunately, {@link AutoComplete} is not very good yet at generating completions for positional parameters. - *

- * - * @return a class whose instances can iterate over the completion candidates for this positional parameter - * @see picocli.CommandLine.IFactory - * @since 3.2 */ - Class> completionCandidates() default NoCompletionCandidates.class; - } - - /** - *

- * Fields annotated with {@code @ParentCommand} will be initialized with the parent command of the current subcommand. - * If the current command does not have a parent command, this annotation has no effect. - *

- * Parent commands often define options that apply to all the subcommands. - * This annotation offers a convenient way to inject a reference to the parent command into a subcommand, so the - * subcommand can access its parent options. For example: - *

-     * @Command(name = "top", subcommands = Sub.class)
-     * class Top implements Runnable {
-     *
-     *     @Option(names = {"-d", "--directory"}, description = "this option applies to all subcommands")
-     *     File baseDirectory;
-     *
-     *     public void run() { System.out.println("Hello from top"); }
-     * }
-     *
-     * @Command(name = "sub")
-     * class Sub implements Runnable {
-     *
-     *     @ParentCommand
-     *     private Top parent;
-     *
-     *     public void run() {
-     *         System.out.println("Subcommand: parent command 'directory' is " + parent.baseDirectory);
-     *     }
-     * }
-     * 
- * @since 2.2 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface ParentCommand { } - - /** - * Fields annotated with {@code @Unmatched} will be initialized with the list of unmatched command line arguments, if any. - * If this annotation is found, picocli automatically sets {@linkplain CommandLine#setUnmatchedArgumentsAllowed(boolean) unmatchedArgumentsAllowed} to {@code true}. - * @see CommandLine#isUnmatchedArgumentsAllowed() - * @since 3.0 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Unmatched { } - - /** - *

- * Fields annotated with {@code @Mixin} are "expanded" into the current command: {@link Option @Option} and - * {@link Parameters @Parameters} in the mixin class are added to the options and positional parameters of this command. - * A {@link DuplicateOptionAnnotationsException} is thrown if any of the options in the mixin has the same name as - * an option in this command. - *

- * The {@code Mixin} annotation provides a way to reuse common options and parameters without subclassing. For example: - *

-     * class HelloWorld implements Runnable {
-     *
-     *     // adds the --help and --version options to this command
-     *     @Mixin
-     *     private HelpOptions = new HelpOptions();
-     *
-     *     @Option(names = {"-u", "--userName"}, required = true, description = "The user name")
-     *     String userName;
-     *
-     *     public void run() { System.out.println("Hello, " + userName); }
-     * }
-     *
-     * // Common reusable help options.
-     * class HelpOptions {
-     *
-     *     @Option(names = { "-h", "--help"}, usageHelp = true, description = "Display this help and exit")
-     *     private boolean help;
-     *
-     *     @Option(names = { "-V", "--version"}, versionHelp = true, description = "Display version info and exit")
-     *     private boolean versionHelp;
-     * }
-     * 
- * @since 3.0 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Mixin { - /** Optionally specify a name that the mixin object can be retrieved with from the {@code CommandSpec}. - * If not specified the name of the annotated field is used. - * @return a String to register the mixin object with, or an empty String if the name of the annotated field should be used */ - String name() default ""; - } - /** - * Fields annotated with {@code @Inject} will be initialized with the {@code CommandSpec} for the command the field is part of. Example usage: - *
-     * class InjectExample implements Runnable {
-     *     @Inject CommandSpec commandSpec;
-     *     //...
-     *     public void run() {
-     *         // do something with the injected objects
-     *     }
-     * }
-     * 
- * @since 3.2 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Inject { } - /** - *

Annotate your class with {@code @Command} when you want more control over the format of the generated help - * message. - *

-     * @Command(name      = "Encrypt", mixinStandardHelpOptions = true,
-     *        description = "Encrypt FILE(s), or standard input, to standard output or to the output file.",
-     *        version     = "Encrypt version 1.0",
-     *        footer      = "Copyright (c) 2017")
-     * public class Encrypt {
-     *     @Parameters(paramLabel = "FILE", 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;
-     * }
- *

- * The structure of a help message looks like this: - *

    - *
  • [header]
  • - *
  • [synopsis]: {@code Usage: [OPTIONS] [FILE...]}
  • - *
  • [description]
  • - *
  • [parameter list]: {@code [FILE...] Any number of input files}
  • - *
  • [option list]: {@code -h, --help prints this help message and exits}
  • - *
  • [footer]
  • - *
*/ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.TYPE, ElementType.LOCAL_VARIABLE, ElementType.PACKAGE}) - public @interface Command { - /** Program name to show in the synopsis. If omitted, {@code "
"} is used. - * For {@linkplain #subcommands() declaratively added} subcommands, this attribute is also used - * by the parser to recognize subcommands in the command line arguments. - * @return the program name to show in the synopsis - * @see CommandSpec#name() - * @see Help#commandName() */ - String name() default "
"; - - /** Alternative command names by which this subcommand is recognized on the command line. - * @return one or more alternative command names - * @since 3.1 */ - String[] aliases() default {}; - - /** A list of classes to instantiate and register as subcommands. When registering subcommands declaratively - * like this, you don't need to call the {@link CommandLine#addSubcommand(String, Object)} method. For example, this: - *
-         * @Command(subcommands = {
-         *         GitStatus.class,
-         *         GitCommit.class,
-         *         GitBranch.class })
-         * public class Git { ... }
-         *
-         * CommandLine commandLine = new CommandLine(new Git());
-         * 
is equivalent to this: - *
-         * // alternative: programmatically add subcommands.
-         * // NOTE: in this case there should be no `subcommands` attribute on the @Command annotation.
-         * @Command public class Git { ... }
-         *
-         * CommandLine commandLine = new CommandLine(new Git())
-         *         .addSubcommand("status",   new GitStatus())
-         *         .addSubcommand("commit",   new GitCommit())
-         *         .addSubcommand("branch",   new GitBranch());
-         * 
- * @return the declaratively registered subcommands of this command, or an empty array if none - * @see CommandLine#addSubcommand(String, Object) - * @see HelpCommand - * @since 0.9.8 - */ - Class[] subcommands() default {}; - - /** String that separates options from option parameters. Default is {@code "="}. Spaces are also accepted. - * @return the string that separates options from option parameters, used both when parsing and when generating usage help - * @see CommandLine#setSeparator(String) */ - String separator() default "="; - - /** Version information for this command, to print to the console when the user specifies an - * {@linkplain Option#versionHelp() option} to request version help. This is not part of the usage help message. - * - * @return a string or an array of strings with version information about this command (each string in the array is displayed on a separate line). - * @since 0.9.8 - * @see CommandLine#printVersionHelp(PrintStream) - */ - String[] version() default {}; - - /** Class that can provide version information dynamically at runtime. An implementation may return version - * information obtained from the JAR manifest, a properties file or some other source. - * @return a Class that can provide version information dynamically at runtime - * @since 2.2 */ - Class versionProvider() default NoVersionProvider.class; - - /** - * Adds the standard {@code -h} and {@code --help} {@linkplain Option#usageHelp() usageHelp} options and {@code -V} - * and {@code --version} {@linkplain Option#versionHelp() versionHelp} options to the options of this command. - *

- * Note that if no {@link #version()} or {@link #versionProvider()} is specified, the {@code --version} option will not print anything. - *

- * @return whether the auto-help mixin should be added to this command - * @since 3.0 */ - boolean mixinStandardHelpOptions() default false; - - /** Set this attribute to {@code true} if this subcommand is a help command, and required options and positional - * parameters of the parent command should not be validated. If a subcommand marked as {@code helpCommand} is - * specified on the command line, picocli will not validate the parent arguments (so no "missing required - * option" errors) and the {@link CommandLine#printHelpIfRequested(List, PrintStream, PrintStream, Help.Ansi)} method will return {@code true}. - * @return {@code true} if this subcommand is a help command and picocli should not check for missing required - * options and positional parameters on the parent command - * @since 3.0 */ - boolean helpCommand() default false; - - /** Set the heading preceding the header section. May contain embedded {@linkplain java.util.Formatter format specifiers}. - * @return the heading preceding the header section - * @see UsageMessageSpec#headerHeading() - * @see Help#headerHeading(Object...) */ - String headerHeading() default ""; - - /** Optional summary description of the command, shown before the synopsis. - * @return summary description of the command - * @see UsageMessageSpec#header() - * @see Help#header(Object...) */ - String[] header() default {}; - - /** Set the heading preceding the synopsis text. May contain embedded - * {@linkplain java.util.Formatter format specifiers}. The default heading is {@code "Usage: "} (without a line - * break between the heading and the synopsis text). - * @return the heading preceding the synopsis text - * @see Help#synopsisHeading(Object...) */ - String synopsisHeading() default "Usage: "; - - /** Specify {@code true} to generate an abbreviated synopsis like {@code "
[OPTIONS] [PARAMETERS...]"}. - * By default, a detailed synopsis with individual option names and parameters is generated. - * @return whether the synopsis should be abbreviated - * @see Help#abbreviatedSynopsis() - * @see Help#detailedSynopsis(Comparator, boolean) */ - boolean abbreviateSynopsis() default false; - - /** Specify one or more custom synopsis lines to display instead of an auto-generated synopsis. - * @return custom synopsis text to replace the auto-generated synopsis - * @see Help#customSynopsis(Object...) */ - String[] customSynopsis() default {}; - - /** Set the heading preceding the description section. May contain embedded {@linkplain java.util.Formatter format specifiers}. - * @return the heading preceding the description section - * @see Help#descriptionHeading(Object...) */ - String descriptionHeading() default ""; - - /** Optional text to display between the synopsis line(s) and the list of options. - * @return description of this command - * @see Help#description(Object...) */ - String[] description() default {}; - - /** Set the heading preceding the parameters list. May contain embedded {@linkplain java.util.Formatter format specifiers}. - * @return the heading preceding the parameters list - * @see Help#parameterListHeading(Object...) */ - String parameterListHeading() default ""; - - /** Set the heading preceding the options list. May contain embedded {@linkplain java.util.Formatter format specifiers}. - * @return the heading preceding the options list - * @see Help#optionListHeading(Object...) */ - String optionListHeading() default ""; - - /** Specify {@code false} to show Options in declaration order. The default is to sort alphabetically. - * @return whether options should be shown in alphabetic order. */ - boolean sortOptions() default true; - - /** Prefix required options with this character in the options list. The default is no marker: the synopsis - * indicates which options and parameters are required. - * @return the character to show in the options list to mark required options */ - char requiredOptionMarker() default ' '; - - /** Specify {@code true} to show default values in the description column of the options list (except for - * boolean options). False by default. - *

Note that picocli 3.2 allows {@linkplain Option#description() embedding default values} anywhere in the - * option or positional parameter description that ignores this setting.

- * @return whether the default values for options and parameters should be shown in the description column */ - boolean showDefaultValues() default false; - - /** Set the heading preceding the subcommands list. May contain embedded {@linkplain java.util.Formatter format specifiers}. - * The default heading is {@code "Commands:%n"} (with a line break at the end). - * @return the heading preceding the subcommands list - * @see Help#commandListHeading(Object...) */ - String commandListHeading() default "Commands:%n"; - - /** Set the heading preceding the footer section. May contain embedded {@linkplain java.util.Formatter format specifiers}. - * @return the heading preceding the footer section - * @see Help#footerHeading(Object...) */ - String footerHeading() default ""; - - /** Optional text to display after the list of options. - * @return text to display after the list of options - * @see Help#footer(Object...) */ - String[] footer() default {}; - - /** - * Set {@code hidden=true} if this command should not be included in the list of commands in the usage help of the parent command. - * @return whether this command should be excluded from the usage message - * @since 3.0 - */ - boolean hidden() default false; - } - /** - *

- * When parsing command line arguments and initializing - * fields annotated with {@link Option @Option} or {@link Parameters @Parameters}, - * String values can be converted to any type for which a {@code ITypeConverter} is registered. - *

- * This interface defines the contract for classes that know how to convert a String into some domain object. - * Custom converters can be registered with the {@link #registerConverter(Class, ITypeConverter)} method. - *

- * Java 8 lambdas make it easy to register custom type converters: - *

- *
-     * commandLine.registerConverter(java.nio.file.Path.class, s -> java.nio.file.Paths.get(s));
-     * commandLine.registerConverter(java.time.Duration.class, s -> java.time.Duration.parse(s));
- *

- * Built-in type converters are pre-registered for the following java 1.5 types: - *

- *
    - *
  • all primitive types
  • - *
  • all primitive wrapper types: Boolean, Byte, Character, Double, Float, Integer, Long, Short
  • - *
  • any enum
  • - *
  • java.io.File
  • - *
  • java.math.BigDecimal
  • - *
  • java.math.BigInteger
  • - *
  • java.net.InetAddress
  • - *
  • java.net.URI
  • - *
  • java.net.URL
  • - *
  • java.nio.charset.Charset
  • - *
  • java.sql.Time
  • - *
  • java.util.Date
  • - *
  • java.util.UUID
  • - *
  • java.util.regex.Pattern
  • - *
  • StringBuilder
  • - *
  • CharSequence
  • - *
  • String
  • - *
- * @param the type of the object that is the result of the conversion - */ - public interface ITypeConverter { - /** - * Converts the specified command line argument value to some domain object. - * @param value the command line argument String value - * @return the resulting domain object - * @throws Exception an exception detailing what went wrong during the conversion - */ - K convert(String value) throws Exception; - } - - /** - * Provides version information for a command. Commands may configure a provider with the - * {@link Command#versionProvider()} annotation attribute. - * @since 2.2 */ - public interface IVersionProvider { - /** - * Returns version information for a command. - * @return version information (each string in the array is displayed on a separate line) - * @throws Exception an exception detailing what went wrong when obtaining version information - */ - String[] getVersion() throws Exception; - } - private static class NoVersionProvider implements IVersionProvider { - public String[] getVersion() throws Exception { throw new UnsupportedOperationException(); } - } - - /** - * Factory for instantiating classes that are registered declaratively with annotation attributes, like - * {@link Command#subcommands()}, {@link Option#converter()}, {@link Parameters#converter()} and {@link Command#versionProvider()}. - * @since 2.2 */ - public interface IFactory { - /** - * Creates and returns an instance of the specified class. - * @param cls the class to instantiate - * @param the type to instantiate - * @return the new instance - * @throws Exception an exception detailing what went wrong when creating the instance - */ - K create(Class cls) throws Exception; - } - /** Returns a default {@link IFactory} implementation. Package-protected for testing purposes. */ - static IFactory defaultFactory() { return new DefaultFactory(); } - private static class DefaultFactory implements IFactory { - public T create(Class cls) throws Exception { - try { - return cls.newInstance(); - } catch (Exception ex) { - Constructor constructor = cls.getDeclaredConstructor(); - constructor.setAccessible(true); - return constructor.newInstance(); - } - } - private static ITypeConverter[] createConverter(IFactory factory, Class>[] classes) { - ITypeConverter[] result = new ITypeConverter[classes.length]; - for (int i = 0; i < classes.length; i++) { - try { - result[i] = factory.create(classes[i]); - } catch (Exception ex) { - throw new InitializationException("Could not instantiate " + classes[i] + ": " + ex, ex); - } - } - return result; - } - public static IVersionProvider createVersionProvider(IFactory factory, Class cls) { - try { return factory.create(cls); } - catch (Exception ex) { throw new InitializationException("Could not instantiate " + cls + ": " + ex, ex); } - } - public static Iterable createCompletionCandidates(IFactory factory, Class> cls) { - try { return factory.create(cls); } - catch (Exception ex) { throw new InitializationException("Could not instantiate " + cls + ": " + ex, ex); } - } - } - /** Describes the number of parameters required and accepted by an option or a positional parameter. - * @since 0.9.7 - */ - public static class Range implements Comparable { - /** Required number of parameters for an option or positional parameter. */ - public final int min; - /** Maximum accepted number of parameters for an option or positional parameter. */ - public final int max; - public final boolean isVariable; - private final boolean isUnspecified; - private final String originalValue; - - /** Constructs a new Range object with the specified parameters. - * @param min minimum number of required parameters - * @param max maximum number of allowed parameters (or Integer.MAX_VALUE if variable) - * @param variable {@code true} if any number or parameters is allowed, {@code false} otherwise - * @param unspecified {@code true} if no arity was specified on the option/parameter (value is based on type) - * @param originalValue the original value that was specified on the option or parameter - */ - public Range(int min, int max, boolean variable, boolean unspecified, String originalValue) { - if (min < 0 || max < 0) { throw new InitializationException("Invalid negative range (min=" + min + ", max=" + max + ")"); } - if (min > max) { throw new InitializationException("Invalid range (min=" + min + ", max=" + max + ")"); } - this.min = min; - this.max = max; - this.isVariable = variable; - this.isUnspecified = unspecified; - this.originalValue = originalValue; - } - /** Returns a new {@code Range} based on the {@link Option#arity()} annotation on the specified field, - * or the field type's default arity if no arity was specified. - * @param field the field whose Option annotation to inspect - * @return a new {@code Range} based on the Option arity annotation on the specified field */ - public static Range optionArity(Field field) { - return field.isAnnotationPresent(Option.class) - ? adjustForType(Range.valueOf(field.getAnnotation(Option.class).arity()), field) - : new Range(0, 0, false, true, "0"); - } - /** Returns a new {@code Range} based on the {@link Parameters#arity()} annotation on the specified field, - * or the field type's default arity if no arity was specified. - * @param field the field whose Parameters annotation to inspect - * @return a new {@code Range} based on the Parameters arity annotation on the specified field */ - public static Range parameterArity(Field field) { - return field.isAnnotationPresent(Parameters.class) - ? adjustForType(Range.valueOf(field.getAnnotation(Parameters.class).arity()), field) - : new Range(0, 0, false, true, "0"); - } - /** Returns a new {@code Range} based on the {@link Parameters#index()} annotation on the specified field. - * @param field the field whose Parameters annotation to inspect - * @return a new {@code Range} based on the Parameters index annotation on the specified field */ - public static Range parameterIndex(Field field) { - return field.isAnnotationPresent(Parameters.class) - ? Range.valueOf(field.getAnnotation(Parameters.class).index()) - : new Range(0, 0, false, true, "0"); - } - static Range adjustForType(Range result, Field field) { - return result.isUnspecified ? defaultArity(field) : result; - } - /** Returns the default arity {@code Range}: for {@link Option options} this is 0 for booleans and 1 for - * other types, for {@link Parameters parameters} booleans have arity 0, arrays or Collections have - * arity "0..*", and other types have arity 1. - * @param field the field whose default arity to return - * @return a new {@code Range} indicating the default arity of the specified field - * @since 2.0 */ - public static Range defaultArity(Field field) { - Class type = field.getType(); - if (field.isAnnotationPresent(Option.class)) { - Class[] typeAttribute = ArgsReflection - .inferTypes(type, field.getAnnotation(Option.class).type(), field.getGenericType()); - boolean zeroArgs = isBoolean(type) || (isMultiValue(type) && isBoolean(typeAttribute[0])); - return zeroArgs ? Range.valueOf("0") : Range.valueOf("1"); - } - if (isMultiValue(type)) { - return Range.valueOf("0..1"); - } - return Range.valueOf("1");// for single-valued fields (incl. boolean positional parameters) - } - /** Returns the default arity {@code Range} for {@link Option options}: booleans have arity 0, other types have arity 1. - * @param type the type whose default arity to return - * @return a new {@code Range} indicating the default arity of the specified type - * @deprecated use {@link #defaultArity(Field)} instead */ - @Deprecated public static Range defaultArity(Class type) { - return isBoolean(type) ? Range.valueOf("0") : Range.valueOf("1"); - } - private int size() { return 1 + max - min; } - static Range parameterCapacity(Field field) { - Range arity = parameterArity(field); - if (!isMultiValue(field)) { return arity; } - Range index = parameterIndex(field); - return parameterCapacity(arity, index); - } - private static Range parameterCapacity(Range arity, Range index) { - if (arity.max == 0) { return arity; } - if (index.size() == 1) { return arity; } - if (index.isVariable) { return Range.valueOf(arity.min + "..*"); } - if (arity.size() == 1) { return Range.valueOf(arity.min * index.size() + ""); } - if (arity.isVariable) { return Range.valueOf(arity.min * index.size() + "..*"); } - return Range.valueOf(arity.min * index.size() + ".." + arity.max * index.size()); - } - - /** Leniently parses the specified String as an {@code Range} value and return the result. A range string can - * be a fixed integer value or a range of the form {@code MIN_VALUE + ".." + MAX_VALUE}. If the - * {@code MIN_VALUE} string is not numeric, the minimum is zero. If the {@code MAX_VALUE} is not numeric, the - * range is taken to be variable and the maximum is {@code Integer.MAX_VALUE}. - * @param range the value range string to parse - * @return a new {@code Range} value */ - public static Range valueOf(String range) { - range = range.trim(); - boolean unspecified = range.length() == 0 || range.startsWith(".."); // || range.endsWith(".."); - int min = -1, max = -1; - boolean variable = false; - int dots = -1; - if ((dots = range.indexOf("..")) >= 0) { - min = parseInt(range.substring(0, dots), 0); - max = parseInt(range.substring(dots + 2), Integer.MAX_VALUE); - variable = max == Integer.MAX_VALUE; - } else { - max = parseInt(range, Integer.MAX_VALUE); - variable = max == Integer.MAX_VALUE; - min = variable ? 0 : max; - } - Range result = new Range(min, max, variable, unspecified, range); - return result; - } - private static int parseInt(String str, int defaultValue) { - try { - return Integer.parseInt(str); - } catch (Exception ex) { - return defaultValue; - } - } - /** Returns a new Range object with the {@code min} value replaced by the specified value. - * The {@code max} of the returned Range is guaranteed not to be less than the new {@code min} value. - * @param newMin the {@code min} value of the returned Range object - * @return a new Range object with the specified {@code min} value */ - public Range min(int newMin) { return new Range(newMin, Math.max(newMin, max), isVariable, isUnspecified, originalValue); } - - /** Returns a new Range object with the {@code max} value replaced by the specified value. - * The {@code min} of the returned Range is guaranteed not to be greater than the new {@code max} value. - * @param newMax the {@code max} value of the returned Range object - * @return a new Range object with the specified {@code max} value */ - public Range max(int newMax) { return new Range(Math.min(min, newMax), newMax, isVariable, isUnspecified, originalValue); } - - /** - * Returns {@code true} if this Range includes the specified value, {@code false} otherwise. - * @param value the value to check - * @return {@code true} if the specified value is not less than the minimum and not greater than the maximum of this Range - */ - public boolean contains(int value) { return min <= value && max >= value; } - - public boolean equals(Object object) { - if (!(object instanceof Range)) { return false; } - Range other = (Range) object; - return other.max == this.max && other.min == this.min && other.isVariable == this.isVariable; - } - public int hashCode() { - return ((17 * 37 + max) * 37 + min) * 37 + (isVariable ? 1 : 0); - } - public String toString() { - return min == max ? String.valueOf(min) : min + ".." + (isVariable ? "*" : max); - } - public int compareTo(Range other) { - int result = min - other.min; - return (result == 0) ? max - other.max : result; - } - } - private static void validatePositionalParameters(List positionalParametersFields) { - int min = 0; - for (PositionalParamSpec positional : positionalParametersFields) { - Range index = positional.index(); - if (index.min > min) { - throw new ParameterIndexGapException("Missing positional parameter with index=" + min + - ". Nearest positional parameter '" + positional.paramLabel() + "' has index=" + index.min); - } - min = Math.max(min, index.max); - min = min == Integer.MAX_VALUE ? min : min + 1; - } - } - @SuppressWarnings("unchecked") private static Stack copy(Stack stack) { return (Stack) stack.clone(); } - private static Stack reverse(Stack stack) { - Collections.reverse(stack); - return stack; - } - private static List reverseList(List list) { - Collections.reverse(list); - return list; - } - - /** This class provides a namespace for classes and interfaces that model concepts and attributes of command line interfaces in picocli. - * @since 3.0 */ - public static final class Model { - private Model() {} - - /** Customizable getter for obtaining the current value of an option or positional parameter. - * When an option or positional parameter is matched on the command line, its getter or setter is invoked to capture the value. - * For example, an option can be bound to a field or a method, and when the option is matched on the command line, the - * field's value is set or the method is invoked with the option parameter value. - * @since 3.0 */ - public static interface IGetter { - /** Returns the current value of the binding. For multi-value options and positional parameters, - * this method returns an array, collection or map to add values to. - * @throws PicocliException if a problem occurred while obtaining the current value - * @throws Exception internally, picocli call sites will catch any exceptions thrown from here and rethrow them wrapped in a PicocliException */ - T get() throws Exception; - } - /** Customizable setter for modifying the value of an option or positional parameter. - * When an option or positional parameter is matched on the command line, its setter is invoked to capture the value. - * For example, an option can be bound to a field or a method, and when the option is matched on the command line, the - * field's value is set or the method is invoked with the option parameter value. - * @since 3.0 */ - public static interface ISetter { - /** Sets the new value of the option or positional parameter. - * - * @param value the new value of the option or positional parameter - * @param type of the value - * @return the previous value of the binding (if supported by this binding) - * @throws PicocliException if a problem occurred while setting the new value - * @throws Exception internally, picocli call sites will catch any exceptions thrown from here and rethrow them wrapped in a PicocliException */ - T set(T value) throws Exception; - } - - /** The {@code CommandSpec} class models a command specification, including the options, positional parameters and subcommands - * supported by the command, as well as attributes for the version help message and the usage help message of the command. - *

- * Picocli views a command line application as a hierarchy of commands: there is a top-level command (usually the Java - * class with the {@code main} method) with optionally a set of command line options, positional parameters and subcommands. - * Subcommands themselves can have options, positional parameters and nested sub-subcommands to any level of depth. - *

- * The object model has a corresponding hierarchy of {@code CommandSpec} objects, each with a set of {@link OptionSpec}, - * {@link PositionalParamSpec} and {@linkplain CommandLine subcommands} associated with it. - * This object model is used by the picocli command line interpreter and help message generator. - *

Picocli can construct a {@code CommandSpec} automatically from classes with {@link Command @Command}, {@link Option @Option} and - * {@link Parameters @Parameters} annotations. Alternatively a {@code CommandSpec} can be constructed programmatically. - *

- * @since 3.0 */ - public static class CommandSpec { - /** Constant String holding the default program name: {@code "
" }. */ - static final String DEFAULT_COMMAND_NAME = "
"; - - /** Constant Boolean holding the default setting for whether this is a help command: {@value}.*/ - static final Boolean DEFAULT_IS_HELP_COMMAND = Boolean.FALSE; - - private final Map commands = new LinkedHashMap(); - private final Map optionsByNameMap = new LinkedHashMap(); - private final Map posixOptionsByKeyMap = new LinkedHashMap(); - private final Map mixins = new LinkedHashMap(); - private final List requiredArgs = new ArrayList(); - private final List options = new ArrayList(); - private final List positionalParameters = new ArrayList(); - private final List unmatchedArgs = new ArrayList(); - private final ParserSpec parser = new ParserSpec(); - private final UsageMessageSpec usageMessage = new UsageMessageSpec(); - - private final Object userObject; - private CommandLine commandLine; - private CommandSpec parent; - - private String name; - private String[] aliases = {}; - private Boolean isHelpCommand; - private IVersionProvider versionProvider; - private String[] version; - private String toString; - - private CommandSpec(Object userObject) { this.userObject = userObject; } - - /** Creates and returns a new {@code CommandSpec} without any associated user object. */ - public static CommandSpec create() { return wrapWithoutInspection(null); } - - /** Creates and returns a new {@code CommandSpec} with the specified associated user object. - * The specified user object is not inspected for annotations. - * @param userObject the associated user object. May be any object, may be {@code null}. - */ - public static CommandSpec wrapWithoutInspection(Object userObject) { return new CommandSpec(userObject); } - - /** Creates and returns a new {@code CommandSpec} initialized from the specified associated user object. The specified - * user object must have at least one {@link Command}, {@link Option} or {@link Parameters} annotation. - * @param userObject the user object annotated with {@link Command}, {@link Option} and/or {@link Parameters} annotations. - * @throws InitializationException if the specified object has no picocli annotations or has invalid annotations - */ - public static CommandSpec forAnnotatedObject(Object userObject) { return forAnnotatedObject(userObject, new DefaultFactory()); } - - /** Creates and returns a new {@code CommandSpec} initialized from the specified associated user object. The specified - * user object must have at least one {@link Command}, {@link Option} or {@link Parameters} annotation. - * @param userObject the user object annotated with {@link Command}, {@link Option} and/or {@link Parameters} annotations. - * @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 object has no picocli annotations or has invalid annotations - */ - public static CommandSpec forAnnotatedObject(Object userObject, IFactory factory) { return CommandReflection.extractCommandSpec(userObject, factory, true); } - - /** Creates and returns a new {@code CommandSpec} initialized from the specified associated user object. If the specified - * user object has no {@link Command}, {@link Option} or {@link Parameters} annotations, an empty {@code CommandSpec} is returned. - * @param userObject the user object annotated with {@link Command}, {@link Option} and/or {@link Parameters} annotations. - * @throws InitializationException if the specified object has invalid annotations - */ - public static CommandSpec forAnnotatedObjectLenient(Object userObject) { return forAnnotatedObjectLenient(userObject, new DefaultFactory()); } - - /** Creates and returns a new {@code CommandSpec} initialized from the specified associated user object. If the specified - * user object has no {@link Command}, {@link Option} or {@link Parameters} annotations, an empty {@code CommandSpec} is returned. - * @param userObject the user object annotated with {@link Command}, {@link Option} and/or {@link Parameters} annotations. - * @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 object has invalid annotations - */ - public static CommandSpec forAnnotatedObjectLenient(Object userObject, IFactory factory) { return CommandReflection.extractCommandSpec(userObject, factory, false); } - - /** Ensures all attributes of this {@code CommandSpec} have a valid value; throws an {@link InitializationException} if this cannot be achieved. */ - void validate() { - Collections.sort(positionalParameters, new PositionalParametersSorter()); - validatePositionalParameters(positionalParameters); - List wrongUsageHelpAttr = new ArrayList(); - List wrongVersionHelpAttr = new ArrayList(); - List usageHelpAttr = new ArrayList(); - List versionHelpAttr = new ArrayList(); - for (OptionSpec option : options()) { - if (option.usageHelp()) { - usageHelpAttr.add(option.longestName()); - if (!isBoolean(option.type())) { wrongUsageHelpAttr.add(option.longestName()); } - } - if (option.versionHelp()) { - versionHelpAttr.add(option.longestName()); - if (!isBoolean(option.type())) { wrongVersionHelpAttr.add(option.longestName()); } - } - } - String wrongType = "Non-boolean options like %s should not be marked as '%s=true'. Usually a command has one %s boolean flag that triggers display of the %s. Alternatively, consider using @Command(mixinStandardHelpOptions = true) on your command instead."; - String multiple = "Multiple options %s are marked as '%s=true'. Usually a command has only one %s option that triggers display of the %s. Alternatively, consider using @Command(mixinStandardHelpOptions = true) on your command instead.%n"; - if (!wrongUsageHelpAttr.isEmpty()) { - throw new InitializationException(String.format(wrongType, wrongUsageHelpAttr, "usageHelp", "--help", "usage help message")); - } - if (!wrongVersionHelpAttr.isEmpty()) { - throw new InitializationException(String.format(wrongType, wrongVersionHelpAttr, "versionHelp", "--version", "version information")); - } - if (usageHelpAttr.size() > 1) { new Tracer().warn(multiple, usageHelpAttr, "usageHelp", "--help", "usage help message"); } - if (versionHelpAttr.size() > 1) { new Tracer().warn(multiple, versionHelpAttr, "versionHelp", "--version", "version information"); } - } - - /** Returns the user object associated with this command. - * @see CommandLine#getCommand() */ - public Object userObject() { return userObject; } - - /** Returns the CommandLine constructed with this {@code CommandSpec} model. */ - public CommandLine commandLine() { return commandLine;} - - /** Sets the CommandLine constructed with this {@code CommandSpec} model. */ - protected CommandSpec commandLine(CommandLine commandLine) { - this.commandLine = commandLine; - for (CommandLine sub : commands.values()) { - sub.getCommandSpec().parent(this); - } - return this; - } - - /** Returns the parser specification for this command. */ - public ParserSpec parser() { return parser; } - /** Initializes the parser specification for this command from the specified settings and returns this commandSpec.*/ - public CommandSpec parser(ParserSpec settings) { parser.initFrom(settings); return this; } - - /** Returns the usage help message specification for this command. */ - public UsageMessageSpec usageMessage() { return usageMessage; } - /** Initializes the usageMessage specification for this command from the specified settings and returns this commandSpec.*/ - public CommandSpec usageMessage(UsageMessageSpec settings) { usageMessage.initFrom(settings); return this; } - - /** Returns a read-only view of the subcommand map. */ - public Map subcommands() { return Collections.unmodifiableMap(commands); } - - /** Adds the specified subcommand with the specified name. - * @param name subcommand name - when this String is encountered in the command line arguments the subcommand is invoked - * @param subcommand describes the subcommand to envoke when the name is encountered on the command line - * @return this {@code CommandSpec} object for method chaining */ - public CommandSpec addSubcommand(String name, CommandSpec subcommand) { - return addSubcommand(name, new CommandLine(subcommand)); - } - - /** Adds the specified subcommand with the specified name. - * @param name subcommand name - when this String is encountered in the command line arguments the subcommand is invoked - * @param subCommandLine the subcommand to envoke when the name is encountered on the command line - * @return this {@code CommandSpec} object for method chaining */ - public CommandSpec addSubcommand(String name, CommandLine subCommandLine) { - CommandLine previous = commands.put(name, subCommandLine); - if (previous != null && previous != subCommandLine) { throw new InitializationException("Another subcommand named '" + name + "' already exists for command '" + this.name() + "'"); } - CommandSpec subSpec = subCommandLine.getCommandSpec(); - if (subSpec.name == null) { subSpec.name(name); } - subSpec.parent(this); - for (String alias : subSpec.aliases()) { - previous = commands.put(alias, subCommandLine); - if (previous != null && previous != subCommandLine) { throw new InitializationException("Alias '" + alias + "' for subcommand '" + name + "' is already used by another subcommand of '" + this.name() + "'"); } - } - return this; - } - - /** Returns the parent command of this subcommand, or {@code null} if this is a top-level command. */ - public CommandSpec parent() { return parent; } - - /** Sets the parent command of this subcommand. - * @return this CommandSpec for method chaining */ - public CommandSpec parent(CommandSpec parent) { this.parent = parent; return this; } - - /** Adds the specified option spec or positional parameter spec to the list of configured arguments to expect. - * @param arg the option spec or positional parameter spec to add - * @return this CommandSpec for method chaining */ - public CommandSpec add(ArgSpec arg) { return arg.isOption() ? addOption((OptionSpec) arg) : addPositional((PositionalParamSpec) arg); } - - /** Adds the specified option spec to the list of configured arguments to expect. - * @param option the option spec to add - * @return this CommandSpec for method chaining - * @throws DuplicateOptionAnnotationsException if any of the names of the specified option is the same as the name of another option */ - public CommandSpec addOption(OptionSpec option) { - options.add(option); - for (String name : option.names()) { // cannot be null or empty - OptionSpec existing = optionsByNameMap.put(name, option); - if (existing != null && !existing.equals(option)) { - throw DuplicateOptionAnnotationsException.create(name, option, existing); - } - if (name.length() == 2 && name.startsWith("-")) { posixOptionsByKeyMap.put(name.charAt(1), option); } - } - if (option.required()) { requiredArgs.add(option); } - return this; - } - /** Adds the specified positional parameter spec to the list of configured arguments to expect. - * @param positional the positional parameter spec to add - * @return this CommandSpec for method chaining */ - public CommandSpec addPositional(PositionalParamSpec positional) { - positionalParameters.add(positional); - if (positional.required()) { requiredArgs.add(positional); } - return this; - } - - /** Adds the specified mixin {@code CommandSpec} object to the map of mixins for this command. - * @param name the name that can be used to later retrieve the mixin - * @param mixin the mixin whose options and positional parameters and other attributes to add to this command - * @return this CommandSpec for method chaining */ - public CommandSpec addMixin(String name, CommandSpec mixin) { - mixins.put(name, mixin); - - parser.initSeparator(mixin.parser.separator()); - initName(mixin.name()); - initVersion(mixin.version()); - initHelpCommand(mixin.helpCommand()); - initVersionProvider(mixin.versionProvider()); - usageMessage.initSynopsisHeading(mixin.usageMessage.synopsisHeading()); - usageMessage.initCommandListHeading(mixin.usageMessage.commandListHeading()); - usageMessage.initRequiredOptionMarker(mixin.usageMessage.requiredOptionMarker()); - usageMessage.initCustomSynopsis(mixin.usageMessage.customSynopsis()); - usageMessage.initDescription(mixin.usageMessage.description()); - usageMessage.initDescriptionHeading(mixin.usageMessage.descriptionHeading()); - usageMessage.initHeader(mixin.usageMessage.header()); - usageMessage.initHeaderHeading(mixin.usageMessage.headerHeading()); - usageMessage.initFooter(mixin.usageMessage.footer()); - usageMessage.initFooterHeading(mixin.usageMessage.footerHeading()); - usageMessage.initParameterListHeading(mixin.usageMessage.parameterListHeading()); - usageMessage.initOptionListHeading(mixin.usageMessage.optionListHeading()); - usageMessage.initAbbreviateSynopsis(mixin.usageMessage.abbreviateSynopsis()); - usageMessage.initSortOptions(mixin.usageMessage.sortOptions()); - usageMessage.initShowDefaultValues(mixin.usageMessage.showDefaultValues()); - usageMessage.initHidden(mixin.usageMessage.hidden()); - - for (Map.Entry entry : mixin.subcommands().entrySet()) { - addSubcommand(entry.getKey(), entry.getValue()); - } - for (OptionSpec optionSpec : mixin.options()) { addOption(optionSpec); } - for (PositionalParamSpec paramSpec : mixin.positionalParameters()) { addPositional(paramSpec); } - return this; - } - - /** Adds the specified {@code UnmatchedArgsBinding} to the list of model objects to capture unmatched arguments for this command. - * @param spec the unmatched arguments binding to capture unmatched arguments - * @return this CommandSpec for method chaining */ - public CommandSpec addUnmatchedArgsBinding(UnmatchedArgsBinding spec) { unmatchedArgs.add(spec); parser().unmatchedArgumentsAllowed(true); return this; } - - /** Returns a map of the mixin names to mixin {@code CommandSpec} objects configured for this command. - * @return an immutable map of mixins added to this command. */ - public Map mixins() { return Collections.unmodifiableMap(mixins); } - - /** Returns the list of options configured for this command. - * @return an immutable list of options that this command recognizes. */ - public List options() { return Collections.unmodifiableList(options); } - - /** Returns the list of positional parameters configured for this command. - * @return an immutable list of positional parameters that this command recognizes. */ - public List positionalParameters() { return Collections.unmodifiableList(positionalParameters); } - - /** Returns a map of the option names to option spec objects configured for this command. - * @return an immutable map of options that this command recognizes. */ - public Map optionsMap() { return Collections.unmodifiableMap(optionsByNameMap); } - - /** Returns a map of the short (single character) option names to option spec objects configured for this command. - * @return an immutable map of options that this command recognizes. */ - public Map posixOptionsMap() { return Collections.unmodifiableMap(posixOptionsByKeyMap); } - - /** Returns the list of required options and positional parameters configured for this command. - * @return an immutable list of the required options and positional parameters for this command. */ - public List requiredArgs() { return Collections.unmodifiableList(requiredArgs); } - - /** Returns the list of {@link UnmatchedArgsBinding UnmatchedArgumentsBindings} configured for this command; - * each {@code UnmatchedArgsBinding} captures the arguments that could not be matched to any options or positional parameters. */ - public List unmatchedArgsBindings() { return Collections.unmodifiableList(unmatchedArgs); } - - /** Returns name of this command. Used in the synopsis line of the help message. - * {@link #DEFAULT_COMMAND_NAME} by default, initialized from {@link Command#name()} if defined. - * @see #qualifiedName() */ - public String name() { return (name == null) ? DEFAULT_COMMAND_NAME : name; } - - /** Returns the alias command names of this subcommand. - * @since 3.1 */ - public String[] aliases() { return aliases.clone(); } - - /** Returns the String to use as the program name in the synopsis line of the help message: - * this command's {@link #name() name}, preceded by the qualified name of the parent command, if any. - * {@link #DEFAULT_COMMAND_NAME} by default, initialized from {@link Command#name()} if defined. - * @since 3.0.1 */ - public String qualifiedName() { - String result = name(); - if (parent() != null) { result = parent().qualifiedName() + " " + result; } - return result; - } - - /** Returns version information for this command, to print to the console when the user specifies an - * {@linkplain OptionSpec#versionHelp() option} to request version help. This is not part of the usage help message. - * @return the version strings generated by the {@link #versionProvider() version provider} if one is set, otherwise the {@linkplain #version(String...) version literals}*/ - public String[] version() { - if (versionProvider != null) { - try { - return versionProvider.getVersion(); - } catch (Exception ex) { - String msg = "Could not get version info from " + versionProvider + ": " + ex; - throw new ExecutionException(this.commandLine, msg, ex); - } - } - return version == null ? UsageMessageSpec.DEFAULT_MULTI_LINE : version; - } - - /** Returns the version provider for this command, to generate the {@link #version()} strings. - * @return the version provider or {@code null} if the version strings should be returned from the {@linkplain #version(String...) version literals}.*/ - public IVersionProvider versionProvider() { return versionProvider; } - - /** Returns whether this subcommand is a help command, and required options and positional - * parameters of the parent command should not be validated. - * @return {@code true} if this subcommand is a help command and picocli should not check for missing required - * options and positional parameters on the parent command - * @see Command#helpCommand() */ - public boolean helpCommand() { return (isHelpCommand == null) ? DEFAULT_IS_HELP_COMMAND : isHelpCommand; } - - /** Returns {@code true} if the standard help options have been mixed in with this command, {@code false} otherwise. */ - public boolean mixinStandardHelpOptions() { return mixins.containsKey(AutoHelpMixin.KEY); } - - /** Returns a string representation of this command, used in error messages and trace messages. */ - public String toString() { return toString; } - - /** Sets the String to use as the program name in the synopsis line of the help message. - * @return this CommandSpec for method chaining */ - public CommandSpec name(String name) { this.name = name; return this; } - - /** Sets the alternative names by which this subcommand is recognized on the command line. - * @return this CommandSpec for method chaining - * @since 3.1 */ - public CommandSpec aliases(String... aliases) { this.aliases = aliases == null ? new String[0] : aliases.clone(); return this; } - - /** Sets version information literals for this command, to print to the console when the user specifies an - * {@linkplain OptionSpec#versionHelp() option} to request version help. Only used if no {@link #versionProvider() versionProvider} is set. - * @return this CommandSpec for method chaining */ - public CommandSpec version(String... version) { this.version = version; return this; } - - /** Sets version provider for this command, to generate the {@link #version()} strings. - * @param versionProvider the version provider to use to generate the version strings, or {@code null} if the {@linkplain #version(String...) version literals} should be used. - * @return this CommandSpec for method chaining */ - public CommandSpec versionProvider(IVersionProvider versionProvider) { this.versionProvider = versionProvider; return this; } - - /** Sets whether this is a help command and required parameter checking should be suspended. - * @return this CommandSpec for method chaining - * @see Command#helpCommand() */ - public CommandSpec helpCommand(boolean newValue) {isHelpCommand = newValue; return this;} - - /** Sets whether the standard help options should be mixed in with this command. - * @return this CommandSpec for method chaining - * @see Command#mixinStandardHelpOptions() */ - public CommandSpec mixinStandardHelpOptions(boolean newValue) { - if (newValue) { addMixin(AutoHelpMixin.KEY, CommandSpec.forAnnotatedObject(new AutoHelpMixin(), new DefaultFactory())); } - else { - CommandSpec helpMixin = mixins.remove(AutoHelpMixin.KEY); - if (helpMixin != null) { - options.removeAll(helpMixin.options); - for (OptionSpec option : helpMixin.options()) { - for (String name : option.names) { - optionsByNameMap.remove(name); - if (name.length() == 2 && name.startsWith("-")) { posixOptionsByKeyMap.remove(name.charAt(1)); } - } - } - } - } - return this; - } - - /** Sets the string representation of this command, used in error messages and trace messages. - * @param newValue the string representation - * @return this CommandSpec for method chaining */ - public CommandSpec withToString(String newValue) { this.toString = newValue; return this; } - - void initName(String value) { if (initializable(name, value, DEFAULT_COMMAND_NAME)) {name = value;} } - void initHelpCommand(boolean value) { if (initializable(isHelpCommand, value, DEFAULT_IS_HELP_COMMAND)) {isHelpCommand = value;} } - void initVersion(String[] value) { if (initializable(version, value, UsageMessageSpec.DEFAULT_MULTI_LINE)) {version = value.clone();} } - void initVersionProvider(IVersionProvider value) { if (versionProvider == null) { versionProvider = value; } } - void initVersionProvider(Class value, IFactory factory) { - if (initializable(versionProvider, value, NoVersionProvider.class)) { versionProvider = (DefaultFactory.createVersionProvider(factory, value)); } - } - - /** Returns the option with the specified short name, or {@code null} if no option with that name is defined for this command. */ - public OptionSpec findOption(char shortName) { return findOption(shortName, options()); } - /** Returns the option with the specified name, or {@code null} if no option with that name is defined for this command. - * @param name used to search the options. May include option name prefix characters or not. */ - public OptionSpec findOption(String name) { return findOption(name, options()); } - - static OptionSpec findOption(char shortName, Iterable options) { - for (OptionSpec option : options) { - for (String name : option.names()) { - if (name.length() == 2 && name.charAt(0) == '-' && name.charAt(1) == shortName) { return option; } - if (name.length() == 1 && name.charAt(0) == shortName) { return option; } - } - } - return null; - } - static OptionSpec findOption(String name, List options) { - for (OptionSpec option : options) { - for (String prefixed : option.names()) { - if (prefixed.equals(name) || stripPrefix(prefixed).equals(name)) { return option; } - } - } - return null; - } - private static String stripPrefix(String prefixed) { - for (int i = 0; i < prefixed.length(); i++) { - if (Character.isJavaIdentifierPart(prefixed.charAt(i))) { return prefixed.substring(i); } - } - return prefixed; - } - } - private static boolean initializable(Object current, Object candidate, Object defaultValue) { - return current == null && !Assert.notNull(defaultValue, "defaultValue").equals(candidate); - } - private static boolean initializable(Object current, Object[] candidate, Object[] defaultValue) { - return current == null && !Arrays.equals(Assert.notNull(defaultValue, "defaultValue"), candidate); - } - /** Models the usage help message specification. - * @since 3.0 */ - public static class UsageMessageSpec { - /** Constant holding the default usage message width: {@value}. */ - public final static int DEFAULT_USAGE_WIDTH = 80; - private final static int MINIMUM_USAGE_WIDTH = 55; - - /** Constant String holding the default synopsis heading: {@value}. */ - static final String DEFAULT_SYNOPSIS_HEADING = "Usage: "; - - /** Constant String holding the default command list heading: {@value}. */ - static final String DEFAULT_COMMAND_LIST_HEADING = "Commands:%n"; - - /** Constant String holding the default string that separates options from option parameters: {@code ' '} ({@value}). */ - static final char DEFAULT_REQUIRED_OPTION_MARKER = ' '; - - /** Constant Boolean holding the default setting for whether to abbreviate the synopsis: {@value}.*/ - static final Boolean DEFAULT_ABBREVIATE_SYNOPSIS = Boolean.FALSE; - - /** Constant Boolean holding the default setting for whether to sort the options alphabetically: {@value}.*/ - static final Boolean DEFAULT_SORT_OPTIONS = Boolean.TRUE; - - /** Constant Boolean holding the default setting for whether to show default values in the usage help message: {@value}.*/ - static final Boolean DEFAULT_SHOW_DEFAULT_VALUES = Boolean.FALSE; - - /** Constant Boolean holding the default setting for whether this command should be listed in the usage help of the parent command: {@value}.*/ - static final Boolean DEFAULT_HIDDEN = Boolean.FALSE; - - static final String DEFAULT_SINGLE_VALUE = ""; - static final String[] DEFAULT_MULTI_LINE = {}; - - private String[] description; - private String[] customSynopsis; - private String[] header; - private String[] footer; - private Boolean abbreviateSynopsis; - private Boolean sortOptions; - private Boolean showDefaultValues; - private Boolean hidden; - private Character requiredOptionMarker; - private String headerHeading; - private String synopsisHeading; - private String descriptionHeading; - private String parameterListHeading; - private String optionListHeading; - private String commandListHeading; - private String footerHeading; - private int width = DEFAULT_USAGE_WIDTH; - - private static int getSysPropertyWidthOrDefault(int defaultWidth) { - String userValue = System.getProperty("picocli.usage.width"); - if (userValue == null) { return defaultWidth; } - try { - int width = Integer.parseInt(userValue); - if (width < MINIMUM_USAGE_WIDTH) { - new Tracer().warn("Invalid picocli.usage.width value %d. Using minimum usage width %d.%n", width, MINIMUM_USAGE_WIDTH); - return MINIMUM_USAGE_WIDTH; - } - return width; - } catch (NumberFormatException ex) { - new Tracer().warn("Invalid picocli.usage.width value '%s'. Using usage width %d.%n", userValue, defaultWidth); - return defaultWidth; - } - } - - /** Returns the maximum usage help message width. Derived from system property {@code "picocli.usage.width"} - * if set, otherwise returns the value set via the {@link #width(int)} method, or if not set, the {@linkplain #DEFAULT_USAGE_WIDTH default width}. - * @return the maximum usage help message width. Never returns less than 55. */ - public int width() { return getSysPropertyWidthOrDefault(width); } - - /** - * Sets the maximum usage help message width to the specified value. Longer values are wrapped. - * @param newValue the new maximum usage help message width. Must be 55 or greater. - * @return this {@code UsageMessageSpec} for method chaining - * @throws IllegalArgumentException if the specified width is less than 55 - */ - public UsageMessageSpec width(int newValue) { - if (newValue < MINIMUM_USAGE_WIDTH) { - throw new IllegalArgumentException("Invalid usage message width " + newValue + ". Minimum value is " + MINIMUM_USAGE_WIDTH); - } - width = newValue; return this; - } - - /** Returns the optional heading preceding the header section. Initialized from {@link Command#headerHeading()}, or null. */ - public String headerHeading() { return headerHeading == null ? DEFAULT_SINGLE_VALUE : headerHeading; } - - /** Returns the optional header lines displayed at the top of the help message. For subcommands, the first header line is - * displayed in the list of commands. Values are initialized from {@link Command#header()} - * if the {@code Command} annotation is present, otherwise this is an empty array and the help message has no - * header. Applications may programmatically set this field to create a custom help message. */ - public String[] header() { return header == null ? DEFAULT_MULTI_LINE : header.clone(); } - - /** Returns the optional heading preceding the synopsis. Initialized from {@link Command#synopsisHeading()}, {@code "Usage: "} by default. */ - public String synopsisHeading() { return (synopsisHeading == null) ? DEFAULT_SYNOPSIS_HEADING : synopsisHeading; } - - /** Returns whether the synopsis line(s) should show an abbreviated synopsis without detailed option names. */ - public boolean abbreviateSynopsis() { return (abbreviateSynopsis == null) ? DEFAULT_ABBREVIATE_SYNOPSIS : abbreviateSynopsis; } - - /** Returns the optional custom synopsis lines to use instead of the auto-generated synopsis. - * Initialized from {@link Command#customSynopsis()} if the {@code Command} annotation is present, - * otherwise this is an empty array and the synopsis is generated. - * Applications may programmatically set this field to create a custom help message. */ - public String[] customSynopsis() { return customSynopsis == null ? DEFAULT_MULTI_LINE : customSynopsis.clone(); } - - /** Returns the optional heading preceding the description section. Initialized from {@link Command#descriptionHeading()}, or null. */ - public String descriptionHeading() { return descriptionHeading == null ? DEFAULT_SINGLE_VALUE : descriptionHeading; } - - /** Returns the optional text lines to use as the description of the help message, displayed between the synopsis and the - * options list. Initialized from {@link Command#description()} if the {@code Command} annotation is present, - * otherwise this is an empty array and the help message has no description. - * Applications may programmatically set this field to create a custom help message. */ - public String[] description() { return description == null ? DEFAULT_MULTI_LINE : description.clone(); } - - /** Returns the optional heading preceding the parameter list. Initialized from {@link Command#parameterListHeading()}, or null. */ - public String parameterListHeading() { return parameterListHeading == null ? DEFAULT_SINGLE_VALUE : parameterListHeading; } - - /** Returns the optional heading preceding the options list. Initialized from {@link Command#optionListHeading()}, or null. */ - public String optionListHeading() { return optionListHeading == null ? DEFAULT_SINGLE_VALUE : optionListHeading; } - - /** Returns whether the options list in the usage help message should be sorted alphabetically. */ - public boolean sortOptions() { return (sortOptions == null) ? DEFAULT_SORT_OPTIONS : sortOptions; } - - /** Returns the character used to prefix required options in the options list. */ - public char requiredOptionMarker() { return (requiredOptionMarker == null) ? DEFAULT_REQUIRED_OPTION_MARKER : requiredOptionMarker; } - - /** Returns whether the options list in the usage help message should show default values for all non-boolean options. */ - public boolean showDefaultValues() { return (showDefaultValues == null) ? DEFAULT_SHOW_DEFAULT_VALUES : showDefaultValues; } - - /** - * Returns whether this command should be hidden from the usage help message of the parent command. - * @return {@code true} if this command should not appear in the usage help message of the parent command - */ - public boolean hidden() { return (hidden == null) ? DEFAULT_HIDDEN : hidden; } - - /** Returns the optional heading preceding the subcommand list. Initialized from {@link Command#commandListHeading()}. {@code "Commands:%n"} by default. */ - public String commandListHeading() { return (commandListHeading == null) ? DEFAULT_COMMAND_LIST_HEADING : commandListHeading; } - - /** Returns the optional heading preceding the footer section. Initialized from {@link Command#footerHeading()}, or null. */ - public String footerHeading() { return footerHeading == null ? DEFAULT_SINGLE_VALUE : footerHeading; } - - /** Returns the optional footer text lines displayed at the bottom of the help message. Initialized from - * {@link Command#footer()} if the {@code Command} annotation is present, otherwise this is an empty array and - * the help message has no footer. - * Applications may programmatically set this field to create a custom help message. */ - public String[] footer() { return footer == null ? DEFAULT_MULTI_LINE : footer.clone(); } - - /** Sets the heading preceding the header section. Initialized from {@link Command#headerHeading()}, or null. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec headerHeading(String headerHeading) { this.headerHeading = headerHeading; return this; } - - /** Sets the optional header lines displayed at the top of the help message. For subcommands, the first header line is - * displayed in the list of commands. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec header(String... header) { this.header = header; return this; } - - /** Sets the optional heading preceding the synopsis. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec synopsisHeading(String newValue) {synopsisHeading = newValue; return this;} - - /** Sets whether the synopsis line(s) should show an abbreviated synopsis without detailed option names. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec abbreviateSynopsis(boolean newValue) {abbreviateSynopsis = newValue; return this;} - - /** Sets the optional custom synopsis lines to use instead of the auto-generated synopsis. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec customSynopsis(String... customSynopsis) { this.customSynopsis = customSynopsis; return this; } - - /** Sets the heading preceding the description section. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec descriptionHeading(String newValue) {descriptionHeading = newValue; return this;} - - /** Sets the optional text lines to use as the description of the help message, displayed between the synopsis and the - * options list. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec description(String... description) { this.description = description; return this; } - - /** Sets the optional heading preceding the parameter list. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec parameterListHeading(String newValue) {parameterListHeading = newValue; return this;} - - /** Sets the heading preceding the options list. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec optionListHeading(String newValue) {optionListHeading = newValue; return this;} - - /** Sets whether the options list in the usage help message should be sorted alphabetically. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec sortOptions(boolean newValue) {sortOptions = newValue; return this;} - - /** Sets the character used to prefix required options in the options list. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec requiredOptionMarker(char newValue) {requiredOptionMarker = newValue; return this;} - - /** Sets whether the options list in the usage help message should show default values for all non-boolean options. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec showDefaultValues(boolean newValue) {showDefaultValues = newValue; return this;} - - /** - * Set the hidden flag on this command to control whether to show or hide it in the help usage text of the parent command. - * @param value enable or disable the hidden flag - * @return this UsageMessageSpec for method chaining - * @see Command#hidden() */ - public UsageMessageSpec hidden(boolean value) { hidden = value; return this; } - - /** Sets the optional heading preceding the subcommand list. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec commandListHeading(String newValue) {commandListHeading = newValue; return this;} - - /** Sets the optional heading preceding the footer section. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec footerHeading(String newValue) {footerHeading = newValue; return this;} - - /** Sets the optional footer text lines displayed at the bottom of the help message. - * @return this UsageMessageSpec for method chaining */ - public UsageMessageSpec footer(String... footer) { this.footer = footer; return this; } - void initSynopsisHeading(String value) { if (initializable(synopsisHeading, value, DEFAULT_SYNOPSIS_HEADING)) {synopsisHeading = value;} } - void initCommandListHeading(String value) { if (initializable(commandListHeading, value, DEFAULT_COMMAND_LIST_HEADING)) {commandListHeading = value;} } - void initRequiredOptionMarker(char value) { if (initializable(requiredOptionMarker, value, DEFAULT_REQUIRED_OPTION_MARKER)) {requiredOptionMarker = value;} } - void initAbbreviateSynopsis(boolean value) { if (initializable(abbreviateSynopsis, value, DEFAULT_ABBREVIATE_SYNOPSIS)) {abbreviateSynopsis = value;} } - void initSortOptions(boolean value) { if (initializable(sortOptions, value, DEFAULT_SORT_OPTIONS)) {sortOptions = value;} } - void initShowDefaultValues(boolean value) { if (initializable(showDefaultValues, value, DEFAULT_SHOW_DEFAULT_VALUES)) {showDefaultValues = value;} } - void initHidden(boolean value) { if (initializable(hidden, value, DEFAULT_HIDDEN)) {hidden = value;} } - void initCustomSynopsis(String[] value) { if (initializable(customSynopsis, value, DEFAULT_MULTI_LINE)) {customSynopsis = value.clone();} } - void initDescription(String[] value) { if (initializable(description, value, DEFAULT_MULTI_LINE)) {description = value.clone();} } - void initDescriptionHeading(String value) { if (initializable(descriptionHeading, value, DEFAULT_SINGLE_VALUE)) {descriptionHeading = value;} } - void initHeader(String[] value) { if (initializable(header, value, DEFAULT_MULTI_LINE)) {header = value.clone();} } - void initHeaderHeading(String value) { if (initializable(headerHeading, value, DEFAULT_SINGLE_VALUE)) {headerHeading = value;} } - void initFooter(String[] value) { if (initializable(footer, value, DEFAULT_MULTI_LINE)) {footer = value.clone();} } - void initFooterHeading(String value) { if (initializable(footerHeading, value, DEFAULT_SINGLE_VALUE)) {footerHeading = value;} } - void initParameterListHeading(String value) { if (initializable(parameterListHeading, value, DEFAULT_SINGLE_VALUE)) {parameterListHeading = value;} } - void initOptionListHeading(String value) { if (initializable(optionListHeading, value, DEFAULT_SINGLE_VALUE)) {optionListHeading = value;} } - - void initFrom(UsageMessageSpec settings) { - description = settings.description; - customSynopsis = settings.customSynopsis; - header = settings.header; - footer = settings.footer; - abbreviateSynopsis = settings.abbreviateSynopsis; - sortOptions = settings.sortOptions; - showDefaultValues = settings.showDefaultValues; - hidden = settings.hidden; - requiredOptionMarker = settings.requiredOptionMarker; - headerHeading = settings.headerHeading; - synopsisHeading = settings.synopsisHeading; - descriptionHeading = settings.descriptionHeading; - parameterListHeading = settings.parameterListHeading; - optionListHeading = settings.optionListHeading; - commandListHeading = settings.commandListHeading; - footerHeading = settings.footerHeading; - width = settings.width; - } - } - /** Models parser configuration specification. - * @since 3.0 */ - public static class ParserSpec { - - /** Constant String holding the default separator between options and option parameters: {@value}.*/ - static final String DEFAULT_SEPARATOR = "="; - private String separator; - private boolean stopAtUnmatched = false; - private boolean stopAtPositional = false; - private boolean toggleBooleanFlags = true; - private boolean overwrittenOptionsAllowed = false; - private boolean unmatchedArgumentsAllowed = false; - private boolean expandAtFiles = true; - private boolean posixClusteredShortOptionsAllowed = true; - private boolean unmatchedOptionsArePositionalParams = false; - private boolean limitSplit = false; - private boolean aritySatisfiedByAttachedOptionParam = false; - private boolean collectErrors = false; - - /** Returns the String to use as the separator between options and option parameters. {@code "="} by default, - * initialized from {@link Command#separator()} if defined.*/ - public String separator() { return (separator == null) ? DEFAULT_SEPARATOR : separator; } - - /** @see CommandLine#isStopAtUnmatched() */ - public boolean stopAtUnmatched() { return stopAtUnmatched; } - /** @see CommandLine#isStopAtPositional() */ - public boolean stopAtPositional() { return stopAtPositional; } - /** @see CommandLine#isToggleBooleanFlags() */ - public boolean toggleBooleanFlags() { return toggleBooleanFlags; } - /** @see CommandLine#isOverwrittenOptionsAllowed() */ - public boolean overwrittenOptionsAllowed() { return overwrittenOptionsAllowed; } - /** @see CommandLine#isUnmatchedArgumentsAllowed() */ - public boolean unmatchedArgumentsAllowed() { return unmatchedArgumentsAllowed; } - /** @see CommandLine#isExpandAtFiles() */ - public boolean expandAtFiles() { return expandAtFiles; } - /** @see CommandLine#isPosixClusteredShortOptionsAllowed() */ - public boolean posixClusteredShortOptionsAllowed() { return posixClusteredShortOptionsAllowed; } - /** @see CommandLine#isUnmatchedOptionsArePositionalParams() */ - public boolean unmatchedOptionsArePositionalParams() { return unmatchedOptionsArePositionalParams; } - private boolean splitFirst() { return limitSplit(); } - /** Returns true if arguments should be split first before any further processing and the number of - * parts resulting from the split is limited to the max arity of the argument. */ - public boolean limitSplit() { return limitSplit; } - /** Returns true if options with attached arguments should not consume subsequent arguments and should not validate arity. */ - public boolean aritySatisfiedByAttachedOptionParam() { return aritySatisfiedByAttachedOptionParam; } - /** Returns true if exceptions during parsing should be collected instead of thrown. - * Multiple errors may be encountered during parsing. These can be obtained from {@link ParseResult#errors()}. - * @since 3.2 */ - public boolean collectErrors() { return collectErrors; } - - /** Sets the String to use as the separator between options and option parameters. - * @return this ParserSpec for method chaining */ - public ParserSpec separator(String separator) { this.separator = separator; return this; } - /** @see CommandLine#setStopAtUnmatched(boolean) */ - public ParserSpec stopAtUnmatched(boolean stopAtUnmatched) { this.stopAtUnmatched = stopAtUnmatched; return this; } - /** @see CommandLine#setStopAtPositional(boolean) */ - public ParserSpec stopAtPositional(boolean stopAtPositional) { this.stopAtPositional = stopAtPositional; return this; } - /** @see CommandLine#setToggleBooleanFlags(boolean) */ - public ParserSpec toggleBooleanFlags(boolean toggleBooleanFlags) { this.toggleBooleanFlags = toggleBooleanFlags; return this; } - /** @see CommandLine#setOverwrittenOptionsAllowed(boolean) */ - public ParserSpec overwrittenOptionsAllowed(boolean overwrittenOptionsAllowed) { this.overwrittenOptionsAllowed = overwrittenOptionsAllowed; return this; } - /** @see CommandLine#setUnmatchedArgumentsAllowed(boolean) */ - public ParserSpec unmatchedArgumentsAllowed(boolean unmatchedArgumentsAllowed) { this.unmatchedArgumentsAllowed = unmatchedArgumentsAllowed; return this; } - /** @see CommandLine#setExpandAtFiles(boolean) */ - public ParserSpec expandAtFiles(boolean expandAtFiles) { this.expandAtFiles = expandAtFiles; return this; } - /** @see CommandLine#setPosixClusteredShortOptionsAllowed(boolean) */ - public ParserSpec posixClusteredShortOptionsAllowed(boolean posixClusteredShortOptionsAllowed) { this.posixClusteredShortOptionsAllowed = posixClusteredShortOptionsAllowed; return this; } - /** @see CommandLine#setUnmatchedOptionsArePositionalParams(boolean) */ - public ParserSpec unmatchedOptionsArePositionalParams(boolean unmatchedOptionsArePositionalParams) { this.unmatchedOptionsArePositionalParams = unmatchedOptionsArePositionalParams; return this; } - /** Sets whether exceptions during parsing should be collected instead of thrown. - * Multiple errors may be encountered during parsing. These can be obtained from {@link ParseResult#errors()}. - * @since 3.2 */ - public ParserSpec collectErrors(boolean collectErrors) { this.collectErrors = collectErrors; return this; } - - /** Returns true if options with attached arguments should not consume subsequent arguments and should not validate arity. */ - public ParserSpec aritySatisfiedByAttachedOptionParam(boolean newValue) { aritySatisfiedByAttachedOptionParam = newValue; return this; } - - /** Sets whether arguments should be {@linkplain ArgSpec#splitRegex() split} first before any further processing. - * If true, the original argument will only be split into as many parts as allowed by max arity. */ - public ParserSpec limitSplit(boolean limitSplit) { this.limitSplit = limitSplit; return this; } - void initSeparator(String value) { if (initializable(separator, value, DEFAULT_SEPARATOR)) {separator = value;} } - public String toString() { - return String.format("posixClusteredShortOptionsAllowed=%s, stopAtPositional=%s, stopAtUnmatched=%s, " + - "separator=%s, overwrittenOptionsAllowed=%s, unmatchedArgumentsAllowed=%s, expandAtFiles=%s, " + - "limitSplit=%s, aritySatisfiedByAttachedOptionParam=%s", - posixClusteredShortOptionsAllowed, stopAtPositional, stopAtUnmatched, - separator, overwrittenOptionsAllowed, unmatchedArgumentsAllowed, expandAtFiles, - limitSplit, aritySatisfiedByAttachedOptionParam); - } - - void initFrom(ParserSpec settings) { - separator = settings.separator; - stopAtUnmatched = settings.stopAtUnmatched; - stopAtPositional = settings.stopAtPositional; - toggleBooleanFlags = settings.toggleBooleanFlags; - overwrittenOptionsAllowed = settings.overwrittenOptionsAllowed; - unmatchedArgumentsAllowed = settings.unmatchedArgumentsAllowed; - expandAtFiles = settings.expandAtFiles; - posixClusteredShortOptionsAllowed = settings.posixClusteredShortOptionsAllowed; - unmatchedOptionsArePositionalParams = settings.unmatchedOptionsArePositionalParams; - limitSplit = settings.limitSplit; - aritySatisfiedByAttachedOptionParam = settings.aritySatisfiedByAttachedOptionParam; - } - } - /** Models the shared attributes of {@link OptionSpec} and {@link PositionalParamSpec}. - * @since 3.0 */ - public abstract static class ArgSpec { - static final String DESCRIPTION_VARIABLE_DEFAULT_VALUE = "${DEFAULT-VALUE}"; - static final String DESCRIPTION_VARIABLE_COMPLETION_CANDIDATES = "${COMPLETION-CANDIDATES}"; - - // help-related fields - private final boolean hidden; - private final String paramLabel; - private final String[] description; - private final Help.Visibility showDefaultValue; - - // parser fields - private final boolean required; - private final String splitRegex; - private final Class type; - private final Class[] auxiliaryTypes; - private final ITypeConverter[] converters; - private final Iterable completionCandidates; - private final String defaultValue; - private final Object initialValue; - private final boolean hasInitialValue; - private final IGetter getter; - private final ISetter setter; - private final Range arity; - private List stringValues = new ArrayList(); - private List originalStringValues = new ArrayList(); - protected String toString; - private List typedValues = new ArrayList(); - Map typedValueAtPosition = new TreeMap(); - - /** Constructs a new {@code ArgSpec}. */ - private ArgSpec(Builder builder) { - description = builder.description == null ? new String[0] : builder.description; - splitRegex = builder.splitRegex == null ? "" : builder.splitRegex; - paramLabel = empty(builder.paramLabel) ? "PARAM" : builder.paramLabel; - converters = builder.converters == null ? new ITypeConverter[0] : builder.converters; - showDefaultValue = builder.showDefaultValue == null ? Help.Visibility.ON_DEMAND : builder.showDefaultValue; - completionCandidates = builder.completionCandidates; - hidden = builder.hidden; - required = builder.required && builder.defaultValue == null; //#261 not required if it has a default - defaultValue = builder.defaultValue; - initialValue = builder.initialValue; - hasInitialValue = builder.hasInitialValue; - toString = builder.toString; - getter = builder.getter; - setter = builder.setter; - - Range tempArity = builder.arity; - if (tempArity == null) { - if (isOption()) { - tempArity = (builder.type == null || isBoolean(builder.type)) ? Range.valueOf("0") : Range.valueOf("1"); - } else { - tempArity = Range.valueOf("1"); - } - } - arity = tempArity; - - if (builder.type == null) { - if (builder.auxiliaryTypes == null || builder.auxiliaryTypes.length == 0) { - if (arity.isVariable || arity.max > 1) { - type = String[].class; - } else if (arity.max == 1) { - type = String.class; - } else { - type = isOption() ? boolean.class : String.class; - } - } else { - type = builder.auxiliaryTypes[0]; - } - } else { - type = builder.type; - } - if (builder.auxiliaryTypes == null || builder.auxiliaryTypes.length == 0) { - if (type.isArray()) { - auxiliaryTypes = new Class[]{type.getComponentType()}; - } else if (Collection.class.isAssignableFrom(type)) { // type is a collection but element type is unspecified - auxiliaryTypes = new Class[] {String.class}; // use String elements - } else if (Map.class.isAssignableFrom(type)) { // type is a map but element type is unspecified - auxiliaryTypes = new Class[] {String.class, String.class}; // use String keys and String values - } else { - auxiliaryTypes = new Class[] {type}; - } - } else { - auxiliaryTypes = builder.auxiliaryTypes; - } - } - - /** Returns whether this is a required option or positional parameter. - * @see Option#required() */ - public boolean required() { return required; } - - /** Returns the description template of this option, before variables are rendered. - * @see Option#description() */ - public String[] description() { return description.clone(); } - - /** Returns the description of this option, after variables are rendered. Used when generating the usage documentation. - * @see Option#description() - * @since 3.2 */ - public String[] renderedDescription() { - if (description == null || description.length == 0) { return description; } - StringBuilder candidates = new StringBuilder(); - if (completionCandidates != null) { - for (String candidate : completionCandidates) { - if (candidates.length() > 0) { candidates.append(", "); } - candidates.append(candidate); - } - } else if (type != null && type.isEnum()) { - candidates.append(Arrays.asList(type.getEnumConstants())); - candidates.delete(0, 1).setLength(candidates.length() - 1); - } - String defaultValueString = defaultValueString = String.valueOf(defaultValue != null ? defaultValue : initialValue); - String[] result = new String[description.length]; - for (int i = 0; i < description.length; i++) { - result[i] = String.format(description[i].replace(DESCRIPTION_VARIABLE_DEFAULT_VALUE, defaultValueString) - .replace(DESCRIPTION_VARIABLE_COMPLETION_CANDIDATES, candidates.toString())); - } - return result; - } - - /** Returns how many arguments this option or positional parameter requires. - * @see Option#arity() */ - public Range arity() { return arity; } - - /** Returns the name of the option or positional parameter used in the usage help message. - * @see Option#paramLabel() {@link Parameters#paramLabel()} */ - public String paramLabel() { return paramLabel; } - - /** Returns auxiliary type information used when the {@link #type()} is a generic {@code Collection}, {@code Map} or an abstract class. - * @see Option#type() */ - public Class[] auxiliaryTypes() { return auxiliaryTypes.clone(); } - - /** Returns one or more {@link CommandLine.ITypeConverter type converters} to use to convert the command line - * argument into a strongly typed value (or key-value pair for map fields). This is useful when a particular - * option or positional parameter should use a custom conversion that is different from the normal conversion for the arg spec's type. - * @see Option#converter() */ - public ITypeConverter[] converters() { return converters.clone(); } - - /** Returns a regular expression to split option parameter values or {@code ""} if the value should not be split. - * @see Option#split() */ - public String splitRegex() { return splitRegex; } - - /** Returns whether this option should be excluded from the usage message. - * @see Option#hidden() */ - public boolean hidden() { return hidden; } - - /** Returns the type to convert the option or positional parameter to before {@linkplain #setValue(Object) setting} the value. */ - public Class type() { return type; } - - /** Returns the default value of this option or positional parameter, before splitting and type conversion. - * A value of {@code null} means this option or positional parameter does not have a default. */ - public String defaultValue() { return defaultValue; } - /** Returns the initial value this option or positional parameter. If {@link #hasInitialValue()} is true, - * the option will be reset to the initial value before parsing (regardless of whether a default value exists), - * to clear values that would otherwise remain from parsing previous input. */ - public Object initialValue() { return initialValue; } - /** Determines whether the option or positional parameter will be reset to the {@link #initialValue()} - * before parsing new input.*/ - public boolean hasInitialValue() { return hasInitialValue; } - - /** Returns whether this option or positional parameter's default value should be shown in the usage help. */ - public Help.Visibility showDefaultValue() { return showDefaultValue; } - - /** Returns the completion candidates for this option or positional parameter, or {@code null} if this option - * or positional parameter does not have any completion candidates. - * @return the completion candidates for this option or positional parameter, or {@code null} - * @since 3.2 */ - public Iterable completionCandidates() { return completionCandidates; } - - /** Returns the {@link IGetter} that is responsible for supplying the value of this argument. */ - public IGetter getter() { return getter; } - /** Returns the {@link ISetter} that is responsible for modifying the value of this argument. */ - public ISetter setter() { return setter; } - - /** Returns the current value of this argument. Delegates to the current {@link #getter()}. */ - public T getValue() throws PicocliException { - try { - return getter.get(); - } catch (PicocliException ex) { throw ex; - } catch (Exception ex) { throw new PicocliException("Could not get value for " + this + ": " + ex, ex); - } - } - /** Sets the value of this argument to the specified value and returns the previous value. Delegates to the current {@link #setter()}. */ - public T setValue(T newValue) throws PicocliException { - try { - return setter.set(newValue); - } catch (PicocliException ex) { throw ex; - } catch (Exception ex) { throw new PicocliException("Could not set value (" + newValue + ") for " + this + ": " + ex, ex); - } - } - - /** Returns {@code true} if this argument's {@link #type()} is an array, a {@code Collection} or a {@code Map}, {@code false} otherwise. */ - public boolean isMultiValue() { return CommandLine.isMultiValue(type()); } - /** Returns {@code true} if this argument is a named option, {@code false} otherwise. */ - public abstract boolean isOption(); - /** Returns {@code true} if this argument is a positional parameter, {@code false} otherwise. */ - public abstract boolean isPositional(); - - /** Returns the untyped command line arguments matched by this option or positional parameter spec. - * @return the matched arguments after {@linkplain #splitRegex() splitting}, but before type conversion. - * For map properties, {@code "key=value"} values are split into the key and the value part. */ - public List stringValues() { return Collections.unmodifiableList(stringValues); } - - /** Returns the typed command line arguments matched by this option or positional parameter spec. - * @return the matched arguments after {@linkplain #splitRegex() splitting} and type conversion. - * For map properties, {@code "key=value"} values are split into the key and the value part. */ - public List typedValues() { return Collections.unmodifiableList(typedValues); } - - /** Sets the {@code stringValues} to a new list instance. */ - protected void resetStringValues() { stringValues = new ArrayList(); } - - /** Returns the original command line arguments matched by this option or positional parameter spec. - * @return the matched arguments as found on the command line: empty Strings for options without value, the - * values have not been {@linkplain #splitRegex() split}, and for map properties values may look like {@code "key=value"}*/ - public List originalStringValues() { return Collections.unmodifiableList(originalStringValues); } - - /** Sets the {@code originalStringValues} to a new list instance. */ - protected void resetOriginalStringValues() { originalStringValues = new ArrayList(); } - - /** Returns whether the default for this option or positional parameter should be shown, potentially overriding the specified global setting. - * @param usageHelpShowDefaults whether the command's UsageMessageSpec is configured to show default values. */ - protected boolean internalShowDefaultValue(boolean usageHelpShowDefaults) { - if (showDefaultValue() == Help.Visibility.ALWAYS) { return true; } // override global usage help setting - if (showDefaultValue() == Help.Visibility.NEVER) { return false; } // override global usage help setting - if (initialValue == null && defaultValue() == null) { return false; } // no default value to show - return usageHelpShowDefaults && !isBoolean(type()); - } - - /** Returns a string respresentation of this option or positional parameter. */ - public String toString() { return toString; } - - private String[] splitValue(String value, ParserSpec parser, Range arity, int consumed) { - if (splitRegex().length() == 0) { return new String[] {value}; } - int limit = parser.limitSplit() ? Math.max(arity.max - consumed, 0) : 0; - return value.split(splitRegex(), limit); - } - protected boolean equalsImpl(ArgSpec other) { - if (other == this) { return true; } - boolean result = Assert.equals(this.defaultValue, other.defaultValue) - && Assert.equals(this.type, other.type) - && Assert.equals(this.arity, other.arity) - && Assert.equals(this.hidden, other.hidden) - && Assert.equals(this.paramLabel, other.paramLabel) - && Assert.equals(this.required, other.required) - && Assert.equals(this.splitRegex, other.splitRegex) - && Arrays.equals(this.description, other.description) - && Arrays.equals(this.auxiliaryTypes, other.auxiliaryTypes) - ; - return result; - } - protected int hashCodeImpl() { - return 17 - + 37 * Assert.hashCode(defaultValue) - + 37 * Assert.hashCode(type) - + 37 * Assert.hashCode(arity) - + 37 * Assert.hashCode(hidden) - + 37 * Assert.hashCode(paramLabel) - + 37 * Assert.hashCode(required) - + 37 * Assert.hashCode(splitRegex) - + 37 * Arrays.hashCode(description) - + 37 * Arrays.hashCode(auxiliaryTypes) - ; - } - - abstract static class Builder> { - private Range arity; - private String[] description; - private boolean required; - private String paramLabel; - private String splitRegex; - private boolean hidden; - private Class type; - private Class[] auxiliaryTypes; - private ITypeConverter[] converters; - private String defaultValue; - private Object initialValue; - private boolean hasInitialValue = true; - private Help.Visibility showDefaultValue; - private Iterable completionCandidates; - private String toString; - private IGetter getter = new ObjectBinding(); - private ISetter setter = (ISetter) getter; - - Builder() {} - Builder(ArgSpec original) { - arity = original.arity; - auxiliaryTypes = original.auxiliaryTypes; - converters = original.converters; - defaultValue = original.defaultValue; - description = original.description; - getter = original.getter; - setter = original.setter; - hidden = original.hidden; - paramLabel = original.paramLabel; - required = original.required; - showDefaultValue = original.showDefaultValue; - completionCandidates = original.completionCandidates; - splitRegex = original.splitRegex; - toString = original.toString; - type = original.type; - } - - public abstract ArgSpec build(); - protected abstract T self(); // subclasses must override to return "this" - /** Returns whether this is a required option or positional parameter. - * @see Option#required() */ - public boolean required() { return required; } - - /** Returns the description of this option, used when generating the usage documentation. - * @see Option#description() */ - public String[] description() { return description; } - - /** Returns how many arguments this option or positional parameter requires. - * @see Option#arity() */ - public Range arity() { return arity; } - - /** Returns the name of the option or positional parameter used in the usage help message. - * @see Option#paramLabel() {@link Parameters#paramLabel()} */ - public String paramLabel() { return paramLabel; } - - /** Returns auxiliary type information used when the {@link #type()} is a generic {@code Collection}, {@code Map} or an abstract class. - * @see Option#type() */ - public Class[] auxiliaryTypes() { return auxiliaryTypes; } - - /** Returns one or more {@link CommandLine.ITypeConverter type converters} to use to convert the command line - * argument into a strongly typed value (or key-value pair for map fields). This is useful when a particular - * option or positional parameter should use a custom conversion that is different from the normal conversion for the arg spec's type. - * @see Option#converter() */ - public ITypeConverter[] converters() { return converters; } - - /** Returns a regular expression to split option parameter values or {@code ""} if the value should not be split. - * @see Option#split() */ - public String splitRegex() { return splitRegex; } - - /** Returns whether this option should be excluded from the usage message. - * @see Option#hidden() */ - public boolean hidden() { return hidden; } - - /** Returns the type to convert the option or positional parameter to before {@linkplain #setValue(Object) setting} the value. */ - public Class type() { return type; } - - /** Returns the default value of this option or positional parameter, before splitting and type conversion. - * A value of {@code null} means this option or positional parameter does not have a default. */ - public String defaultValue() { return defaultValue; } - /** Returns the initial value this option or positional parameter. If {@link #hasInitialValue()} is true, - * the option will be reset to the initial value before parsing (regardless of whether a default value exists), - * to clear values that would otherwise remain from parsing previous input. */ - public Object initialValue() { return initialValue; } - /** Determines whether the option or positional parameter will be reset to the {@link #initialValue()} - * before parsing new input.*/ - public boolean hasInitialValue() { return hasInitialValue; } - - /** Returns whether this option or positional parameter's default value should be shown in the usage help. */ - public Help.Visibility showDefaultValue() { return showDefaultValue; } - - /** Returns the completion candidates for this option or positional parameter, or {@code null}. - * @since 3.2 */ - public Iterable completionCandidates() { return completionCandidates; } - - /** Returns the {@link IGetter} that is responsible for supplying the value of this argument. */ - public IGetter getter() { return getter; } - /** Returns the {@link ISetter} that is responsible for modifying the value of this argument. */ - public ISetter setter() { return setter; } - - public String toString() { return toString; } - - /** Sets whether this is a required option or positional parameter, and returns this builder. */ - public T required(boolean required) { this.required = required; return self(); } - - /** Sets the description of this option, used when generating the usage documentation, and returns this builder. - * @see Option#description() */ - public T description(String... description) { this.description = Assert.notNull(description, "description").clone(); return self(); } - - /** Sets how many arguments this option or positional parameter requires, and returns this builder. */ - public T arity(String range) { return arity(Range.valueOf(range)); } - - /** Sets how many arguments this option or positional parameter requires, and returns this builder. */ - public T arity(Range arity) { this.arity = Assert.notNull(arity, "arity"); return self(); } - - /** Sets the name of the option or positional parameter used in the usage help message, and returns this builder. */ - public T paramLabel(String paramLabel) { this.paramLabel = Assert.notNull(paramLabel, "paramLabel"); return self(); } - - /** Sets auxiliary type information, and returns this builder. - * @param types the element type(s) when the {@link #type()} is a generic {@code Collection} or a {@code Map}; - * or the concrete type when the {@link #type()} is an abstract class. */ - public T auxiliaryTypes(Class... types) { this.auxiliaryTypes = Assert.notNull(types, "types").clone(); return self(); } - - /** Sets option/positional param-specific converter (or converters for Maps), and returns this builder. */ - public T converters(ITypeConverter... cs) { this.converters = Assert.notNull(cs, "type converters").clone(); return self(); } - - /** Sets a regular expression to split option parameter values or {@code ""} if the value should not be split, and returns this builder. */ - public T splitRegex(String splitRegex) { this.splitRegex = Assert.notNull(splitRegex, "splitRegex"); return self(); } - - /** Sets whether this option or positional parameter's default value should be shown in the usage help, and returns this builder. */ - public T showDefaultValue(Help.Visibility visibility) { showDefaultValue = Assert.notNull(visibility, "visibility"); return self(); } - - /** Sets the completion candidates for this option or positional parameter, and returns this builder. - * @since 3.2 */ - public T completionCandidates(Iterable completionCandidates) { this.completionCandidates = Assert.notNull(completionCandidates, "completionCandidates"); return self(); } - - /** Sets whether this option should be excluded from the usage message, and returns this builder. */ - public T hidden(boolean hidden) { this.hidden = hidden; return self(); } - - /** Sets the type to convert the option or positional parameter to before {@linkplain #setValue(Object) setting} the value, and returns this builder. - * @param propertyType the type of this option or parameter. For multi-value options and positional parameters this can be an array, or a (sub-type of) Collection or Map. */ - public T type(Class propertyType) { this.type = Assert.notNull(propertyType, "type"); return self(); } - - /** Sets the default value of this option or positional parameter to the specified value, and returns this builder. - * Before parsing the command line, the result of {@linkplain #splitRegex() splitting} and {@linkplain #converters() type converting} - * this default value is applied to the option or positional parameter. A value of {@code null} means no default. */ - public T defaultValue(String defaultValue) { this.defaultValue = defaultValue; return self(); } - - /** Sets the initial value of this option or positional parameter to the specified value, and returns this builder. - * If {@link #hasInitialValue()} is true, the option will be reset to the initial value before parsing (regardless - * of whether a default value exists), to clear values that would otherwise remain from parsing previous input. */ - public T initialValue(Object initialValue) { this.initialValue = initialValue; return self(); } - - /** Determines whether the option or positional parameter will be reset to the {@link #initialValue()} - * before parsing new input.*/ - public T hasInitialValue(boolean hasInitialValue) { this.hasInitialValue = hasInitialValue; return self(); } - - /** Sets the {@link IGetter} that is responsible for getting the value of this argument, and returns this builder. */ - public T getter(IGetter getter) { this.getter = getter; return self(); } - /** Sets the {@link ISetter} that is responsible for modifying the value of this argument, and returns this builder. */ - public T setter(ISetter setter) { this.setter = setter; return self(); } - - /** Sets the string respresentation of this option or positional parameter to the specified value, and returns this builder. */ - public T withToString(String toString) { this.toString = toString; return self(); } - } - } - /** The {@code OptionSpec} class models aspects of a named option of a {@linkplain CommandSpec command}, including whether - * it is required or optional, the option parameters supported (or required) by the option, - * and attributes for the usage help message describing the option. - *

- * An option has one or more names. The option is matched when the parser encounters one of the option names in the command line arguments. - * Depending on the option's {@link #arity() arity}, - * the parser may expect it to have option parameters. The parser will call {@link #setValue(Object) setValue} on - * the matched option for each of the option parameters encountered. - *

- * For multi-value options, the {@code type} may be an array, a {@code Collection} or a {@code Map}. In this case - * the parser will get the data structure by calling {@link #getValue() getValue} and modify the contents of this data structure. - * (In the case of arrays, the array is replaced with a new instance with additional elements.) - *

- * Before calling the setter, picocli converts the option parameter value from a String to the option parameter's type. - *

- *
    - *
  • If a option-specific {@link #converters() converter} is configured, this will be used for type conversion. - * If the option's type is a {@code Map}, the map may have different types for its keys and its values, so - * {@link #converters() converters} should provide two converters: one for the map keys and one for the map values.
  • - *
  • Otherwise, the option's {@link #type() type} is used to look up a converter in the list of - * {@linkplain CommandLine#registerConverter(Class, ITypeConverter) registered converters}. - * For multi-value options, - * the {@code type} may be an array, or a {@code Collection} or a {@code Map}. In that case the elements are converted - * based on the option's {@link #auxiliaryTypes() auxiliaryTypes}. The auxiliaryType is used to look up - * the converter(s) to use to convert the individual parameter values. - * Maps may have different types for its keys and its values, so {@link #auxiliaryTypes() auxiliaryTypes} - * should provide two types: one for the map keys and one for the map values.
  • - *
- *

- * {@code OptionSpec} objects are used by the picocli command line interpreter and help message generator. - * Picocli can construct an {@code OptionSpec} automatically from fields and methods with {@link Option @Option} - * annotations. Alternatively an {@code OptionSpec} can be constructed programmatically. - *

- * When an {@code OptionSpec} is created from an {@link Option @Option} -annotated field or method, it is "bound" - * to that field or method: this field is set (or the method is invoked) when the option is matched and - * {@link #setValue(Object) setValue} is called. - * Programmatically constructed {@code OptionSpec} instances will remember the value passed to the - * {@link #setValue(Object) setValue} method so it can be retrieved with the {@link #getValue() getValue} method. - * This behaviour can be customized by installing a custom {@link IGetter} and {@link ISetter} on the {@code OptionSpec}. - *

- * @since 3.0 */ - public static class OptionSpec extends ArgSpec { - private String[] names; - private boolean help; - private boolean usageHelp; - private boolean versionHelp; - - public static OptionSpec.Builder builder(String name, String... names) { - String[] copy = new String[Assert.notNull(names, "names").length + 1]; - copy[0] = Assert.notNull(name, "name"); - System.arraycopy(names, 0, copy, 1, names.length); - return new Builder(copy); - } - public static OptionSpec.Builder builder(String[] names) { return new Builder(names); } - - /** Ensures all attributes of this {@code OptionSpec} have a valid value; throws an {@link InitializationException} if this cannot be achieved. */ - private OptionSpec(Builder builder) { - super(builder); - names = builder.names; - help = builder.help; - usageHelp = builder.usageHelp; - versionHelp = builder.versionHelp; - - if (names == null || names.length == 0 || Arrays.asList(names).contains("")) { - throw new InitializationException("Invalid names: " + Arrays.toString(names)); - } - if (toString() == null) { toString = "option " + longestName(); } - } - - /** Returns a new Builder initialized with the attributes from this {@code OptionSpec}. Calling {@code build} immediately will return a copy of this {@code OptionSpec}. - * @return a builder that can create a copy of this spec - */ - public Builder toBuilder() { return new Builder(this); } - public boolean isOption() { return true; } - public boolean isPositional() { return false; } - - protected boolean internalShowDefaultValue(boolean usageMessageShowDefaults) { - return super.internalShowDefaultValue(usageMessageShowDefaults) && !help() && !versionHelp() && !usageHelp(); - } - - /** Returns one or more option names. The returned array will contain at least one option name. - * @see Option#names() */ - public String[] names() { return names.clone(); } - - /** Returns the longest {@linkplain #names() option name}. */ - public String longestName() { return Help.ShortestFirst.longestFirst(names.clone())[0]; } - - /** Returns whether this option disables validation of the other arguments. - * @see Option#help() - * @deprecated Use {@link #usageHelp()} and {@link #versionHelp()} instead. */ - @Deprecated public boolean help() { return help; } - - /** Returns whether this option allows the user to request usage help. - * @see Option#usageHelp() */ - public boolean usageHelp() { return usageHelp; } - - /** Returns whether this option allows the user to request version information. - * @see Option#versionHelp() */ - public boolean versionHelp() { return versionHelp; } - public boolean equals(Object obj) { - if (obj == this) { return true; } - if (!(obj instanceof OptionSpec)) { return false; } - OptionSpec other = (OptionSpec) obj; - boolean result = super.equalsImpl(other) - && help == other.help - && usageHelp == other.usageHelp - && versionHelp == other.versionHelp - && new HashSet(Arrays.asList(names)).equals(new HashSet(Arrays.asList(other.names))); - return result; - } - public int hashCode() { - return super.hashCodeImpl() - + 37 * Assert.hashCode(help) - + 37 * Assert.hashCode(usageHelp) - + 37 * Assert.hashCode(versionHelp) - + 37 * Arrays.hashCode(names); - } - - /** Builder responsible for creating valid {@code OptionSpec} objects. - * @since 3.0 - */ - public static class Builder extends ArgSpec.Builder { - private String[] names; - private boolean help; - private boolean usageHelp; - private boolean versionHelp; - - private Builder(String[] names) { this.names = names.clone(); } - private Builder(OptionSpec original) { - super(original); - names = original.names; - help = original.help; - usageHelp = original.usageHelp; - versionHelp = original.versionHelp; - } - - /** Returns a valid {@code OptionSpec} instance. */ - @Override public OptionSpec build() { return new OptionSpec(this); } - /** Returns this builder. */ - @Override protected Builder self() { return this; } - - /** Returns one or more option names. At least one option name is required. - * @see Option#names() */ - public String[] names() { return names; } - - /** Returns whether this option disables validation of the other arguments. - * @see Option#help() - * @deprecated Use {@link #usageHelp()} and {@link #versionHelp()} instead. */ - @Deprecated public boolean help() { return help; } - - /** Returns whether this option allows the user to request usage help. - * @see Option#usageHelp() */ - public boolean usageHelp() { return usageHelp; } - - /** Returns whether this option allows the user to request version information. - * @see Option#versionHelp() */ - public boolean versionHelp() { return versionHelp; } - - /** Replaces the option names with the specified values. At least one option name is required, and returns this builder. - * @return this builder instance to provide a fluent interface */ - public Builder names(String... names) { this.names = Assert.notNull(names, "names").clone(); return self(); } - - /** Sets whether this option disables validation of the other arguments, and returns this builder. */ - public Builder help(boolean help) { this.help = help; return self(); } - - /** Sets whether this option allows the user to request usage help, and returns this builder. */ - public Builder usageHelp(boolean usageHelp) { this.usageHelp = usageHelp; return self(); } - - /** Sets whether this option allows the user to request version information, and returns this builder.*/ - public Builder versionHelp(boolean versionHelp) { this.versionHelp = versionHelp; return self(); } - } - } - /** The {@code PositionalParamSpec} class models aspects of a positional parameter of a {@linkplain CommandSpec command}, including whether - * it is required or optional, and attributes for the usage help message describing the positional parameter. - *

- * Positional parameters have an {@link #index() index} (or a range of indices). A positional parameter is matched when the parser - * encounters a command line argument at that index. Named options and their parameters do not change the index counter, - * so the command line can contain a mixture of positional parameters and named options. - *

- * Depending on the positional parameter's {@link #arity() arity}, the parser may consume multiple command line - * arguments starting from the current index. The parser will call {@link #setValue(Object) setValue} on - * the {@code PositionalParamSpec} for each of the parameters encountered. - * For multi-value positional parameters, the {@code type} may be an array, a {@code Collection} or a {@code Map}. In this case - * the parser will get the data structure by calling {@link #getValue() getValue} and modify the contents of this data structure. - * (In the case of arrays, the array is replaced with a new instance with additional elements.) - *

- * Before calling the setter, picocli converts the positional parameter value from a String to the parameter's type. - *

- *
    - *
  • If a positional parameter-specific {@link #converters() converter} is configured, this will be used for type conversion. - * If the positional parameter's type is a {@code Map}, the map may have different types for its keys and its values, so - * {@link #converters() converters} should provide two converters: one for the map keys and one for the map values.
  • - *
  • Otherwise, the positional parameter's {@link #type() type} is used to look up a converter in the list of - * {@linkplain CommandLine#registerConverter(Class, ITypeConverter) registered converters}. For multi-value positional parameters, - * the {@code type} may be an array, or a {@code Collection} or a {@code Map}. In that case the elements are converted - * based on the positional parameter's {@link #auxiliaryTypes() auxiliaryTypes}. The auxiliaryType is used to look up - * the converter(s) to use to convert the individual parameter values. - * Maps may have different types for its keys and its values, so {@link #auxiliaryTypes() auxiliaryTypes} - * should provide two types: one for the map keys and one for the map values.
  • - *
- *

- * {@code PositionalParamSpec} objects are used by the picocli command line interpreter and help message generator. - * Picocli can construct a {@code PositionalParamSpec} automatically from fields and methods with {@link Parameters @Parameters} - * annotations. Alternatively a {@code PositionalParamSpec} can be constructed programmatically. - *

- * When a {@code PositionalParamSpec} is created from a {@link Parameters @Parameters} -annotated field or method, - * it is "bound" to that field or method: this field is set (or the method is invoked) when the position is matched - * and {@link #setValue(Object) setValue} is called. - * Programmatically constructed {@code PositionalParamSpec} instances will remember the value passed to the - * {@link #setValue(Object) setValue} method so it can be retrieved with the {@link #getValue() getValue} method. - * This behaviour can be customized by installing a custom {@link IGetter} and {@link ISetter} on the {@code PositionalParamSpec}. - *

- * @since 3.0 */ - public static class PositionalParamSpec extends ArgSpec { - private Range index; - private Range capacity; - - /** Ensures all attributes of this {@code PositionalParamSpec} have a valid value; throws an {@link InitializationException} if this cannot be achieved. */ - private PositionalParamSpec(Builder builder) { - super(builder); - index = builder.index == null ? Range.valueOf("*") : builder.index; - capacity = builder.capacity == null ? Range.parameterCapacity(arity(), index) : builder.capacity; - if (toString == null) { toString = "positional parameter[" + index() + "]"; } - } - /** Returns a new Builder initialized with the attributes from this {@code PositionalParamSpec}. Calling {@code build} immediately will return a copy of this {@code PositionalParamSpec}. - * @return a builder that can create a copy of this spec - */ - public Builder toBuilder() { return new Builder(this); } - public boolean isOption() { return false; } - public boolean isPositional() { return true; } - - /** Returns an index or range specifying which of the command line arguments should be assigned to this positional parameter. - * @see Parameters#index() */ - public Range index() { return index; } - private Range capacity() { return capacity; } - public static Builder builder() { return new Builder(); } - - public int hashCode() { - return super.hashCodeImpl() - + 37 * Assert.hashCode(capacity) - + 37 * Assert.hashCode(index); - } - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof PositionalParamSpec)) { - return false; - } - PositionalParamSpec other = (PositionalParamSpec) obj; - return super.equalsImpl(other) - && Assert.equals(this.capacity, other.capacity) - && Assert.equals(this.index, other.index); - } - - /** Builder responsible for creating valid {@code PositionalParamSpec} objects. - * @since 3.0 - */ - public static class Builder extends ArgSpec.Builder { - private Range capacity; - private Range index; - private Builder() {} - private Builder(PositionalParamSpec original) { - super(original); - index = original.index; - capacity = original.capacity; - } - /** Returns a valid {@code PositionalParamSpec} instance. */ - @Override public PositionalParamSpec build() { return new PositionalParamSpec(this); } - /** Returns this builder. */ - @Override protected Builder self() { return this; } - - /** Returns an index or range specifying which of the command line arguments should be assigned to this positional parameter. - * @see Parameters#index() */ - public Range index() { return index; } - - /** Sets the index or range specifying which of the command line arguments should be assigned to this positional parameter, and returns this builder. */ - public Builder index(String range) { return index(Range.valueOf(range)); } - - /** Sets the index or range specifying which of the command line arguments should be assigned to this positional parameter, and returns this builder. */ - public Builder index(Range index) { this.index = index; return self(); } - - Builder capacity(Range capacity) { this.capacity = capacity; return self(); } - } - } - - /** This class allows applications to specify a custom binding that will be invoked for unmatched arguments. - * A binding can be created with a {@code ISetter} that consumes the unmatched arguments {@code String[]}, or with a - * {@code IGetter} that produces a {@code Collection} that the unmatched arguments can be added to. - * @since 3.0 */ - public static class UnmatchedArgsBinding { - private final IGetter getter; - private final ISetter setter; - - /** Creates a {@code UnmatchedArgsBinding} for a setter that consumes {@code String[]} objects. - * @param setter consumes the String[] array with unmatched arguments. */ - public static UnmatchedArgsBinding forStringArrayConsumer(ISetter setter) { return new UnmatchedArgsBinding(null, setter); } - - /** Creates a {@code UnmatchedArgsBinding} for a getter that produces a {@code Collection} that the unmatched arguments can be added to. - * @param getter supplies a {@code Collection} that the unmatched arguments can be added to. */ - public static UnmatchedArgsBinding forStringCollectionSupplier(IGetter getter) { return new UnmatchedArgsBinding(getter, null); } - - private UnmatchedArgsBinding(IGetter getter, ISetter setter) { - if (getter == null && setter == null) { throw new IllegalArgumentException("Getter and setter cannot both be null"); } - this.setter = setter; - this.getter = getter; - } - /** Returns the getter responsible for producing a {@code Collection} that the unmatched arguments can be added to. */ - public IGetter getter() { return getter; } - /** Returns the setter responsible for consuming the unmatched arguments. */ - public ISetter setter() { return setter; } - void addAll(String[] unmatched) { - if (setter != null) { - try { - setter.set(unmatched); - } catch (Exception ex) { - throw new PicocliException(String.format("Could not invoke setter (%s) with unmatched argument array '%s': %s", setter, Arrays.toString(unmatched), ex), ex); - } - } - if (getter != null) { - try { - Collection collection = getter.get(); - Assert.notNull(collection, "getter returned null Collection"); - collection.addAll(Arrays.asList(unmatched)); - } catch (Exception ex) { - throw new PicocliException(String.format("Could not add unmatched argument array '%s' to collection returned by getter (%s): %s", - Arrays.toString(unmatched), getter, ex), ex); - } - } - } - } - private static class CommandReflection { - static CommandSpec extractCommandSpec(Object command, IFactory factory, boolean annotationsAreMandatory) { - if (command instanceof CommandSpec) { return (CommandSpec) command; } - - CommandSpec result = CommandSpec.wrapWithoutInspection(Assert.notNull(command, "command")); - - Class cls = command.getClass(); - boolean hasCommandAnnotation = false; - while (cls != null) { - boolean thisCommandHasAnnotation = updateCommandAttributes(cls, result, factory); - hasCommandAnnotation |= thisCommandHasAnnotation; - hasCommandAnnotation |= initFromAnnotatedFields(command, cls, result, factory); - if (thisCommandHasAnnotation) { //#377 Standard help options should be added last - result.mixinStandardHelpOptions(cls.getAnnotation(Command.class).mixinStandardHelpOptions()); - } - cls = cls.getSuperclass(); - } - if (annotationsAreMandatory) {validateCommandSpec(result, hasCommandAnnotation, command); } - result.withToString(command.getClass().getName()).validate(); - return result; - } - - private static boolean updateCommandAttributes(Class cls, CommandSpec commandSpec, IFactory factory) { - // superclass values should not overwrite values if both class and superclass have a @Command annotation - if (!cls.isAnnotationPresent(Command.class)) { return false; } - - Command cmd = cls.getAnnotation(Command.class); - commandSpec.aliases(cmd.aliases()); - initSubcommands(cmd, commandSpec, factory); - - commandSpec.parser().initSeparator(cmd.separator()); - commandSpec.initName(cmd.name()); - commandSpec.initVersion(cmd.version()); - commandSpec.initHelpCommand(cmd.helpCommand()); - commandSpec.initVersionProvider(cmd.versionProvider(), factory); - commandSpec.usageMessage().initSynopsisHeading(cmd.synopsisHeading()); - commandSpec.usageMessage().initCommandListHeading(cmd.commandListHeading()); - commandSpec.usageMessage().initRequiredOptionMarker(cmd.requiredOptionMarker()); - commandSpec.usageMessage().initCustomSynopsis(cmd.customSynopsis()); - commandSpec.usageMessage().initDescription(cmd.description()); - commandSpec.usageMessage().initDescriptionHeading(cmd.descriptionHeading()); - commandSpec.usageMessage().initHeader(cmd.header()); - commandSpec.usageMessage().initHeaderHeading(cmd.headerHeading()); - commandSpec.usageMessage().initFooter(cmd.footer()); - commandSpec.usageMessage().initFooterHeading(cmd.footerHeading()); - commandSpec.usageMessage().initParameterListHeading(cmd.parameterListHeading()); - commandSpec.usageMessage().initOptionListHeading(cmd.optionListHeading()); - commandSpec.usageMessage().initAbbreviateSynopsis(cmd.abbreviateSynopsis()); - commandSpec.usageMessage().initSortOptions(cmd.sortOptions()); - commandSpec.usageMessage().initShowDefaultValues(cmd.showDefaultValues()); - commandSpec.usageMessage().initHidden(cmd.hidden()); - return true; - } - private static void initSubcommands(Command cmd, CommandSpec parent, IFactory factory) { - for (Class sub : cmd.subcommands()) { - try { - if (Help.class == sub) { throw new InitializationException(Help.class.getName() + " is not a valid subcommand. Did you mean " + HelpCommand.class.getName() + "?"); } - CommandLine subcommandLine = toCommandLine(factory.create(sub), factory); - parent.addSubcommand(subcommandName(sub), subcommandLine); - initParentCommand(subcommandLine.getCommandSpec().userObject(), parent.userObject()); - } - catch (InitializationException ex) { throw ex; } - catch (NoSuchMethodException ex) { throw new InitializationException("Cannot instantiate subcommand " + - sub.getName() + ": the class has no constructor", ex); } - catch (Exception ex) { - throw new InitializationException("Could not instantiate and add subcommand " + - sub.getName() + ": " + ex, ex); - } - } - } - static void initParentCommand(Object subcommand, Object parent) { - if (subcommand == null) { return; } - try { - Class cls = subcommand.getClass(); - while (cls != null) { - for (Field f : cls.getDeclaredFields()) { - if (f.isAnnotationPresent(ParentCommand.class)) { - f.setAccessible(true); - f.set(subcommand, parent); - } - } - cls = cls.getSuperclass(); - } - } catch (Exception ex) { - throw new InitializationException("Unable to initialize @ParentCommand field: " + ex, ex); - } - } - private static String subcommandName(Class sub) { - Command subCommand = sub.getAnnotation(Command.class); - if (subCommand == null || Help.DEFAULT_COMMAND_NAME.equals(subCommand.name())) { - throw new InitializationException("Subcommand " + sub.getName() + - " is missing the mandatory @Command annotation with a 'name' attribute"); - } - return subCommand.name(); - } - private static boolean initFromAnnotatedFields(Object scope, Class cls, CommandSpec receiver, IFactory factory) { - boolean result = false; - for (Field field : cls.getDeclaredFields()) { - if (isMixin(field)) { - validateMixin(field); - receiver.addMixin(mixinName(field), buildMixinForField(field, scope, factory)); - result = true; - } - if (isUnmatched(field)) { - validateUnmatched(field); - receiver.addUnmatchedArgsBinding(buildUnmatchedForField(field, scope)); - } - if (isArgSpec(field)) { - validateArgSpecField(field); - if (isOption(field)) { receiver.addOption(ArgsReflection.extractOptionSpec(scope, field, factory)); } - if (isParameter(field)) { receiver.addPositional(ArgsReflection.extractPositionalParamSpec(scope, field, factory)); } - } - if (isInject(field)) { - validateInject(field); - field.setAccessible(true); - new FieldBinding(scope, field).set(receiver); - } - } - return result; - } - private static String mixinName(Field field) { - String annotationName = field.getAnnotation(Mixin.class).name(); - return empty(annotationName) ? field.getName() : annotationName; - } - private static void validateMixin(Field field) { - if (isMixin(field) && (isOption(field) || isParameter(field))) { - throw new DuplicateOptionAnnotationsException("A field cannot be both a @Mixin command and an @Option or @Parameters, but '" + field + "' is both."); - } - if (isMixin(field) && (isUnmatched(field))) { - throw new DuplicateOptionAnnotationsException("A field cannot be both a @Mixin command and an @Unmatched but '" + field + "' is both."); - } - } - private static void validateUnmatched(Field field) { - if (isUnmatched(field) && (isOption(field) || isParameter(field))) { - throw new DuplicateOptionAnnotationsException("A field cannot have both @Unmatched and @Option or @Parameters annotations, but '" + field + "' has both."); - } - } - private static void validateArgSpecField(Field field) { - if (isOption(field) && isParameter(field)) { - throw new DuplicateOptionAnnotationsException("A field can be either @Option or @Parameters, but '" + field + "' is both."); - } - if (isMixin(field) && (isOption(field) || isParameter(field))) { - throw new DuplicateOptionAnnotationsException("A field cannot be both a @Mixin command and an @Option or @Parameters, but '" + field + "' is both."); - } - if (Modifier.isFinal(field.getModifiers()) && (field.getType().isPrimitive() || String.class.isAssignableFrom(field.getType()))) { - throw new InitializationException("Constant (final) primitive and String fields like " + field + " cannot be used as " + - (isOption(field) ? "an @Option" : "a @Parameter") + ": compile-time constant inlining may hide new values written to it."); - } - } - private static void validateCommandSpec(CommandSpec result, boolean hasCommandAnnotation, Object command) { - if (!hasCommandAnnotation && result.positionalParameters.isEmpty() && result.optionsByNameMap.isEmpty() && result.unmatchedArgs.isEmpty()) { - throw new InitializationException(command.getClass().getName() + " is not a command: it has no @Command, @Option, @Parameters or @Unmatched annotations"); - } - } - private static void validateInject(Field field) { - if (isInject(field) && (isOption(field) || isParameter(field))) { - throw new DuplicateOptionAnnotationsException("A field cannot have both @Inject and @Option or @Parameters annotations, but '" + field + "' has both."); - } - if (isInject(field) && isUnmatched(field)) { - throw new DuplicateOptionAnnotationsException("A field cannot have both @Inject and @Unmatched annotations, but '" + field + "' has both."); - } - if (isInject(field) && isMixin(field)) { - throw new DuplicateOptionAnnotationsException("A field cannot have both @Inject and @Mixin annotations, but '" + field + "' has both."); - } - if (field.getType() != CommandSpec.class) { throw new InitializationException("@picocli.CommandLine.Inject annotation is only supported on fields of type " + CommandSpec.class.getName()); } - } - private static CommandSpec buildMixinForField(Field field, Object scope, IFactory factory) { - try { - field.setAccessible(true); - Object userObject = field.get(scope); - if (userObject == null) { - userObject = factory.create(field.getType()); - field.set(scope, userObject); - } - CommandSpec result = CommandSpec.forAnnotatedObject(userObject, factory); - return result.withToString(abbreviate("mixin from field " + field.toGenericString())); - } catch (InitializationException ex) { - throw ex; - } catch (Exception ex) { - throw new InitializationException("Could not access or modify mixin field " + field + ": " + ex, ex); - } - } - private static UnmatchedArgsBinding buildUnmatchedForField(final Field field, final Object scope) { - if (!(field.getType().equals(String[].class) || - (List.class.isAssignableFrom(field.getType()) && field.getGenericType() instanceof ParameterizedType - && ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(String.class)))) { - throw new InitializationException("Invalid type for " + field + ": must be either String[] or List"); - } - field.setAccessible(true); - if (field.getType().equals(String[].class)) { - return UnmatchedArgsBinding.forStringArrayConsumer(new FieldBinding(scope, field)); - } else { - return UnmatchedArgsBinding.forStringCollectionSupplier(new IGetter() { - @SuppressWarnings("unchecked") public T get() throws Exception { - field.setAccessible(true); - List result = (List) field.get(scope); - if (result == null) { - result = new ArrayList(); - field.set(scope, result); - } - return (T) result; - } - }); - } - } - static boolean isArgSpec(Field f) { return isOption(f) || isParameter(f); } - static boolean isOption(Field f) { return f.isAnnotationPresent(Option.class); } - static boolean isParameter(Field f) { return f.isAnnotationPresent(Parameters.class); } - static boolean isMixin(Field f) { return f.isAnnotationPresent(Mixin.class); } - static boolean isUnmatched(Field f) { return f.isAnnotationPresent(Unmatched.class); } - static boolean isInject(Field f) { return f.isAnnotationPresent(Inject.class); } - } - - /** Helper class to reflectively create OptionSpec and PositionalParamSpec objects from annotated elements. - * Package protected for testing. CONSIDER THIS CLASS PRIVATE. */ - static class ArgsReflection { - static OptionSpec extractOptionSpec(Object scope, Field field, IFactory factory) { - Option option = field.getAnnotation(Option.class); - OptionSpec.Builder builder = OptionSpec.builder(option.names()); - initCommon(builder, scope, field); - - builder.help(option.help()); - builder.usageHelp(option.usageHelp()); - builder.versionHelp(option.versionHelp()); - builder.showDefaultValue(option.showDefaultValue()); - if (!NoCompletionCandidates.class.equals(option.completionCandidates())) { - builder.completionCandidates(DefaultFactory.createCompletionCandidates(factory, option.completionCandidates())); - } - - builder.arity(Range.optionArity(field)); - builder.required(option.required()); - builder.description(option.description()); - Class[] elementTypes = inferTypes(field.getType(), option.type(), field.getGenericType()); - builder.auxiliaryTypes(elementTypes); - builder.paramLabel(inferLabel(option.paramLabel(), field.getName(), field.getType(), elementTypes)); - builder.splitRegex(option.split()); - builder.hidden(option.hidden()); - builder.converters(DefaultFactory.createConverter(factory, option.converter())); - return builder.build(); - } - static PositionalParamSpec extractPositionalParamSpec(Object scope, Field field, IFactory factory) { - PositionalParamSpec.Builder builder = PositionalParamSpec.builder(); - initCommon(builder, scope, field); - Range arity = Range.parameterArity(field); - builder.arity(arity); - builder.index(Range.parameterIndex(field)); - builder.capacity(Range.parameterCapacity(field)); - builder.required(arity.min > 0); - - Parameters parameters = field.getAnnotation(Parameters.class); - builder.description(parameters.description()); - Class[] elementTypes = inferTypes(field.getType(), parameters.type(), field.getGenericType()); - builder.auxiliaryTypes(elementTypes); - builder.paramLabel(inferLabel(parameters.paramLabel(), field.getName(), field.getType(), elementTypes)); - builder.splitRegex(parameters.split()); - builder.hidden(parameters.hidden()); - builder.converters(DefaultFactory.createConverter(factory, parameters.converter())); - builder.showDefaultValue(parameters.showDefaultValue()); - if (!NoCompletionCandidates.class.equals(parameters.completionCandidates())) { - builder.completionCandidates(DefaultFactory.createCompletionCandidates(factory, parameters.completionCandidates())); - } - return builder.build(); - } - private static void initCommon(ArgSpec.Builder builder, Object scope, Field field) { - field.setAccessible(true); - builder.type(field.getType()); // field type - builder.initialValue(getInitialValue(scope, field)); - builder.withToString(abbreviate("field " + field.toGenericString())); - FieldBinding binding = new FieldBinding(scope, field); - builder.getter(binding).setter(binding); - } - static String abbreviate(String text) { - return text.replace("field private ", "field ") - .replace("field protected ", "field ") - .replace("field public ", "field ") - .replace("java.lang.", ""); - } - private static String inferLabel(String label, String fieldName, Class fieldType, Class[] types) { - if (!empty(label)) { return label.trim(); } - String name = fieldName; - if (Map.class.isAssignableFrom(fieldType)) { // #195 better param labels for map fields - Class[] paramTypes = types; - if (paramTypes.length < 2 || paramTypes[0] == null || paramTypes[1] == null) { - name = "String=String"; - } else { name = paramTypes[0].getSimpleName() + "=" + paramTypes[1].getSimpleName(); } - } - return "<" + name + ">"; - } - private static Class[] inferTypes(Class propertyType, Class[] annotationTypes, Type genericType) { - if (annotationTypes.length > 0) { return annotationTypes; } - if (propertyType.isArray()) { return new Class[] { propertyType.getComponentType() }; } - if (CommandLine.isMultiValue(propertyType)) { - if (genericType instanceof ParameterizedType) {// e.g. Map - ParameterizedType parameterizedType = (ParameterizedType) genericType; - Type[] paramTypes = parameterizedType.getActualTypeArguments(); // e.g. ? extends Number - Class[] result = new Class[paramTypes.length]; - for (int i = 0; i < paramTypes.length; i++) { - if (paramTypes[i] instanceof Class) { result[i] = (Class) paramTypes[i]; continue; } // e.g. Long - if (paramTypes[i] instanceof WildcardType) { // e.g. ? extends Number - WildcardType wildcardType = (WildcardType) paramTypes[i]; - Type[] lower = wildcardType.getLowerBounds(); // e.g. [] - if (lower.length > 0 && lower[0] instanceof Class) { result[i] = (Class) lower[0]; continue; } - Type[] upper = wildcardType.getUpperBounds(); // e.g. Number - if (upper.length > 0 && upper[0] instanceof Class) { result[i] = (Class) upper[0]; continue; } - } - Arrays.fill(result, String.class); return result; // too convoluted generic type, giving up - } - return result; // we inferred all types from ParameterizedType - } - return new Class[] {String.class, String.class}; // field is multi-value but not ParameterizedType - } - return new Class[] {propertyType}; // not a multi-value field - } - static Object getInitialValue(Object scope, Field field) { - try { return field.get(scope); } catch (Exception ex) { return null; } - } - } - private static class FieldBinding implements IGetter, ISetter { - private final Object scope; - private final Field field; - FieldBinding(Object scope, Field field) { this.scope = scope; this.field = field; } - public T get() throws PicocliException { - try { - @SuppressWarnings("unchecked") T result = (T) field.get(scope); - return result; - } catch (Exception ex) { - throw new PicocliException("Could not get value for field " + field, ex); - } - } - public T set(T value) throws PicocliException { - try { - @SuppressWarnings("unchecked") T result = (T) field.get(scope); - field.set(scope, value); - return result; - } catch (Exception ex) { - throw new PicocliException("Could not set value for field " + field + " to " + value, ex); - } - } - } - private static class ObjectBinding implements IGetter, ISetter { - private Object value; - @SuppressWarnings("unchecked") public T get() { return (T) value; } - public T set(T value) { - @SuppressWarnings("unchecked") T result = (T) value; - this.value = value; - return result; - } - } - } - - /** Encapsulates the result of parsing an array of command line arguments. - * @since 3.0 */ - public static class ParseResult { - /** Creates and returns a new {@code ParseResult.Builder} for the specified command spec. */ - public static Builder builder(CommandSpec commandSpec) { return new Builder(commandSpec); } - /** Builds immutable {@code ParseResult} instances. */ - public static class Builder { - private final CommandSpec commandSpec; - private final Set options = new LinkedHashSet(); - private final Set positionals = new LinkedHashSet(); - private final List unmatched = new ArrayList(); - private final List originalArgList = new ArrayList(); - private final List> positionalParams = new ArrayList>(); - private ParseResult subcommand; - private boolean usageHelpRequested; - private boolean versionHelpRequested; - boolean isInitializingDefaultValues; - private List errors = new ArrayList(1); - private List nowProcessing; - - private Builder(CommandSpec spec) { commandSpec = Assert.notNull(spec, "commandSpec"); } - /** Creates and returns a new {@code ParseResult} instance for this builder's configuration. */ - public ParseResult build() { return new ParseResult(this); } - - private void nowProcessing(ArgSpec spec, Object value) { if (nowProcessing != null) { nowProcessing.add(spec.isPositional() ? spec : value); } } - - /** Adds the specified {@code OptionSpec} or {@code PositionalParamSpec} to the list of options and parameters - * that were matched on the command line. - * @param arg the matched {@code OptionSpec} or {@code PositionalParamSpec} - * @param position the command line position at which the {@code PositionalParamSpec} was matched. Ignored for {@code OptionSpec}s. - * @return this builder for method chaining */ - public Builder add(ArgSpec arg, int position) { - if (arg.isOption()) { - addOption((OptionSpec) arg); - } else { - addPositionalParam((PositionalParamSpec) arg, position); - } - return this; - } - /** Adds the specified {@code OptionSpec} to the list of options that were matched on the command line. */ - public Builder addOption(OptionSpec option) { if (!isInitializingDefaultValues) {options.add(option);} return this; } - /** Adds the specified {@code PositionalParamSpec} to the list of parameters that were matched on the command line. - * @param positionalParam the matched {@code PositionalParamSpec} - * @param position the command line position at which the {@code PositionalParamSpec} was matched. - * @return this builder for method chaining */ - public Builder addPositionalParam(PositionalParamSpec positionalParam, int position) { - if (isInitializingDefaultValues) { return this; } - positionals.add(positionalParam); - while (positionalParams.size() <= position) { positionalParams.add(new ArrayList()); } - positionalParams.get(position).add(positionalParam); - return this; - } - /** Adds the specified command line argument to the list of unmatched command line arguments. */ - public Builder addUnmatched(String arg) { unmatched.add(arg); return this; } - /** Adds all elements of the specified command line arguments stack to the list of unmatched command line arguments. */ - public Builder addUnmatched(Stack args) { while (!args.isEmpty()) { addUnmatched(args.pop()); } return this; } - /** Sets the specified {@code ParseResult} for a subcommand that was matched on the command line. */ - public Builder subcommand(ParseResult subcommand) { this.subcommand = subcommand; return this; } - /** Sets the specified command line arguments that were parsed. */ - public Builder originalArgs(String[] originalArgs) { originalArgList.addAll(Arrays.asList(originalArgs)); return this;} - - void addStringValue (ArgSpec argSpec, String value) { if (!isInitializingDefaultValues) { argSpec.stringValues.add(value);} } - void addOriginalStringValue(ArgSpec argSpec, String value) { if (!isInitializingDefaultValues) { argSpec.originalStringValues.add(value); } } - void addTypedValues(ArgSpec argSpec, int position, Object typedValue) { - if (!isInitializingDefaultValues) { - argSpec.typedValues.add(typedValue); - argSpec.typedValueAtPosition.put(position, typedValue); - } - } - public void addError(PicocliException ex) { - errors.add(Assert.notNull(ex, "exception")); - } - } - private final CommandSpec commandSpec; - private final List matchedOptions; - private final List matchedUniquePositionals; - private final List originalArgs; - private final List unmatched; - private final List> matchedPositionalParams; - private final List errors; - final List tentativeMatch; - - private final ParseResult subcommand; - private final boolean usageHelpRequested; - private final boolean versionHelpRequested; - - private ParseResult(ParseResult.Builder builder) { - commandSpec = builder.commandSpec; - subcommand = builder.subcommand; - matchedOptions = new ArrayList(builder.options); - unmatched = new ArrayList(builder.unmatched); - originalArgs = new ArrayList(builder.originalArgList); - matchedUniquePositionals = new ArrayList(builder.positionals); - matchedPositionalParams = new ArrayList>(builder.positionalParams); - errors = new ArrayList(builder.errors); - usageHelpRequested = builder.usageHelpRequested; - versionHelpRequested = builder.versionHelpRequested; - tentativeMatch = builder.nowProcessing; - } - /** Returns the option with the specified short name, or {@code null} if no option with that name was matched - * on the command line. - *

Use {@link OptionSpec#getValue() getValue} on the returned {@code OptionSpec} to get the matched value (or values), - * converted to the type of the option. Alternatively, use {@link OptionSpec#stringValues() stringValues} - * to get the matched String values after they were {@linkplain OptionSpec#splitRegex() split} into parts, or - * {@link OptionSpec#originalStringValues() originalStringValues} to get the original String values that were - * matched on the command line, before any processing. - *

To get the {@linkplain OptionSpec#defaultValue() default value} of an option that was - * {@linkplain #hasMatchedOption(char) not matched} on the command line, use - * {@code parseResult.commandSpec().findOption(shortName).getValue()}.

- * @see CommandSpec#findOption(char) */ - public OptionSpec matchedOption(char shortName) { return CommandSpec.findOption(shortName, matchedOptions); } - - /** Returns the option with the specified name, or {@code null} if no option with that name was matched on the command line. - *

Use {@link OptionSpec#getValue() getValue} on the returned {@code OptionSpec} to get the matched value (or values), - * converted to the type of the option. Alternatively, use {@link OptionSpec#stringValues() stringValues} - * to get the matched String values after they were {@linkplain OptionSpec#splitRegex() split} into parts, or - * {@link OptionSpec#originalStringValues() originalStringValues} to get the original String values that were - * matched on the command line, before any processing. - *

To get the {@linkplain OptionSpec#defaultValue() default value} of an option that was - * {@linkplain #hasMatchedOption(String) not matched} on the command line, use - * {@code parseResult.commandSpec().findOption(String).getValue()}.

- * @see CommandSpec#findOption(String) - * @param name used to search the matched options. May be an alias of the option name that was actually specified on the command line. - * The specified name may include option name prefix characters or not. */ - public OptionSpec matchedOption(String name) { return CommandSpec.findOption(name, matchedOptions); } - - /** Returns the first {@code PositionalParamSpec} that matched an argument at the specified position, or {@code null} if no positional parameters were matched at that position. */ - public PositionalParamSpec matchedPositional(int position) { - if (matchedPositionalParams.size() <= position || matchedPositionalParams.get(position).isEmpty()) { return null; } - return matchedPositionalParams.get(position).get(0); - } - - /** Returns all {@code PositionalParamSpec} objects that matched an argument at the specified position, or an empty list if no positional parameters were matched at that position. */ - public List matchedPositionals(int position) { - if (matchedPositionalParams.size() <= position) { return Collections.emptyList(); } - return matchedPositionalParams.get(position) == null ? Collections.emptyList() : matchedPositionalParams.get(position); - } - /** Returns the {@code CommandSpec} for the matched command. */ - public CommandSpec commandSpec() { return commandSpec; } - - /** Returns whether an option whose aliases include the specified short name was matched on the command line. - * @param shortName used to search the matched options. May be an alias of the option name that was actually specified on the command line. */ - public boolean hasMatchedOption(char shortName) { return matchedOption(shortName) != null; } - /** Returns whether an option whose aliases include the specified name was matched on the command line. - * @param name used to search the matched options. May be an alias of the option name that was actually specified on the command line. - * The specified name may include option name prefix characters or not. */ - public boolean hasMatchedOption(String name) { return matchedOption(name) != null; } - /** Returns whether the specified option was matched on the command line. */ - public boolean hasMatchedOption(OptionSpec option) { return matchedOptions.contains(option); } - - /** Returns whether a positional parameter was matched at the specified position. */ - public boolean hasMatchedPositional(int position) { return matchedPositional(position) != null; } - /** Returns whether the specified positional parameter was matched on the command line. */ - public boolean hasMatchedPositional(PositionalParamSpec positional) { return matchedUniquePositionals.contains(positional); } - - /** Returns a list of matched options, in the order they were found on the command line. */ - public List matchedOptions() { return Collections.unmodifiableList(matchedOptions); } - - /** Returns a list of matched positional parameters. */ - public List matchedPositionals() { return Collections.unmodifiableList(matchedUniquePositionals); } - - /** Returns a list of command line arguments that did not match any options or positional parameters. */ - public List unmatched() { return Collections.unmodifiableList(unmatched); } - - /** Returns the command line arguments that were parsed. */ - public List originalArgs() { return Collections.unmodifiableList(originalArgs); } - - /** If {@link ParserSpec#collectErrors} is {@code true}, returns the list of exceptions that were encountered during parsing, otherwise, returns an empty list. - * @since 3.2 */ - public List errors() { return Collections.unmodifiableList(errors); } - - /** Returns the command line argument value of the option with the specified name, converted to the {@linkplain OptionSpec#type() type} of the option, or the specified default value if no option with the specified name was matched. */ - public T matchedOptionValue(char shortName, T defaultValue) { return matchedOptionValue(matchedOption(shortName), defaultValue); } - /** Returns the command line argument value of the option with the specified name, converted to the {@linkplain OptionSpec#type() type} of the option, or the specified default value if no option with the specified name was matched. */ - public T matchedOptionValue(String name, T defaultValue) { return matchedOptionValue(matchedOption(name), defaultValue); } - /** Returns the command line argument value of the specified option, converted to the {@linkplain OptionSpec#type() type} of the option, or the specified default value if the specified option is {@code null}. */ - @SuppressWarnings("unchecked") - private T matchedOptionValue(OptionSpec option, T defaultValue) { return option == null ? defaultValue : (T) option.getValue(); } - - /** Returns the command line argument value of the positional parameter at the specified position, converted to the {@linkplain PositionalParamSpec#type() type} of the positional parameter, or the specified default value if no positional parameter was matched at that position. */ - public T matchedPositionalValue(int position, T defaultValue) { return matchedPositionalValue(matchedPositional(position), defaultValue); } - /** Returns the command line argument value of the specified positional parameter, converted to the {@linkplain PositionalParamSpec#type() type} of the positional parameter, or the specified default value if the specified positional parameter is {@code null}. */ - @SuppressWarnings("unchecked") - private T matchedPositionalValue(PositionalParamSpec positional, T defaultValue) { return positional == null ? defaultValue : (T) positional.getValue(); } - - /** Returns {@code true} if a subcommand was matched on the command line, {@code false} otherwise. */ - public boolean hasSubcommand() { return subcommand != null; } - - /** Returns the {@code ParseResult} for the subcommand of this command that was matched on the command line, or {@code null} if no subcommand was matched. */ - public ParseResult subcommand() { return subcommand; } - - /** Returns {@code true} if one of the options that was matched on the command line is a {@link OptionSpec#usageHelp() usageHelp} option. */ - public boolean isUsageHelpRequested() { return usageHelpRequested; } - - /** Returns {@code true} if one of the options that was matched on the command line is a {@link OptionSpec#versionHelp() versionHelp} option. */ - public boolean isVersionHelpRequested() { return versionHelpRequested; } - - /** Returns this {@code ParseResult} as a list of {@code CommandLine} objects, one for each matched command/subcommand. - * For backwards compatibility with pre-3.0 methods. */ - public List asCommandLineList() { - List result = new ArrayList(); - ParseResult pr = this; - while (pr != null) { result.add(pr.commandSpec().commandLine()); pr = pr.hasSubcommand() ? pr.subcommand() : null; } - return result; - } - } - private enum LookBehind { SEPARATE, ATTACHED, ATTACHED_WITH_SEPARATOR; - public boolean isAttached() { return this != LookBehind.SEPARATE; } - } - /** - * Helper class responsible for processing command line arguments. - */ - private class Interpreter { - private final Map, ITypeConverter> converterRegistry = new HashMap, ITypeConverter>(); - private boolean isHelpRequested; - private int position; - private boolean endOfOptions; - private ParseResult.Builder parseResult; - - Interpreter() { registerBuiltInConverters(); } - - private void registerBuiltInConverters() { - converterRegistry.put(Object.class, new BuiltIn.StringConverter()); - converterRegistry.put(String.class, new BuiltIn.StringConverter()); - converterRegistry.put(StringBuilder.class, new BuiltIn.StringBuilderConverter()); - converterRegistry.put(CharSequence.class, new BuiltIn.CharSequenceConverter()); - converterRegistry.put(Byte.class, new BuiltIn.ByteConverter()); - converterRegistry.put(Byte.TYPE, new BuiltIn.ByteConverter()); - converterRegistry.put(Boolean.class, new BuiltIn.BooleanConverter()); - converterRegistry.put(Boolean.TYPE, new BuiltIn.BooleanConverter()); - converterRegistry.put(Character.class, new BuiltIn.CharacterConverter()); - converterRegistry.put(Character.TYPE, new BuiltIn.CharacterConverter()); - converterRegistry.put(Short.class, new BuiltIn.ShortConverter()); - converterRegistry.put(Short.TYPE, new BuiltIn.ShortConverter()); - converterRegistry.put(Integer.class, new BuiltIn.IntegerConverter()); - converterRegistry.put(Integer.TYPE, new BuiltIn.IntegerConverter()); - converterRegistry.put(Long.class, new BuiltIn.LongConverter()); - converterRegistry.put(Long.TYPE, new BuiltIn.LongConverter()); - converterRegistry.put(Float.class, new BuiltIn.FloatConverter()); - converterRegistry.put(Float.TYPE, new BuiltIn.FloatConverter()); - converterRegistry.put(Double.class, new BuiltIn.DoubleConverter()); - converterRegistry.put(Double.TYPE, new BuiltIn.DoubleConverter()); - converterRegistry.put(File.class, new BuiltIn.FileConverter()); - converterRegistry.put(URI.class, new BuiltIn.URIConverter()); - converterRegistry.put(URL.class, new BuiltIn.URLConverter()); - converterRegistry.put(Date.class, new BuiltIn.ISO8601DateConverter()); - converterRegistry.put(BigDecimal.class, new BuiltIn.BigDecimalConverter()); - converterRegistry.put(BigInteger.class, new BuiltIn.BigIntegerConverter()); - converterRegistry.put(Charset.class, new BuiltIn.CharsetConverter()); - converterRegistry.put(InetAddress.class, new BuiltIn.InetAddressConverter()); - converterRegistry.put(Pattern.class, new BuiltIn.PatternConverter()); - converterRegistry.put(UUID.class, new BuiltIn.UUIDConverter()); - converterRegistry.put(Currency.class, new BuiltIn.CurrencyConverter()); - converterRegistry.put(TimeZone.class, new BuiltIn.TimeZoneConverter()); - converterRegistry.put(ByteOrder.class, new BuiltIn.ByteOrderConverter()); - converterRegistry.put(Class.class, new BuiltIn.ClassConverter()); - converterRegistry.put(NetworkInterface.class, new BuiltIn.NetworkInterfaceConverter()); - - BuiltIn.ISO8601TimeConverter.registerIfAvailable(converterRegistry, tracer); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.sql.Connection", "java.sql.DriverManager","getConnection", String.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.sql.Driver", "java.sql.DriverManager","getDriver", String.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.sql.Timestamp", "java.sql.Timestamp","valueOf", String.class); - - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.Duration", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.Instant", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.LocalDate", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.LocalDateTime", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.LocalTime", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.MonthDay", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.OffsetDateTime", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.OffsetTime", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.Period", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.Year", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.YearMonth", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.ZonedDateTime", "parse", CharSequence.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.ZoneId", "of", String.class); - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.time.ZoneOffset", "of", String.class); - - BuiltIn.registerIfAvailable(converterRegistry, tracer, "java.nio.file.Path", "java.nio.file.Paths", "get", String.class, String[].class); - } - private ParserSpec config() { return commandSpec.parser(); } - /** - * Entry point into parsing command line arguments. - * @param args the command line arguments - * @return a list with all commands and subcommands initialized by this method - * @throws ParameterException if the specified command line arguments are invalid - */ - List parse(String... args) { - Assert.notNull(args, "argument array"); - if (tracer.isInfo()) {tracer.info("Parsing %d command line args %s%n", args.length, Arrays.toString(args));} - if (tracer.isDebug()){tracer.debug("Parser configuration: %s%n", config());} - List expanded = new ArrayList(); - for (String arg : args) { addOrExpand(arg, expanded, new LinkedHashSet()); } - Stack arguments = new Stack(); - arguments.addAll(reverseList(expanded)); - List result = new ArrayList(); - parse(result, arguments, args, new ArrayList()); - return result; - } - - private void addOrExpand(String arg, List arguments, Set visited) { - if (config().expandAtFiles() && !arg.equals("@") && arg.startsWith("@")) { - arg = arg.substring(1); - if (arg.startsWith("@")) { - if (tracer.isInfo()) { tracer.info("Not expanding @-escaped argument %s (trimmed leading '@' char)%n", arg); } - } else { - if (tracer.isInfo()) { tracer.info("Expanding argument file @%s%n", arg); } - expandArgumentFile(arg, arguments, visited); - return; - } - } - arguments.add(arg); - } - private void expandArgumentFile(String fileName, List arguments, Set visited) { - File file = new File(fileName); - if (!file.canRead()) { - if (tracer.isInfo()) {tracer.info("File %s does not exist or cannot be read; treating argument literally%n", fileName);} - arguments.add("@" + fileName); - } else if (visited.contains(file.getAbsolutePath())) { - if (tracer.isInfo()) {tracer.info("Already visited file %s; ignoring...%n", file.getAbsolutePath());} - } else { - expandValidArgumentFile(fileName, file, arguments, visited); - } - } - private void expandValidArgumentFile(String fileName, File file, List arguments, Set visited) { - visited.add(file.getAbsolutePath()); - List result = new ArrayList(); - LineNumberReader reader = null; - try { - reader = new LineNumberReader(new FileReader(file)); - StreamTokenizer tok = new StreamTokenizer(reader); - tok.resetSyntax(); - tok.wordChars(' ', 255); - tok.whitespaceChars(0, ' '); - tok.commentChar('#'); - tok.quoteChar('"'); - tok.quoteChar('\''); - while (tok.nextToken() != StreamTokenizer.TT_EOF) { - addOrExpand(tok.sval, result, visited); - } - } catch (Exception ex) { - throw new InitializationException("Could not read argument file @" + fileName, ex); - } finally { - if (reader != null) { try {reader.close();} catch (Exception ignored) {} } - } - if (tracer.isInfo()) {tracer.info("Expanded file @%s to arguments %s%n", fileName, result);} - arguments.addAll(result); - } - - private void clear() { - position = 0; - endOfOptions = false; - isHelpRequested = false; - parseResult = ParseResult.builder(getCommandSpec()); - for (OptionSpec option : getCommandSpec().options()) { clear(option); } - for (PositionalParamSpec positional : getCommandSpec().positionalParameters()) { clear(positional); } - } - private void clear(ArgSpec argSpec) { - argSpec.resetStringValues(); - argSpec.resetOriginalStringValues(); - argSpec.typedValues.clear(); - argSpec.typedValueAtPosition.clear(); - if (argSpec.hasInitialValue()) { - try { - argSpec.setter().set(argSpec.initialValue()); - tracer.debug("Set initial value for %s of type %s to %s.%n", argSpec, argSpec.type(), String.valueOf(argSpec.initialValue())); - } catch (Exception ex) { - tracer.warn("Could not set initial value for %s of type %s to %s: %s%n", argSpec, argSpec.type(), String.valueOf(argSpec.initialValue()), ex); - } - } else { - tracer.debug("Initial value not available for %s%n", argSpec); - } - } - private void maybeThrow(PicocliException ex) throws PicocliException { - if (commandSpec.parser().collectErrors) { - parseResult.addError(ex); - } else { - throw ex; - } - } - - private void parse(List parsedCommands, Stack argumentStack, String[] originalArgs, List nowProcessing) { - clear(); // first reset any state in case this CommandLine instance is being reused - if (tracer.isDebug()) {tracer.debug("Initializing %s: %d options, %d positional parameters, %d required, %d subcommands.%n", - commandSpec.toString(), new HashSet(commandSpec.optionsMap().values()).size(), - commandSpec.positionalParameters().size(), commandSpec.requiredArgs().size(), commandSpec - .subcommands().size());} - parsedCommands.add(CommandLine.this); - List required = new ArrayList(commandSpec.requiredArgs()); - Set initialized = new HashSet(); - Collections.sort(required, new PositionalParametersSorter()); - boolean continueOnError = commandSpec.parser().collectErrors; - do { - int stackSize = argumentStack.size(); - try { - applyDefaultValues(required); - processArguments(parsedCommands, argumentStack, required, initialized, originalArgs, nowProcessing); - } catch (ParameterException ex) { - maybeThrow(ex); - } catch (Exception ex) { - int offendingArgIndex = originalArgs.length - argumentStack.size() - 1; - String arg = offendingArgIndex >= 0 && offendingArgIndex < originalArgs.length ? originalArgs[offendingArgIndex] : "?"; - maybeThrow(ParameterException.create(CommandLine.this, ex, arg, offendingArgIndex, originalArgs)); - } - if (continueOnError && stackSize == argumentStack.size() && stackSize > 0) { - parseResult.unmatched.add(argumentStack.pop()); - } - } while (!argumentStack.isEmpty() && continueOnError); - if (!isAnyHelpRequested() && !required.isEmpty()) { - for (ArgSpec missing : required) { - if (missing.isOption()) { - maybeThrow(MissingParameterException.create(CommandLine.this, required, config().separator())); - } else { - assertNoMissingParameters(missing, missing.arity(), argumentStack); - } - } - } - if (!parseResult.unmatched.isEmpty()) { - String[] unmatched = parseResult.unmatched.toArray(new String[0]); - for (UnmatchedArgsBinding unmatchedArgsBinding : getCommandSpec().unmatchedArgsBindings()) { - unmatchedArgsBinding.addAll(unmatched.clone()); - } - if (!isUnmatchedArgumentsAllowed()) { maybeThrow(new UnmatchedArgumentException(CommandLine.this, Collections.unmodifiableList(parseResult.unmatched))); } - if (tracer.isInfo()) { tracer.info("Unmatched arguments: %s%n", parseResult.unmatched); } - } - } - - private void applyDefaultValues(List required) throws Exception { - parseResult.isInitializingDefaultValues = true; - for (OptionSpec option : commandSpec.options()) { applyDefault(option, required); } - for (PositionalParamSpec positional : commandSpec.positionalParameters()) { applyDefault(positional, required); } - parseResult.isInitializingDefaultValues = false; - } - - private void applyDefault(ArgSpec arg, List required) throws Exception { - if (arg.defaultValue() == null) { return; } - if (tracer.isDebug()) {tracer.debug("Applying defaultValue (%s) to %s%n", arg.defaultValue(), arg);} - Range arity = arg.arity().min(Math.max(1, arg.arity().min)); - applyOption(arg, LookBehind.SEPARATE, arity, stack(arg.defaultValue()), new HashSet(), arg.toString); - required.remove(arg); - } - private Stack stack(String value) {Stack result = new Stack(); result.push(value); return result;} - - private void processArguments(List parsedCommands, - Stack args, - Collection required, - Set initialized, - String[] originalArgs, - List nowProcessing) throws Exception { - // arg must be one of: - // 1. the "--" double dash separating options from positional arguments - // 1. a stand-alone flag, like "-v" or "--verbose": no value required, must map to boolean or Boolean field - // 2. a short option followed by an argument, like "-f file" or "-ffile": may map to any type of field - // 3. a long option followed by an argument, like "-file out.txt" or "-file=out.txt" - // 3. one or more remaining arguments without any associated options. Must be the last in the list. - // 4. a combination of stand-alone options, like "-vxr". Equivalent to "-v -x -r", "-v true -x true -r true" - // 5. a combination of stand-alone options and one option with an argument, like "-vxrffile" - - parseResult.originalArgs(originalArgs); - parseResult.nowProcessing = nowProcessing; - String separator = config().separator(); - while (!args.isEmpty()) { - if (endOfOptions) { - processRemainderAsPositionalParameters(required, initialized, args); - return; - } - String arg = args.pop(); - if (tracer.isDebug()) {tracer.debug("Processing argument '%s'. Remainder=%s%n", arg, reverse(copy(args)));} - - // Double-dash separates options from positional arguments. - // If found, then interpret the remaining args as positional parameters. - if ("--".equals(arg)) { - tracer.info("Found end-of-options delimiter '--'. Treating remainder as positional parameters.%n"); - endOfOptions = true; - processRemainderAsPositionalParameters(required, initialized, args); - return; // we are done - } - - // if we find another command, we are done with the current command - if (commandSpec.subcommands().containsKey(arg)) { - CommandLine subcommand = commandSpec.subcommands().get(arg); - nowProcessing.add(subcommand.commandSpec); - updateHelpRequested(subcommand.commandSpec); - if (!isAnyHelpRequested() && !required.isEmpty()) { // ensure current command portion is valid - throw MissingParameterException.create(CommandLine.this, required, separator); - } - if (tracer.isDebug()) {tracer.debug("Found subcommand '%s' (%s)%n", arg, subcommand.commandSpec.toString());} - subcommand.interpreter.parse(parsedCommands, args, originalArgs, nowProcessing); - parseResult.subcommand(subcommand.interpreter.parseResult.build()); - return; // remainder done by the command - } - - // First try to interpret the argument as a single option (as opposed to a compact group of options). - // A single option may be without option parameters, like "-v" or "--verbose" (a boolean value), - // or an option may have one or more option parameters. - // A parameter may be attached to the option. - boolean paramAttachedToOption = false; - int separatorIndex = arg.indexOf(separator); - if (separatorIndex > 0) { - String key = arg.substring(0, separatorIndex); - // be greedy. Consume the whole arg as an option if possible. - if (commandSpec.optionsMap().containsKey(key) && !commandSpec.optionsMap().containsKey(arg)) { - paramAttachedToOption = true; - String optionParam = arg.substring(separatorIndex + separator.length()); - args.push(optionParam); - arg = key; - if (tracer.isDebug()) {tracer.debug("Separated '%s' option from '%s' option parameter%n", key, optionParam);} - } else { - if (tracer.isDebug()) {tracer.debug("'%s' contains separator '%s' but '%s' is not a known option%n", arg, separator, key);} - } - } else { - if (tracer.isDebug()) {tracer.debug("'%s' cannot be separated into