Search code examples
picocli

Picocli: arbitrary length of paired parameters


In Picocli, is it possible to pair parameters of an arbitrary length? For example:

grades Abby 4.0 Billy 3.5 Caily 3.5 Danny 4.0

where each pair must have a name and a grade but the total length is unknown, i.e.:

grades <name> <grade> [<name> <grade>]*

A parameter map is the closest that appears might work, e.g.

@Parameters(index = "0..*") Map<String, float> grades;

would parse:

grades Abby=4.0 Billy=3.5 Caily=3.5 Danny=4.0

into the map but it'd be nicer if the equals wasn't there...


Solution

  • Update: picocli 4.3 has been released with improved support for positional parameters in argument groups.

    @Command(name = "grades", mixinStandardHelpOptions = true, version = "grades 1.0")
    public class Grades implements Runnable {
    
        static class StudentGrade {
            @Parameters(index = "0") String name;
            @Parameters(index = "1") BigDecimal grade;
        }
    
        @ArgGroup(exclusive = false, multiplicity = "1..*")
        List<StudentGrade> gradeList;
    
        @Override
        public void run() {
            gradeList.forEach(e -> System.out.println(e.name + ": " + e.grade));
        }
    
        public static void main(String[] args) {
            System.exit(new CommandLine(new Grades()).execute(args));
        }
    }
    

    Running the above program with this input:

    Alice 3.5 Betty 4.0 "X Æ A-12" 3.5 Zaphod 3.4
    

    Produces the following output:

    Alice: 3.5
    Betty: 4.0
    X Æ A-12: 3.5
    Zaphod: 3.4
    

    Prior to picocli 4.3, applications can do the following to accomplish this:

    import picocli.CommandLine;
    import picocli.CommandLine.Command;
    import picocli.CommandLine.Parameters;
    
    import java.math.BigDecimal;
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    
    @Command(name = "grades", mixinStandardHelpOptions = true, version = "grades 1.0")
    public class Grades implements Runnable {
    
        @Parameters(arity = "2",
                description = "Each pair must have a name and a grade.",
                paramLabel = "(NAME GRADE)...", hideParamSyntax = true)
        List<String> gradeList;
    
        @Override
        public void run() {
            System.out.println(gradeList);
            Map<String, BigDecimal> map = new LinkedHashMap<>();
            for (int i = 0; i < gradeList.size(); i += 2) {
                map.put(gradeList.get(i), new BigDecimal(gradeList.get(i + 1)));
            }
        }
    
        public static void main(String[] args) {
            int exitCode = new CommandLine(new Grades()).execute(args);
            System.exit(exitCode);
        }
    }