Thank you for retracting your -1, Gary.

All,
Would core.tools be a better place for this package, just core, or is
core.util okay?

On Tue, Aug 15, 2017 at 3:03 AM, Gary Gregory <garydgreg...@gmail.com>
wrote:

> Can we please avoid "util" packages, that just tells me "I can't be
> bothered to think of a good name". Just remove the "util" level IMO.
>
> Gary
>
> On Mon, Aug 14, 2017 at 12:02 PM, Gary Gregory <garydgreg...@gmail.com>
> wrote:
>
> > Ugh, not a fan, but I'll retract my -1.
> >
> > "a single class", yes, but a 4000 line class.
> >
> > Gary
> >
> > On Mon, Aug 14, 2017 at 11:06 AM, Matt Sicker <boa...@gmail.com> wrote:
> >
> >> Embedding a single class? I don't see the problem with that. We do it
> with
> >> several Commons classes.
> >>
> >> On 14 August 2017 at 11:59, Gary Gregory <garydgreg...@gmail.com>
> wrote:
> >>
> >> > Wait a minute? We are embedding a third party jar? Yuk! -1, sorry that
> >> is
> >> > not what I thought was happening.
> >> >
> >> > Gary
> >> >
> >> > On Aug 14, 2017 10:18, <rpo...@apache.org> wrote:
> >> >
> >> > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/
> >> > > c2818bec/log4j-core/src/main/java/org/apache/logging/log4j/
> >> > > core/util/picocli/CommandLine.java
> >> > > ------------------------------------------------------------
> >> ----------
> >> > > diff --git a/log4j-core/src/main/java/
> org/apache/logging/log4j/core/
> >> > util/picocli/CommandLine.java
> >> > > b/log4j-core/src/main/java/org/apache/logging/log4j/core/
> >> > > util/picocli/CommandLine.java
> >> > > new file mode 100644
> >> > > index 0000000..5c4e9cc
> >> > > --- /dev/null
> >> > > +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/
> >> > > util/picocli/CommandLine.java
> >> > > @@ -0,0 +1,3900 @@
> >> > > +/*
> >> > > + * Licensed to the Apache Software Foundation (ASF) under one or
> more
> >> > > + * contributor license agreements. See the NOTICE file distributed
> >> with
> >> > > + * this work for additional information regarding copyright
> >> ownership.
> >> > > + * The ASF licenses this file to You under the Apache license,
> >> Version
> >> > 2.0
> >> > > + * (the "License"); you may not use this file except in compliance
> >> with
> >> > > + * the License. You may obtain a copy of the License at
> >> > > + *
> >> > > + *      http://www.apache.org/licenses/LICENSE-2.0
> >> > > + *
> >> > > + * Unless required by applicable law or agreed to in writing,
> >> software
> >> > > + * distributed under the License is distributed on an "AS IS"
> BASIS,
> >> > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> >> > > implied.
> >> > > + * See the license for the specific language governing permissions
> >> and
> >> > > + * limitations under the license.
> >> > > + */
> >> > > +package org.apache.logging.log4j.core.util.picocli;
> >> > > +
> >> > > +import java.awt.Point;
> >> > > +import java.io.File;
> >> > > +import java.io.PrintStream;
> >> > > +import java.lang.annotation.ElementType;
> >> > > +import java.lang.annotation.Retention;
> >> > > +import java.lang.annotation.RetentionPolicy;
> >> > > +import java.lang.annotation.Target;
> >> > > +import java.lang.reflect.Array;
> >> > > +import java.lang.reflect.Constructor;
> >> > > +import java.lang.reflect.Field;
> >> > > +import java.math.BigDecimal;
> >> > > +import java.math.BigInteger;
> >> > > +import java.net.InetAddress;
> >> > > +import java.net.MalformedURLException;
> >> > > +import java.net.URI;
> >> > > +import java.net.URISyntaxException;
> >> > > +import java.net.URL;
> >> > > +import java.nio.charset.Charset;
> >> > > +import java.nio.file.Path;
> >> > > +import java.nio.file.Paths;
> >> > > +import java.sql.Time;
> >> > > +import java.text.BreakIterator;
> >> > > +import java.text.ParseException;
> >> > > +import java.text.SimpleDateFormat;
> >> > > +import java.util.ArrayList;
> >> > > +import java.util.Arrays;
> >> > > +import java.util.Collection;
> >> > > +import java.util.Collections;
> >> > > +import java.util.Comparator;
> >> > > +import java.util.Date;
> >> > > +import java.util.HashMap;
> >> > > +import java.util.HashSet;
> >> > > +import java.util.LinkedHashMap;
> >> > > +import java.util.LinkedList;
> >> > > +import java.util.List;
> >> > > +import java.util.Map;
> >> > > +import java.util.Queue;
> >> > > +import java.util.Set;
> >> > > +import java.util.SortedMap;
> >> > > +import java.util.SortedSet;
> >> > > +import java.util.Stack;
> >> > > +import java.util.TreeMap;
> >> > > +import java.util.TreeSet;
> >> > > +import java.util.UUID;
> >> > > +import java.util.regex.Pattern;
> >> > > +
> >> > > +import org.apache.logging.log4j.core.
> util.picocli.CommandLine.Help.
> >> > Ansi;
> >> > > +import org.apache.logging.log4j.core.
> util.picocli.CommandLine.Help.
> >> > > Ansi.IStyle;
> >> > > +import org.apache.logging.log4j.core.
> util.picocli.CommandLine.Help.
> >> > > Ansi.Style;
> >> > > +import org.apache.logging.log4j.core.
> util.picocli.CommandLine.Help.
> >> > > Ansi.Text;
> >> > > +
> >> > > +import static java.util.Locale.ENGLISH;
> >> > > +import static org.apache.logging.log4j.core.
> >> > > util.picocli.CommandLine.Help.Column.Overflow.SPAN;
> >> > > +import static org.apache.logging.log4j.core.
> >> > > util.picocli.CommandLine.Help.Column.Overflow.TRUNCATE;
> >> > > +import static org.apache.logging.log4j.core.
> >> > > util.picocli.CommandLine.Help.Column.Overflow.WRAP;
> >> > > +
> >> > > +/**
> >> > > + * <p>
> >> > > + * CommandLine interpreter that uses reflection to initialize an
> >> > > annotated domain object with values obtained from the
> >> > > + * command line arguments.
> >> > > + * </p><h2>Example</h2>
> >> > > + * <pre>import static picocli.CommandLine.*;
> >> > > + *
> >> > > + * &#064;Command(header = "Encrypt FILE(s), or standard input, to
> >> > > standard output or to the output file.")
> >> > > + * public class Encrypt {
> >> > > + *
> >> > > + *     &#064;Parameters(type = File.class, description = "Any
> number
> >> of
> >> > > input files")
> >> > > + *     private List&lt;File&gt; files = new
> ArrayList&lt;File&gt;();
> >> > > + *
> >> > > + *     &#064;Option(names = { "-o", "--out" }, description =
> "Output
> >> > file
> >> > > (default: print to console)")
> >> > > + *     private File outputFile;
> >> > > + *
> >> > > + *     &#064;Option(names = { "-v", "--verbose"}, description =
> >> > > "Verbosely list files processed")
> >> > > + *     private boolean verbose;
> >> > > + *
> >> > > + *     &#064;Option(names = { "-h", "--help", "-?", "-help"}, help
> =
> >> > > true, description = "Display this help and exit")
> >> > > + *     private boolean help;
> >> > > + * }
> >> > > + * </pre>
> >> > > + * <p>
> >> > > + * Use {@code CommandLine} to initialize a domain object as
> follows:
> >> > > + * </p><pre>
> >> > > + * public static void main(String... args) {
> >> > > + *     try {
> >> > > + *         Encrypt encrypt = CommandLine.populateCommand(new
> >> Encrypt(),
> >> > > args);
> >> > > + *         if (encrypt.help) {
> >> > > + *             CommandLine.usage(encrypt, System.out);
> >> > > + *         } else {
> >> > > + *             runProgram(encrypt);
> >> > > + *         }
> >> > > + *     } catch (ParameterException ex) { // command line arguments
> >> could
> >> > > not be parsed
> >> > > + *         System.err.println(ex.getMessage());
> >> > > + *         CommandLine.usage(new Encrypt(), System.err);
> >> > > + *     }
> >> > > + * }
> >> > > + * </pre><p>
> >> > > + * Invoke the above program with some command line arguments. The
> >> below
> >> > > are all equivalent:
> >> > > + * </p>
> >> > > + * <pre>
> >> > > + * --verbose --out=outfile in1 in2
> >> > > + * --verbose --out outfile in1 in2
> >> > > + * -v --out=outfile in1 in2
> >> > > + * -v -o outfile in1 in2
> >> > > + * -v -o=outfile in1 in2
> >> > > + * -vo outfile in1 in2
> >> > > + * -vo=outfile in1 in2
> >> > > + * -v -ooutfile in1 in2
> >> > > + * -vooutfile in1 in2
> >> > > + * </pre>
> >> > > + *
> >> > > + * <p>
> >> > > + * Copied and modified from <a href="http://github.com/remkop
> >> /picocli/
> >> > > ">picocli</a>.
> >> > > + * </p>
> >> > > + *
> >> > > + * @since 2.9
> >> > > + */
> >> > > +public class CommandLine {
> >> > > +    /** This is picocli version {@value}. */
> >> > > +    public static final String VERSION = "0.9.8";
> >> > > +
> >> > > +    private final Interpreter interpreter;
> >> > > +    private boolean overwrittenOptionsAllowed = false;
> >> > > +    private boolean unmatchedArgumentsAllowed = false;
> >> > > +    private List<String> unmatchedArguments = new
> >> ArrayList<String>();
> >> > > +    private CommandLine parent;
> >> > > +    private boolean usageHelpRequested;
> >> > > +    private boolean versionHelpRequested;
> >> > > +    private List<String> versionLines = new ArrayList<String>();
> >> > > +
> >> > > +    /**
> >> > > +     * Constructs a new {@code CommandLine} interpreter with the
> >> > > specified annotated object.
> >> > > +     * When the {@link #parse(String...)} method is called, fields
> of
> >> > the
> >> > > specified object that are annotated
> >> > > +     * with {@code @Option} or {@code @Parameters} will be
> >> initialized
> >> > > based on command line arguments.
> >> > > +     * @param command the object to initialize from the command
> line
> >> > > arguments
> >> > > +     * @throws IllegalArgumentException if the specified command
> >> object
> >> > > does not have a {@link Command}, {@link Option} or {@link
> Parameters}
> >> > > annotation
> >> > > +     */
> >> > > +    public CommandLine(Object command) {
> >> > > +        interpreter = new Interpreter(command);
> >> > > +    }
> >> > > +
> >> > > +    /** Registers a subcommand with the specified name. For
> example:
> >> > > +     * <pre>
> >> > > +     * CommandLine commandLine = new CommandLine(new Git())
> >> > > +     *         .addSubcommand("status",   new GitStatus())
> >> > > +     *         .addSubcommand("commit",   new GitCommit();
> >> > > +     *         .addSubcommand("add",      new GitAdd())
> >> > > +     *         .addSubcommand("branch",   new GitBranch())
> >> > > +     *         .addSubcommand("checkout", new GitCheckout())
> >> > > +     *         //...
> >> > > +     *         ;
> >> > > +     * </pre>
> >> > > +     *
> >> > > +     * <p>The specified object can be an annotated object or a
> >> > > +     * {@code CommandLine} instance with its own nested
> subcommands.
> >> For
> >> > > example:</p>
> >> > > +     * <pre>
> >> > > +     * CommandLine commandLine = new CommandLine(new MainCommand())
> >> > > +     *         .addSubcommand("cmd1",                 new
> >> > > ChildCommand1()) // subcommand
> >> > > +     *         .addSubcommand("cmd2",                 new
> >> > ChildCommand2())
> >> > > +     *         .addSubcommand("cmd3", new CommandLine(new
> >> > > ChildCommand3()) // subcommand with nested sub-subcommands
> >> > > +     *                 .addSubcommand("cmd3sub1",
>  new
> >> > > GrandChild3Command1())
> >> > > +     *                 .addSubcommand("cmd3sub2",
>  new
> >> > > GrandChild3Command2())
> >> > > +     *                 .addSubcommand("cmd3sub3", new
> CommandLine(new
> >> > > GrandChild3Command3()) // deeper nesting
> >> > > +     *                         .addSubcommand("cmd3sub3sub1", new
> >> > > GreatGrandChild3Command3_1())
> >> > > +     *                         .addSubcommand("cmd3sub3sub2", new
> >> > > GreatGrandChild3Command3_2())
> >> > > +     *                 )
> >> > > +     *         );
> >> > > +     * </pre>
> >> > > +     * <p>The default type converters are available on all
> >> subcommands
> >> > > and nested sub-subcommands, but custom type
> >> > > +     * converters are registered only with the subcommand hierarchy
> >> as
> >> > it
> >> > > existed when the custom type was registered.
> >> > > +     * To ensure a custom type converter is available to all
> >> > subcommands,
> >> > > register the type converter last, after
> >> > > +     * adding subcommands.</p>
> >> > > +     *
> >> > > +     * @param name the string to recognize on the command line as a
> >> > > subcommand
> >> > > +     * @param command the object to initialize with command line
> >> > > arguments following the subcommand name.
> >> > > +     *          This may be a {@code CommandLine} instance with its
> >> own
> >> > > (nested) subcommands
> >> > > +     * @return this CommandLine object, to allow method chaining
> >> > > +     * @see #registerConverter(Class, ITypeConverter)
> >> > > +     * @since 0.9.7
> >> > > +     */
> >> > > +    public CommandLine addSubcommand(String name, Object command) {
> >> > > +        CommandLine commandLine = toCommandLine(command);
> >> > > +        commandLine.parent = this;
> >> > > +        interpreter.commands.put(name, commandLine);
> >> > > +        return this;
> >> > > +    }
> >> > > +    /** Returns a map with the subcommands {@linkplain
> >> > > #addSubcommand(String, Object) registered} on this instance.
> >> > > +     * @return a map with the registered subcommands
> >> > > +     * @since 0.9.7
> >> > > +     */
> >> > > +    public Map<String, CommandLine> getSubcommands() {
> >> > > +        return new LinkedHashMap<String, CommandLine>(interpreter.
> >> > > commands);
> >> > > +    }
> >> > > +    /**
> >> > > +     * Returns the command that this is a subcommand of, or {@code
> >> null}
> >> > > if this is a top-level command.
> >> > > +     * @return the command that this is a subcommand of, or {@code
> >> null}
> >> > > if this is a top-level command
> >> > > +     * @see #addSubcommand(String, Object)
> >> > > +     * @see Command#subcommands()
> >> > > +     * @since 0.9.8
> >> > > +     */
> >> > > +    public CommandLine getParent() {
> >> > > +        return parent;
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * Returns the annotated object that this {@code CommandLine}
> >> > > instance was constructed with.
> >> > > +     * @return the annotated object that this {@code CommandLine}
> >> > > instance was constructed with
> >> > > +     * @since 0.9.7
> >> > > +     */
> >> > > +    public Object getCommand() {
> >> > > +        return interpreter.command;
> >> > > +    }
> >> > > +
> >> > > +    /** Returns {@code true} if an option annotated with {@link
> >> > > Option#usageHelp()} was specified on the command line.
> >> > > +     * @return whether the parser encountered an option annotated
> >> with
> >> > > {@link Option#usageHelp()}  */
> >> > > +    public boolean isUsageHelpRequested() { return
> >> usageHelpRequested; }
> >> > > +
> >> > > +    /** Returns {@code true} if an option annotated with {@link
> >> > > Option#versionHelp()} was specified on the command line.
> >> > > +     * @return whether the parser encountered an option annotated
> >> with
> >> > > {@link Option#versionHelp()}  */
> >> > > +    public boolean isVersionHelpRequested() { return
> >> > > versionHelpRequested; }
> >> > > +
> >> > > +    /** Returns whether options for single-value fields can be
> >> specified
> >> > > multiple times on the command line.
> >> > > +     * The default is {@code false} and a {@link
> >> > > OverwrittenOptionException} is thrown if this happens.
> >> > > +     * When {@code true}, the last specified value is retained.
> >> > > +     * @return {@code true} if options for single-value fields can
> be
> >> > > specified multiple times on the command line, {@code false}
> otherwise
> >> > > +     * @since 0.9.7
> >> > > +     */
> >> > > +    public boolean isOverwrittenOptionsAllowed() {
> >> > > +        return overwrittenOptionsAllowed;
> >> > > +    }
> >> > > +
> >> > > +    /** Sets whether options for single-value fields can be
> specified
> >> > > multiple times on the command line without a {@link
> >> > > OverwrittenOptionException} being thrown.
> >> > > +     * <p>The specified setting will be registered with this {@code
> >> > > CommandLine} and the full hierarchy of its
> >> > > +     * subcommands and nested sub-subcommands <em>at the moment
> this
> >> > > method is called</em>. Subcommands added
> >> > > +     * later will have the default setting. To ensure a setting is
> >> > > applied to all
> >> > > +     * subcommands, call the setter last, after adding
> >> subcommands.</p>
> >> > > +     * @param newValue the new setting
> >> > > +     * @return this {@code CommandLine} object, to allow method
> >> chaining
> >> > > +     * @since 0.9.7
> >> > > +     */
> >> > > +    public CommandLine setOverwrittenOptionsAllowed(boolean
> >> newValue) {
> >> > > +        this.overwrittenOptionsAllowed = newValue;
> >> > > +        for (CommandLine command : interpreter.commands.values()) {
> >> > > +            command.setOverwrittenOptionsAllowed(newValue);
> >> > > +        }
> >> > > +        return this;
> >> > > +    }
> >> > > +
> >> > > +    /** Returns whether the end user may specify arguments on the
> >> > command
> >> > > line that are not matched to any option or parameter fields.
> >> > > +     * The default is {@code false} and a {@link
> >> > > UnmatchedArgumentException} is thrown if this happens.
> >> > > +     * When {@code true}, the last unmatched arguments are
> available
> >> via
> >> > > the {@link #getUnmatchedArguments()} method.
> >> > > +     * @return {@code true} if the end use may specify unmatched
> >> > > arguments on the command line, {@code false} otherwise
> >> > > +     * @see #getUnmatchedArguments()
> >> > > +     * @since 0.9.7
> >> > > +     */
> >> > > +    public boolean isUnmatchedArgumentsAllowed() {
> >> > > +        return unmatchedArgumentsAllowed;
> >> > > +    }
> >> > > +
> >> > > +    /** Sets whether the end user may specify unmatched arguments
> on
> >> the
> >> > > command line without a {@link UnmatchedArgumentException} being
> >> thrown.
> >> > > +     * <p>The specified setting will be registered with this {@code
> >> > > CommandLine} and the full hierarchy of its
> >> > > +     * subcommands and nested sub-subcommands <em>at the moment
> this
> >> > > method is called</em>. Subcommands added
> >> > > +     * later will have the default setting. To ensure a setting is
> >> > > applied to all
> >> > > +     * subcommands, call the setter last, after adding
> >> subcommands.</p>
> >> > > +     * @param newValue the new setting
> >> > > +     * @return this {@code CommandLine} object, to allow method
> >> chaining
> >> > > +     * @since 0.9.7
> >> > > +     */
> >> > > +    public CommandLine setUnmatchedArgumentsAllowed(boolean
> >> newValue) {
> >> > > +        this.unmatchedArgumentsAllowed = newValue;
> >> > > +        for (CommandLine command : interpreter.commands.values()) {
> >> > > +            command.setUnmatchedArgumentsAllowed(newValue);
> >> > > +        }
> >> > > +        return this;
> >> > > +    }
> >> > > +
> >> > > +    /** Returns the list of unmatched command line arguments, if
> any.
> >> > > +     * @return the list of unmatched command line arguments or an
> >> empty
> >> > > list
> >> > > +     * @see #isUnmatchedArgumentsAllowed()
> >> > > +     * @since 0.9.7
> >> > > +     */
> >> > > +    public List<String> getUnmatchedArguments() {
> >> > > +        return unmatchedArguments;
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * <p>
> >> > > +     * Convenience method that initializes the specified annotated
> >> > object
> >> > > from the specified command line arguments.
> >> > > +     * </p><p>
> >> > > +     * This is equivalent to
> >> > > +     * </p><pre>
> >> > > +     * CommandLine cli = new CommandLine(command);
> >> > > +     * cli.parse(args);
> >> > > +     * return command;
> >> > > +     * </pre>
> >> > > +     *
> >> > > +     * @param command the object to initialize. This object
> contains
> >> > > fields annotated with
> >> > > +     *          {@code @Option} or {@code @Parameters}.
> >> > > +     * @param args the command line arguments to parse
> >> > > +     * @param <T> the type of the annotated object
> >> > > +     * @return the specified annotated object
> >> > > +     * @throws IllegalArgumentException if the specified command
> >> object
> >> > > does not have a {@link Command}, {@link Option} or {@link
> Parameters}
> >> > > annotation
> >> > > +     * @throws ParameterException if the specified command line
> >> > arguments
> >> > > are invalid
> >> > > +     * @since 0.9.7
> >> > > +     */
> >> > > +    public static <T> T populateCommand(T command, String... args)
> {
> >> > > +        CommandLine cli = toCommandLine(command);
> >> > > +        cli.parse(args);
> >> > > +        return command;
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * <p>
> >> > > +     * Initializes the annotated object that this {@code
> CommandLine}
> >> > was
> >> > > constructed with as well as
> >> > > +     * possibly any registered commands, based on the specified
> >> command
> >> > > line arguments,
> >> > > +     * and returns a list of all commands and subcommands that were
> >> > > initialized by this method.
> >> > > +     * </p>
> >> > > +     *
> >> > > +     * @param args the command line arguments to parse
> >> > > +     * @return a list with all commands and subcommands initialized
> >> by
> >> > > this method
> >> > > +     * @throws ParameterException if the specified command line
> >> > arguments
> >> > > are invalid
> >> > > +     */
> >> > > +    public List<CommandLine> parse(String... args) {
> >> > > +        return interpreter.parse(args);
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * Equivalent to {@code new CommandLine(command).usage(out)}.
> >> See
> >> > > {@link #usage(PrintStream)} for details.
> >> > > +     * @param command the object annotated with {@link Command},
> >> {@link
> >> > > Option} and {@link Parameters}
> >> > > +     * @param out the print stream to print the help message to
> >> > > +     * @throws IllegalArgumentException if the specified command
> >> object
> >> > > does not have a {@link Command}, {@link Option} or {@link
> Parameters}
> >> > > annotation
> >> > > +     */
> >> > > +    public static void usage(Object command, PrintStream out) {
> >> > > +        toCommandLine(command).usage(out);
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * Equivalent to {@code new CommandLine(command).usage(out,
> >> ansi)}.
> >> > > +     * See {@link #usage(PrintStream, Help.Ansi)} for details.
> >> > > +     * @param command the object annotated with {@link Command},
> >> {@link
> >> > > Option} and {@link Parameters}
> >> > > +     * @param out the print stream to print the help message to
> >> > > +     * @param ansi whether the usage message should contain ANSI
> >> escape
> >> > > codes or not
> >> > > +     * @throws IllegalArgumentException if the specified command
> >> object
> >> > > does not have a {@link Command}, {@link Option} or {@link
> Parameters}
> >> > > annotation
> >> > > +     */
> >> > > +    public static void usage(Object command, PrintStream out,
> >> Help.Ansi
> >> > > ansi) {
> >> > > +        toCommandLine(command).usage(out, ansi);
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * Equivalent to {@code new CommandLine(command).usage(out,
> >> > > colorScheme)}.
> >> > > +     * See {@link #usage(PrintStream, Help.ColorScheme)} for
> details.
> >> > > +     * @param command the object annotated with {@link Command},
> >> {@link
> >> > > Option} and {@link Parameters}
> >> > > +     * @param out the print stream to print the help message to
> >> > > +     * @param colorScheme the {@code ColorScheme} defining the
> styles
> >> > for
> >> > > options, parameters and commands when ANSI is enabled
> >> > > +     * @throws IllegalArgumentException if the specified command
> >> object
> >> > > does not have a {@link Command}, {@link Option} or {@link
> Parameters}
> >> > > annotation
> >> > > +     */
> >> > > +    public static void usage(Object command, PrintStream out,
> >> > > Help.ColorScheme colorScheme) {
> >> > > +        toCommandLine(command).usage(out, colorScheme);
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * Delegates to {@link #usage(PrintStream, Help.Ansi)} with the
> >> > > {@linkplain Help.Ansi#AUTO platform default}.
> >> > > +     * @param out the printStream to print to
> >> > > +     * @see #usage(PrintStream, Help.ColorScheme)
> >> > > +     */
> >> > > +    public void usage(PrintStream out) {
> >> > > +        usage(out, Help.Ansi.AUTO);
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * Delegates to {@link #usage(PrintStream, Help.ColorScheme)}
> >> with
> >> > > the {@linkplain Help#defaultColorScheme(CommandLine.Help.Ansi)
> >> default
> >> > > color scheme}.
> >> > > +     * @param out the printStream to print to
> >> > > +     * @param ansi whether the usage message should include ANSI
> >> escape
> >> > > codes or not
> >> > > +     * @see #usage(PrintStream, Help.ColorScheme)
> >> > > +     */
> >> > > +    public void usage(PrintStream out, Help.Ansi ansi) {
> >> > > +        usage(out, Help.defaultColorScheme(ansi));
> >> > > +    }
> >> > > +    /**
> >> > > +     * Prints a usage help message for the annotated command class
> to
> >> > the
> >> > > specified {@code PrintStream}.
> >> > > +     * Delegates construction of the usage help message to the
> {@link
> >> > > Help} inner class and is equivalent to:
> >> > > +     * <pre>
> >> > > +     * Help help = new Help(command).addAllSubcommands(
> >> > getSubcommands());
> >> > > +     * StringBuilder sb = new StringBuilder()
> >> > > +     *         .append(help.headerHeading())
> >> > > +     *         .append(help.header())
> >> > > +     *         .append(help.synopsisHeading())      //e.g. Usage:
> >> > > +     *         .append(help.synopsis())             //e.g. &lt;main
> >> > > class&gt; [OPTIONS] &lt;command&gt; [COMMAND-OPTIONS] [ARGUMENTS]
> >> > > +     *         .append(help.descriptionHeading())   //e.g.
> >> > > %nDescription:%n%n
> >> > > +     *         .append(help.description())          //e.g.
> {"Converts
> >> > > foos to bars.", "Use options to control conversion mode."}
> >> > > +     *         .append(help.parameterListHeading()) //e.g.
> >> %nPositional
> >> > > parameters:%n%n
> >> > > +     *         .append(help.parameterList())        //e.g.
> >> [FILE...] the
> >> > > files to convert
> >> > > +     *         .append(help.optionListHeading())    //e.g.
> >> > %nOptions:%n%n
> >> > > +     *         .append(help.optionList())           //e.g. -h,
> --help
> >> > >  displays this help and exits
> >> > > +     *         .append(help.commandListHeading())   //e.g.
> >> > > %nCommands:%n%n
> >> > > +     *         .append(help.commandList())          //e.g.    add
> >> > >  adds the frup to the frooble
> >> > > +     *         .append(help.footerHeading())
> >> > > +     *         .append(help.footer());
> >> > > +     * out.print(sb);
> >> > > +     * </pre>
> >> > > +     * <p>Annotate your class with {@link Command} to control many
> >> > > aspects of the usage help message, including
> >> > > +     * the program name, text of section headings and section
> >> contents,
> >> > > and some aspects of the auto-generated sections
> >> > > +     * of the usage help message.
> >> > > +     * <p>To customize the auto-generated sections of the usage
> help
> >> > > message, like how option details are displayed,
> >> > > +     * instantiate a {@link Help} object and use a {@link
> >> > Help.TextTable}
> >> > > with more of fewer columns, a custom
> >> > > +     * {@linkplain Help.Layout layout}, and/or a custom option
> >> > > {@linkplain Help.IOptionRenderer renderer}
> >> > > +     * for ultimate control over which aspects of an Option or
> Field
> >> are
> >> > > displayed where.</p>
> >> > > +     * @param out the {@code PrintStream} to print the usage help
> >> > message
> >> > > to
> >> > > +     * @param colorScheme the {@code ColorScheme} defining the
> styles
> >> > for
> >> > > options, parameters and commands when ANSI is enabled
> >> > > +     */
> >> > > +    public void usage(PrintStream out, Help.ColorScheme
> colorScheme)
> >> {
> >> > > +        Help help = new Help(interpreter.command, colorScheme).
> >> > > addAllSubcommands(getSubcommands());
> >> > > +        StringBuilder sb = new StringBuilder()
> >> > > +                .append(help.headerHeading())
> >> > > +                .append(help.header())
> >> > > +                .append(help.synopsisHeading())      //e.g. Usage:
> >> > > +                .append(help.synopsis(help.
> synopsisHeadingLength()))
> >> > > //e.g. &lt;main class&gt; [OPTIONS] &lt;command&gt;
> [COMMAND-OPTIONS]
> >> > > [ARGUMENTS]
> >> > > +                .append(help.descriptionHeading())   //e.g.
> >> > > %nDescription:%n%n
> >> > > +                .append(help.description())          //e.g.
> >> {"Converts
> >> > > foos to bars.", "Use options to control conversion mode."}
> >> > > +                .append(help.parameterListHeading()) //e.g.
> >> > %nPositional
> >> > > parameters:%n%n
> >> > > +                .append(help.parameterList())        //e.g.
> [FILE...]
> >> > the
> >> > > files to convert
> >> > > +                .append(help.optionListHeading())    //e.g.
> >> > > %nOptions:%n%n
> >> > > +                .append(help.optionList())           //e.g. -h,
> >> --help
> >> > >  displays this help and exits
> >> > > +                .append(help.commandListHeading())   //e.g.
> >> > > %nCommands:%n%n
> >> > > +                .append(help.commandList())          //e.g.    add
> >> > >  adds the frup to the frooble
> >> > > +                .append(help.footerHeading())
> >> > > +                .append(help.footer());
> >> > > +        out.print(sb);
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * Delegates to {@link #printVersionHelp(PrintStream,
> Help.Ansi)}
> >> > > with the {@linkplain Help.Ansi#AUTO platform default}.
> >> > > +     * @param out the printStream to print to
> >> > > +     * @see #printVersionHelp(PrintStream, Help.Ansi)
> >> > > +     */
> >> > > +    public void printVersionHelp(PrintStream out) {
> >> > printVersionHelp(out,
> >> > > Help.Ansi.AUTO); }
> >> > > +
> >> > > +    /**
> >> > > +     * Prints version information from the {@link
> Command#version()}
> >> > > annotation to the specified {@code PrintStream}.
> >> > > +     * Each element of the array of version strings is printed on a
> >> > > separate line. Version strings may contain
> >> > > +     * <a href="http://picocli.info/#_us
> >> age_help_with_styles_and_colors
> >> > ">markup
> >> > > for colors and style</a>.
> >> > > +     * @param out the printStream to print to
> >> > > +     * @param ansi whether the usage message should include ANSI
> >> escape
> >> > > codes or not
> >> > > +     * @see Command#version()
> >> > > +     * @see Option#versionHelp()
> >> > > +     * @see #isVersionHelpRequested()
> >> > > +     */
> >> > > +    public void printVersionHelp(PrintStream out, Help.Ansi ansi) {
> >> > > +        for (String versionInfo : versionLines) {
> >> > > +            out.println(ansi.new Text(versionInfo));
> >> > > +        }
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * Delegates to {@link #run(Runnable, PrintStream, Help.Ansi,
> >> > > String...)} with {@link Help.Ansi#AUTO}.
> >> > > +     * @param command the command to run when {@linkplain
> >> > > #populateCommand(Object, String...) parsing} succeeds.
> >> > > +     * @param out the printStream to print to
> >> > > +     * @param args the command line arguments to parse
> >> > > +     * @param <R> the annotated object must implement Runnable
> >> > > +     * @see #run(Runnable, PrintStream, Help.Ansi, String...)
> >> > > +     * @throws IllegalArgumentException if the specified command
> >> object
> >> > > does not have a {@link Command}, {@link Option} or {@link
> Parameters}
> >> > > annotation
> >> > > +     */
> >> > > +    public static <R extends Runnable> void run(R command,
> >> PrintStream
> >> > > out, String... args) {
> >> > > +        run(command, out, Help.Ansi.AUTO, args);
> >> > > +    }
> >> > > +    /**
> >> > > +     * Convenience method to allow command line application authors
> >> to
> >> > > avoid some boilerplate code in their application.
> >> > > +     * The annotated object needs to implement {@link Runnable}.
> >> Calling
> >> > > this method is equivalent to:
> >> > > +     * <pre>
> >> > > +     * CommandLine cmd = new CommandLine(command);
> >> > > +     * try {
> >> > > +     *     cmd.parse(args);
> >> > > +     * } catch (Exception ex) {
> >> > > +     *     System.err.println(ex.getMessage());
> >> > > +     *     cmd.usage(out, ansi);
> >> > > +     *     return;
> >> > > +     * }
> >> > > +     * command.run();
> >> > > +     * </pre>
> >> > > +     * Note that this method is not suitable for commands with
> >> > > subcommands.
> >> > > +     * @param command the command to run when {@linkplain
> >> > > #populateCommand(Object, String...) parsing} succeeds.
> >> > > +     * @param out the printStream to print to
> >> > > +     * @param ansi whether the usage message should include ANSI
> >> escape
> >> > > codes or not
> >> > > +     * @param args the command line arguments to parse
> >> > > +     * @param <R> the annotated object must implement Runnable
> >> > > +     * @throws IllegalArgumentException if the specified command
> >> object
> >> > > does not have a {@link Command}, {@link Option} or {@link
> Parameters}
> >> > > annotation
> >> > > +     */
> >> > > +    public static <R extends Runnable> void run(R command,
> >> PrintStream
> >> > > out, Help.Ansi ansi, String... args) {
> >> > > +        CommandLine cmd = new CommandLine(command); // validate
> >> command
> >> > > outside of try-catch
> >> > > +        try {
> >> > > +            cmd.parse(args);
> >> > > +        } catch (Exception ex) {
> >> > > +            out.println(ex.getMessage());
> >> > > +            cmd.usage(out, ansi);
> >> > > +            return;
> >> > > +        }
> >> > > +        command.run();
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * Registers the specified type converter for the specified
> >> class.
> >> > > When initializing fields annotated with
> >> > > +     * {@link Option}, the field's type is used as a lookup key to
> >> find
> >> > > the associated type converter, and this
> >> > > +     * type converter converts the original command line argument
> >> string
> >> > > value to the correct type.
> >> > > +     * <p>
> >> > > +     * Java 8 lambdas make it easy to register custom type
> >> converters:
> >> > > +     * </p>
> >> > > +     * <pre>
> >> > > +     * commandLine.registerConverter(java.nio.file.Path.class, s
> >> -&gt;
> >> > > java.nio.file.Paths.get(s));
> >> > > +     * commandLine.registerConverter(java.time.Duration.class, s
> >> -&gt;
> >> > > java.time.Duration.parse(s));</pre>
> >> > > +     * <p>
> >> > > +     * Built-in type converters are pre-registered for the
> following
> >> > java
> >> > > 1.5 types:
> >> > > +     * </p>
> >> > > +     * <ul>
> >> > > +     *   <li>all primitive types</li>
> >> > > +     *   <li>all primitive wrapper types: Boolean, Byte, Character,
> >> > > Double, Float, Integer, Long, Short</li>
> >> > > +     *   <li>any enum</li>
> >> > > +     *   <li>java.io.File</li>
> >> > > +     *   <li>java.math.BigDecimal</li>
> >> > > +     *   <li>java.math.BigInteger</li>
> >> > > +     *   <li>java.net.InetAddress</li>
> >> > > +     *   <li>java.net.URI</li>
> >> > > +     *   <li>java.net.URL</li>
> >> > > +     *   <li>java.nio.charset.Charset</li>
> >> > > +     *   <li>java.sql.Time</li>
> >> > > +     *   <li>java.util.Date</li>
> >> > > +     *   <li>java.util.UUID</li>
> >> > > +     *   <li>java.util.regex.Pattern</li>
> >> > > +     *   <li>StringBuilder</li>
> >> > > +     *   <li>CharSequence</li>
> >> > > +     *   <li>String</li>
> >> > > +     * </ul>
> >> > > +     * <p>The specified converter will be registered with this
> {@code
> >> > > CommandLine} and the full hierarchy of its
> >> > > +     * subcommands and nested sub-subcommands <em>at the moment the
> >> > > converter is registered</em>. Subcommands added
> >> > > +     * later will not have this converter added automatically. To
> >> ensure
> >> > > a custom type converter is available to all
> >> > > +     * subcommands, register the type converter last, after adding
> >> > > subcommands.</p>
> >> > > +     *
> >> > > +     * @param cls the target class to convert parameter string
> >> values to
> >> > > +     * @param converter the class capable of converting string
> >> values to
> >> > > the specified target type
> >> > > +     * @param <K> the target type
> >> > > +     * @return this CommandLine object, to allow method chaining
> >> > > +     * @see #addSubcommand(String, Object)
> >> > > +     */
> >> > > +    public <K> CommandLine registerConverter(Class<K> cls,
> >> > > ITypeConverter<K> converter) {
> >> > > +        interpreter.converterRegistry.put(Assert.notNull(cls,
> >> "class"),
> >> > > Assert.notNull(converter, "converter"));
> >> > > +        for (CommandLine command : interpreter.commands.values()) {
> >> > > +            command.registerConverter(cls, converter);
> >> > > +        }
> >> > > +        return this;
> >> > > +    }
> >> > > +
> >> > > +    /** Returns the String that separates option names from option
> >> > values
> >> > > when parsing command line options. {@code '='} by default.
> >> > > +     * @return the String the parser uses to separate option names
> >> from
> >> > > option values */
> >> > > +    public String getSeparator() {
> >> > > +        return interpreter.separator;
> >> > > +    }
> >> > > +
> >> > > +    /** Sets the String the parser uses to separate option names
> from
> >> > > option values to the specified value.
> >> > > +     * @param separator the String that separates option names from
> >> > > option values */
> >> > > +    public void setSeparator(String separator) {
> >> > > +        interpreter.separator = Assert.notNull(separator,
> >> "separator");
> >> > > +    }
> >> > > +    private static boolean empty(String str) { return str == null
> ||
> >> > > str.trim().length() == 0; }
> >> > > +    private static boolean empty(Object[] array) { return array ==
> >> null
> >> > > || array.length == 0; }
> >> > > +    private static boolean empty(Text txt) { return txt == null ||
> >> > > txt.plain.toString().trim().length() == 0; }
> >> > > +    private static String str(String[] arr, int i) { return (arr ==
> >> null
> >> > > || arr.length == 0) ? "" : arr[i]; }
> >> > > +    private static boolean isBoolean(Class<?> type) { return type
> ==
> >> > > Boolean.class || type == Boolean.TYPE; }
> >> > > +    private static CommandLine toCommandLine(Object obj) { return
> obj
> >> > > instanceof CommandLine ? (CommandLine) obj : new CommandLine(obj);}
> >> > > +    /**
> >> > > +     * <p>
> >> > > +     * Annotate fields in your class with {@code @Option} and
> picocli
> >> > > will initialize these fields when matching
> >> > > +     * arguments are specified on the command line.
> >> > > +     * </p><p>
> >> > > +     * For example:
> >> > > +     * </p>
> >> > > +     * <pre>import static picocli.CommandLine.*;
> >> > > +     *
> >> > > +     * public class MyClass {
> >> > > +     *     &#064;Parameters(type = File.class, description = "Any
> >> number
> >> > > of input files")
> >> > > +     *     private List&lt;File&gt; files = new
> >> ArrayList&lt;File&gt;();
> >> > > +     *
> >> > > +     *     &#064;Option(names = { "-o", "--out" }, description =
> >> "Output
> >> > > file (default: print to console)")
> >> > > +     *     private File outputFile;
> >> > > +     *
> >> > > +     *     &#064;Option(names = { "-v", "--verbose"}, description =
> >> > > "Verbosely list files processed")
> >> > > +     *     private boolean verbose;
> >> > > +     *
> >> > > +     *     &#064;Option(names = { "-h", "--help", "-?", "-help"},
> >> help =
> >> > > true, description = "Display this help and exit")
> >> > > +     *     private boolean help;
> >> > > +     *
> >> > > +     *     &#064;Option(names = { "-V", "--version"}, help = true,
> >> > > description = "Display version information and exit")
> >> > > +     *     private boolean version;
> >> > > +     * }
> >> > > +     * </pre>
> >> > > +     * <p>
> >> > > +     * A field cannot be annotated with both {@code @Parameters}
> and
> >> > > {@code @Option} or a
> >> > > +     * {@code ParameterException} is thrown.
> >> > > +     * </p>
> >> > > +     */
> >> > > +    @Retention(RetentionPolicy.RUNTIME)
> >> > > +    @Target(ElementType.FIELD)
> >> > > +    public @interface Option {
> >> > > +        /**
> >> > > +         * One or more option names. At least one option name is
> >> > required.
> >> > > +         * <p>
> >> > > +         * 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.
> >> > > +         * </p><p>
> >> > > +         * <b>*nix</b>
> >> > > +         * </p><p>
> >> > > +         * In Unix and Linux, options have a short
> (single-character)
> >> > > name, a long name or both.
> >> > > +         * Short options
> >> > > +         * (<a href="http://pubs.opengroup.or
> >> g/onlinepubs/9699919799/
> >> > > basedefs/V1_chap12.html#tag_12_02">POSIX
> >> > > +         * style</a> are single-character and are preceded by the
> >> {@code
> >> > > '-'} character, e.g., {@code `-v'}.
> >> > > +         * <a href="https://www.gnu.org/soft
> >> ware/tar/manual/html_node/
> >> > > Long-Options.html">GNU-style</a> long
> >> > > +         * (or <em>mnemonic</em>) options start with two dashes in
> a
> >> > row,
> >> > > e.g., {@code `--file'}.
> >> > > +         * </p><p>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:
> >> > > +         * </p><pre>
> >> > > +         * -xvfFILE
> >> > > +         * -xvf FILE
> >> > > +         * -xvf=FILE
> >> > > +         * -xv --file FILE
> >> > > +         * -xv --file=FILE
> >> > > +         * -x -v --file FILE
> >> > > +         * -x -v --file=FILE
> >> > > +         * </pre><p>
> >> > > +         * <b>DOS</b>
> >> > > +         * </p><p>
> >> > > +         * 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:
> >> > > +         * </p><pre>
> >> > > +         * DIR /S /A:D /T:C
> >> > > +         * </pre><p>
> >> > > +         * <b>PowerShell</b>
> >> > > +         * </p><p>
> >> > > +         * 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.
> >> > > +         * </p>
> >> > > +         * @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.
> >> > > +         * <p>
> >> > > +         * 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).
> >> > > +         * </p>
> >> > > +         * <p>
> >> > > +         * 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.
> >> > > +         * </p>
> >> > > +         * @return whether this option disables validation of the
> >> other
> >> > > arguments
> >> > > +         */
> >> > > +        boolean help() default false;
> >> > > +
> >> > > +        /**
> >> > > +         * Set {@code usageHelp=true} if this option allows the
> user
> >> to
> >> > > request usage help. 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}.
> >> > > +         * <p>
> >> > > +         * This attribute is useful for special options like help
> >> > ({@code
> >> > > -h} and {@code --help} on unix,
> >> > > +         * {@code -?} and {@code -Help} on Windows).
> >> > > +         * </p>
> >> > > +         * <p>
> >> > > +         * Note that the {@link #parse(String...)} method will not
> >> print
> >> > > usage 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.
> >> > > +         * </p>
> >> > > +         * @return whether this option allows the user to request
> >> usage
> >> > > help
> >> > > +         */
> >> > > +        boolean usageHelp() default false;
> >> > > +
> >> > > +        /**
> >> > > +         * Set {@code versionHelp=true} if this option allows the
> >> user
> >> > to
> >> > > request version information. 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#
> >> > isVersionHelpRequested()}
> >> > > method will return {@code true}.
> >> > > +         * <p>
> >> > > +         * This attribute is useful for special options like
> version
> >> > > ({@code -V} and {@code --version} on unix,
> >> > > +         * {@code -Version} on Windows).
> >> > > +         * </p>
> >> > > +         * <p>
> >> > > +         * Note that the {@link #parse(String...)} method will not
> >> print
> >> > > version information. 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.
> >> > > +         * </p>
> >> > > +         * @return whether this option allows the user to request
> >> > version
> >> > > information
> >> > > +         */
> >> > > +        boolean versionHelp() default false;
> >> > > +
> >> > > +        /**
> >> > > +         * Description of this option, used when generating the
> usage
> >> > > documentation.
> >> > > +         * @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.
> >> > > +         * <p>
> >> > > +         * 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.
> >> > > +         * </p><p>
> >> > > +         * 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.
> >> > > +         * </p><p>
> >> > > +         * For example, if an option has 2 required parameters and
> >> any
> >> > > number of optional parameters,
> >> > > +         * specify {@code @Option(names = "-example", arity =
> >> "2..*")}.
> >> > > +         * </p>
> >> > > +         * <b>A note on boolean options</b>
> >> > > +         * <p>
> >> > > +         * 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: </p>
> >> > > +         * <pre>&#064;Option(names = "-v", arity = "1") boolean
> >> > > verbose;</pre>
> >> > > +         * <p>
> >> > > +         * Because this boolean field is defined with arity 1, the
> >> user
> >> > > must specify either {@code <program> -v false}
> >> > > +         * or {@code <program> -v true}
> >> > > +         * on the command line, or a {@link
> >> MissingParameterException}
> >> > is
> >> > > thrown by the {@link #parse(String...)}
> >> > > +         * method.
> >> > > +         * </p><p>
> >> > > +         * To make the boolean parameter possible but optional,
> >> define
> >> > > the field with {@code arity = "0..1"}.
> >> > > +         * For example: </p>
> >> > > +         * <pre>&#064;Option(names="-v", arity="0..1") boolean
> >> > > verbose;</pre>
> >> > > +         * <p>This will accept any of the below without throwing an
> >> > > exception:</p>
> >> > > +         * <pre>
> >> > > +         * -v
> >> > > +         * -v true
> >> > > +         * -v false
> >> > > +         * </pre>
> >> > > +         * @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:
> >> > > +         * <pre>class Example {
> >> > > +         *     &#064;Option(names = {"-o", "--output"},
> >> > > paramLabel="FILE", description="path of the output file")
> >> > > +         *     private File out;
> >> > > +         *     &#064;Option(names = {"-j", "--jobs"}, arity="0..1",
> >> > > description="Allow N jobs at once; infinite jobs with no arg.")
> >> > > +         *     private int maxJobs = -1;
> >> > > +         * }</pre>
> >> > > +         * <p>By default, the above gives a usage help message like
> >> the
> >> > > following:</p><pre>
> >> > > +         * Usage: &lt;main class&gt; [OPTIONS]
> >> > > +         * -o, --output FILE       path of the output file
> >> > > +         * -j, --jobs [&lt;maxJobs&gt;]  Allow N jobs at once;
> >> infinite
> >> > > jobs with no arg.
> >> > > +         * </pre>
> >> > > +         * @return name of the option parameter used in the usage
> >> help
> >> > > message
> >> > > +         */
> >> > > +        String paramLabel() default "";
> >> > > +
> >> > > +        /**
> >> > > +         * <p>
> >> > > +         * Specify a {@code type} if the annotated field is a
> {@code
> >> > > Collection} that should hold objects other than Strings.
> >> > > +         * </p><p>
> >> > > +         * If the field's type is a {@code Collection}, the generic
> >> type
> >> > > parameter of the collection is erased and
> >> > > +         * cannot be determined at runtime. Specify a {@code type}
> >> > > attribute to store values other than String in
> >> > > +         * the Collection. Picocli will use the {@link
> >> ITypeConverter}
> >> > > +         * that is {@linkplain #registerConverter(Class,
> >> ITypeConverter)
> >> > > registered} for that type to convert
> >> > > +         * the raw String values before they are added to the
> >> > collection.
> >> > > +         * </p><p>
> >> > > +         * When the field's type is an array, the {@code type}
> >> attribute
> >> > > is ignored: the values will be converted
> >> > > +         * to the array component type and the array will be
> replaced
> >> > > with a new instance containing both the old and
> >> > > +         * the new values. </p>
> >> > > +         * @return the type to convert the raw String values to
> >> before
> >> > > adding them to the Collection
> >> > > +         */
> >> > > +        Class<?> type() default String.class;
> >> > > +
> >> > > +        /**
> >> > > +         * 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 documentation.
> >> > > +         * @return whether this option should be excluded from the
> >> usage
> >> > > message
> >> > > +         */
> >> > > +        boolean hidden() default false;
> >> > > +    }
> >> > > +    /**
> >> > > +     * <p>
> >> > > +     * Fields annotated with {@code @Parameters} will be
> initialized
> >> > with
> >> > > positional parameters. By specifying the
> >> > > +     * {@link #index()} attribute you can pick which (or what
> range)
> >> of
> >> > > the positional parameters to apply. If no index
> >> > > +     * is specified, the field will get all positional parameters
> >> (so it
> >> > > should be an array or a collection).
> >> > > +     * </p><p>
> >> > > +     * When parsing the command line arguments, picocli first tries
> >> to
> >> > > match arguments to {@link Option Options}.
> >> > > +     * Positional parameters are the arguments that follow the
> >> options,
> >> > > or the arguments that follow a "--" (double
> >> > > +     * dash) argument on the command line.
> >> > > +     * </p><p>
> >> > > +     * For example:
> >> > > +     * </p>
> >> > > +     * <pre>import static picocli.CommandLine.*;
> >> > > +     *
> >> > > +     * public class MyCalcParameters {
> >> > > +     *     &#064;Parameters(type = BigDecimal.class, description =
> >> "Any
> >> > > number of input numbers")
> >> > > +     *     private List&lt;BigDecimal&gt; files = new
> >> > > ArrayList&lt;BigDecimal&gt;();
> >> > > +     *
> >> > > +     *     &#064;Option(names = { "-h", "--help", "-?", "-help"},
> >> help =
> >> > > true, description = "Display this help and exit")
> >> > > +     *     private boolean help;
> >> > > +     * }
> >> > > +     * </pre><p>
> >> > > +     * A field cannot be annotated with both {@code @Parameters}
> and
> >> > > {@code @Option} or a {@code ParameterException}
> >> > > +     * is thrown.</p>
> >> > > +     */
> >> > > +    @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.
> >> > > +         * @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.
> >> > > +         * <p>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.</p>
> >> > > +         * @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:
> >> > > +         * <pre>class Example {
> >> > > +         *     &#064;Parameters(paramLabel="FILE",
> >> description="path of
> >> > > the input FILE(s)")
> >> > > +         *     private File[] inputFiles;
> >> > > +         * }</pre>
> >> > > +         * <p>By default, the above gives a usage help message like
> >> the
> >> > > following:</p><pre>
> >> > > +         * Usage: &lt;main class&gt; [FILE...]
> >> > > +         * [FILE...]       path of the input FILE(s)
> >> > > +         * </pre>
> >> > > +         * @return name of the positional parameter used in the
> usage
> >> > > help message
> >> > > +         */
> >> > > +        String paramLabel() default "";
> >> > > +
> >> > > +        /**
> >> > > +         * <p>
> >> > > +         * Specify a {@code type} if the annotated field is a
> {@code
> >> > > Collection} that should hold objects other than Strings.
> >> > > +         * </p><p>
> >> > > +         * If the field's type is a {@code Collection}, the generic
> >> type
> >> > > parameter of the collection is erased and
> >> > > +         * cannot be determined at runtime. Specify a {@code type}
> >> > > attribute to store values other than String in
> >> > > +         * the Collection. Picocli will use the {@link
> >> ITypeConverter}
> >> > > +         * that is {@linkplain #registerConverter(Class,
> >> ITypeConverter)
> >> > > registered} for that type to convert
> >> > > +         * the raw String values before they are added to the
> >> > collection.
> >> > > +         * </p><p>
> >> > > +         * When the field's type is an array, the {@code type}
> >> attribute
> >> > > is ignored: the values will be converted
> >> > > +         * to the array component type and the array will be
> replaced
> >> > > with a new instance containing both the old and
> >> > > +         * the new values. </p>
> >> > > +         * @return the type to convert the raw String values to
> >> before
> >> > > adding them to the Collection
> >> > > +         */
> >> > > +        Class<?> type() default String.class;
> >> > > +
> >> > > +        /**
> >> > > +         * 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;
> >> > > +    }
> >> > > +
> >> > > +    /**
> >> > > +     * <p>Annotate your class with {@code @Command} when you want
> >> more
> >> > > control over the format of the generated help
> >> > > +     * message.
> >> > > +     * </p><pre>
> >> > > +     * &#064;Command(name      = "Encrypt",
> >> > > +     *        description = "Encrypt FILE(s), or standard input, to
> >> > > standard output or to the output file.",
> >> > > +     *        footer      = "Copyright (c) 2017")
> >> > > +     * public class Encrypt {
> >> > > +     *     &#064;Parameters(paramLabel = "FILE", type = File.class,
> >> > > description = "Any number of input files")
> >> > > +     *     private List&lt;File&gt; files     = new
> >> > > ArrayList&lt;File&gt;();
> >> > > +     *
> >> > > +     *     &#064;Option(names = { "-o", "--out" }, description =
> >> "Output
> >> > > file (default: print to console)")
> >> > > +     *     private File outputFile;
> >> > > +     * }</pre>
> >> > > +     * <p>
> >> > > +     * The structure of a help message looks like this:
> >> > > +     * </p><ul>
> >> > > +     *   <li>[header]</li>
> >> > > +     *   <li>[synopsis]: {@code Usage: <commandName> [OPTIONS]
> >> > > [FILE...]}</li>
> >> > > +     *   <li>[description]</li>
> >> > > +     *   <li>[parameter list]: {@code      [FILE...]   Any number
> of
> >> > > input files}</li>
> >> > > +     *   <li>[option list]: {@code   -h, --help   prints this help
> >> > > message and exits}</li>
> >> > > +     *   <li>[footer]</li>
> >> > > +     * </ul> */
> >> > > +    @Retention(RetentionPolicy.RUNTIME)
> >> > > +    @Target(ElementType.TYPE)
> >> > > +    public @interface Command {
> >> > > +        /** Program name to show in the synopsis. If omitted,
> {@code
> >> > > "<main class>"} 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 Help#commandName */
> >> > > +        String name() default "<main class>";
> >> > > +
> >> > > +        /** 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:
> >> > > +         * <pre>
> >> > > +         * &#064;Command(subcommands = {
> >> > > +         *         GitStatus.class,
> >> > > +         *         GitCommit.class,
> >> > > +         *         GitBranch.class })
> >> > > +         * public class Git { ... }
> >> > > +         *
> >> > > +         * CommandLine commandLine = new CommandLine(new Git());
> >> > > +         * </pre> is equivalent to this:
> >> > > +         * <pre>
> >> > > +         * // alternative: programmatically add subcommands.
> >> > > +         * // NOTE: in this case there should be no `subcommands`
> >> > > attribute on the @Command annotation.
> >> > > +         * &#064;Command public class Git { ... }
> >> > > +         *
> >> > > +         * CommandLine commandLine = new CommandLine(new Git())
> >> > > +         *         .addSubcommand("status",   new GitStatus())
> >> > > +         *         .addSubcommand("commit",   new GitCommit())
> >> > > +         *         .addSubcommand("branch",   new GitBranch());
> >> > > +         * </pre>
> >> > > +         * @return the declaratively registered subcommands of this
> >> > > command, or an empty array if none
> >> > > +         * @see CommandLine#addSubcommand(String, Object)
> >> > > +         * @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 Help#separator
> >> > > +         * @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.
> >> > > +         * @since 0.9.8
> >> > > +         * @see CommandLine#printVersionHelp(PrintStream)
> >> > > +         */
> >> > > +        String[] version() default {};
> >> > > +
> >> > > +        /** Set the heading preceding the header section. May
> contain
> >> > > embedded {@linkplain java.util.Formatter format specifiers}.
> >> > > +         * @return the heading preceding the header section
> >> > > +         * @see Help#headerHeading(Object...)  */
> >> > > +        String headerHeading() default "";
> >> > > +
> >> > > +        /** Optional summary description of the command, shown
> before
> >> > the
> >> > > synopsis.
> >> > > +         * @return summary description of the command
> >> > > +         * @see Help#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 "<main> [OPTIONS] [PARAMETERS...]"}.
> >> > > +         * By default, a detailed synopsis with individual option
> >> names
> >> > > and parameters is generated.
> >> > > +         * @return whether the synopsis should be abbreviated
> >> > > +         * @see Help#abbreviateSynopsis
> >> > > +         * @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
> >> > > +         * @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
> >> > > +         * @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.
> >> > > +         * @see Help#sortOptions */
> >> > > +        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
> >> > > +         * @see Help#requiredOptionMarker */
> >> > > +        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.
> >> > > +         * @return whether the default values for options and
> >> parameters
> >> > > should be shown in the description column
> >> > > +         * @see Help#showDefaultValues */
> >> > > +        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
> >> > > +         * @see Help#footer(Object...) */
> >> > > +        String[] footer() default {};
> >> > > +    }
> >> > > +    /**
> >> > > +     * <p>
> >> > > +     * 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.
> >> > > +     * </p><p>
> >> > > +     * 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.
> >> > > +     * </p><p>
> >> > > +     * Java 8 lambdas make it easy to register custom type
> >> converters:
> >> > > +     * </p>
> >> > > +     * <pre>
> >> > > +     * commandLine.registerConverter(java.nio.file.Path.class, s
> >> -&gt;
> >> > > java.nio.file.Paths.get(s));
> >> > > +     * commandLine.registerConverter(java.time.Duration.class, s
> >> -&gt;
> >> > > java.time.Duration.parse(s));</pre>
> >> > > +     * <p>
> >> > > +     * Built-in type converters are pre-registered for the
> following
> >> > java
> >> > > 1.5 types:
> >> > > +     * </p>
> >> > > +     * <ul>
> >> > > +     *   <li>all primitive types</li>
> >> > > +     *   <li>all primitive wrapper types: Boolean, Byte, Character,
> >> > > Double, Float, Integer, Long, Short</li>
> >> > > +     *   <li>any enum</li>
> >> > > +     *   <li>java.io.File</li>
> >> > > +     *   <li>java.math.BigDecimal</li>
> >> > > +     *   <li>java.math.BigInteger</li>
> >> > > +     *   <li>java.net.InetAddress</li>
> >> > > +     *   <li>java.net.URI</li>
> >> > > +     *   <li>java.net.URL</li>
> >> > > +     *   <li>java.nio.charset.Charset</li>
> >> > > +     *   <li>java.sql.Time</li>
> >> > > +     *   <li>java.util.Date</li>
> >> > > +     *   <li>java.util.UUID</li>
> >> > > +     *   <li>java.util.regex.Pattern</li>
> >> > > +     *   <li>StringBuilder</li>
> >> > > +     *   <li>CharSequence</li>
> >> > > +     *   <li>String</li>
> >> > > +     * </ul>
> >> > > +     * @param <K> the type of the object that is the result of the
> >> > > conversion
> >> > > +     */
> >> > > +    public interface ITypeConverter<K> {
> >> > > +        /**
> >> > > +         * 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;
> >> > > +    }
> >> > > +    /** 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<Range> {
> >> > > +        /** 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) {
> >> > > +            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(fi
> >> eld.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.getType())
> >> > :
> >> > > result;
> >> > > +        }
> >> > > +        /** Returns a new {@code Range} based on the specified
> type:
> >> > > booleans have arity 0, arrays or Collections have
> >> > > +         * arity "0..*", and 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 */
> >> > > +        public static Range defaultArity(Class<?> type) {
> >> > > +            if (isBoolean(type)) {
> >> > > +                return Range.valueOf("0");
> >> > > +            } else if (type.isArray() || Collection.class.
> >> > isAssignableFrom(type))
> >> > > {
> >> > > +                return Range.valueOf("0..*");
> >> > > +            }
> >> > > +            return Range.valueOf("1");// for single-valued fields
> >> > > +        }
> >> > > +        /** 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); }
> >> > > +
> >> > > +        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 init(Class<?> cls,
> >> > > +                             List<Field> requiredFields,
> >> > > +                             Map<String, Field> optionName2Field,
> >> > > +                             Map<Character, Field>
> >> > singleCharOption2Field,
> >> > > +                             List<Field>
> positionalParametersFields)
> >> {
> >> > > +        Field[] declaredFields = cls.getDeclaredFields();
> >> > > +        for (Field field : declaredFields) {
> >> > > +            field.setAccessible(true);
> >> > > +            if (field.isAnnotationPresent(Option.class)) {
> >> > > +                Option option = field.getAnnotation(Option.class);
> >> > > +                if (option.required()) {
> >> > > +                    requiredFields.add(field);
> >> > > +                }
> >> > > +                for (String name : option.names()) { // cannot be
> >> null
> >> > or
> >> > > empty
> >> > > +                    Field existing = optionName2Field.put(name,
> >> field);
> >> > > +                    if (existing != null && existing != field) {
> >> > > +                        throw DuplicateOptionAnnotationsExce
> >> > ption.create(name,
> >> > > field, existing);
> >> > > +                    }
> >> > > +                    if (name.length() == 2 &&
> name.startsWith("-")) {
> >> > > +                        char flag = name.charAt(1);
> >> > > +                        Field existing2 =
> singleCharOption2Field.put(
> >> > flag,
> >> > > field);
> >> > > +                        if (existing2 != null && existing2 !=
> field)
> >> {
> >> > > +                            throw DuplicateOptionAnnotationsExce
> >> > ption.create(name,
> >> > > field, existing2);
> >> > > +                        }
> >> > > +                    }
> >> > > +                }
> >> > > +            }
> >> > > +            if (field.isAnnotationPresent(Parameters.class)) {
> >> > > +                if (field.isAnnotationPresent(Option.class)) {
> >> > > +                    throw new ParameterException("A field can be
> >> either
> >> > > @Option or @Parameters, but '"
> >> > > +                            + field.getName() + "' is both.");
> >> > > +                }
> >> > > +                positionalParametersFields.add(field);
> >> > > +                Range arity = Range.parameterArity(field);
> >> > > +                if (arity.min > 0) {
> >> > > +                    requiredFields.add(field);
> >> > > +                }
> >> > > +            }
> >> > > +        }
> >> > > +    }
> >> > > +    static void validatePositionalParameters(List<Field>
> >> > > positionalParametersFields) {
> >> > > +        int min = 0;
> >> > > +        for (Field field : positionalParametersFields) {
> >> > > +            Range index = Range.parameterIndex(field);
> >> > > +            if (index.min > min) {
> >> > > +                throw new ParameterIndexGapException("Missing
> field
> >> > > annotated with @Parameter(index=" + min +
> >> > > +                        "). Nearest field '" + field.getName() + "'
> >> has
> >> > > index=" + index.min);
> >> > > +            }
> >> > > +            min = Math.max(min, index.max);
> >> > > +            min = min == Integer.MAX_VALUE ? min : min + 1;
> >> > > +        }
> >> > > +    }
> >> > > +    private static <T> Stack<T> reverse(Stack<T> stack) {
> >> > > +        Collections.reverse(stack);
> >> > > +        return stack;
> >> > > +    }
> >> > > +    /**
> >> > > +     * Helper class responsible for processing command line
> >> arguments.
> >> > > +     */
> >> > > +    private class Interpreter {
> >> > > +        private final Map<String, CommandLine> commands
> >> > > = new LinkedHashMap<String, CommandLine>();
> >> > > +        private final Map<Class<?>, ITypeConverter<?>>
> >> converterRegistry
> >> > > = new HashMap<Class<?>, ITypeConverter<?>>();
> >> > > +        private final Map<String, Field> optionName2Field
> >> > > = new HashMap<String, Field>();
> >> > > +        private final Map<Character, Field> singleCharOption2Field
> >> > >  = new HashMap<Character, Field>();
> >> > > +        private final List<Field> requiredFields
> >> > >  = new ArrayList<Field>();
> >> > > +        private final List<Field> positionalParametersFields
> >> > >  = new ArrayList<Field>();
> >> > > +        private final Object command;
> >> > > +        private boolean isHelpRequested;
> >> > > +        private String separator = "=";
> >> > > +
> >> > > +        Interpreter(Object command) {
> >> > > +            converterRegistry.put(Path.class,          new
> >> > > BuiltIn.PathConverter());
> >> > > +            converterRegistry.put(String.class,        new
> >> > > BuiltIn.StringConverter());
> >> > > +            converterRegistry.put(StringBuilder.class, new
> BuiltIn.
> >> > > StringBuilderConverter());
> >> > > +            converterRegistry.put(CharSequence.class,  new
> >> > > BuiltIn.CharSequenceConverter());
> >> > > +            converterRegistry.put(Byte.class,          new
> >> > > BuiltIn.ByteConverter());
> >> > > +            converterRegistry.put(Byte.TYPE,           new
> >> > > BuiltIn.ByteConverter());
> >> > > +            converterRegistry.put(Boolean.class,       new
> >> > > BuiltIn.BooleanConverter());
> >> > > +            converterRegistry.put(Boolean.TYPE,        new
> >> > > BuiltIn.BooleanConverter());
> >> > > +            converterRegistry.put(Character.class,     new
> >> > > BuiltIn.CharacterConverter());
> >> > > +            converterRegistry.put(Character.TYPE,      new
> >> > > BuiltIn.CharacterConverter());
> >> > > +            converterRegistry.put(Short.class,         new
> >> > > BuiltIn.ShortConverter());
> >> > > +            converterRegistry.put(Short.TYPE,          new
> >> > > BuiltIn.ShortConverter());
> >> > > +            converterRegistry.put(Integer.class,       new
> >> > > BuiltIn.IntegerConverter());
> >> > > +            converterRegistry.put(Integer.TYPE,        new
> >> > > BuiltIn.IntegerConverter());
> >> > > +            converterRegistry.put(Long.class,          new
> >> > > BuiltIn.LongConverter());
> >> > > +            converterRegistry.put(Long.TYPE,           new
> >> > > BuiltIn.LongConverter());
> >> > > +            converterRegistry.put(Float.class,         new
> >> > > BuiltIn.FloatConverter());
> >> > > +            converterRegistry.put(Float.TYPE,          new
> >> > > BuiltIn.FloatConverter());
> >> > > +            converterRegistry.put(Double.class,        new
> >> > > BuiltIn.DoubleConverter());
> >> > > +            converterRegistry.put(Double.TYPE,         new
> >> > > BuiltIn.DoubleConverter());
> >> > > +            converterRegistry.put(File.class,          new
> >> > > BuiltIn.FileConverter());
> >> > > +            converterRegistry.put(URI.class,           new
> >> > > BuiltIn.URIConverter());
> >> > > +            converterRegistry.put(URL.class,           new
> >> > > BuiltIn.URLConverter());
> >> > > +            converterRegistry.put(Date.class,          new
> >> > > BuiltIn.ISO8601DateConverter());
> >> > > +            converterRegistry.put(Time.class,          new
> >> > > BuiltIn.ISO8601TimeConverter());
> >> > > +            converterRegistry.put(BigDecimal.class,    new
> >> > > BuiltIn.BigDecimalConverter());
> >> > > +            converterRegistry.put(BigInteger.class,    new
> >> > > BuiltIn.BigIntegerConverter());
> >> > > +            converterRegistry.put(Charset.class,       new
> >> > > BuiltIn.CharsetConverter());
> >> > > +            converterRegistry.put(InetAddress.class,   new
> >> > > BuiltIn.InetAddressConverter());
> >> > > +            converterRegistry.put(Pattern.class,       new
> >> > > BuiltIn.PatternConverter());
> >> > > +            converterRegistry.put(UUID.class,          new
> >> > > BuiltIn.UUIDConverter());
> >> > > +
> >> > > +            this.command             = Assert.notNull(command,
> >> > "command");
> >> > > +            Class<?> cls             = command.getClass();
> >> > > +            String declaredSeparator = null;
> >> > > +            boolean hasCommandAnnotation = false;
> >> > > +            while (cls != null) {
> >> > > +                init(cls, requiredFields, optionName2Field,
> >> > > singleCharOption2Field, positionalParametersFields);
> >> > > +                if (cls.isAnnotationPresent(Command.class)) {
> >> > > +                    hasCommandAnnotation = true;
> >> > > +                    Command cmd = cls.getAnnotation(Command.
> class);
> >> > > +                    declaredSeparator = (declaredSeparator ==
> null) ?
> >> > > cmd.separator() : declaredSeparator;
> >> > > +                    CommandLine.this.versionLines.
> >> > > addAll(Arrays.asList(cmd.version()));
> >> > > +
> >> > > +                    for (Class<?> sub : cmd.subcommands()) {
> >> > > +                        Command subCommand =
> >> sub.getAnnotation(Command.
> >> > > class);
> >> > > +                        if (subCommand == null ||
> >> > > Help.DEFAULT_COMMAND_NAME.equals(subCommand.name())) {
> >> > > +                            throw new IllegalArgumentException("
> >> > Subcommand
> >> > > " + sub.getName() +
> >> > > +                                    " is missing the mandatory
> >> @Command
> >> > > annotation with a 'name' attribute");
> >> > > +                        }
> >> > > +                        try {
> >> > > +                            Constructor<?> constructor =
> >> > > sub.getDeclaredConstructor();
> >> > > +                            constructor.setAccessible(true);
> >> > > +                            CommandLine commandLine =
> >> > > toCommandLine(constructor.newInstance());
> >> > > +                            commandLine.parent = CommandLine.this;
> >> > > +                            commands.put(subCommand.name(),
> >> > commandLine);
> >> > > +                        }
> >> > > +                        catch (IllegalArgumentException ex) { throw
> >> ex;
> >> > }
> >> > > +                        catch (NoSuchMethodException ex) { throw
> new
> >> > > IllegalArgumentException("Cannot instantiate subcommand " +
> >> > > +                                sub.getName() + ": the class has no
> >> > > constructor", ex); }
> >> > > +                        catch (Exception ex) {
> >> > > +                            throw new IllegalStateException("Could
> >> not
> >> > > instantiate and add subcommand " +
> >> > > +                                    sub.getName() + ": " + ex, ex);
> >> > > +                        }
> >> > > +                    }
> >> > > +                }
> >> > > +                cls = cls.getSuperclass();
> >> > > +            }
> >> > > +            separator = declaredSeparator != null ?
> >> declaredSeparator :
> >> > > separator;
> >> > > +            Collections.sort(positionalParametersFields, new
> >> > > PositionalParametersSorter());
> >> > > +            validatePositionalParameters(p
> >> ositionalParametersFields);
> >> > > +
> >> > > +            if (positionalParametersFields.isEmpty() &&
> >> > > optionName2Field.isEmpty() && !hasCommandAnnotation) {
> >> > > +                throw new IllegalArgumentException(command + " ("
> +
> >> > > command.getClass() +
> >> > > +                        ") is not a command: it has no @Command,
> >> @Option
> >> > > or @Parameters annotations");
> >> > > +            }
> >> > > +        }
> >> > > +
> >> > > +        /**
> >> > > +         * Entry point into parsing command line arguments.
> >> > > +         * @param args the command line arguments
> >> > > +         * @return a list with all commands and subcommands
> >> initialized
> >> > > by this method
> >> > > +         * @throws ParameterException if the specified command line
> >> > > arguments are invalid
> >> > > +         */
> >> > > +        List<CommandLine> parse(String... args) {
> >> > > +            Assert.notNull(args, "argument array");
> >> > > +            Stack<String> arguments = new Stack<String>();
> >> > > +            for (int i = args.length - 1; i >= 0; i--) {
> >> > > +                arguments.push(args[i]);
> >> > > +            }
> >> > > +            List<CommandLine> result = new
> ArrayList<CommandLine>();
> >> > > +            parse(result, arguments, args);
> >> > > +            return result;
> >> > > +        }
> >> > > +
> >> > > +        private void parse(List<CommandLine> parsedCommands,
> >> > > Stack<String> argumentStack, String[] originalArgs) {
> >> > > +            // first reset any state in case this CommandLine
> >> instance
> >> > is
> >> > > being reused
> >> > > +            isHelpRequested = false;
> >> > > +            CommandLine.this.versionHelpRequested = false;
> >> > > +            CommandLine.this.usageHelpRequested = false;
> >> > > +
> >> > > +            parsedCommands.add(CommandLine.this);
> >> > > +            List<Field> required = new ArrayList<Field>(
> >> > requiredFields);
> >> > > +            Set<Field> initialized = new HashSet<Field>();
> >> > > +            Collections.sort(required, new
> >> > PositionalParametersSorter());
> >> > > +            try {
> >> > > +                processArguments(parsedCommands, argumentStack,
> >> > > required, initialized, originalArgs);
> >> > > +            } catch (ParameterException ex) {
> >> > > +                throw ex;
> >> > > +            } catch (Exception ex) {
> >> > > +                int offendingArgIndex = originalArgs.length -
> >> > > argumentStack.size();
> >> > > +                String arg = offendingArgIndex >= 0 &&
> >> offendingArgIndex
> >> > > < originalArgs.length ? originalArgs[offendingArgIndex] : "?";
> >> > > +                throw ParameterException.create(ex, arg,
> >> > > argumentStack.size(), originalArgs);
> >> > > +            }
> >> > > +            if (!isAnyHelpRequested() && !required.isEmpty()) {
> >> > > +                if (required.get(0).isAnnotationP
> >> resent(Option.class))
> >> > {
> >> > > +                    throw MissingParameterException.crea
> >> te(required);
> >> > > +                } else {
> >> > > +                    try {
> >> > > +                        processPositionalParameters0(required,
> true,
> >> > new
> >> > > Stack<String>());
> >> > > +                    } catch (ParameterException ex) { throw ex;
> >> > > +                    } catch (Exception ex) { throw new
> >> > > IllegalStateException("Internal error: " + ex, ex); }
> >> > > +                }
> >> > > +            }
> >> > > +        }
> >> > > +
> >> > > +        private void processArguments(List<CommandLine>
> >> parsedCommands,
> >> > > +                                      Stack<String> args,
> >> > > +                                      Collection<Field> required,
> >> > > +                                      Set<Field> initialized,
> >> > > +                                      String[] originalArgs) throws
> >> > > Exception {
> >> > > +            // arg must be one of:
> >> > > +            // 1. the "--" double dash separating options from
> >> > positional
> >> > > arguments
> >> > > +            // 1. a stand-alone flag, like "-v" or "--verbose": no
> >> value
> >> > > required, must map to boolean or Boolean field
> >> > > +            // 2. a short option followed by an argument, like "-f
> >> file"
> >> > > or "-ffile": may map to any type of field
> >> > > +            // 3. a long option followed by an argument, like
> "-file
> >> > > out.txt" or "-file=out.txt"
> >> > > +            // 3. one or more remaining arguments without any
> >> associated
> >> > > options. Must be the last in the list.
> >> > > +            // 4. a combination of stand-alone options, like
> "-vxr".
> >> > > Equivalent to "-v -x -r", "-v true -x true -r true"
> >> > > +            // 5. a combination of stand-alone options and one
> option
> >> > > with an argument, like "-vxrffile"
> >> > > +
> >> > > +            while (!args.isEmpty()) {
> >> > > +                String arg = args.pop();
> >> > > +
> >> > > +                // Double-dash separates options from positional
> >> > > arguments.
> >> > > +                // If found, then interpret the remaining args as
> >> > > positional parameters.
> >> > > +                if ("--".equals(arg)) {
> >> > > +                    processPositionalParameters(required, args);
> >> > > +                    return; // we are done
> >> > > +                }
> >> > > +
> >> > > +                // if we find another command, we are done with the
> >> > > current command
> >> > > +                if (commands.containsKey(arg)) {
> >> > > +                    if (!isHelpRequested && !required.isEmpty()) {
> //
> >> > > ensure current command portion is valid
> >> > > +                        throw MissingParameterException.
> >> > create(required);
> >> > > +                    }
> >> > > +                    commands.get(arg).interpreter.
> >> parse(parsedCommands,
> >> > > args, originalArgs);
> >> > > +                    return; // remainder done by the command
> >> > > +                }
> >> > > +
> >> > > +                // First try to interpret the argument as a single
> >> > option
> >> > > (as opposed to a compact group of options).
> >> > > +                // A single option may be without option
> parameters,
> >> > like
> >> > > "-v" or "--verbose" (a boolean value),
> >> > > +                // or an option may have one or more option
> >> parameters.
> >> > > +                // A parameter may be attached to the option.
> >> > > +                boolean paramAttachedToOption = false;
> >> > > +                int separatorIndex = arg.indexOf(separator);
> >> > > +                if (separatorIndex > 0) {
> >> > > +                    String key = arg.substring(0, separatorIndex);
> >> > > +                    // be greedy. Consume the whole arg as an
> option
> >> if
> >> > > possible.
> >> > > +                    if (optionName2Field.containsKey(key) &&
> >> > > !optionName2Field.containsKey(arg)) {
> >> > > +                        paramAttachedToOption = true;
> >> > > +                        String optionParam =
> >> > arg.substring(separatorIndex
> >> > > + separator.length());
> >> > > +                        args.push(optionParam);
> >> > > +                        arg = key;
> >> > > +                    }
> >> > > +                }
> >> > > +                if (optionName2Field.containsKey(arg)) {
> >> > > +                    processStandaloneOption(required, initialized,
> >> arg,
> >> > > args, paramAttachedToOption);
> >> > > +                }
> >> > > +                // Compact (single-letter) options can be grouped
> >> with
> >> > > other options or with an argument.
> >> > > +                // only single-letter options can be combined with
> >> other
> >> > > options or with an argument
> >> > > +                else if (arg.length() > 2 && arg.startsWith("-")) {
> >> > > +                    processClusteredShortOptions(required,
> >> initialized,
> >> > > arg, args);
> >> > > +                }
> >> > > +                // The argument could not be interpreted as an
> >> option.
> >> > > +                // We take this to mean that the remainder are
> >> > positional
> >> > > arguments
> >> > > +                else {
> >> > > +                    args.push(arg);
> >> > > +                    processPositionalParameters(required, args);
> >> > > +                    return;
> >> > > +                }
> >> > > +            }
> >> > > +        }
> >> > > +
> >> > > +        private void processPositionalParameters(Collection<Field>
> >> > > required, Stack<String> args) throws Exception {
> >> > > +            processPositionalParameters0(required, false, args);
> >> > > +            if (!args.empty()) {
> >> > > +                handleUnmatchedArguments(args);
> >> > > +                return;
> >> > > +            };
> >> > > +        }
> >> > > +
> >> > > +        private void handleUnmatchedArguments(Stack<String> args)
> {
> >> > > +            if (!isUnmatchedArgumentsAllowed()) { throw new
> >> > > UnmatchedArgumentException(args); }
> >> > > +            while (!args.isEmpty()) { unmatchedArguments.add(args.
> >> > pop());
> >> > > } // addAll would give args in reverse order
> >> > > +        }
> >> > > +
> >> > > +        private void processPositionalParameters0(
> Collection<Field>
> >> > > required, boolean validateOnly, Stack<String> args) throws
> Exception {
> >> > > +            int max = -1;
> >> > > +            for (Field positionalParam :
> positionalParametersFields)
> >> {
> >> > > +                Range indexRange = Range.parameterIndex(
> >> > positionalParam);
> >> > > +                max = Math.max(max, indexRange.max);
> >> > > +                @SuppressWarnings("unchecked")
> >> > > +                Stack<String> argsCopy = reverse((Stack<String>)
> >> > > args.clone());
> >> > > +                if (!indexRange.isVariable) {
> >> > > +                    for (int i = argsCopy.size() - 1; i >
> >> > indexRange.max;
> >> > > i--) {
> >> > > +                        argsCopy.removeElementAt(i);
> >> > > +                    }
> >> > > +                }
> >> > > +                Collections.reverse(argsCopy);
> >> > > +                for (int i = 0; i < indexRange.min &&
> >> > > !argsCopy.isEmpty(); i++) { argsCopy.pop(); }
> >> > > +                Range arity = Range.parameterArity(
> positionalParam);
> >> > > +                assertNoMissingParameters(positionalParam,
> >> arity.min,
> >> > > argsCopy);
> >> > > +                if (!validateOnly) {
> >> > > +                    applyOption(positionalParam, Parameters.class,
> >> > arity,
> >> > > false, argsCopy, null);
> >> > > +                    required.remove(positionalParam);
> >> > > +                }
> >> > > +            }
> >> > > +            // remove processed args from the stack
> >> > > +            if (!validateOnly && !positionalParametersFields.is
> >> Empty())
> >> > {
> >> > > +                int processedArgCount = Math.min(args.size(), max <
> >> > > Integer.MAX_VALUE ? max + 1 : Integer.MAX_VALUE);
> >> > > +                for (int i = 0; i < processedArgCount; i++) {
> >> > args.pop();
> >> > > }
> >> > > +            }
> >> > > +        }
> >> > > +
> >> > > +        private void processStandaloneOption(Collection<Field>
> >> > required,
> >> > > +                                             Set<Field>
> initialized,
> >> > > +                                             String arg,
> >> > > +                                             Stack<String> args,
> >> > > +                                             boolean
> >> paramAttachedToKey)
> >> > > throws Exception {
> >> > > +            Field field = optionName2Field.get(arg);
> >> > > +            required.remove(field);
> >> > > +            Range arity = Range.optionArity(field);
> >> > > +            if (paramAttachedToKey) {
> >> > > +                arity = arity.min(Math.max(1, arity.min)); // if
> >> > > key=value, minimum arity is at least 1
> >> > > +            }
> >> > > +            applyOption(field, Option.class, arity,
> >> paramAttachedToKey,
> >> > > args, initialized);
> >> > > +        }
> >> > > +
> >> > > +        private void processClusteredShortOptions(
> Collection<Field>
> >> > > required,
> >> > > +                                                  Set<Field>
> >> > initialized,
> >> > > +                                                  String arg,
> >> > > +                                                  Stack<String>
> args)
> >> > > +                throws Exception {
> >> > > +            String prefix = arg.substring(0, 1);
> >> > > +            String cluster = arg.substring(1);
> >> > > +            boolean paramAttachedToOption = true;
> >> > > +            do {
> >> > > +                if (cluster.length() > 0 && singleCharOption2Field.
> >> > > containsKey(cluster.charAt(0))) {
> >> > > +                    Field field = singleCharOption2Field.get(
> >> > > cluster.charAt(0));
> >> > > +                    required.remove(field);
> >> > > +                    cluster = cluster.length() > 0 ?
> >> > cluster.substring(1)
> >> > > : "";
> >> > > +                    paramAttachedToOption = cluster.length() > 0;
> >> > > +                    Range arity = Range.optionArity(field);
> >> > > +                    if (cluster.startsWith(separator)) {//
> attached
> >> > with
> >> > > separator, like -f=FILE or -v=true
> >> > > +                        cluster = cluster.substring(separator.
> >> > length());
> >> > > +                        arity = arity.min(Math.max(1, arity.min));
> >> // if
> >> > > key=value, minimum arity is at least 1
> >> > > +                    }
> >> > > +                    args.push(cluster); // interpret remainder as
> >> option
> >> > > parameter (CAUTION: may be empty string!)
> >> > > +                    // arity may be >= 1, or
> >> > > +                    // arity <= 0 && !cluster.startsWith(separator)
> >> > > +                    // e.g., boolean @Option("-v", arity=0,
> >> > > varargs=true); arg "-rvTRUE", remainder cluster="TRUE"
> >> > > +                    int consumed = applyOption(field, Option.class,
> >> > > arity, paramAttachedToOption, args, initialized);
> >> > > +                    // only return if cluster (and maybe more) was
> >> > > consumed, otherwise continue do-while loop
> >> > > +                    if (consumed > 0) {
> >> > > +                        return;
> >> > > +                    }
> >> > > +                    cluster = args.pop();
> >> > > +                } else { // cluster is empty || cluster.charAt(0)
> is
> >> not
> >> > > a short option key
> >> > > +                    if (cluster.length() == 0) { // we finished
> >> parsing
> >> > a
> >> > > group of short options like -rxv
> >> > > +                        return; // return normally and parse the
> next
> >> > arg
> >> > > +                    }
> >> > > +                    // We get here when the remainder of the
> cluster
> >> > > group is neither an option,
> >> > > +                    // nor a parameter that the last option could
> >> > consume.
> >> > > +                    if (arg.endsWith(cluster)) {
> >> > > +                        // remainder was part of a clustered group
> >> that
> >> > > could not be completely parsed
> >> > > +                        args.push(paramAttachedToOption ? prefix +
> >> > > cluster : cluster);
> >> > > +                        handleUnmatchedArguments(args);
> >> > > +                    }
> >> > > +                    args.push(cluster);
> >> > > +                    processPositionalParameters(required, args);
> >> > > +                    return;
> >> > > +                }
> >> > > +            } while (true);
> >> > > +        }
> >> > > +
> >> > > +        private int applyOption(Field field,
> >> > > +                                Class<?> annotation,
> >> > > +                                Range arity,
> >> > > +                                boolean valueAttachedToOption,
> >> > > +                                Stack<String> args,
> >> > > +                                Set<Field> initialized) throws
> >> > Exception {
> >> > > +            updateHelpRequested(field);
> >> > > +            if (!args.isEmpty() && args.peek().length() == 0 && !v
> >> > >
> >> > > <TRUNCATED>
> >> > >
> >> >
> >>
> >>
> >>
> >> --
> >> Matt Sicker <boa...@gmail.com>
> >>
> >
> >
>

Reply via email to