Better bounds checking
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Thu, 20 Mar 2025 21:39:26 +0000 (23:39 +0200)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Thu, 20 Mar 2025 21:39:26 +0000 (23:39 +0200)
src/main/java/eu/svjatoslav/commons/cli_helper/CLIHelper.java

index 16e5aa2..7866fc6 100755 (executable)
@@ -6,10 +6,6 @@ package eu.svjatoslav.commons.cli_helper;
 
 import java.util.Scanner;
 
-import static java.lang.Float.parseFloat;
-import static java.lang.Long.parseLong;
-import static java.lang.String.valueOf;
-
 /**
  * Command-line interface helper.
  */
@@ -62,24 +58,56 @@ public class CLIHelper {
 
     /**
      * Asks the user for a float value using the specified prompt on the command line.
-     * The user is prompted until a valid float within the specified range [min..max] is provided.
-     * An empty input (just pressing ENTER) will use the provided defaultValue.
+     * The user is prompted until a valid float (within optional ranges) is provided.
+     * <ul>
+     *   <li>If {@code defaultValue} is specified and user presses Enter, that default is returned.</li>
+     *   <li>If {@code defaultValue} is {@code null} and user presses Enter, returns {@code null}.</li>
+     *   <li>If {@code min} is not {@code null}, we enforce that the entered float is >= min.</li>
+     *   <li>If {@code max} is not {@code null}, we enforce that the entered float is <= max.</li>
+     * </ul>
      *
-     * @param prompt       the message to display to the user
-     * @param defaultValue the float value to use if the user provides no input
-     * @param min          the minimum acceptable value (inclusive)
-     * @param max          the maximum acceptable value (inclusive)
-     * @return a float value entered by the user, or {@code defaultValue} if empty input
+     * @param prompt       The prompt displayed to the user
+     * @param defaultValue The default float if user simply presses Enter (may be null)
+     * @param min          The minimum acceptable value (inclusive), or null if no lower bound
+     * @param max          The maximum acceptable value (inclusive), or null if no upper bound
+     * @return A Float value that the user entered, or the defaultValue, or null if no defaultValue was given
      */
-    public float askFloat(String prompt, float defaultValue, float min, float max) {
+    public Float askFloat(String prompt, Float defaultValue, Float min, Float max) {
         while (true) {
-            String input = askString(prompt, valueOf(defaultValue));
+            // If we have a defaultValue, display it in brackets; otherwise display no default
+            String displayPrompt = prompt
+                    + (defaultValue != null ? " [" + defaultValue + "]" : "")
+                    + ": ";
+
+            // Read user input
+            System.out.print(displayPrompt);
+            String input = new Scanner(System.in).nextLine().trim();
+
+            // If user just pressed Enter:
+            if (input.isEmpty()) {
+                // If a defaultValue was supplied, return it; else return null
+                return defaultValue;
+            }
+
+            // Parse float value
             try {
-                float parsedValue = parseFloat(input);
-                if (parsedValue >= min && parsedValue <= max) {
-                    return parsedValue;
+                float parsedValue = Float.parseFloat(input);
+
+                // Check against min if specified
+                if (min != null && parsedValue < min) {
+                    System.out.println("Value must be at least " + min + ".");
+                    continue;
                 }
-                System.out.println("Value must be between " + min + " and " + max + ".");
+
+                // Check against max if specified
+                if (max != null && parsedValue > max) {
+                    System.out.println("Value must be at most " + max + ".");
+                    continue;
+                }
+
+                // Parsed successfully within optional bounds
+                return parsedValue;
+
             } catch (NumberFormatException e) {
                 System.out.println("Invalid number format. Try again.");
             }
@@ -88,24 +116,56 @@ public class CLIHelper {
 
     /**
      * Asks the user for a long value using the specified prompt on the command line.
-     * The user is prompted until a valid long within the specified range [min..max] is provided.
-     * An empty input (just pressing ENTER) will use the provided defaultValue.
+     * The user is prompted until a valid long (within optional ranges) is provided.
+     * <ul>
+     *   <li>If {@code defaultValue} is specified and user presses Enter, that default is returned.</li>
+     *   <li>If {@code defaultValue} is {@code null} and user presses Enter, returns {@code null}.</li>
+     *   <li>If {@code min} is not {@code null}, we enforce that the entered long is >= min.</li>
+     *   <li>If {@code max} is not {@code null}, we enforce that the entered long is <= max.</li>
+     * </ul>
      *
-     * @param prompt       the message to display to the user
-     * @param defaultValue the long value to use if the user provides no input
-     * @param min          the minimum acceptable value (inclusive)
-     * @param max          the maximum acceptable value (inclusive)
-     * @return a long value entered by the user, or {@code defaultValue} if empty input
+     * @param prompt       The prompt displayed to the user
+     * @param defaultValue The default long if user simply presses Enter (may be null)
+     * @param min          The minimum acceptable value (inclusive), or null if no lower bound
+     * @param max          The maximum acceptable value (inclusive), or null if no upper bound
+     * @return A Long value that the user entered, or the defaultValue, or null if no defaultValue was given
      */
-    public long askLong(String prompt, long defaultValue, long min, long max) {
+    public Long askLong(String prompt, Long defaultValue, Long min, Long max) {
         while (true) {
-            String input = askString(prompt, valueOf(defaultValue));
+            // If we have a defaultValue, display it in brackets; otherwise display no default
+            String displayPrompt = prompt
+                    + (defaultValue != null ? " [" + defaultValue + "]" : "")
+                    + ": ";
+
+            // Read user input
+            System.out.print(displayPrompt);
+            String input = new Scanner(System.in).nextLine().trim();
+
+            // If user just pressed Enter:
+            if (input.isEmpty()) {
+                // If a defaultValue was supplied, return it; else return null
+                return defaultValue;
+            }
+
+            // Parse long value
             try {
-                long parsedValue = parseLong(input);
-                if (parsedValue >= min && parsedValue <= max) {
-                    return parsedValue;
+                long parsedValue = Long.parseLong(input);
+
+                // Check against min if specified
+                if (min != null && parsedValue < min) {
+                    System.out.println("Value must be at least " + min + ".");
+                    continue;
+                }
+
+                // Check against max if specified
+                if (max != null && parsedValue > max) {
+                    System.out.println("Value must be at most " + max + ".");
+                    continue;
                 }
-                System.out.println("Value must be between " + min + " and " + max + ".");
+
+                // Parsed successfully within optional bounds
+                return parsedValue;
+
             } catch (NumberFormatException e) {
                 System.out.println("Invalid number format. Try again.");
             }