Search code examples
javacommand-line-interfacecommand-line-argumentspicocli

arggroup with default value in picocli


Write cmd interface with Java code used picocli v4.6.3

My case: ./cmd [-a -b [-c]]

user put a,b option or got default value example:

  • user only put: ./cmd x1

    a,b,c option got default value

  • ./cmd x1 -a=a1

    request input b option and vice versa (c option still get default value)

  • ./cmd x1 -c=c1

    a & b option get default value

@Command
public class CMD implements Runnable {

    @Parameters
    private String x;

    
    @ArgGroup(exclusive = false)
    private Group group;

    static class Group {
        @Option(names = "-a", required = true, defaultValue = "aa")
        public static String a;

        @Option(names = "-b", required = true, defaultValue = "bb")
        public static String b;

        @Option(names = "-c", required = false, defaultValue = "cc")
        public static String c;
    }

But it didn't work as I wanted I didn't have the solution


Solution

  • Original Answer

    The picocli user manual has a detailed section on assigning default values in argument groups.

    Applications need to do both of the below:

    • specify default values in both the @Option annotation, and in the initial value of the @Option-annotated field. (Yes, that means some duplication.)
    • manually instantiate the @ArgGroup-annotated field

    For your example, that means:

    @Command
    public class CMD implements Runnable {
    
        @Parameters
        private String x;
        
        @ArgGroup(exclusive = false)
        private Group group = new Group();
    
        static class Group {
            @Option(names = "-a", required = true, defaultValue = "aa")
            public static String a = "aa";
    
            @Option(names = "-b", required = true, defaultValue = "bb")
            public static String b = "bb";
    
            @Option(names = "-c", required = false, defaultValue = "cc")
            public static String c = "cc";
        }
    

    Update (Answer #2)

    @kienbui pointed out that I missed these requirements:

    (...) if -a is specified, then -b becomes mandatory (user input becomes required), and similarly if -b is specified, then -a becomes mandatory. Otherwise, if neither -a nor -b is specified, none of the options are required and default values are assigned to the options that are not specified by the end user.

    This can be achieved by making a separate inner arg-group for just options -a and -b, and removing the default values from the option declarations, but initializing them with their default values. For example:

    @Command
    static class CMD implements Runnable {
    
        @Parameters
        private String x;
    
        @ArgGroup(exclusive = false)
        private Group group = new Group();
    
        static class Group {
            @ArgGroup(exclusive = false)
            private InnerGroup inner = new InnerGroup("aa", "bb"); // default values
    
            @Option(names = "-c", required = false, defaultValue = "cc")
            public String c = "cc";
        }
    
        static class InnerGroup {
            // default constructor, used by picocli when
            // one or more options in this group are 
            // matched on the command line
            public InnerGroup() {}
    
            // this constructor assigns default values,
            // used only when *both* options are missing
            public InnerGroup(String a, String b) {
                this.a = a;
                this.b = b;
            }
    
            @Option(names = "-a", required = true)
            public String a;
    
            @Option(names = "-b", required = true)
            public String b;
        }
    
        @Override
        public void run() {
            System.out.printf("a=%s, b=%s, c=%s, x=%s%n", 
                    group.inner.a, group.inner.b, group.c, x);
        }
    }