marinier
marinier

Reputation: 509

How to use picocli to handle option with multiple types

I'm converting an existing application to use picocli. One of the existing options looks like this:

-t, --threads           [1, n] for fixed thread pool, 'cpus' for number of cpus, 'cached' for cached

This allows the option to be a positive integer or one of a couple special strings. The existing code treats it as a string, and if it's not one of the special strings, passes it to Integer.parseInt.

I can do the same thing with picocli, of course, but I was wondering if there was a better way to handle this. E.g., something that allows multiple fields to be used for the same option, with the appropriate one being filled in based on what was passed? This might also allow me to use a enum for the possible string options.

Upvotes: 2

Views: 839

Answers (1)

Remko Popma
Remko Popma

Reputation: 36754

One idea is to create a class for this, like, maybe ThreadPoolSize, that encapsulates either a fixed numeric value or an enum for dynamic values. You would need to create a custom converter for this data type.

Then you can define the option as follows:

@Option(names = { "-t", "--threads" }, converter = ThreadPoolSizeConverter.class,
  description = "[1, n] for fixed thread pool, 'cpus' for number of cpus, 'cached' for cached")
ThreadPoolSize threadPoolSize;

The custom data type for thread pool size and the converter can look something like this:

class ThreadPoolSize {
    enum Dynamic { cpus, cached }

    int fixed = -1;  // if -1, then use the dynamic value
    Dynamic dynamic; // if null, then use the fixed value
}

class ThreadPoolSizeConverter implements CommandLine.ITypeConverter<ThreadPoolSize> {

    @Override
    public ThreadPoolSize convert(String value) throws Exception {
        ThreadPoolSize result = new ThreadPoolSize();
        try {
            result.fixed = Integer.parseInt(value);
            if (result.fixed < 1) {
                throw new CommandLine.TypeConversionException("Invalid value " +
                        value + ": must be 1 or more.");
            }
        } catch (NumberFormatException nan) {
            try {
                result.dynamic = ThreadPoolSize.Dynamic.valueOf(
                        value.toLowerCase(Locale.ENGLISH));
            } catch (IllegalArgumentException ex) {
                throw new CommandLine.TypeConversionException("Invalid value " +
                        value + ": must be one of " + 
                        Arrays.toString(ThreadPoolSize.Dynamic.values()));
            }
        }
        return result;
    }
}

Upvotes: 2

Related Questions