Search code examples
converterspicocli

Picocli: argument type mismatch while processing argument at or before arg[1]; converter: String to double[]


This Code:

public class Test {
    @CommandLine.Option(names = {"-c", "--crop"}, converter = Test.checkCropping.class)
    public double[] foo;

    public static void main(String[] args) {
        new CommandLine(new Test()).parseArgs(args);
    }

    private static class checkCropping implements CommandLine.ITypeConverter<double[]> {
        public double[] convert(String outputPixel) throws IllegalArgumentException {
            return new double[]{0.0,0.0,0.0,0.0};
        }
    }
}

throws this Error:

Exception in thread "main" picocli.CommandLine$ParameterException: IllegalArgumentException: argument type mismatch while processing argument at or before arg[1] '100x100+0+0' in [-c, 100x100+0+0]: java.lang.IllegalArgumentException: argument type mismatch
    at picocli.CommandLine$ParameterException.create(CommandLine.java:18062)
    at picocli.CommandLine$ParameterException.access$20400(CommandLine.java:17986)
    at picocli.CommandLine$Interpreter.parse(CommandLine.java:13186)
    at picocli.CommandLine$Interpreter.parse(CommandLine.java:13146)
    at picocli.CommandLine$Interpreter.parse(CommandLine.java:13047)
    at picocli.CommandLine.parseArgs(CommandLine.java:1478)
    at Test.main(Test.java:13)

Thanks for your help.

I tried different variable types of foo, but could not find the solution.


Solution

  • Interesting problem! Picocli considers the --crop option in your example a multi-valued option, that is, the end user may specify the option multiple times, like this: -c=1.0 -c=2 -c=3.0, and picocli will add all these values to the foo array.

    It appears that this is not what you want, you want the option to be specified a single time, with some encoded string parameter, and your custom type converter should decode this parameter and create a double[] result.

    This does not match with picocli's interpretation of array types.

    However, there are still ways to achieve your objective.

    One idea is to declare the foo field as a 2-dimensional array:

    @Option(names = {"-c", "--crop"}, converter = Test.checkCropping.class)
    public double[][] foo;
    

    I believe (I haven't tested it) that this will allow your custom type converter to take single string values and convert them to double[] values that will be added to the double[][] foo two-dimensional array. This is not ideal, because it will still allow the end user to specify the --crop option multiple times, and each occurrence will result in a double[] array object to be added to the 2-dimensional array.

    An alternative is to create your own custom type, maybe have a Pixel type with its own fields, and then have a PixelConverter that converts the String into a single Pixel instance. The foo field would then be of type Pixel if --crop should only be specified once, and can be of type Pixel[] if users may specify the --crop option multiple times.

    Personally I like the last solution best. Let me know how it goes!