Как использовать picocli для обработки option с несколькими типами

#java #picocli

#java #picocli

Вопрос:

Я конвертирую существующее приложение для использования picocli. Один из существующих вариантов выглядит следующим образом:

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

Это позволяет параметру быть положительным целым числом или одной из пары специальных строк. Существующий код обрабатывает его как строку, и если это не одна из специальных строк, передает ее Integer.parseInt .

Конечно, я могу сделать то же самое с picocli, но мне было интересно, есть ли лучший способ справиться с этим. Например, что-то, что позволяет использовать несколько полей для одного и того же параметра, при этом соответствующий заполняется на основе того, что было передано? Это также может позволить мне использовать перечисление для возможных строковых параметров.

Ответ №1:

Одна из идей состоит в том, чтобы создать для этого класс, например, maybe ThreadPoolSize , который инкапсулирует либо фиксированное числовое значение, либо перечисление для динамических значений. Вам нужно будет создать пользовательский конвертер для этого типа данных.

Затем вы можете определить параметр следующим образом:

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

Пользовательский тип данных для размера пула потоков и преобразователя может выглядеть примерно так:

 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 resu<
    }
}

 

Комментарии:

1. Спасибо — это лучше, чем просто использовать строку! Интересно, может ли в picocli быть место для универсального составного типа — например, вы создаете класс, в котором есть поле для каждого возможного типа, и класс получает что-то вроде @Option(composite = true) . Затем он автоматически пытается преобразовать аргумент в каждый тип поля в том порядке, в котором они определены, останавливаясь при успешном завершении. Внутренние поля также могут быть аннотированы, если они требуют пользовательских преобразований и т. Д. Возможно, этот случай слишком редок, чтобы его стоило поддерживать напрямую.

2. Моя интуиция заключается в том, что делать это в пользовательском преобразователе типов, как мы сделали здесь, чище, чем пытаться поместить это в библиотеку.