It is a menu based application, which means:
Adding a new menu item could be as easy as adding a new class in this package. See LoadMediaObjectsMenuItem.java as an example below:
package com.mediarentalsystem.menu.item.main;
import com.mediarentalsystem.menu.item.AbstractMenuItem;
import com.mediarentalsystem.menu.runner.main.LoadMediaObjectsRunner;
import static com.mediarentalsystem.menu.ParentMenu.MAIN;
class LoadMediaObjectsMenuItem extends AbstractMenuItem {
private LoadMediaObjectsMenuItem() {
super(false,
MAIN,
"1",
"Load Media objects...",
new LoadMediaObjectsRunner()
);
}
}
Adding a new runner/handler to handle menu item could be as easy as adding a new class in this package. See LoadMediaObjectsRunner.java as an example below:
package com.mediarentalsystem.menu.runner.main;
import com.mediarentalsystem.menu.runner.MenuOptionRunnable;
import static com.mediarentalsystem.utils.Const.LINE_FEED;
public class LoadMediaObjectsRunner implements MenuOptionRunnable {
@Override
public void run() {
System.out.print(LINE_FEED + "Enter path (directory) where to load from: ");
final String pathToLoadFrom = inputScanner.nextLine();
final boolean isMediaLoaded = mediaService.loadMedia(pathToLoadFrom);
if (isMediaLoaded) {
System.out.println();
} else {
System.out.println("File cannot be opened: Could not load, no such directory" + LINE_FEED);
}
}
}
We saw private constructor in LoadMediaObjectsMenuItem.java and all menu items are actually having private constructors. The static block of code in MenuItemsInitializer.java is the one take care of the initialisation of all the menu items through reflection:
private static final Map<ParentMenu, Map<String, MenuItem>> ID_TO_ITEM_MAP = new HashMap<>();
static {
// caching to avoid calculating again and again
LOGGER.info("Initializing menu items...");
final Reflections reflections = new Reflections(MAIN_PACKAGE);
final Set<Class<? extends AbstractMenuItem>> menuItemsSubTypes = reflections.getSubTypesOf(AbstractMenuItem.class);
for (Class<? extends AbstractMenuItem> menuItemClass : menuItemsSubTypes) {
try {
final Constructor<? extends AbstractMenuItem> classDeclaredConstructor = menuItemClass.getDeclaredConstructor();
classDeclaredConstructor.setAccessible(true);
final AbstractMenuItem menuItem = classDeclaredConstructor.newInstance();
final ParentMenu parentMenu = menuItem.getParentMenu();
ID_TO_ITEM_MAP.putIfAbsent(parentMenu, new HashMap<>());
ID_TO_ITEM_MAP.get(parentMenu).put(menuItem.getItemId(), menuItem);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
That code above, is using reflection to load all available menu items into system. And that’s how simply creating a new menu item extended it from AbstractMenuItem.java will start working automagically.