Obtaining the Values Parsed

If dfh.cli.Cli successfully parses a command line, the next task is to obtain the values parsed. These are retained in the parser itself, but I generally extract them into their own variables immediately after parsing.

// command line specification
Object[][][] spec = {
    { { Cli.Opt.ARGS, "qux" } },
    { { "foo" } },
    { { "bar" } },
    { { "baz" } },
    { { "quux", Integer.class } },
    { { "corge", Integer.class } },
};
// parse arguments
Cli cli = new Cli(spec);
cli.parse(args);
// obtain option and argument values
String qux = cli.argument("qux");
boolean foo = cli.bool("foo"), bar = cli.bool("bar"), baz = cli.bool("baz");
Integer quux = cli.integer("quux"), corge = cli.integer("corge");

Options

The Significance of null

Except for boolean options and those with default values, it is always possible that an option remains unset. In these cases you should use Integer , Double , and so forth rather than int and double and check these objects for nullity before using them. Note, if you require options to have a a value you should mark them as required or provide a default.

Types

Boolean

Boolean is the default type of an option. As such, all it needs is a name, though you may specify its type as a Boolean.class . Boolean options always have a default value, false . You must explicitly specify the type if you wish to specify the default, so if you want the default to be true .

        Object[][][] spec = {
            // equivalent specifications
            { { "foo" } },
            { { "bar", Boolean.class } },
            { { "quux", Boolean.class, false } },
            // make the default true
            { { "baz", Boolean.class, true } },
        };

        Cli cli = new Cli(spec);
        cli.parse();
        for (String s : "foo bar quux baz".split(" "))
            System.out.println(cli.bool(s));
false
false
false
true

Boolean options are not repeatable.

        Object[][][] spec = {
            { { "foo" },{},{Res.REPEATABLE} }, // will cause runtime error during Cli construction
        };

String

String options are pretty straightforward:

    Object[][][] spec = { { { "foo", String.class } } };
    Cli cli = new Cli(spec);
    cli.parse("--foo=bar");
    String foo = cli.string("foo"); // bar

Numeric

dfh.cli allows you to specify an option as containing most of the subtypes of Number :

Object[][][] spec = {
        { { 'a', Byte.class, 1 } },         // byte
        { { 'b', Short.class, 1 } },        // short
        { { 'c', Integer.class, 1 } },      // int
        { { 'd', BigInteger.class, "1" } }, // java.math.BigIntegerNote that the arbitrary precision numeric types, 
			BigInteger and BigDecimal, can take 
			either an instance of their type or a String as their default 
			value -- {{ 'a', BigInteger.class, new BigInteger("1") }} 
			will give you the same results as {{ 'a', BigInteger.class, "1" }}. 
			The String default syntax is provided simply because the usual 
			form is cumbersome to type.
        { { 'e', Float.class, 1 } },        // float
        { { 'f', Double.class, 1 } },       // double
        { { 'g', BigDecimal.class, "1" } }, // java.math.BigDecimal
};
Cli cli = new Cli(spec);
cli.parse();
System.out.println(cli.byt("a"));            // 1
System.out.println(cli.shrt("b"));           // 1
System.out.println(cli.integer("c"));        // 1
System.out.println(cli.num("d"));            // 1
System.out.println(cli.flt("e"));            // 1.0
System.out.println(cli.dbl("f"));            // 1.0
System.out.println(cli.num("g"));            // 1
As you can see, convenience accessors are provided for all of these but BigInteger and BigDecimal , for which you can rely on the num(String) accessor, which returns any numeric option, or the object(String) accessor, which works for any option at all. The actual value returned is an object in all cases: if the option has not been set and there is no default, then null will be returned.

Enums

If you want an option to take some enum constant, or collection of enum constants, as a value, you can simply specify the enum class as its type:

    public static void main(String[] args) {
        Object[][][] spec = { 
            { {
                "foo",
                Example.class, // --foo will be an Example constant
                Example.foo    // its default value will be Example.foo
            } },
        };

        Cli cli = new Cli(spec);
        cli.parse();
        Example ex = (Example) cli.object("foo");
    }

    enum Example {
        foo, bar, baz, quux
    }

If you pass this class the --help flag it produces

USAGE: EXECUTABLE [options] <arg>*

    --foo        <str>  a string; value must be in {foo, bar, baz, quux};
                        default: foo

    --help -? -h        print usage information

    

As you can see, the possible enum constants are listed in the description.

An enum option is functionally similar to a string option with a StrSet validation rule, but it's a little less verbose and saves your coercing the string to the corresponding enum constant if that's what you really want.

Coercions

To produce options containing any value other than those listed above, one must either “coerce” strings into the desired object type oneself, or use a dfh.cli.Coercion class.

Object[][][] spec = {
    {{ "date1", String.class, "20120922" }},
    {{ "date2", DateCoercion.C, "20120921" }},
};
Cli cli = new Cli(spec);
cli.parse();
Date d1 = new SimpleDateFormat("yyyyMMdd").parse(cli.string("date1"));
Date d2 = (Date) cli.object("date2");

The advantages of using pre-defined coercions instead of doing it oneself is that they

For example the specification above gives you the following usage text:

USAGE: EXECUTABLE [options] <arg>*

    --date1      <str>   string option; default: 20120922
    --date2      <date>  java.util.Date; date string must be parsable as
                         'yyyyMMdd'; default: Fri Sep 21 00:00:00 EDT 2012

    --help -? -h         print usage information

All the coercions in dfh.cli.coercions provide a singleton instance named C , though you may also simply instantiate them. Default values may be either an object of the appropriate type or, as above, a string which the coercion object can coerce into the right type. A dfh.cli.Cli object provides no accessor method for any particular coerced type. Instead, as above, you must use the generic object(String) method and cast the value returned to the correct type.

dfh.cli.coercions provides the following coercions.

java.util.Date
Object[][][] spec = {
    {{ "date1", DateCoercion.C }},                                     // default date format
    {{ "date2", new DateCoercion("yyyyMMdd", "yyyy", "yyyy/MM/dd") }}, // a variety of date formats
};
Cli cli = new Cli(spec);
cli.parse(args);
Date d = (Date) cli.object("date1");

DateCoercion attempts to parse date strings according to the conventions understood by java.text.SimpleDateFormat . If you specify no format, the default format yyyyMMdd , is used. Otherwise you pass in a list of date format strings and the DateCoercion will try these in order. For a parse to be successful it must consume all characters in the option's argument.

java.util.regex.Pattern
Object[][][] spec = {
    {{ "pat", RxCoercion.C, "^pat$" }},
};
Cli cli = new Cli(spec);
cli.parse(args);
Pattern p = (Pattern) cli.object("pat");
java.io.File
Object[][][] spec = {
    {{ 'f', FileCoercion.C, File.createTempFile("temporary", null) }},
};
Cli cli = new Cli(spec);
cli.parse(args);
File f = (File) cli.object("f");

FileCoercion coerces a file name into a file object. It does no validation of the existence, readability, or writeability of the file created, however.

java.io.PrintStream
Object[][][] spec = {
    {{ "log", StreamCoercion.C, System.err }},
};
Cli cli = new Cli(spec);
cli.parse(args);
PrintStream log = (PrintStream) cli.object("log");

StreamCoercion takes a file name and turns it into a java.io.PrintStream printing into this file. I find I often use the arrangement illustrated above, printing output to STDERR unless an output file is provided.

ad hoc coercions

I have only created a few coercions I tend to use a lot. Frankly, I doubt anyone will add to this list. After all, the whole point is to type less and have few libraries on one's class path. If you have to hande the string coercion yourself, it's easy enough to do it after you've parsed the CLI arguments. However, it is easy enough to add coercion classes simply by extending dfh.cli.Coercion . Here, for example, is the implementation of RxCoercion :This is at the time of writing, of course. I've stripped away imports, comments, and annotations for the sake of concision.

public class RxCoercion extends Coercion<Pattern> {

    public static final RxCoercion C = new RxCoercion();

    public Pattern coerce(String s) throws ValidationException {
        try {
            return Pattern.compile(s);
        } catch (PatternSyntaxException e) {
            throw new ValidationException(e.getMessage());
        }
    }

    public String argName() {
        return "rx";
    }

    public Collection<String> constraintDescriptions() {
        Collection<String> list = new ArrayList<String>(1);
        list.add("value must parse as regex");
        return list;
    }

    public String type() {
        return Pattern.class.getCanonicalName();
    }
}

Most of this code serves to make the command line more explanatory. Really the following would be a complete implementation of the coercion.

public class RxCoercion extends Coercion<Pattern> {
    public Pattern coerce(String s) throws ValidationException {
        try {
            return Pattern.compile(s);
        } catch (PatternSyntaxException e) {
            throw new ValidationException(e.getMessage());
        }
    }
}

And if you're indifferent to printing out usage information in the case of compilation failures, you can further simplify this to.

public class RxCoercion extends Coercion<Pattern> {
    public Pattern coerce(String s) throws ValidationException {
        return Pattern.compile(s);
    }
}

Something this small could easily be in-lined as an anonymous inner class.

Sets

An option marked via Cli.Res.SET will be represented internally as a java.util.LinkedHashSet . This retains only one instance of every value but it otherwise preserves the order of the values on the command line. You can retrieve a set like so:

Object[][][] spec = {
    { { "foo", String.class }, {}, { Cli.Res.SET } },
};
Cli cli = new Cli(spec);
cli.parse(args);
Set<String> foo = (Set<String>) cli.collection("foo");

Anything can come in sets except booleans. { { 'f' }, { }, { Res.Set } } , a specification for a set of booleans named "f", will throw a dfh.cli.ValidationException

Lists

If an option is marked as Cli.Res.REPEATABLE and is not marked as a set (though you only ever need one or the other), all the values found for this option will be collected into a list.

Object[][][] spec = {
    { { "foo", String.class }, {}, { Cli.Res.REPEATABLE } },
};
Cli cli = new Cli(spec);
cli.parse(args);
List<String> foo = (List<String>) cli.collection("foo");

Underlyingly a repeatable option is a java.util.LinkedList .

First Items

The same method that retrieves the value of a non-repeatable option will obtain the first value of a repeatable one -- the first item in a set or list.

Object[][][] spec = {
    { { "foo", String.class }, {}, { Cli.Res.REPEATABLE } },
};
Cli cli = new Cli(spec);
cli.parse(args);
String foo = cli.string("foo");

Arguments

The “arguments” are just the arguments left after all options are assigned values. Unlike options, arguments are only checked for existence, not type. Any argument validation must follow command line parsing.

Example Specifications

default

Object[][][] spec = {
    {{ "foo" }},
};
USAGE: EXECUTABLE [options] <arg>*

    --foo           a boolean option

    --help -? -h    print usage information
	

no args

Object[][][] spec = {
    {{ Cli.Opt.ARGS }},
    {{ "foo" }},
};
USAGE: EXECUTABLE [options]

    --foo           a boolean option

    --help -? -h    print usage information

fixed args

Object[][][] spec = {
    {{ Cli.Opt.ARGS, "bar", "baz" }},
    {{ "foo" }},
};
USAGE: EXECUTABLE [options] <bar> <baz>

    --foo           a boolean option

    --help -? -h    print usage information

optional args

Object[][][] spec = {
    {{ Cli.Opt.ARGS , "bar", Cli.Opt.QMARK }},
    {{ "foo" }},
};
USAGE: EXECUTABLE [options] <bar>?

    --foo           a boolean option

    --help -? -h    print usage information

required argument list

Object[][][] spec = {
    {{ Cli.Opt.ARGS, "bar", Cli.Opt.PLUS }},
    {{ "foo" }},
};
USAGE: EXECUTABLE [options] <bar>+

    --foo           a boolean option

    --help -? -h    print usage information

optional argument list

Object[][][] spec = {
    {{ Cli.Opt.ARGS, "bar", Cli.Opt.STAR }},
    {{ "foo" }},
};
USAGE: EXECUTABLE [options] <bar>*

    --foo           a boolean option

    --help -? -h    print usage information

mixed

Object[][][] spec = {
    {{ Cli.Opt.ARGS, "bar", "baz", Cli.Opt.QMARK, "corge", Cli.Opt.STAR }},
    {{ "foo" }},
};
USAGE: EXECUTABLE [options] <bar> <baz>? <corge>*

    --foo           a boolean option

    --help -? -h    print usage information

argument order

Argument parsing doesn't backtrack. This means required arguments must be listed before optional arguments and any argument name modified by Cli.Opt.STAR or Cli.Opt.PLUS must appear last. If you attempt an impossible argument order this will be caught during Cli construction and an error will be thrown.

double dash

To halt option parsing and force all remaining items on the command line to be parsed as arguments, one may provide a double dash argument:

executable --foo bar --quux -- --corge

In this example --corge will be taken in as un unparsed string rather than a boolean option, even if the specification provides a --corge option. Such a boolean option will continue to have a false value and the first argument of the executable will be --corge .

Likewise, if any argument, parsing from left to right, cannot be interpreted as the value of an option, it and all remaining arguments will be passed to the executable as arguments.

anonymous

The argument list may always be retrieved as a list of strings.

Object[][][] spec = {
    { { Cli.Opt.ARGS, "foo", "bar", Cli.Opt.PLUS } },
};
Cli cli = new Cli(spec);
cli.parse("a", "b", "c", "d");
for (String s: cli.argList())
    System.out.println(s);
a
b
c
d
Object[][][] spec = { { { "foo" } } };
Cli cli = new Cli(spec);
cli.parse("a", "b", "c", "d");
for (String s : cli.argList())
    System.out.println(s);
a
b
c
d

named

Any explicitly named argument not marked with a * or ? is required --

{ { Cli.Opt.ARGS, "foo" } }
or
{ { Cli.Opt.ARGS, "foo", Cli.Opt.PLUS } }
as opposed to
{ { Cli.Opt.ARGS, "foo", Cli.Opt.STAR } }
or
{ { Cli.Opt.ARGS, "foo", Cli.Opt.QMARK } }

Named arguments can be retrieved by their name.

{ { Cli.Opt.ARGS, "foo" } };
//...
String foo = cli.argument("foo");

As with repeatable options, this same method will retrieve the first in a list of named slurped arguments (see below).

slurped

A slurped argument is a named argument marked with Cli.Opt.STAR or Cli.Opt.PLUS -- a repeatable argument. Such arguments are necessarily last in the argument list. If you want to retrieve the first value of such an argument, you can extract it by name.

Object[][][] spec = { { { Cli.Opt.ARGS, "foo", "bar", Cli.Opt.STAR } } };
Cli cli = new Cli(spec);
cli.parse("a", "b", "c", "d");
System.out.println(cli.argument("bar"));
b

One may the entire list of slurped values.

Object[][][] spec = { { { Cli.Opt.ARGS, "foo", "bar", Cli.Opt.STAR } } };
Cli cli = new Cli(spec);
cli.parse("a", "b", "c", "d");
for (String s : cli.slurpedArguments())
	System.out.println(s);
b
c
d

As long as the slurped argument is not required -- it is marked by Cli.Opt.STAR rather than Cli.Opt.PLUS -- it need not have any value. In this case Cli.slurpedArguments() will return an empty list.