Skip to content
Remko Popma edited this page Mar 14, 2018 · 45 revisions

(This page is work in progress)

Programmatic API

Tip
For most applications the annotations API is a better fit than the programmatic API: the annotation syntax is more compact, easier to read, and easier to maintain. See this introductory article and for more details, the user manual.

Picocli 3.0 offers a programmatic API for creating command line applications, in addition to annotations. A programmatic API allows applications to dynamically create command line options on the fly, and also makes it possible to create idiomatic domain-specific languages for processing command line arguments, using picocli, in other JVM languages.

Example

CommandSpec spec = CommandSpec.create();
spec.mixinStandardHelpOptions(true); // usageHelp and versionHelp options
spec.add(OptionSpec.builder("-c", "--count")
        .paramLabel("COUNT")
        .type(int.class)
        .description("number of times to execute").build());
spec.add(PositionalParamSpec.builder()
        .paramLabel("FILES")
        .type(List.class)
        .auxiliaryTypes(File.class) // List<File>
        .description("The files to process").build());
CommandLine commandLine = new CommandLine(spec);

// AbstractSimpleParseResultHandler prints help if requested,
// otherwise calls process

commandLine.parseWithSimpleHandlers(new AbstractSimpleParseResultHandler() {
    public void process(ParseResult pr) {
        int count = pr.typedOptionValue('c', 1);
        List<File> files = pr.typedPositionalValue(0, Collections.<File>emptyList());
        for (int i = 0; i < count; i++) {
            for (File f : files) {
                System.out.printf("%d: %s%n", i, f);
            }
        }
    }
}.useOut(System.out).andExit(123), new DefaultExceptionHandler<Void>().andExit(567), args);

Main classes

The following classes are the main classes in the object model:

  • CommandSpec

  • OptionSpec

  • PositionalParamSpec

  • ParseResult

CommandSpec

CommandSpec models a command. It has a name and a version, both of which may be empty. It also has a UsageMessageSpec to configure aspects of the usage help message, and a ParserSpec where the behaviour of the parser can be controlled to some extent.

CommandSpec has methods to add options (OptionSpec objects) and positional parameters (PositionalParamSpec objects). A CommandSpec can be mixed in with another CommandSpec, so its options, positional parameters and usage help attributes are merged into the other CommandSpec.

Finally, CommandSpec objects can be subcommands of other CommandSpecs. There is no limit to the depth of a hierarchy of command and subcommands. CommandSpec also allows registration of type converters that are used while parsing the command line arguments to convert a command line argument string to the strongly typed value of a OptionSpec or PositionalParamSpec

OptionSpec

OptionSpec models a command option. An OptionSpec must have at least one name, which is used during parsing to match command line arguments. Other attributes can be left empty and picocli will give them a reasonable default value. This defaulting is why OptionSpec objects are created with a builder: this allows you to specify only some attributes and let picocli initialise the other attributes. For example, if only the option’s name is specified, picocli assumes the option takes no parameters (arity = 0), and is of type boolean. Another example, if arity is larger than 1, picocli sets the type to List and the auxiliary type to String.

Once an OptionSpec is constructed, its configuration becomes immutable, but its value can still be modified. Usually the value is set during command line parsing when a command line argument matches one of the option names.

The value is set via a binding. We’ll come back to bindings later in this document.

Similar to the annotation API, OptionSpec objects have help, usageHelp and versionHelp attributes. When the parser matches an option that was marked with any of these attributes, it will no longer validate that all required arguments exist. (TODO: which APIs automatically print help.)

PositionalParamSpec

PositionalParamSpec objects don’t have names, but have an index instead.

Parsing and Result Processing

Basic Processing

The most basic way to parse the command line is to call the CommandLine::parseArgs method and inspect the resulting ParseResult object.

For example:

CommandSpec spec = CommandSpec.create();
// add options and positional parameters

CommandLine commandLine = new CommandLine(spec);


ParseResult pr = null;
try {
    pr = commandLine.parseArgs(args);
    if (CommandLine.printHelpIfRequested(pr)) {
        return;
    }
    int count = pr.typedOptionValue('c', 1);
    List<File> files = pr.typedPositionalValue(0, Collections.<File>emptyList());
    for (int i = 0; i < count; i++) {
        for (File f : files) {
            System.out.printf("%d: %s%n", i, f);
        }
    }
} catch (ParseException invalidInput) {
    System.err.println(invalidInput.getMessage());
    invalidInput.getCommandLine().usage(System.err);
}

Convenience Methods

There are a number of convenience methods to reduce some boilerplate when processing the ParseResult programmatically.

`parseWithHandler`

Handlers with Return Value

Example:

CommandSpec spec = CommandSpec.create();
spec.mixinStandardHelpOptions(true); // usageHelp and versionHelp options
// add options

CommandLine commandLine = new CommandLine(spec);

// AbstractSimpleParseResultHandler prints help if requested,
// otherwise calls process

commandLine.parseWithHandler(new AbstractParseResultHandler() {
    public void process(ParseResult pr) {
        int count = pr.typedOptionValue('c', 1);
        List<File> files = pr.typedPositionalValue(0, Collections.<File>emptyList());
        for (int i = 0; i < count; i++) {
            for (File f : files) {
                System.out.printf("%d: %s%n", i, f);
            }
        }
    }
}, args);

Handlers Without Return Value

Example:

CommandSpec spec = CommandSpec.create();

// add options and positional parameters

CommandLine commandLine = new CommandLine(spec);

// AbstractSimpleParseResultHandler prints help if requested,
// otherwise calls process

commandLine.parseWithSimpleHandler(new AbstractSimpleParseResultHandler() {
    public void process(ParseResult pr) {
        int count = pr.typedOptionValue('c', 1);
        List<File> files = pr.typedPositionalValue(0, Collections.<File>emptyList());
        for (int i = 0; i < count; i++) {
            for (File f : files) {
                System.out.printf("%d: %s%n", i, f);
            }
        }
    }
}, args);

A variation of this method, parseWithSimpleHandlers, additionally takes an IExceptionHandler2<Void > to customize how invalid input should be handled and optionally set an exit code.

Example:

CommandSpec spec = CommandSpec.create();
// add options and positional parameters

CommandLine commandLine = new CommandLine(spec);
commandLine.parseWithSimpleHandlers(new AbstractSimpleParseResultHandler() {
    public void process(ParseResult pr) {...}
}.useOut(System.out).andExit(123), new DefaultExceptionHandler<Void>().andExit(567), args);
Clone this wiki locally