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 extends ITypeConverter>>[] 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 extends Iterable> 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 extends ITypeConverter>>[] 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 extends Iterable> 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 extends IVersionProvider> 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 extends ITypeConverter>>[] 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 extends IVersionProvider> 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 extends Iterable> 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 extends IVersionProvider> 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