Я конвертирую существующее приложение для использования picocli. Один из существующих вариантов выглядит так:
-t, --threads [1, n] for fixed thread pool, 'cpus' for number of cpus, 'cached' for cached
Это позволяет параметру быть положительным целым числом или одной из пары специальных строк. Существующий код рассматривает ее как строку и, если это не одна из специальных строк, передает ее в Integer.parseInt
.
Я могу сделать то же самое с пикокли, конечно, но мне было интересно, есть ли лучший способ справиться с этим. Например, что-то, что позволяет использовать несколько полей для одной и той же опции, при этом соответствующее поле заполняется в зависимости от того, что было передано? Это также может позволить мне использовать перечисление для возможных параметров строки.
Одна из идей состоит в том, чтобы создать для этого класс, например, 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 result;
}
}
Моя интуиция подсказывает, что делать это в пользовательском преобразователе типов, как мы сделали здесь, чище, чем пытаться впихнуть это в библиотеку.
Спасибо - это лучше, чем просто использование String! Интересно, есть ли в picocli место для универсального составного типа — например, вы создаете класс, в котором есть поле для каждого возможного типа, и класс получает что-то вроде
@Option(composite = true)
. Затем он автоматически пытается преобразовать arg в каждый тип поля в том порядке, в котором они определены, и останавливается в случае успеха. Внутренние поля также могут быть аннотированы, если они требуют пользовательских преобразований и т. д. Возможно, этот случай слишком редок, чтобы его стоило поддерживать напрямую.