Search code examples
javapicocli

Picocli: Specify default value for an option which has a type of Map


I have an option like so

    @CommandLine.Option(names = "-D", description = "Define a symbol.")
    /* A list of defines provided by the user. */
    Map<String, String> defines = new LinkedHashMap<String, String>();

This does work when I do the following:

-Dkey=value

however when I do this

-Dkey

it does not work. Is there a way to add in a default value for keys which do not have a value associated with them?


Solution

  • Update: from picocli 4.6, this can be accomplished by specifying a mapFallbackValue in the option or positional parameter.

    @Option(names = {"-P", "--properties"}, mapFallbackValue = Option.NULL_VALUE)
    Map<String, Optional<Integer>> properties;
    
    @Parameters(mapFallbackValue= "INFO", description= "... ${MAP-FALLBACK-VALUE} ...")
    Map<Class<?>, LogLevel> logLevels;
    

    The value type may be wrapped in a java.util.Optional. (If it isn't, and the fallback value is Option.NULL_VALUE, picocli will put the value null in the map for the specified key.)


    (Original answer follows below):

    This can be accomplished with a custom parameterConsumer. For example:

    /* A list of defines provided by the user. */
    @Option(names = "-D", parameterConsumer = MyMapParameterConsumer.class,
      description = "Define a symbol.")
    Map<String, String> defines = new LinkedHashMap<String, String>();
    

    ... where MyMapParameterConsumer can look something like this:

    
    class MyMapParameterConsumer implements IParameterConsumer {
        @Override
        public void consumeParameters(
                Stack<String> args, 
                ArgSpec argSpec, 
                CommandSpec commandSpec) {
    
            if (args.isEmpty()) {
                throw new ParameterException(commandSpec.commandLine(), 
                        "Missing required parameter");
            }
            String parameter = args.pop();
            String[] keyValue = parameter.split("=", 1);
            String key = keyValue[0];
            String value = keyValue.length > 1 
                    ? keyValue[1]
                    : "MY_DEFAULT";
            Map<String, String> map = argSpec.getValue();
            map.put(key, value);
        }
    }