io.bretty.console.view.AbstractView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of console-view Show documentation
Show all versions of console-view Show documentation
A Java framework to instantly build the View layer of your command line app. No more worries about anything like
printing menus or validating user inputs. Just focus on your app logic.
package io.bretty.console.view;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
* The parent class of all View classes, which defines infrastructure attributes (e.g. {@code parentView} and methods (e.g. {@code prompt()}
*/
public abstract class AbstractView {
/**
* the parent view of the current view
* if the current view is the root view, this field will always be {@code null}
*/
protected AbstractView parentView;
/**
* the string always to be displayed in the first line
* whenever this view is running
*/
protected String runningTitle;
/**
* the name of this view when displayed in a parent {@link MenuView MenuView}
*/
protected String nameInParentMenu;
/**
* the configuration of default strings in the UI
*/
protected ViewConfig viewConfig;
/**
* the {@code Scanner} to handle user command line input
*/
protected Scanner keyboard = new Scanner(System.in);
/**
* default constructor
*
* @param runningTitle see {@link io.bretty.console.view.AbstractView#runningTitle runningTitle}
* @param nameInParentMenu see {@link io.bretty.console.view.AbstractView#nameInParentMenu nameInParentView}
*/
public AbstractView(String runningTitle, String nameInParentMenu) {
this.runningTitle = runningTitle;
this.nameInParentMenu = nameInParentMenu;
this.viewConfig = ViewConfig.DEFAULT;
}
/**
* create a view with your own choice of words
*
* @param runningTitle see {@link io.bretty.console.view.AbstractView#runningTitle runningTitle}
* @param nameInParentMenu see {@link io.bretty.console.view.AbstractView#nameInParentMenu nameInParentView}
* @param viewConfig
*/
public AbstractView(String runningTitle, String nameInParentMenu, ViewConfig viewConfig) {
this.runningTitle = runningTitle;
this.nameInParentMenu = nameInParentMenu;
this.viewConfig = viewConfig;
}
/**
* display this view in the console
*/
public abstract void display();
/**
* Override this method to execute your own logic when the view is going back to the parent view
* Remember to call "super.onBack()" after your own logic
*/
protected void onBack() {
}
/**
* Override this method to execute your own logic when the user is quiting (i.e. this view doesn't have any parent view)
* Remember to call "super.onQuit()" after your own logic
*/
protected void onQuit() {
this.println(this.viewConfig.getQuitMessage());
}
/**
* Try to go back to the parent view.
* if the parent view is null, then print quit message and terminate the program;
* else if the parent view is a menu, then call the parent {@code .display()}
* else if the parent view is an action, then just return without printing anything
*/
protected void goBack() {
// if there is a parent view, call .onback()
if (this.parentView != null) {
this.onBack();
// if the parent view is a menu, display that menu
if(this.parentView instanceof MenuView){
this.parentView.display();
}
}
// if there is no parent view, call .onBack() and quit
else {
this.onQuit();
System.exit(0);
}
}
/**
* print the default pause message, and the user may press enter to continue.
*/
protected void pause() {
this.print(this.viewConfig.getPauseMessage());
this.keyboard.nextLine();
}
/**
* Show confirmation dialog; if confirmed, return; else, print {@code this.viewConfig.actionCanceledMessage}
*
* @param warningMessage e.g. "Make a booking now?"
* @return true if user has confirmed; false otherwise
*/
protected boolean confirmDialog(String warningMessage) {
String input = this.prompt(warningMessage + this.viewConfig.getConfirmOption(), String.class);
input = input.replace("\n", "");
boolean confirmed = this.viewConfig.getConfirmValidator().isValid(input);
if (!confirmed) {
this.actionCanceled();
}
return confirmed;
}
/**
* print {@code this.viewConfig.actionCanceledMessage}
*/
protected void actionCanceled() {
this.println(this.viewConfig.getActionCanceledMessage());
}
/**
* print {@code this.viewConfig.actionSuccessfulMessage}
*/
protected void actionSuccessful() {
this.println(this.viewConfig.getActionSuccessfulMessage());
}
/**
* print {@code this.viewConfig.actionFailedMessage}
*/
protected void actionFailed() {
this.println(this.viewConfig.getActionFailedMessage());
}
/**
* A wrapper of {@code System.out.print(Object o)}
*
* @param o the object to print
*/
protected void print(Object o) {
System.out.print(o);
}
/**
* A wrapper of {@code System.out.println(Object o)}
*
* @param o the object to print
*/
protected void println(Object o) {
System.out.println(o);
}
/**
* A wrapper of {@code System.out.println()}
*/
protected void println() {
System.out.println();
}
/**
* Read an object from command line user input with default type validation.
* It will repeatedly ask the user for re-try if validation continues to fail.
*
* @param message The message to print asking for user's input
* @param expectedClass The class of the expected data scanned (e.g. {@code Integer.class}).
* Currently all expected classes as in the {@code .nextInt()},
* {@code .nextDouble()} etc. are supported. Aka when you create a
* {@code Scanner keyboard = new Scanner(System.in);}, all the {@code next...()}
* methods you will invoke on the {@code keyboard} object can be
* replaced by this method
* @param Expected class
* @return the scanned input of the expected type
*/
protected T prompt(String message, Class expectedClass) {
return this.prompt(message, expectedClass, null);
}
/**
* Read an object from command line user input with custom validator
* It will repeatedly ask the user for re-try if validation continues to fail.
*
* @param message The message to print asking for user's input
* @param expectedClass The class of the expected data scanned (e.g. {@code Integer.class}).
* Currently all expected classes as in the {@code .nextInt()},
* {@code .nextDouble()} etc. are supported. Aka when you create a
* {@code Scanner keyboard = new Scanner(System.in);}, all the {@code next...()}
* methods you will invoke on the {@code keyboard} object can be
* replaced by this method
* @param validator A custom validator to validate the user input
* @param Expected class
* @return the scanned input of the expected type
*/
protected T prompt(String message, Class expectedClass, Validator validator) {
boolean isValid = false;
Object input = null;
T output = null;
this.print(message);
while (!isValid) {
try {
if (expectedClass == Integer.class) {
input = keyboard.nextInt();
} else if (expectedClass == Double.class) {
input = keyboard.nextDouble();
} else if (expectedClass == String.class) {
input = keyboard.nextLine();
} else if (expectedClass == Byte.class) {
input = keyboard.nextByte();
} else if (expectedClass == BigDecimal.class) {
input = keyboard.nextBigDecimal();
} else if (expectedClass == BigInteger.class) {
input = keyboard.nextBigInteger();
} else if (expectedClass == Boolean.class) {
input = keyboard.nextBoolean();
} else if (expectedClass == Float.class) {
input = keyboard.nextFloat();
} else if (expectedClass == Long.class) {
input = keyboard.nextLong();
} else if (expectedClass == Short.class) {
input = keyboard.nextShort();
}
output = expectedClass.cast(input);
isValid = validator == null || validator.isValid(output);
} catch (InputMismatchException e) {
this.print(this.viewConfig.getInputErrorMessage());
} finally {
if (expectedClass != String.class) {
keyboard.nextLine();
}
}
}
return output;
}
public AbstractView getParentView() {
return parentView;
}
public void setParentView(AbstractView parentView) {
this.parentView = parentView;
}
public String getRunningTitle() {
return runningTitle;
}
public void setRunningTitle(String runningTitle) {
this.runningTitle = runningTitle;
}
public String getNameInParentMenu() {
return nameInParentMenu;
}
public void setNameInParentMenu(String nameInParentMenu) {
this.nameInParentMenu = nameInParentMenu;
}
public ViewConfig getViewConfig() {
return viewConfig;
}
public void setViewConfig(ViewConfig viewConfig) {
this.viewConfig = viewConfig;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy