• ConsoleFieldScanner.java

  • §
    package MusicLandscape.util;
    
    import java.util.Scanner;
    import java.util.function.Function;
    import java.util.function.Predicate;
    
    /**
     * Generic console function for getting a new value for an entity field from the user.
     *
     * <p>
     * Example:
     *
     * <pre>
     * Integer num = new ConsoleFieldScanner(Integer::parseInt, (Integer i) -&gt; i &gt; 0, null)
     *     .scan("Give me a number")
     * </pre>
     *
     * Produces this output:
     *
     * <pre>
     * Give me a number: &lt;user inputs 123 and presses Enter&gt;
     * </pre>
     *
     * Leading to the variable <kbd>num</kbd> having the value <kbd>123</kbd>.
     *
     * @param <T> The type of the value being read
     * @author Jonas Altrock (ew20b126@technikum-wien.at)
     * @version 1
     * @since ExerciseSheet04
     */
    public class ConsoleFieldScanner<T> {
        /**
         * constant to allow readable indication of a skippable input
         */
        public static final boolean SKIPPABLE = true;
        /**
         * constant to allow readable indication of a non-skippable input
         */
        public static final boolean NOT_SKIPPABLE = false;
    
        /**
         * The default input scanner. Uses System.in if not set from outside.
         */
        public static Scanner defaultScanner;
    
        /**
         * Whether the input can be skipped (by pressing Enter for example).
         */
        public boolean skippable = SKIPPABLE;
    
        /**
         * The input to value transformer function.
         */
        public Function<String, T> transformer;
    
        /**
         * The input value validator function.
         */
        public Predicate<T> validator;
    
        /**
         * The scanner object in use.
         */
        public Scanner scanner;
    
        /**
         * Create a scanner for a single value. Skippable by default.
         *
         * @param transformer a function that transforms the input string to a value
         * @param validator a function that validates the given value
         * @param scanner optional Scanner to use, pass null to use the default scanner (System.in)
         */
        public ConsoleFieldScanner(
                Function<String, T> transformer,
                Predicate<T> validator,
                Scanner scanner
        ) {
            this.transformer = transformer;
            this.validator = validator;
    
            if (scanner != null) {
                this.scanner = scanner;
            } else {
                this.scanner = getDefaultScanner();
            }
        }
    
        /**
         * Create a scanner for a single value.
         *
         * @param transformer a function that transforms the input string to a value
         * @param validator a function that validates the given value
         * @param scanner optional Scanner to use, pass null to use the default scanner (System.in)
         * @param skippable whether the user is allowed to skip entry by just pressing Enter
         */
        public ConsoleFieldScanner(
                Function<String, T> transformer,
                Predicate<T> validator,
                Scanner scanner,
                boolean skippable
        ) {
            this(transformer, validator, scanner);
            this.skippable = skippable;
        }
    
        /**
         * Initialise the default scanner object.
         *
         * @return the default scanner
         */
        protected Scanner getDefaultScanner() {
            if (defaultScanner == null) {
                defaultScanner = new Scanner(System.in);
            }
            return defaultScanner;
        }
    
        /**
         * Prompt the user for an input value.
         *
         * @param message string to print before prompt input
         * @return a value or null, if no value was given
         */
        public T scan(String message) {
            T value;
    
            do {
                System.out.print(message + ": ");
    
                try {
                    String in = scanner.nextLine();
    
                    if (skippable && in.isEmpty()) {
                        return null;
                    }
    
                    value = transformer.apply(in);
                } catch (Exception e) {
                    value = null;
                }
    
                if (value == null) {
                    System.out.println("Invalid input, try again?");
                    continue;
                }
    
                if (!validator.test(value)) {
                    System.out.println("Value not allowed, try again?");
                    value = null;
                }
            } while (value == null);
    
            return value;
        }
    
    
    }