/** * Reads a value of type T. * It repeatedly prompts the users to enter the value, until they provide a valid input string. * @param prompt the messages to be displayed for prompting the user to enter the value * @return the value of type T parsed from the input string */ public T read(String... prompt) { return read(Arrays.asList(prompt)); }
/** * Reads a value of type T. * It repeatedly prompts the user to enter a value, until a valid input string is provided. * @param prompt the list of messages to be displayed for prompting the user to enter the value * @return the value of type T parsed from the input string */ public T read(List<String> prompt) { valueListMode = false; checkConfiguration(); return executeWithTerminal(textTerminal -> { while(true) { String sVal = readWithPrompt(textTerminal, prompt); if(sVal != null && inputTrimming) sVal = sVal.trim(); if(sVal == null || sVal.isEmpty()) { if(defaultValue != null) return defaultValue; } T value = getValueFromStringOrIndex(sVal, textTerminal); if (value != null) return value; } }); }
/** * If no {@link #parseErrorMessagesProvider} exists, this method is used to provide the list of error messages for the input string <tt>s</tt>. * It should return a non-empty list of messages. */ protected List<String> getDefaultErrorMessages(String s) { return new ArrayList<>(Collections.singleton(getDefaultErrorMessage(s))); }
private T getValueFromStringOrIndex(String sVal, TextTerminal<?> textTerminal) { if(possibleValues == null || !numberedPossibleValues) return getValueFromString(sVal, textTerminal); else return getValueFromIndex(sVal, textTerminal); }
@Override public void run() { setChoices(choices.stream().map(Object::toString).collect(Collectors.toList())); setHistory(historyStore.getValues(key)); try { R inputReader = inputReaderSupplier.get(); if (showPrevious) inputReader.withDefaultValue(defaultValueSupplier.get()); if(inputReaderConfigurator != null) { inputReaderConfigurator.accept(inputReader); } if(constrainedInput) { inputReader.withValueChecker((val,name) -> choices.contains(val) ? null : Arrays.asList("'" + val + "' is not in the choice list.")); } T value = inputReader.read(prompt); historyStore.addValue(key, value.toString()); valueSetter.accept(value); } finally { setChoices(Collections.emptyList()); setHistory(Collections.emptyList()); } }
private T getValueFromString(String sVal, TextTerminal<?> textTerminal) { ParseResult<T> result = parseAndCheck(sVal); List<String> errMessages = result.getErrorMessages(); if(errMessages == null) { Optional<T> value = getPossibleValue(result.getValue()); if(value.isPresent()) return value.get(); textTerminal.executeWithPropertiesPrefix(PROPS_PREFIX_ERROR_MESSAGE, t -> { t.print(getDefaultErrorMessage(sVal)); if(inlinePossibleValues) { String options = possibleValues.stream() .map(val -> "'" + valueFormatter.apply(val) + "'") .collect(Collectors.joining(", ")); t.println(" Please enter one of: " + options + "."); } else { t.println(" Please enter one of the displayed values."); } }); textTerminal.println(); } else { textTerminal.executeWithPropertiesPrefix(PROPS_PREFIX_ERROR_MESSAGE, t -> t.println(errMessages)); textTerminal.println(); } return null; }
/** * Parses the input string and runs all value checkers in order to find constraint violations. * @param s the input string * @return a {@link ParseResult} that holds the parsed value and/or the error messages, if errors occurred. */ protected ParseResult<T> parseAndCheck(String s) { ParseResult<T> res = parse(s); if(res.errorMessages == null) { List<String> allErrors = new ArrayList<>(); for(ValueChecker<T> checker : valueCheckers) { List<String> errors = checker.getErrorMessages(res.value, itemName); if(errors != null) allErrors.addAll(errors); } if(!allErrors.isEmpty()) { allErrors.add(0, getDefaultErrorMessage(s)); res = new ParseResult<>(res.value, allErrors); } } return res; }
@Override protected List<String> getDefaultErrorMessages(String s) { List<String> errList = super.getDefaultErrorMessages(s); errList.add("Expected: " + trueInput + " / " + falseInput); return errList; }
/** In addition to the checks performed by {@link InputReader#checkConfiguration()}, it checks if minVal <= maxVal */ @Override protected void checkConfiguration() throws IllegalArgumentException { super.checkConfiguration(); if(minLength > 0 && maxLength > 0 && minLength > maxLength) throw new IllegalArgumentException("minLength = " + minLength + ", maxLength = " + maxLength); }
/** * Checks if the reader is correctly configured. * This default implementation checks if the defaultValue is among the possibleValues. * @throws java.lang.IllegalArgumentException */ protected void checkConfiguration() throws java.lang.IllegalArgumentException { if(defaultValue != null && !isPossibleValue(defaultValue)) { throw new IllegalArgumentException("Invalid default value: " + valueFormatter.apply(defaultValue) + ". Allowed values: " + possibleValues); } for(ValueChecker<T> checker : valueCheckers) { List<String> errors; if(defaultValue != null) { errors = checker.getErrorMessages(defaultValue, itemName); if(errors != null) throw new IllegalArgumentException("Invalid default value: " + valueFormatter.apply(defaultValue) + ".\n" + errors); } if(possibleValues != null) { for(T val : possibleValues) { errors = checker.getErrorMessages(val, itemName); if(errors != null) throw new IllegalArgumentException("Invalid entry in the list of possible values: " + valueFormatter.apply(val) + ".\n" + errors); } } } }
/** * Provides the list of error messages for the input string <tt>s</tt>. * If a {@link #parseErrorMessagesProvider} exists, it will be used. Otherwise, {@link #getDefaultErrorMessages(String)} will be called. */ protected final List<String> getErrorMessages(String s) { if(parseErrorMessagesProvider != null) return parseErrorMessagesProvider.getErrorMessages(s, itemName); return getDefaultErrorMessages(s); }
/** In addition to the checks performed by {@link InputReader#checkConfiguration()}, it checks if minVal <= maxVal */ @Override public void checkConfiguration() throws IllegalArgumentException { super.checkConfiguration(); if(minVal != null && maxVal != null && minVal.compareTo(maxVal) > 0) throw new IllegalArgumentException("minVal = " + minVal + ", maxVal = " + maxVal); }
public List<T> readList(List<String> prompt) { valueListMode = true; checkConfiguration(); return executeWithTerminal(textTerminal -> { mainLoop: while(true) { String sInput = readWithPrompt(textTerminal, prompt); String[] sValues = (sInput == null) ? new String[0] : sInput.split(","); if(inputTrimming) { List<T> values = new ArrayList<>(); for(String sVal : sValues) { T value = getValueFromStringOrIndex(sVal, textTerminal); if(value == null) continue mainLoop; values.add(value); allErrors.add(0, getDefaultErrorMessage(null)); textTerminal.executeWithPropertiesPrefix(PROPS_PREFIX_ERROR_MESSAGE, t ->t.println(allErrors)); textTerminal.println();
private T getValueFromIndex(String sVal, TextTerminal<?> textTerminal) { try { int optIndex = Integer.parseInt(sVal); if(optIndex > 0 && optIndex <= possibleValues.size()) { return possibleValues.get(optIndex - 1); } } catch (NumberFormatException e) { // Continue the execution. The next statement will print the error message. } textTerminal.executeWithPropertiesPrefix(PROPS_PREFIX_ERROR_MESSAGE, t -> { textTerminal.print(getDefaultErrorMessage(sVal)); textTerminal.println(" Enter a value between 1 and " + possibleValues.size() + "."); }); textTerminal.println(); return null; }