github remkop/picocli v3.2.0
picocli 3.2.0

latest releases: v4.7.5, v4.7.4, v4.7.3...
5 years ago

Picocli 3.2.0

The picocli community is pleased to announce picocli 3.2.0.

This release contains new features and enhancements:

  • Improved support for Dependency Injection
  • Methods can now be annotated with @Option and @Parameters
  • Support for JLine-based interactive command line interfaces (completionCandidates attribute on @Option and @Parameters, and the AutoComplete.complete method)
  • New @Spec annotation for injecting a command with its CommandSpec

This is the thirty-third public release.
Picocli follows semantic versioning.

Table of Contents

  • New and noteworthy
  • Promoted features
  • Fixed issues
  • Deprecations
  • Potential breaking changes

New and Noteworthy

Dependency Injection

This release makes integration with Dependency Injection containers extremely easy:

  • CommandLine constructor now accepts a Class instance as the user object, and will delegate to the IFactory to get an instance.
  • New CommandLine.run(Class<Runnable>, IFactory, ...) and CommandLine.call(Class<Callable>, IFactory, ...) methods. These work the same as the existing run and call methods except that the Runnable or Callable instance is created by the factory.

The below example shows how to create an IFactory implementation with a Guice Injector:

import com.google.inject.*;
import picocli.CommandLine.IFactory;

public class GuiceFactory implements IFactory {
    private final Injector injector = Guice.createInjector(new DemoModule());

    @Override
    public <K> K create(Class<K> aClass) throws Exception {
        return injector.getInstance(aClass);
    }

    static class DemoModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(java.util.List.class).to(java.util.LinkedList.class);
            bind(Runnable.class).to(InjectionDemo.class);
        }
    }
}

Use the custom factory when creating a CommandLine instance, or when invoking the run or call convenience methods:

import javax.inject.Inject;

@Command(name = "di-demo")
public class InjectionDemo implements Runnable {

    @Inject java.util.List list;

    // @Option(names = "-x") int x; // add options etc as needed...

    public static void main(String[] args) {
        CommandLine.run(Runnable.class, new GuiceFactory(), args);
    }

    @Override
    public void run() {
        assert list instanceof java.util.LinkedList;
    }
}

Annotated Methods

From this release, @Option and @Parameter annotations can be added to methods as well as fields of a class.

For concrete classes, annotate "setter" methods (methods that accept a parameter) and when the option is specified on the command line, picocli will invoke the method with the value specified on the command line, converted to the type of the method parameter.

Alternatively, you may annotate "getter-like" methods (methods that return a value) on an interface, and picocli will create an instance of the interface that returns the values specified on the command line, converted to the method return type. This feature is inspired by Jewel CLI.

Annotating Methods of an Interface

The @Option and @Parameters annotations can be used on methods of an interface that return a value. For example:

interface Counter {
    @Option(names = "--count")
    int getCount();
}

You use it by specifying the class of the interface:

CommandLine cmd = new CommandLine(Counter.class); // specify a class
String[] args = new String[] {"--count", "3"};
cmd.parse(args);
Counter counter = cmd.getCommand(); // picocli created an instance
assert counter.getCount() == 3; // method returns command line value

Annotating Methods of a Concrete Class

The @Option and @Parameters annotations can be used on methods of a class that accept a parameter. For example:

class Counter {
    int count;
    
    @Option(names = "--count")
    void setCount(int count) {
        this.count = count;
    }
}

You use it by passing an instance of the class:

Counter counter = new Counter(); // the instance to populate
CommandLine cmd = new CommandLine(counter);
String[] args = new String[] {"--count", "3"};
cmd.parse(args);
assert counter.count == 3; // method was invoked with command line value

JLine Tab-Completion Support

This release adds support for JLine Tab-Completion.

Jline 2.x and 3.x is a Java library for handling console input, often used to create interactive shell applications.

Command line applications based on picocli can generate completion candidates for the command line in the JLine shell. The generated completion candidates are context sensitive, so once a subcommand is specified, only the options for that subcommand are shown, and once an option is specified, only parameters for that option are shown.

Below is an example picocli Completer implementation for JLine 2.x:

import jline.console.completer.ArgumentCompleter;
import jline.console.completer.Completer;
import picocli.AutoComplete;
import picocli.CommandLine;
import picocli.CommandLine.Model.CommandSpec;

import java.util.List;

public class PicocliJLineCompleter implements Completer {
    private final CommandSpec spec;

    public PicocliJLineCompleter(CommandSpec spec) {
        this.spec = spec;
    }

    @Override
    public int complete(String buffer, int cursor, List<CharSequence> candidates) {
        // use the jline internal parser to split the line into tokens
        ArgumentCompleter.ArgumentList list =
                new ArgumentCompleter.WhitespaceArgumentDelimiter().delimit(buffer, cursor);

        // let picocli generate completion candidates for the token where the cursor is at
        return AutoComplete.complete(spec,
                list.getArguments(),
                list.getCursorArgumentIndex(),
                list.getArgumentPosition(),
                cursor,
                candidates);
    }
}

Completion Candidates

From this release, @Options and @Parameters have a new completionCandidates attribute that can be used to generate a list of completions for this option or positional parameter. For example:

static class MyAbcCandidates extends ArrayList<String> {
    MyAbcCandidates() { super(Arrays.asList("A", "B", "C")); }
}

class ValidValuesDemo {
    @Option(names = "-o", completionCandidates = MyAbcCandidates.class)
    String option;
}

This will generate completion option values A, B and C in the generated bash auto-completion script and in JLine.

${DEFAULT-VALUE} Variable

From picocli 3.2, it is possible to embed the default values in the description for an option or positional parameter by
specifying the variable ${DEFAULT-VALUE} in the description text.
Picocli uses reflection to get the default values from the annotated fields.

The variable is replaced with the default value regardless of the @Command(showDefaultValues) attribute
and regardless of the @Option(showDefaultValues) or @Parameters(showDefaultValues) attribute.

class DefaultValues {
    @Option(names = {"-f", "--file"},
            description = "the file to use (default: ${DEFAULT-VALUE})")
    File file = new File("config.xml");
}

CommandLine.usage(new DefaultValues(), System.out);

This produces the following usage help:

Usage: <main class> -f=<file>
  -f, --file=<file>   the file to use (default: config.xml)

${COMPLETION-CANDIDATES} Variable

Similarly, it is possible to embed the completion candidates in the description for an option or positional parameter by
specifying the variable ${COMPLETION-CANDIDATES} in the description text.

This works for java enum classes and for options or positional parameters of non-enum types for which completion candidates are specified.

enum Lang { java, groovy, kotlin, javascript, frege, clojure }

static class MyAbcCandidates extends ArrayList<String> {
    MyAbcCandidates() { super(Arrays.asList("A", "B", "C")); }
}

class ValidValuesDemo {
    @Option(names = "-l", description = "Enum. Values: ${COMPLETION-CANDIDATES}")
    Lang lang = null;

    @Option(names = "-o", completionCandidates = MyAbcCandidates.class,
            description = "Candidates: ${COMPLETION-CANDIDATES}")
    String option;
}

CommandLine.usage(new ValidValuesDemo(), System.out);

This produces the following usage help:

Usage: <main class> -l=<lang> -o=<option>
  -l=<lang>     Enum. Values: java, groovy, kotlin, javascript, frege, clojure
  -o=<option>   Candidates: A, B, C

@Spec Annotation

A new @Spec annotation is now available that injects the CommandSpec model of the command into a command field.

This is useful when a command needs to use the picocli API, for example to walk the command hierarchy and iterate over its sibling commands.
This complements the @ParentCommand annotation; the @ParentCommand annotation injects a user-defined command object, whereas this annotation injects a picocli class.

class InjectSpecExample implements Runnable {
   @Spec CommandSpec commandSpec;
   //...
   public void run() {
       // do something with the injected spec
   }
}
  

Lenient Parse Mode

This release adds the ability to continue parsing invalid input to the end.
When collectErrors is set to true, and a problem occurs during parsing, an Exception is added to the ParseResult.errors() list and parsing continues. The default behaviour (when collectErrors is false) is to abort parsing by throwing the Exception.

This is useful when generating completion candidates on partial input, and is also useful when using picocli in
languages like Clojure where idiomatic error handling does not involve throwing and catching exceptions.

When using this feature, applications are responsible for actively verifying that no errors occurred before executing the business logic. Use with care!

Promoted Features

Promoted features are features that were incubating in previous versions of picocli but are now supported and subject to backwards compatibility.

No features have been promoted in this picocli release.

Fixed issues

  • [#182] New Feature: Add support for annotating methods with @Option and @Parameters.
  • [#393] New feature: Add support for JLine completers.
  • [#389] New feature: Support 'lenient' parsing mode: don't throw Exceptions but add them to the ParseResult.errors() list and continue parsing.
  • [#392] New feature: Ability to map command line arguments to picocli spec elements. Internally used for generating completion candidates.
  • [#391] New feature: Add API to get completion candidates for option and positional parameter values of any type.
  • [#395] New feature: Allow embedding default values anywhere in description for @Option or @Parameters.
  • [#259] New Feature: Added @Spec annotation to inject CommandSpec into application field.
  • [#400] Enhancement: Add run/call static methods that accept an IFactory. This allows Dependency Injection containers to provide the Runnable/Callable implementation.
  • [#404] Enhancement: Ask IFactory for implementation before creating Proxy for interface. Needed for Dependency Injection.
  • [#398] Enhancement: Allow @PicocliScript annotation on Groovy script @Field variables instead of just on imports.
  • [#322] Enhancement: Add defaultValue attribute to @option and @parameters annotation.
  • [#375] Enhancement: Improve ParameterIndexGapException error message. Thanks to gpettey.
  • [#405] Enhancement: Add method CommandLine.getUsageMessage().
  • [#406] Enhancement: Added fields to ParameterException. Thanks to David Hait.
  • [#401] Doc: The user manual no longer includes the full CommandLine.java source code.

Deprecations

No features were deprecated in this release.

Potential breaking changes

This release has no breaking changes.

Don't miss a new picocli release

NewReleases is sending notifications on new releases.