From 70cea4b406f1cd592c59c4103c1b9b301d3b5907 Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Mon, 11 Apr 2016 22:34:40 -0400 Subject: Some minor changes to the CLI system --- .../java/bjc/utils/cli/GeneralCommandMode.java | 265 --------------- .../main/java/bjc/utils/cli/GenericCommand.java | 21 -- .../java/bjc/utils/cli/GenericCommandMode.java | 356 +++++++++++++++++++++ .../src/main/java/bjc/utils/cli/GenericHelp.java | 36 +++ .../java/bjc/utils/funcdata/FunctionalMap.java | 23 ++ .../java/bjc/utils/funcdata/IFunctionalMap.java | 17 + 6 files changed, 432 insertions(+), 286 deletions(-) delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/GeneralCommandMode.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommandMode.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/GenericHelp.java diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/GeneralCommandMode.java b/BJC-Utils2/src/main/java/bjc/utils/cli/GeneralCommandMode.java deleted file mode 100644 index 880e8c5..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/GeneralCommandMode.java +++ /dev/null @@ -1,265 +0,0 @@ -package bjc.utils.cli; - -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IFunctionalMap; - -/** - * A general command mode, with a customizable set of commands - * - * There is a small set of commands which is handled by default. The first - * is 'list', which lists all the commands the user can input. The second - * is 'alias', which allows the user to bind a new name to a command - * - * @author ben - * - */ -public class GeneralCommandMode implements ICommandMode { - private IFunctionalMap commandHandlers; - private IFunctionalMap defaultHandlers; - - private IFunctionalMap helpTopics; - - private BiConsumer unknownCommandHandler; - - private Consumer errorOutput; - private Consumer normalOutput; - - private String modeName; - - private String customPrompt; - - /** - * Create a new general command mode - * - * @param normalOutput - * The function to use for normal output - * @param errorOutput - * The function to use for error output - */ - public GeneralCommandMode(Consumer normalOutput, - Consumer errorOutput) { - this.normalOutput = normalOutput; - this.errorOutput = errorOutput; - - commandHandlers = new FunctionalMap<>(); - defaultHandlers = new FunctionalMap<>(); - - defaultHandlers.put("list", new GenericCommand((args) -> { - listCommands(); - - return this; - }, "list\tList available command", - "Lists all of the commands available in this mode," - + " as well as the commands that are valid in any mode.")); - - defaultHandlers.put("alias", new GenericCommand((args) -> { - aliasCommands(args); - - return this; - }, "alias\tAlias one command to another", - "alias gives a command another name it can be invoked by. It is invoked" - + " with two arguments, the name of the command to alias" - + ", and the alias to give that command.")); - - defaultHandlers.put("help", new GenericCommand((args) -> { - // TODO implement help system - return this; - }, "help\tConsult the help system", - "help consults the internal help system." - + " It can be invoked in two ways. Invoking it with no arguments" - + " causes it to print out all the topics you can ask for details on," - + " while invoking it with the name of a topic will print the entry" - + " for that topic")); - } - - /** - * Add an alias to an existing command - * - * @param commandName - * The name of the command to add an alias for - * @param aliasName - * The new alias for the command - * - * @throws IllegalArgumentException - * if the specified command doesn't have a bound handler, - * or if the alias name already has a bound value - */ - public void addCommandAlias(String commandName, String aliasName) { - if (commandName == null) { - throw new NullPointerException( - "Command name must not be null"); - } else if (aliasName == null) { - throw new NullPointerException("Alias name must not be null"); - } else if (!commandHandlers.containsKey(commandName)) { - throw new IllegalArgumentException( - "Cannot alias non-existant command '" + commandName - + "'"); - } else if (commandHandlers.containsKey(aliasName)) { - throw new IllegalArgumentException("Cannot bind alias '" - + aliasName + "' to a command with a bound handler"); - } else { - commandHandlers.put(aliasName, - commandHandlers.get(commandName)); - } - } - - /** - * Add a command to this command mode - * - * @param command - * The command to add - * @param handler - * The handler to use for the specified command - * - * @throws IllegalArgumentException - * if the specified command already has a handler - * registered - */ - public void addCommandHandler(String command, ICommand handler) { - if (command == null) { - throw new NullPointerException("Command must not be null"); - } else if (handler == null) { - throw new NullPointerException("Handler must not be null"); - } else if (canHandleCommand(command)) { - throw new IllegalArgumentException("Command " + command - + " already has a handler registered"); - } else { - commandHandlers.put(command, handler); - } - } - - private void aliasCommands(String[] args) { - if (args.length != 2) { - errorOutput.accept("ERROR: Alias requires two arguments. " - + "The command name, and the alias for that command"); - } else { - String commandName = args[0]; - String aliasName = args[1]; - - if (!canHandleCommand(commandName)) { - errorOutput.accept("ERROR: '" + commandName - + "' is not a valid command."); - } else if (canHandleCommand(aliasName)) { - errorOutput.accept("ERROR: Cannot overwrite command '" - + aliasName + "'"); - } else { - addCommandAlias(commandName, aliasName); - } - } - } - - @Override - public boolean canHandleCommand(String command) { - return commandHandlers.containsKey(command) - || defaultHandlers.containsKey(command); - } - - @Override - public String getCustomPrompt() { - if (customPrompt != null) { - return customPrompt; - } - - return ICommandMode.super.getCustomPrompt(); - } - - @Override - public String getName() { - if (modeName != null) { - return modeName; - } - - return ICommandMode.super.getName(); - } - - private void listCommands() { - normalOutput.accept( - "The available commands for this mode are as follows:\n"); - - commandHandlers.keyList().forEach((commandName) -> { - normalOutput.accept("\t" + commandName); - }); - - normalOutput.accept( - "\nThe following commands are available in all modes:\n"); - - defaultHandlers.keyList().forEach((commandName) -> { - normalOutput.accept("\t" + commandName); - }); - - normalOutput.accept("\n"); - } - - @Override - public ICommandMode processCommand(String command, String[] args) { - normalOutput.accept("\n"); - - if (defaultHandlers.containsKey(command)) { - return defaultHandlers.get(command).getHandler().handle(args); - } else if (commandHandlers.containsKey(command)) { - return commandHandlers.get(command).getHandler().handle(args); - } else { - if (args != null) { - errorOutput.accept("ERROR: Unrecognized command " + command - + String.join(" ", args)); - } else { - errorOutput - .accept("ERROR: Unrecognized command " + command); - } - - if (unknownCommandHandler == null) { - throw new UnsupportedOperationException( - "Command " + command + " is invalid."); - } - - unknownCommandHandler.accept(command, args); - } - - return this; - } - - /** - * Set the custom prompt for this mode - * - * @param prompt - * The custom prompt for this mode, or null to disable the - * custom prompt - */ - public void setCustomPrompt(String prompt) { - customPrompt = prompt; - } - - /** - * Set the name of this mode - * - * @param name - * The desired name of this mode, or null to use the default - * name - */ - public void setModeName(String name) { - modeName = name; - } - - /** - * Set the handler to use for unknown commands - * - * @param handler - * The handler to use for unknown commands - */ - public void setUnknownCommandHandler( - BiConsumer handler) { - if (handler == null) { - throw new NullPointerException("Handler must not be null"); - } - - unknownCommandHandler = handler; - } - - @Override - public boolean useCustomPrompt() { - return customPrompt != null; - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommand.java b/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommand.java index 99951cc..522dfbd 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommand.java +++ b/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommand.java @@ -7,27 +7,6 @@ package bjc.utils.cli; * */ public class GenericCommand implements ICommand { - private static class GenericHelp implements ICommandHelp { - private String summary; - private String description; - - public GenericHelp(String summary, String description) { - this.summary = summary; - this.description = description; - } - - @Override - public String getSummary() { - return summary; - } - - @Override - public String getDescription() { - return description; - } - - } - private ICommandHandler handler; private ICommandHelp help; diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommandMode.java b/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommandMode.java new file mode 100644 index 0000000..5b35a0b --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommandMode.java @@ -0,0 +1,356 @@ +package bjc.utils.cli; + +import java.util.TreeMap; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IFunctionalMap; + +/** + * A general command mode, with a customizable set of commands + * + * There is a small set of commands which is handled by default. The first + * is 'list', which lists all the commands the user can input. The second + * is 'alias', which allows the user to bind a new name to a command + * + * @author ben + * + */ +public class GenericCommandMode implements ICommandMode { + private IFunctionalMap commandHandlers; + private IFunctionalMap defaultHandlers; + + private IFunctionalMap helpTopics; + + private BiConsumer unknownCommandHandler; + + private Consumer errorOutput; + private Consumer normalOutput; + + private String modeName; + + private String customPrompt; + + /** + * Create a new generic command mode + * + * @param normalOutput + * The function to use for normal output + * @param errorOutput + * The function to use for error output + */ + public GenericCommandMode(Consumer normalOutput, + Consumer errorOutput) { + this.normalOutput = normalOutput; + this.errorOutput = errorOutput; + + commandHandlers = new FunctionalMap<>(new TreeMap<>()); + defaultHandlers = new FunctionalMap<>(new TreeMap<>()); + helpTopics = new FunctionalMap<>(new TreeMap<>()); + + defaultHandlers.put("list", new GenericCommand((args) -> { + listCommands(); + + return this; + }, "list\tList available command", + "Lists all of the commands available in this mode," + + " as well as the commands that are valid in any mode.")); + + defaultHandlers.put("alias", new GenericCommand((args) -> { + aliasCommands(args); + + return this; + }, "alias\tAlias one command to another", + "alias gives a command another name it can be invoked by. It is invoked" + + " with two arguments, the name of the command to alias" + + ", and the alias to give that command.")); + + defaultHandlers.put("help", new GenericCommand((args) -> { + if (args == null || args.length == 0) { + // Invoke general help + helpSummary(); + } else { + // Invoke help for a command + helpCommand(args[0]); + } + + return this; + }, "help\tConsult the help system", + "help consults the internal help system." + + " It can be invoked in two ways. Invoking it with no arguments" + + " causes it to print out all the topics you can ask for details on," + + " while invoking it with the name of a topic will print the entry" + + " for that topic")); + + // Add commands handled in a upper layer + defaultHandlers.put("clear", new GenericCommand((args) -> { + errorOutput.accept( + "ERROR: This is a bug. Please report to the developer"); + + return this; + }, "clear\tClear the screen", + "clear clears the screen of all the text on it," + + " and prepares a fresh prompt.")); + + defaultHandlers.put("exit", new GenericCommand((args) -> { + errorOutput.accept( + "ERROR: This is a bug. Please report to the developer"); + + return this; + }, "exit\tExit the game", + "exit first prompts the user to make sure they want to exit," + + " and if they affirm it, it quits the game")); + } + + /** + * Add an alias to an existing command + * + * @param commandName + * The name of the command to add an alias for + * @param aliasName + * The new alias for the command + * + * @throws IllegalArgumentException + * if the specified command doesn't have a bound handler, + * or if the alias name already has a bound value + */ + public void addCommandAlias(String commandName, String aliasName) { + if (commandName == null) { + throw new NullPointerException( + "Command name must not be null"); + } else if (aliasName == null) { + throw new NullPointerException("Alias name must not be null"); + } else if (!commandHandlers.containsKey(commandName)) { + throw new IllegalArgumentException( + "Cannot alias non-existant command '" + commandName + + "'"); + } else if (commandHandlers.containsKey(aliasName)) { + throw new IllegalArgumentException("Cannot bind alias '" + + aliasName + "' to a command with a bound handler"); + } else { + commandHandlers.put(aliasName, + commandHandlers.get(commandName)); + } + } + + /** + * Add a command to this command mode + * + * @param command + * The command to add + * @param handler + * The handler to use for the specified command + * + * @throws IllegalArgumentException + * if the specified command already has a handler + * registered + */ + public void addCommandHandler(String command, ICommand handler) { + if (command == null) { + throw new NullPointerException("Command must not be null"); + } else if (handler == null) { + throw new NullPointerException("Handler must not be null"); + } else if (canHandleCommand(command)) { + throw new IllegalArgumentException("Command " + command + + " already has a handler registered"); + } else { + commandHandlers.put(command, handler); + } + } + + /** + * Add a help topic to this command mode that isn't tied to a command + * + * @param topicName + * The name of the topic + * @param help + * The contents of the topic + */ + public void addHelpTopic(String topicName, ICommandHelp help) { + helpTopics.put(topicName, help); + } + + private void aliasCommands(String[] args) { + if (args.length != 2) { + errorOutput.accept("ERROR: Alias requires two arguments. " + + "The command name, and the alias for that command"); + } else { + String commandName = args[0]; + String aliasName = args[1]; + + if (!canHandleCommand(commandName)) { + errorOutput.accept("ERROR: '" + commandName + + "' is not a valid command."); + } else if (canHandleCommand(aliasName)) { + errorOutput.accept("ERROR: Cannot overwrite command '" + + aliasName + "'"); + } else { + addCommandAlias(commandName, aliasName); + } + } + } + + @Override + public boolean canHandleCommand(String command) { + return commandHandlers.containsKey(command) + || defaultHandlers.containsKey(command); + } + + @Override + public String getCustomPrompt() { + if (customPrompt != null) { + return customPrompt; + } + + return ICommandMode.super.getCustomPrompt(); + } + + @Override + public String getName() { + if (modeName != null) { + return modeName; + } + + return ICommandMode.super.getName(); + } + + private void helpCommand(String commandName) { + if (commandHandlers.containsKey(commandName)) { + normalOutput.accept("\n" + commandHandlers.get(commandName) + .getHelp().getDescription()); + } else if (defaultHandlers.containsKey(commandName)) { + normalOutput.accept("\n" + defaultHandlers.get(commandName) + .getHelp().getDescription()); + } else if (helpTopics.containsKey(commandName)) { + normalOutput.accept( + "\n" + helpTopics.get(commandName).getDescription()); + } else { + errorOutput + .accept("ERROR: I'm sorry, but there is no help available for '" + + commandName + "'"); + } + } + + private void helpSummary() { + normalOutput.accept( + "Help topics for this command mode are as follows:\n"); + if (commandHandlers.getSize() > 0) { + commandHandlers.forEachValue((command) -> { + normalOutput.accept( + "\t" + command.getHelp().getSummary() + "\n"); + }); + } else { + normalOutput.accept("\tNone available\n"); + } + + normalOutput.accept( + "\nHelp topics available in all command modes are as follows\n"); + if (defaultHandlers.getSize() > 0) { + defaultHandlers.forEachValue((command) -> { + normalOutput.accept( + "\t" + command.getHelp().getSummary() + "\n"); + }); + } else { + normalOutput.accept("\tNone available\n"); + } + + normalOutput.accept( + "\nHelp topics not associated with a command are as follows\n"); + if (helpTopics.getSize() > 0) { + helpTopics.forEachValue((topic) -> { + normalOutput.accept("\t" + topic.getSummary() + "\n"); + }); + } else { + normalOutput.accept("\tNone available\n"); + } + } + + private void listCommands() { + normalOutput.accept( + "The available commands for this mode are as follows:\n"); + + commandHandlers.keyList().forEach((commandName) -> { + normalOutput.accept("\t" + commandName); + }); + + normalOutput.accept( + "\nThe following commands are available in all modes:\n"); + + defaultHandlers.keyList().forEach((commandName) -> { + normalOutput.accept("\t" + commandName); + }); + + normalOutput.accept("\n"); + } + + @Override + public ICommandMode processCommand(String command, String[] args) { + normalOutput.accept("\n"); + + if (defaultHandlers.containsKey(command)) { + return defaultHandlers.get(command).getHandler().handle(args); + } else if (commandHandlers.containsKey(command)) { + return commandHandlers.get(command).getHandler().handle(args); + } else { + if (args != null) { + errorOutput.accept("ERROR: Unrecognized command " + command + + String.join(" ", args)); + } else { + errorOutput + .accept("ERROR: Unrecognized command " + command); + } + + if (unknownCommandHandler == null) { + throw new UnsupportedOperationException( + "Command " + command + " is invalid."); + } + + unknownCommandHandler.accept(command, args); + } + + return this; + } + + /** + * Set the custom prompt for this mode + * + * @param prompt + * The custom prompt for this mode, or null to disable the + * custom prompt + */ + public void setCustomPrompt(String prompt) { + customPrompt = prompt; + } + + /** + * Set the name of this mode + * + * @param name + * The desired name of this mode, or null to use the default + * name + */ + public void setModeName(String name) { + modeName = name; + } + + /** + * Set the handler to use for unknown commands + * + * @param handler + * The handler to use for unknown commands + */ + public void setUnknownCommandHandler( + BiConsumer handler) { + if (handler == null) { + throw new NullPointerException("Handler must not be null"); + } + + unknownCommandHandler = handler; + } + + @Override + public boolean useCustomPrompt() { + return customPrompt != null; + } +} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericHelp.java b/BJC-Utils2/src/main/java/bjc/utils/cli/GenericHelp.java new file mode 100644 index 0000000..f508de8 --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/cli/GenericHelp.java @@ -0,0 +1,36 @@ +package bjc.utils.cli; + +/** + * Generic implementation of a help topic + * + * @author ben + * + */ +public class GenericHelp implements ICommandHelp { + private String summary; + private String description; + + /** + * Create a new help topic + * + * @param summary + * The summary of this help topic + * @param description + * The description of this help topic + */ + public GenericHelp(String summary, String description) { + this.summary = summary; + this.description = description; + } + + @Override + public String getSummary() { + return summary; + } + + @Override + public String getDescription() { + return description; + } + +} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java index fc4c4de..b505ebd 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java +++ b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java @@ -3,6 +3,7 @@ package bjc.utils.funcdata; import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; import bjc.utils.data.Pair; @@ -89,6 +90,18 @@ public class FunctionalMap implements IFunctionalMap { public int getSize() { return mapToTransform.getSize(); } + + @Override + public void forEachKey(Consumer action) { + mapToTransform.forEachKey(action); + } + + @Override + public void forEachValue(Consumer action) { + mapToTransform.forEachValue((val) -> { + action.accept(transformer.apply(val)); + }); + } } private Map wrappedMap; @@ -220,4 +233,14 @@ public class FunctionalMap implements IFunctionalMap { public int getSize() { return wrappedMap.size(); } + + @Override + public void forEachKey(Consumer action) { + wrappedMap.keySet().forEach(action); + } + + @Override + public void forEachValue(Consumer action) { + wrappedMap.values().forEach(action); + } } \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/IFunctionalMap.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/IFunctionalMap.java index 9bd62bc..e089850 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/IFunctionalMap.java +++ b/BJC-Utils2/src/main/java/bjc/utils/funcdata/IFunctionalMap.java @@ -1,6 +1,7 @@ package bjc.utils.funcdata; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; /** @@ -101,4 +102,20 @@ public interface IFunctionalMap { * @return The number of entries in this map */ int getSize(); + + /** + * Perform an action for each key in the map + * + * @param action + * The action to perform on each key in the map + */ + void forEachKey(Consumer action); + + /** + * Perform an action for each value in the map + * + * @param action + * The action to perform on each value in the map + */ + void forEachValue(Consumer action); } \ No newline at end of file -- cgit v1.2.3