import java.util.Scanner;
/**
- * Command-line interface helper.
+ * <p>
+ * A utility class that provides methods for reading and validating various
+ * data types from the command line. The methods in this class prompt the user
+ * with a given message, read the user’s input, validate and parse that input,
+ * and then either return a valid value or re-prompt on invalid input.
+ * </p>
+ *
+ * <p>
+ * Each method optionally supports:
+ * </p>
+ * <ul>
+ * <li>Returning a specified default value if the user presses ENTER without providing input.</li>
+ * <li>Allowing empty input (returning <code>null</code>) when no default value is provided.</li>
+ * <li>Enforcing minimum/maximum ranges or lengths (when applicable).</li>
+ * </ul>
+ *
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * Boolean answer = CLIHelper.askBoolean("Do you want to continue?", true);
+ * Integer number = CLIHelper.askInteger("Enter a number between 5 and 10:", 7, 5, 10, false);
+ * String name = CLIHelper.askString("What's your name?", "Anonymous", 2, 20, false);
+ * }
+ * </pre>
*/
public class CLIHelper {
+ /**
+ * A single Scanner for all input reading, so we aren't creating new Scanners on
+ * {@link System#in} repeatedly.
+ */
+ private static final Scanner SCANNER = new Scanner(System.in);
+
/**
* Asks the user for a boolean value using the specified prompt on the command line.
- * The user may respond with one of the following to indicate "true":
- * <ul>
- * <li>y</li>
- * <li>yes</li>
- * <li>true</li>
- * </ul>
- * ...or one of the following to indicate "false":
+ * <p>
+ * Valid “true” responses are: <code>y</code>, <code>yes</code>, <code>true</code>.
+ * Valid “false” responses are: <code>n</code>, <code>no</code>, <code>false</code>.
+ * </p>
+ * <p>
+ * If the user presses ENTER (empty input):
+ * </p>
* <ul>
- * <li>n</li>
- * <li>no</li>
- * <li>false</li>
+ * <li>and {@code defaultValue} is non-null, return it;</li>
+ * <li>otherwise if {@code allowEmpty} is true, return null;</li>
+ * <li>otherwise keep prompting until valid input is given.</li>
* </ul>
- * If the user presses ENTER (empty input), the provided defaultValue is returned.
*
* @param prompt the message to display to the user
- * @param defaultValue the boolean value to return if the user provides no input. If null, user must provide input.
- * @return {@code true} if the user answered affirmatively,
- * {@code false} if they answered negatively,
- * or {@code defaultValue} if input was empty
+ * @param defaultValue the boolean value to return if the user provides no input (may be {@code null})
+ * @param allowEmpty if {@code true}, empty input is acceptable (and method returns {@code null} if no default); otherwise keep asking
+ * @return {@code true} if the user answered affirmatively, {@code false} if negatively, or the default/empty result as described above
*/
public static Boolean askBoolean(final String prompt, final Boolean defaultValue, boolean allowEmpty) {
while (true) {
-
// 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 line = new Scanner(System.in).nextLine().trim();
- // If user just pressed Enter:
- if (line.isEmpty()){
+ String line = SCANNER.nextLine().trim();
- // If a defaultValue was supplied, return it
+ // If user just pressed Enter:
+ if (line.isEmpty()) {
if (defaultValue != null) {
return defaultValue;
}
-
if (allowEmpty) {
return null;
} else {
}
final String userInput = line.toLowerCase();
-
if ("y".equals(userInput) || "yes".equals(userInput) || "true".equals(userInput)) {
return true;
}
if ("n".equals(userInput) || "no".equals(userInput) || "false".equals(userInput)) {
return false;
}
-
System.out.println("Invalid input. Please enter y/yes/true or n/no/false.");
}
}
+ /**
+ * Convenience method that calls {@link #askBoolean(String, Boolean, boolean)} with {@code allowEmpty = false}.
+ *
+ * @param prompt the message to display to the user
+ * @param defaultValue the boolean value to return if the user provides no input (may be {@code null})
+ * @return {@code true} if the user answered affirmatively, {@code false} if negatively, or the default if provided
+ */
public static Boolean askBoolean(String prompt, Boolean defaultValue) {
return askBoolean(prompt, defaultValue, false);
}
+ /**
+ * Convenience method that calls {@link #askBoolean(String, Boolean, boolean)} with no default and {@code allowEmpty = false}.
+ *
+ * @param prompt the message to display to the user
+ * @return {@code true} if the user answered affirmatively, {@code false} if negatively
+ * @throws IllegalStateException if the user never provides a valid input
+ */
public static Boolean askBoolean(String prompt) {
return askBoolean(prompt, null, false);
}
/**
* Asks the user for a float value using the specified prompt on the command line.
- * The user is prompted until a valid float (within optional ranges) is provided.
+ * <p>
+ * The user is prompted until a valid float (optionally enforced via {@code min}/{@code max})
+ * is provided. If {@code defaultValue} is specified and the user presses Enter, that default is
+ * returned. If {@code defaultValue} is {@code null} and the user presses Enter, the behavior
+ * depends on {@code allowEmpty}:
+ * </p>
* <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 {@code >= min}.</li>
- * <li>If {@code max} is not {@code null}, we enforce that the entered float is {@code <= max}.</li>
+ * <li>if {@code allowEmpty} is true, return {@code null};</li>
+ * <li>otherwise, keep prompting until valid input is given.</li>
* </ul>
*
- * @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
+ * @param prompt the prompt displayed to the user
+ * @param defaultValue the default float if user simply presses Enter (may be {@code null})
+ * @param min the minimum acceptable value (inclusive), or {@code null} if no lower bound
+ * @param max the maximum acceptable value (inclusive), or {@code null} if no upper bound
+ * @param allowEmpty if {@code true}, empty input returns {@code null} when {@code defaultValue} is null
+ * @return a {@code Float} value entered by the user, or {@code defaultValue}, or {@code null}
*/
public static Float askFloat(String prompt, Float defaultValue, Float min, Float max, boolean allowEmpty) {
while (true) {
- // 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();
+ String input = SCANNER.nextLine().trim();
- if (input.isEmpty()){
- // If a defaultValue was supplied, return it
+ if (input.isEmpty()) {
if (defaultValue != null) {
return defaultValue;
}
-
if (allowEmpty) {
return null;
} else {
}
}
-
- // Parse float value
try {
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;
}
-
- // 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.");
}
}
}
+ /**
+ * Convenience method for {@link #askFloat(String, Float, Float, Float, boolean)} with no min/max range and {@code allowEmpty = false}.
+ */
public static Float askFloat(String prompt, Float defaultValue) {
return askFloat(prompt, defaultValue, null, null, false);
}
+ /**
+ * Convenience method for {@link #askFloat(String, Float, Float, Float, boolean)} with no defaultValue or min/max range and {@code allowEmpty = false}.
+ */
public static Float askFloat(String prompt) {
return askFloat(prompt, null, null, null, false);
}
/**
* Asks the user for a long value using the specified prompt on the command line.
- * The user is prompted until a valid long (within optional ranges) is provided.
+ * <p>
+ * The user is prompted until a valid long (optionally enforced via {@code min}/{@code max})
+ * is provided. If {@code defaultValue} is specified and the user presses Enter, that default is
+ * returned. If {@code defaultValue} is {@code null} and the user presses Enter, the behavior
+ * depends on {@code allowEmpty}:
+ * </p>
* <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 {@code >= min}.</li>
- * <li>If {@code max} is not {@code null}, we enforce that the entered long is {@code <= max}.</li>
+ * <li>if {@code allowEmpty} is true, return {@code null};</li>
+ * <li>otherwise, keep prompting until valid input is given.</li>
* </ul>
*
- * @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
+ * @param prompt the prompt displayed to the user
+ * @param defaultValue the default long if user simply presses Enter (may be {@code null})
+ * @param min the minimum acceptable value (inclusive), or {@code null} if no lower bound
+ * @param max the maximum acceptable value (inclusive), or {@code null} if no upper bound
+ * @param allowEmpty if {@code true}, empty input returns {@code null} when {@code defaultValue} is null
+ * @return a {@code Long} value entered by the user, or {@code defaultValue}, or {@code null}
*/
public static Long askLong(String prompt, Long defaultValue, Long min, Long max, boolean allowEmpty) {
while (true) {
- // 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 (input.isEmpty()){
+ String input = SCANNER.nextLine().trim();
- // If a defaultValue was supplied, return it
+ if (input.isEmpty()) {
if (defaultValue != null) {
return defaultValue;
}
-
if (allowEmpty) {
return null;
} else {
}
}
- // Parse long value
try {
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;
}
-
- // Parsed successfully within optional bounds
return parsedValue;
-
} catch (NumberFormatException e) {
System.out.println("Invalid number format. Try again.");
}
}
}
+ /**
+ * Convenience method for {@link #askLong(String, Long, Long, Long, boolean)} with no min/max range and {@code allowEmpty = false}.
+ */
public static Long askLong(String prompt, Long defaultValue) {
return askLong(prompt, defaultValue, null, null, false);
}
+ /**
+ * Convenience method for {@link #askLong(String, Long, Long, Long, boolean)} with no defaultValue or min/max range and {@code allowEmpty = false}.
+ */
public static Long askLong(String prompt) {
return askLong(prompt, null, null, null, false);
}
-
/**
* Asks the user for an integer value using the specified prompt on the command line.
- * The user is prompted until a valid integer (within optional ranges) is provided.
+ * <p>
+ * The user is prompted until a valid integer (optionally enforced via {@code min}/{@code max})
+ * is provided. If {@code defaultValue} is specified and the user presses Enter, that default is
+ * returned. If {@code defaultValue} is {@code null} and the user presses Enter, the behavior
+ * depends on {@code allowEmpty}:
+ * </p>
* <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 integer is {@code >= min}.</li>
- * <li>If {@code max} is not {@code null}, we enforce that the entered integer is {@code <= max}.</li>
+ * <li>if {@code allowEmpty} is true, return {@code null};</li>
+ * <li>otherwise, keep prompting until valid input is given.</li>
* </ul>
*
- * @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.
- * @param allowEmpty If <code>true</code>, empty input is acceptable. Otherwise keep asking user.
- * @return An integer value that the user entered, or the defaultValue, or null if no defaultValue was given.
+ * @param prompt the prompt displayed to the user
+ * @param defaultValue the default integer if the user simply presses Enter (may be {@code null})
+ * @param min the minimum acceptable value (inclusive), or {@code null} if no lower bound
+ * @param max the maximum acceptable value (inclusive), or {@code null} if no upper bound
+ * @param allowEmpty if {@code true}, empty input returns {@code null} when {@code defaultValue} is null
+ * @return an {@code Integer} value that the user entered, or the {@code defaultValue}, or {@code null} if allowed
*/
public static Integer askInteger(String prompt, Integer defaultValue, Integer min, Integer max, boolean allowEmpty) {
while (true) {
- // 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()){
+ String input = SCANNER.nextLine().trim();
- // If a defaultValue was supplied, return it
+ if (input.isEmpty()) {
if (defaultValue != null) {
return defaultValue;
}
-
if (allowEmpty) {
return null;
} else {
}
}
- // Parse long value
try {
int parsedValue = Integer.parseInt(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;
}
-
- // Parsed successfully within optional bounds
return parsedValue;
-
} catch (NumberFormatException e) {
System.out.println("Invalid number format. Try again.");
}
}
}
+ /**
+ * Convenience method for {@link #askInteger(String, Integer, Integer, Integer, boolean)} with no min/max range and {@code allowEmpty = false}.
+ */
public static Integer askInteger (String prompt, Integer defaultValue) {
return askInteger(prompt, defaultValue, null, null, false);
}
+ /**
+ * Convenience method for {@link #askInteger(String, Integer, Integer, Integer, boolean)} with no defaultValue or min/max range and {@code allowEmpty = false}.
+ */
public static Integer askInteger (String prompt) {
return askInteger(prompt, null, null, null, false);
}
+
/**
* Asks the user for a string value using the specified prompt on the command line.
- * If the user presses ENTER without typing anything and {@code defaultValue} is non-null,
- * that default value is returned.
+ * <p>
+ * If the user presses ENTER without typing anything:
+ * </p>
+ * <ul>
+ * <li>and {@code defaultValue} is non-null, return that default;</li>
+ * <li>otherwise if {@code allowEmpty} is true, return {@code null};</li>
+ * <li>otherwise, keep prompting until valid input is given.</li>
+ * </ul>
+ * <p>
+ * Additionally, if {@code minLength} or {@code maxLength} are specified, the input
+ * must fall within those character bounds.
+ * </p>
*
* @param prompt the message to display to the user
- * @param defaultValue the value to return if the user provides no input
- * @return the value typed by the user, or {@code defaultValue} if empty input
+ * @param defaultValue the value to return if the user provides no input (may be {@code null})
+ * @param minLength the minimum number of characters required, or {@code null} if no lower bound
+ * @param maxLength the maximum number of characters allowed, or {@code null} if no upper bound
+ * @param allowEmpty if {@code true}, empty input returns {@code null} when {@code defaultValue} is null
+ * @return the value typed by the user, {@code defaultValue}, or {@code null} as described above
*/
public static String askString(String prompt, String defaultValue, Integer minLength, Integer maxLength, boolean allowEmpty) {
while (true) {
- // 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();
+ String input = SCANNER.nextLine().trim();
- if (input.isEmpty()){
-
- // If a defaultValue was supplied, return it
+ if (input.isEmpty()) {
if (defaultValue != null) {
return defaultValue;
}
-
if (allowEmpty) {
return null;
} else {
System.out.println("Input must be at least " + minLength + " characters long.");
continue;
}
-
if (maxLength != null && input.length() > maxLength) {
System.out.println("Input must be at most " + maxLength + " characters long.");
continue;
}
}
+ /**
+ * Convenience method for {@link #askString(String, String, Integer, Integer, boolean)} with no min/max length and {@code allowEmpty = false}.
+ */
public static String askString(String prompt, String defaultValue) {
return askString(prompt, defaultValue, null, null, false);
}
+ /**
+ * Convenience method for {@link #askString(String, String, Integer, Integer, boolean)} with no defaultValue or min/max length and {@code allowEmpty = false}.
+ */
public static String askString(String prompt) {
return askString(prompt, null, null, null, false);
}
-
}