Picocli: лучший способ указать опцию с необязательным значением, которое печатает текущее значение, когда значение не указано - PullRequest
0 голосов
/ 21 сентября 2018

Я пишу REPL (поэтому я использую picocli для анализа команд, введенных в приложении, а не для анализа аргументов командной строки), и у меня есть команда с опцией, которую я хочу вести следующим образом:

> cmd --myopt
Myopt value = 5
> cmd --myopt 4
> cmd --myopt
Myopt value = 4

То есть, если опция указана без значения, печатается текущее значение параметра, но если оно указано со значением, то значение устанавливается.Я думал сделать это так:

int value = 1; // default
@Option(names = {"-e", "--epsilon"}, arity = "0..1",
        description = "Acceptable values: [0, 1] default: ${DEFAULT-VALUE}")
void setValue(String strValue) {
    if (strValue == "") {
        printValue();
    } else {
        try {
            value = Integer.parseInt(strValue);
            // validate value
        } catch (NumberFormatException e) {
            // print help for this option
        }
    }
}

Это лучший способ?Есть ли другой способ зафиксировать значение по умолчанию в описании, в то же время позволяя setValue узнать, что значение не указано?

(См. Также https://github.com/remkop/picocli/issues/490)

Ответы [ 2 ]

0 голосов
/ 21 сентября 2018

Я фактически выбрал другой подход;Моему приложению полезно иметь возможность напрямую назначать полю фактического типа (поскольку мы разрабатываем функцию, в которой вы можете «обнаружить» команды, принимающие аргументы различных типов, поэтому наличие поля в качестве фактического типа делает этообратный поиск проще).

Итак, в итоге я сделал это:

static class DoubleConverter implements ITypeConverter<Double> {
    public Double convert(String value) throws Exception {
        if(value.isEmpty()) return Double.NaN; // this is a special value that indicates the option was present without a value
        return Double.valueOf(value);
    }
}

@Option(names = {"-e", "--epsilon"}, arity="0..1", description="Acceptable values: [0, 1] default: 0.1", converter=DoubleConverter.class)
Double epsilon;

По сути, я использую конвертер для хранения специального значения (в данном случае NaN, потому что в итоге мы использовалиdouble), чтобы указать, что опция присутствовала без значения (которое отличается от того, что оно вообще не присутствовало, в этом случае оно было бы нулевым).

Затем проверка и другое поведение выполняются в run() метод, как вы предложили:

@Override
public void run() {
    // null indicates the option was not present, so do nothing
    if(epsilon != null) {
        // NaN indicates the option was present but with no value, which means we should print the current value
        if(epsilon.equals(Double.NaN)) {
            // print current value from the application
            printEpsilonValue();
        }
        else {
            // validate value
            if(epsilon < 0.0 || epsilon > 1.0) {
                throw new ParameterException(spec.commandLine(), "Invalid parameter value");
            } else {
                // set the value in the application
                setEpsilonValue(episilon);
            }
        }
    }       
}

Я не смог указать значение по умолчанию в описании, используя переменную, потому что фактическое значение по умолчанию в этом случае должно быть нулевым.Хотя это небольшая жертва.

Я понимаю, что это необычный случай, но было бы неплохо поддержать этот тип опции (не булево с арностью 0..n) без необходимости прибегать кособые ценности.Возможно, возможность указать другое поле в качестве логического значения, которое указывает, присутствовал ли параметр или нет.Тогда не было бы необходимости в настраиваемом конвертере, и, возможно, все еще можно было бы указать значение по умолчанию (т. Е. Поле Double в этом случае было бы установлено в значение по умолчанию, но если опция не присутствовала, соответствующее булево полебыть ложным, поэтому приложение будет знать, что не следует использовать значение Double).

0 голосов
/ 21 сентября 2018

Это, безусловно, один из способов сделать это.Имейте в виду, что метод установки может вызываться несколько раз: один раз для сброса значений по умолчанию, а затем снова каждый раз, когда параметр соответствует командной строке.

Альтернативой является изменение типа поля на String, добавление к полю аннотации @Option и вызов этой логики (то есть в методе setValue выше) из вашего run илиcall метод.

...