I want to create menus with options that select other menus, without causing a stack overflow error due to recursion.
But the menus have to be initialized, and a menu a can point to a menu b and b to a. It's also important that some menus can have varying amounts of options.
public class Menus {
public static Menu mainMenu = new Menu("Main Menu");
public static Menu homeMenu = new Menu("Home Menu");
public static Menu BMenu = new Menu("B Menu");
public static void initMenus() {
mainMenu.addOptions(
new MenuOption(
"bmenu",
BMenu
),
new MenuOption(
"exit",
mainMenu,
CLI::stop
)
);
BMenu.addOptions(
new MenuOption(
"main",
mainMenu
),
new MenuOption(
"exit",
BMenu,
CLI::stop
)
);
}
}
public class MenuOption {
private final String name;
private final Runnable[] actions;
private final Menu nextMenu;
public MenuOption(String name, Menu nextMenu, Runnable... actions) {
this.name = name;
this.actions = actions;
this.nextMenu = nextMenu;
}
public MenuOption(String name, Menu nextMenu) {
this.name = name;
this.actions = new Runnable[0];
this.nextMenu = nextMenu;
}
public void run() {
for (Runnable r : actions) {
r.run();
}
}
public void run(int delay) {
for (Runnable r : actions) {
r.run();
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
public String getName() {
return name;
}
public Menu getNextMenu() {
return nextMenu;
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Menu {
private final String title;
private final List<MenuOption> options;
public Menu(String title, List<MenuOption> options) {
this.title = title;
this.options = options;
}
public Menu(String title, MenuOption... options) {
this.title = title;
this.options = new ArrayList<>(Arrays.asList(options));
}
public String getTitle() {
return title;
}
public List<MenuOption> getOptions() {
return options;
}
public Menu select(int index) {
if (index > 0 && index <= options.size()) {
options.get(index-1).run();
return options.get(index-1).getNextMenu();
}
return this;
}
public void addOption(MenuOption option) {
options.add(option);
}
public void addOptions(MenuOption... options) {
for (MenuOption option : options) {
addOption(option);
}
}
}
import java.util.Arrays;
import java.util.Scanner;
public class CLI {
private static Menu currentState = Menus.mainMenu;
private static volatile boolean running = true;
public static void run() {
Scanner scanner = new Scanner(System.in);
Menus.initMenus();
while (running) {
clearAndDisplay();
currentState = currentState.select(scanner.nextInt());
}
}
public static String generateMenu(String Title, String... options ) {
StringBuilder menu = new StringBuilder();
menu.append(Title).append("\n");
for (int option = 1; option <= options.length; option++) {
menu.append(option).append(". ").append(options[option-1]).append("\n");
}
return menu.toString();
}
public static String generateMenu(Menu state) {
return generateMenu(state.getTitle(),
state.getOptions().stream()
.map(MenuOption::getName)
.toArray(String[]::new)
);
}
public static void clear() {
System.out.print("\033[2J");
System.out.print("\033[1;1H");
System.out.flush();
}
public static void clearAndDisplay() {
clear();
System.out.print(generateMenu(currentState));
}
public static void stop() {
running = false;
}
}
Currently, they are assigned to values, if they don't exist, and if they do, return the value. But first menu can't be created, because the other one is null, and the second one can't be created, because the first one is null. I was thinking, I could maybe use a placeholder, leaving the problematic option empty, creating the menus and then assigning the option, but I'm not sure how I would go about it.
Is there a much easier way to create this concept that I'm not seeing?
I tried to figure out a way to get both menus initialized, but I can't figure this out.
EDIT: I managed to fix the problem, but I'm not confident that this implementation is done well, please let me know if you see an issue.