From 8718596be2ebb47898fb697637e7f003409c1ac6 Mon Sep 17 00:00:00 2001 From: delvh Date: Fri, 24 Jul 2020 11:09:05 +0200 Subject: [PATCH] Added default values, SystemCommandBuilder Additionally removed sending of SystemCommands as messages and added sorting of recommendations by relevance. --- .../commands/InterruptingSystemCommand.java | 52 --- .../client/data/commands/SystemCommand.java | 55 ++- .../data/commands/SystemCommandBuilder.java | 144 ++++++++ .../data/commands/SystemCommandsMap.java | 333 +++++++----------- .../envoy/client/event/InterruptEvent.java | 28 -- .../envoy/client/ui/controller/ChatScene.java | 29 +- 6 files changed, 347 insertions(+), 294 deletions(-) delete mode 100644 client/src/main/java/envoy/client/data/commands/InterruptingSystemCommand.java create mode 100644 client/src/main/java/envoy/client/data/commands/SystemCommandBuilder.java delete mode 100644 client/src/main/java/envoy/client/event/InterruptEvent.java diff --git a/client/src/main/java/envoy/client/data/commands/InterruptingSystemCommand.java b/client/src/main/java/envoy/client/data/commands/InterruptingSystemCommand.java deleted file mode 100644 index 90434c4..0000000 --- a/client/src/main/java/envoy/client/data/commands/InterruptingSystemCommand.java +++ /dev/null @@ -1,52 +0,0 @@ -package envoy.client.data.commands; - -import java.util.function.Function; -import java.util.function.Supplier; - -import envoy.client.event.InterruptEvent; -import envoy.event.EventBus; - -/** - * This is a Envoy-specific subclass that allows - * - * Project: envoy-client
- * File: InterruptingSystemCommand.java
- * Created: 23.07.2020
- * - * @author Leon Hofmeister - * @since Envoy Client v0.2-beta - */ -public class InterruptingSystemCommand extends SystemCommand { - - private static final EventBus eventBus = EventBus.getInstance(); - - /** - * The reason why an {@link InterruptEvent} will be dispatched from this class. - */ - public static final String interruptionReason = "command executed"; - - /** - * Constructs a new {@code SystemCommand}. - * - * @param action the action that should be performed - * @param numberOfArguments the amount of arguments that need to be parsed for - * the underlying function - * @param description the description of this {@code SystemCommand} - * @since Envoy Client v0.2-beta - */ - public InterruptingSystemCommand(Function action, int numberOfArguments, String description) { - super(action, numberOfArguments, description); - } - - @Override - public void onCall() { - super.onCall(); - eventBus.dispatch(new InterruptEvent(interruptionReason)); - } - - @Override - public void onCall(Supplier consumer) { - super.onCall(consumer); - eventBus.dispatch(new InterruptEvent(interruptionReason)); - } -} diff --git a/client/src/main/java/envoy/client/data/commands/SystemCommand.java b/client/src/main/java/envoy/client/data/commands/SystemCommand.java index 17ea801..3f1a9a5 100644 --- a/client/src/main/java/envoy/client/data/commands/SystemCommand.java +++ b/client/src/main/java/envoy/client/data/commands/SystemCommand.java @@ -1,6 +1,9 @@ package envoy.client.data.commands; -import java.util.function.Function; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; import java.util.function.Supplier; /** @@ -8,7 +11,7 @@ import java.util.function.Supplier; * action and a number of arguments that should be used as input for this * function. * No {@code SystemCommand} can return anything. - * Every {@code SystemCommand} must have as argument type {@code String[]} so + * Every {@code SystemCommand} must have as argument type {@code List} so * that the words following the indicator String can be used as input of the * function. This approach has one limitation:
* Order matters! Changing the order of arguments will likely result in @@ -23,35 +26,39 @@ import java.util.function.Supplier; */ public class SystemCommand implements OnCall { + protected int relevance; + /** * The argument count of the command. */ protected final int numberOfArguments; /** - * This function takes a {@code String[]} as argument because automatically + * This function takes a {@code List} as argument because automatically * {@code SystemCommand#numberOfArguments} words following the necessary command - * will be put into this array. + * will be put into this list. * * @see String#split(String) */ - protected final Function action; + protected final Consumer> action; protected final String description; - protected int relevance; + protected final List defaults; /** * Constructs a new {@code SystemCommand}. * * @param action the action performed by the command * @param numberOfArguments the argument count accepted by the action + * @param defaults the default values for the corresponding arguments * @param description the description of this {@code SystemCommand} * @since Envoy Client v0.2-beta */ - public SystemCommand(Function action, int numberOfArguments, String description) { + public SystemCommand(Consumer> action, int numberOfArguments, List defaults, String description) { this.numberOfArguments = numberOfArguments; this.action = action; + this.defaults = defaults == null ? new ArrayList<>() : defaults; this.description = description; } @@ -59,7 +66,7 @@ public class SystemCommand implements OnCall { * @return the action that should be performed * @since Envoy Client v0.2-beta */ - public Function getAction() { return action; } + public Consumer> getAction() { return action; } /** * @return the argument count of the command @@ -85,12 +92,44 @@ public class SystemCommand implements OnCall { */ public void setRelevance(int relevance) { this.relevance = relevance; } + /** + * Increments the relevance of this {@code SystemCommand}. + */ @Override public void onCall() { relevance++; } + /** + * Increments the relevance of this {@code SystemCommand} and executes the + * supplier. + */ @Override public void onCall(Supplier consumer) { onCall(); consumer.get(); } + + /** + * @return the defaults + * @since Envoy Client v0.2-beta + */ + public List getDefaults() { return defaults; } + + @Override + public int hashCode() { return Objects.hash(action); } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final SystemCommand other = (SystemCommand) obj; + return Objects.equals(action, other.action); + } + + @Override + public String toString() { + return "SystemCommand [relevance=" + relevance + ", numberOfArguments=" + numberOfArguments + ", " + + (action != null ? "action=" + action + ", " : "") + (description != null ? "description=" + description + ", " : "") + + (defaults != null ? "defaults=" + defaults : "") + "]"; + } } diff --git a/client/src/main/java/envoy/client/data/commands/SystemCommandBuilder.java b/client/src/main/java/envoy/client/data/commands/SystemCommandBuilder.java new file mode 100644 index 0000000..fde7ba2 --- /dev/null +++ b/client/src/main/java/envoy/client/data/commands/SystemCommandBuilder.java @@ -0,0 +1,144 @@ +package envoy.client.data.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * This class acts as a builder for {@link SystemCommand}s. + *

+ * Project: envoy-client
+ * File: SystemCommandBuilder.java
+ * Created: 23.07.2020
+ * + * @author Leon Hofmeister + * @since Envoy Client v0.2-beta + */ +public class SystemCommandBuilder { + + private int numberOfArguments; + private Consumer> action; + private List defaults; + private String description; + private int relevance; + + /** + * @param numberOfArguments the numberOfArguments to set + * @return this {@code SystemCommandBuilder} + * @since Envoy Client v0.2-beta + */ + public SystemCommandBuilder setNumberOfArguments(int numberOfArguments) { + this.numberOfArguments = numberOfArguments; + return this; + } + + /** + * @param action the action to set + * @return this {@code SystemCommandBuilder} + * @since Envoy Client v0.2-beta + */ + public SystemCommandBuilder setAction(Consumer> action) { + this.action = action; + return this; + } + + /** + * @param description the description to set + * @return this {@code SystemCommandBuilder} + * @since Envoy Client v0.2-beta + */ + public SystemCommandBuilder setDescription(String description) { + this.description = description; + return this; + } + + /** + * @param relevance the relevance to set + * @return this {@code SystemCommandBuilder} + * @since Envoy Client v0.2-beta + */ + public SystemCommandBuilder setRelevance(int relevance) { + this.relevance = relevance; + return this; + } + + /** + * @param defaults the defaults to set + * @return this {@code SystemCommandBuilder} + * @since Envoy Client v0.2-beta + */ + public SystemCommandBuilder setDefaults(String... defaults) { + this.defaults = List.of(defaults); + return this; + } + + /** + * Resets all values stored. + * + * @return this {@code SystemCommandBuilder} + * @since Envoy Client v0.2-beta + */ + public SystemCommandBuilder reset() { + numberOfArguments = 0; + action = null; + defaults = new ArrayList<>(); + description = ""; + relevance = 0; + return this; + } + + /** + * Builds a {@code SystemCommand} based upon the previously entered data. + * + * @return the built {@code SystemCommand} + * @since Envoy Client v0.2-beta + */ + public SystemCommand build() { return build(true); } + + /** + * Builds a {@code SystemCommand} based upon the previously entered data.
+ * {@code SystemCommand#numberOfArguments} will be set to 0, regardless of the + * previous value.
+ * At the end, this {@code SystemCommandBuilder} will be reset. + * + * @return the built {@code SystemCommand} + * @since Envoy Client v0.2-beta + */ + public SystemCommand buildNoArg() { + numberOfArguments = 0; + return build(true); + } + + /** + * Builds a {@code SystemCommand} based upon the previously entered data.
+ * {@code SystemCommand#numberOfArguments} will be set to use the rest of the + * string as argument, regardless of the previous value.
+ * At the end, this {@code SystemCommandBuilder} will be reset. + * + * @return the built {@code SystemCommand} + * @since Envoy Client v0.2-beta + */ + public SystemCommand buildRemainingArg() { + numberOfArguments = -1; + return build(true); + } + + /** + * Builds a {@code SystemCommand} based upon the previously entered data.
+ * At the end, this {@code SystemCommandBuilder} can be reset but must + * not be. + * + * @param reset whether this {@code SystemCommandBuilder} should be reset + * afterwards.
+ * This can be useful if another command wants to execute something + * similar + * @return the built {@code SystemCommand} + * @since Envoy Client v0.2-beta + */ + public SystemCommand build(boolean reset) { + final var sc = new SystemCommand(action, numberOfArguments, defaults, description); + sc.setRelevance(relevance); + if (reset) reset(); + return sc; + } +} diff --git a/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java b/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java index 4ba6dfb..cfcb0a4 100644 --- a/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java +++ b/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java @@ -21,200 +21,46 @@ import envoy.util.EnvoyLog; */ public final class SystemCommandsMap { - private final HashMap systemCommands = new HashMap<>(); + private final Map systemCommands = new HashMap<>(); - private final Pattern commandBounds = Pattern.compile("^[a-zA-Z0-9_:!\\(\\)\\?\\.\\,\\;\\-]+$"); + private final Pattern commandPattern = Pattern.compile("^[a-zA-Z0-9_:!\\(\\)\\?\\.\\,\\;\\-]+$"); private static final Logger logger = EnvoyLog.getLogger(SystemCommandsMap.class); - private boolean commandExecuted; - /** * Adds a new command to the map if the command name is valid. * - * @param command the string that must be inputted to execute the - * given action - * @param action the action that should be performed - * @param numberOfArguments the amount of arguments that need to be parsed for - * the underlying function + * @param command the input string to execute the + * given action + * @param systemCommand the command to add - can be built using + * {@link SystemCommandBuilder} * @see SystemCommandsMap#isValidKey(String) * @since Envoy Client v0.2-beta */ - public void addCommand(String command, Function action, int numberOfArguments) { - if (isValidKey(command)) systemCommands.put(command, new SystemCommand(action, numberOfArguments, "")); - } - - /** - * Adds a command with according action, the number of arguments that should be - * parsed and a description of the system command, if the command does not - * violate API constrictions, to the map. - * - * @param command the string that must be inputted to execute the - * given action - * @param action the action that should be performed - * @param numberOfArguments the amount of arguments that need to be parsed for - * the underlying function - * @param description the description of this {@link SystemCommand} - * @see SystemCommandsMap#isValidKey(String) - * @since Envoy Client v0.2-beta - */ - public void addCommand(String command, Function action, int numberOfArguments, String description) { - if (isValidKey(command)) systemCommands.put(command, new SystemCommand(action, numberOfArguments, description)); - } - - /** - * Adds a command with according action that does not depend on arguments, if - * the command does not violate API constrictions, to the map. - * - * @param command the string that must be inputted to execute the given action - * @param action the action that should be performed. To see why this Function - * takes a {@code String[]}, see {@link SystemCommand} - * @see SystemCommandsMap#isValidKey(String) - * @since Envoy Client v0.2-beta - */ - public void addNoArgCommand(String command, Function action) { addCommand(command, action, 0); } - - /** - * Adds a command with according action that does not depend on arguments and a - * description of that action, if the command does not violate API - * constrictions, to the map. - * - * @param command the string that must be inputted to execute the given - * action - * @param action the action that should be performed. To see why this - * Function takes a {@code String[]}, see - * {@link SystemCommand} - * @param description the description of this {@link SystemCommand} - * @see SystemCommandsMap#isValidKey(String) - * @since Envoy Client v0.2-beta - */ - public void addNoArgCommand(String command, Function action, String description) { addCommand(command, action, 0); } - - /** - * Convenience method that does the same as - * {@link SystemCommandsMap#addCommand(String, Function, int)}. - *

- * Adds a command with according action and the number of arguments that should - * be parsed if the command does not violate API constrictions to the map. - * - * @param command the string that must be inputted to execute the - * given action - * @param action the action that should be performed. To see why this - * Function takes a {@code String[]}, see - * {@link SystemCommand} - * @param numberOfArguments the amount of arguments that need to be parsed for - * the underlying function - * @see SystemCommandsMap#isValidKey(String) - * @since Envoy Client v0.2-beta - */ - public void add(String command, Function action, int numberOfArguments) { addCommand(command, action, numberOfArguments); } - - /** - * Examines whether a key can be put in the map and logs it with - * {@code Level.WARNING} if that key violates API constrictions
- * (allowed chars are a-zA-Z0-9_:!()?.,;-) - *

- * The approach to not throw an exception was taken so that an ugly try-catch - * block for every addition to the system commands map could be avoided, an - * error that - * should only occur during implementation and not in production. - * - * @param command the key to examine - * @return whether this key can be used in the map - * @since Envoy Client v0.2-beta - */ - public boolean isValidKey(String command) { - final boolean valid = commandBounds.matcher(command).matches(); - if (!valid) logger - .log(Level.WARNING, - "The command \"" + command - + "\" is not valid. As it will cause problems in execution, it will not be entered into the map. Only the characters " - + commandBounds + "are allowed"); - return valid; - } + public void add(String command, SystemCommand systemCommand) { if (isValidKey(command)) systemCommands.put(command, systemCommand); } /** * This method checks if the input String is a key in the map and returns the * wrapped System command if present. - * Its intended usage is after a "/" has been detected in the input String. * It will return an empty optional if the value after the slash is not a key in * the map, which is a valid case (i.e. input="3/4" and "4" is not a key in the * map). *

* Usage example:
* {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}
- * {@code systemCommands.add("example", Function.identity, 1);}
+ * {@code Button button = new Button();} + * {@code systemCommands.add("example", text -> button.setText(text.get(0), 1);}
* {@code ....}
* user input: {@code "/example xyz ..."}
- * {@code systemCommands.checkPresent("example xyz ...")} - * result: {@code SystemCommand[action=Function.identity, numberOfArguments=1]} + * {@code systemCommands.get("example xyz ...")} or + * {@code systemCommands.get("/example xyz ...")} + * result: {@code Optional} * - * @param input the input string given by the user, excluding the "/" + * @param input the input string given by the user * @return the wrapped system command, if present * @since Envoy Client v0.2-beta */ - public Optional checkPresent(String input) { return Optional.ofNullable(systemCommands.get(getCommand(input))); } - - /** - * Takes a 'raw' string (the whole input) and checks if a command is present - * after a "/". If that is the case, it will be executed. - *

- * Only one system command can be present, afterwards checking will not be - * continued. - * - * @param raw the raw input string - * @since Envoy Client v0.2-beta - */ - public void checkForCommands(String raw) { checkForCommands(raw, 0); } - - /** - * Takes a 'raw' string (the whole input) and checks from {@code fromIndex} on - * if a command is present - * after a "/". If that is the case, it will be executed. - *

- * Only one system command can be present, afterwards checking will not be - * continued. - * - * @param raw the raw input string - * @param fromIndex the index to start checking on - * @since Envoy Client v0.2-beta - */ - public void checkForCommands(String raw, int fromIndex) { - // The minimum length of a command is "/" + a letter, hence raw.length()-2 is - // the highest index needed - for (int i = fromIndex; i < raw.length() - 2; i++) - // possibly a command was detected - if (raw.charAt(i) == '/') { - executeIfPresent(getCommand(raw, i)); - // the command was executed successfully - no further checking needed - if (commandExecuted) { - commandExecuted = false; - logger.log(Level.FINE, "executed system command " + getCommand(raw, i)); - break; - } - } - } - - /** - * This method ensures that the "/" of a {@link SystemCommand} is stripped.
- * It returns the command as (most likely) entered as key in the map starting - * from {@code fromIndex}.
- * It should only be called on strings that contain a "/" . - * - * @param raw the input - * @param fromIndex the index from which to expect the system command - - * regardless of whether the slash is still present at this - * index - * @return the command as entered in the map - * @since Envoy Client v0.2-beta - * @apiNote this method will (most likely) not return anything useful if - * whatever is entered after the slash is not a system command. Only - * exception: for recommendation purposes. - */ - public String getCommand(String raw, int fromIndex) { - final var index = raw.indexOf(' '); - return raw.substring(fromIndex + raw.charAt(0) == '/' ? 1 : 0, index < 1 ? raw.length() : index); - } + public Optional get(String input) { return Optional.ofNullable(systemCommands.get(getCommand(input))); } /** * This method ensures that the "/" of a {@link SystemCommand} is stripped.
@@ -229,7 +75,52 @@ public final class SystemCommandsMap { * whatever is entered after the slash is not a system command. Only * exception: for recommendation purposes. */ - public String getCommand(String raw) { return getCommand(raw, 0); } + public String getCommand(String raw) { + final var trimmed = raw.stripLeading(); + final var index = trimmed.indexOf(' '); + return trimmed.substring(trimmed.charAt(0) == '/' ? 1 : 0, index < 1 ? trimmed.length() : index); + } + + /** + * Examines whether a key can be put in the map and logs it with + * {@code Level.WARNING} if that key violates API constrictions.
+ * (allowed chars are a-zA-Z0-9_:!()?.,;-) + *

+ * The approach to not throw an exception was taken so that an ugly try-catch + * block for every addition to the system commands map could be avoided, an + * error that should only occur during implementation and not in production. + * + * @param command the key to examine + * @return whether this key can be used in the map + * @since Envoy Client v0.2-beta + */ + public boolean isValidKey(String command) { + final boolean valid = commandPattern.matcher(command).matches(); + if (!valid) logger.log(Level.WARNING, + "The command \"" + command + + "\" is not valid. As it will cause problems in execution, it will not be entered into the map. Only the characters " + + commandPattern + "are allowed"); + return valid; + } + + /** + * Takes a 'raw' string (the whole input) and checks if "/" is the first visible + * character and then checks if a command is present after that "/". If that is + * the case, it will be executed. + *

+ * + * @param raw the raw input string + * @return whether a command could be found + * @since Envoy Client v0.2-beta + */ + public boolean executeIfAnyPresent(String raw) { + // possibly a command was detected and could be executed + final var raw2 = raw.stripLeading(); + final var commandFound = raw2.startsWith("/") ? executeIfPresent(raw2) : false; + // the command was executed successfully - no further checking needed + if (commandFound) logger.log(Level.FINE, "executed system command " + getCommand(raw2)); + return commandFound; + } /** * This method checks if the input String is a key in the map and executes the @@ -242,33 +133,55 @@ public final class SystemCommandsMap { * Usage example:
* {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}
* {@code Button button = new Button();}
- * {@code systemCommands.add("example", (words)-> button.setText(words[0]), 1);}
+ * {@code systemCommands.add("example", (words)-> button.setText(words.get(0), 1);}
* {@code ....}
* user input: {@code "/example xyz ..."}
* {@code systemCommands.executeIfPresent("example xyz ...")} * result: {@code button.getText()=="xyz"} * - * @param input the input string given by the user, excluding the "/" + * @param input the input string given by the user + * @return whether a command could be found * @since Envoy Client v0.2-beta */ - public void executeIfPresent(String input) { - checkPresent(input).ifPresent(systemCommand -> { + public boolean executeIfPresent(String input) { + final var command = getCommand(input); + final var value = get(command); + value.ifPresent(systemCommand -> { // Splitting the String so that the leading command including the first " " is // removed and only as many following words as allowed by the system command // persist - final var remainingString = input.substring(input.indexOf(" ") + 1); - // TODO: Current implementation will fail in certain cases, i.e. two spaces - // behind each other (" "), not enough words, ... - final var arguments = Arrays.copyOfRange(remainingString.split(" "), 0, systemCommand.getNumberOfArguments()); + final var arguments = extractArguments(input, systemCommand); // Executing the function try { - systemCommand.getAction().apply(arguments); - commandExecuted = true; + systemCommand.getAction().accept(arguments); systemCommand.onCall(); } catch (final Exception e) { - logger.log(Level.WARNING, "The system command " + getCommand(input) + " threw an exception: ", e); + logger.log(Level.WARNING, "The system command " + command + " threw an exception: ", e); } }); + return value.isPresent(); + } + + /** + * Supplies missing values with default values. + * + * @param input the input String + * @param systemCommand the command that is expected + * @return the list of arguments that can be used to parse the systemCommand + * @since Envoy Client v0.2-beta + */ + private List extractArguments(String input, SystemCommand systemCommand) { + // no more arguments follow after the command (e.g. text = "/DABR") + final var indexOfSpace = input.indexOf(" "); + if (indexOfSpace < 0) return supplementDefaults(new String[] {}, systemCommand); + // the arguments behind a system command + final var remainingString = input.substring(indexOfSpace + 1); + final var numberOfArguments = systemCommand.getNumberOfArguments(); + // splitting those arguments and supplying default values + final var textArguments = remainingString.split(" ", -1); + final var originalArguments = numberOfArguments >= 0 ? Arrays.copyOfRange(textArguments, 0, numberOfArguments) : textArguments; + final var arguments = supplementDefaults(originalArguments, systemCommand); + return arguments; } /** @@ -278,28 +191,13 @@ public final class SystemCommandsMap { * If none are present, nothing will be done.
* Otherwise the given function will be executed on the recommendations.
* - * @param input the input + * @param input the input string * @param action the action that should be taken for the recommendations, if any * are present * @since Envoy Client v0.2-beta */ - public void requestRecommendations(String input, Function, Void> action) { requestRecommendations(input, 0, action); } - - /** - * Retrieves the recommendations based on the current input entered.
- * The word beginning at {@code fromIndex} is used for the recommendations and - * it does not matter if the "/" is at its beginning or not.
- * If none are present, nothing will be done.
- * Otherwise the given function will be executed on the recommendations.
- * - * @param input the input - * @param fromIndex the index to start checking on - * @param action the action that should be taken for the recommendations, if - * any are present - * @since Envoy Client v0.2-beta - */ - private void requestRecommendations(String input, int fromIndex, Function, Void> action) { - final var partialCommand = getCommand(input, fromIndex); + public void requestRecommendations(String input, Function, Void> action) { + final var partialCommand = getCommand(input); // Get the expected commands final var recommendations = recommendCommands(partialCommand); if (recommendations.isEmpty()) return; @@ -319,12 +217,47 @@ public final class SystemCommandsMap { private Set recommendCommands(String partialCommand) { // current implementation only looks if input is contained within a command, // might be updated - return systemCommands.keySet().stream().filter(command -> command.contains(partialCommand)).collect(Collectors.toSet()); + return systemCommands.keySet() + .stream() + .filter(command -> command.contains(partialCommand)) + .sorted((command1, command2) -> Integer.compare(systemCommands.get(command1).getRelevance(), systemCommands.get(command2).getRelevance())) + .collect(Collectors.toSet()); + } + + /** + * + * Supplies the default values for arguments if none are present in the text for + * any argument.
+ * Will only work for {@code SystemCommand}s whose argument counter is bigger + * than 1. + * + * @param textArguments the arguments that were parsed from the text + * @param toEvaluate the system command whose default values should be used + * @return the final argument list + * @since Envoy Client v0.2-beta + * @apiNote this method will insert an empty String if the size of the list + * given to the {@code SystemCommand} is smaller than its argument + * counter and no more text arguments could be found. + */ + private List supplementDefaults(String[] textArguments, SystemCommand toEvaluate) { + final var defaults = toEvaluate.getDefaults(); + final var numberOfArguments = toEvaluate.getNumberOfArguments(); + final List result = new ArrayList<>(); + + if (toEvaluate.getNumberOfArguments() > 0) for (int index = 0; index < numberOfArguments; index++) { + String textArg = null; + if (index < textArguments.length) textArg = textArguments[index]; + // Set the argument at position index to the current argument of the text, if it + // is present. Otherwise the default for that argument will be taken if present. + // In the worst case, an empty String will be used. + result.add(!(textArg == null) && !textArg.isBlank() ? textArg : index < defaults.size() ? defaults.get(index) : ""); + } + return result; } /** * @return all {@link SystemCommand}s used with the underlying command as key * @since Envoy Client v0.2-beta */ - public HashMap getSystemCommands() { return systemCommands; } + public Map getSystemCommands() { return systemCommands; } } diff --git a/client/src/main/java/envoy/client/event/InterruptEvent.java b/client/src/main/java/envoy/client/event/InterruptEvent.java deleted file mode 100644 index 7676267..0000000 --- a/client/src/main/java/envoy/client/event/InterruptEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -package envoy.client.event; - -import envoy.event.Event; - -/** - * This event serves the purpose of interrupting something so that it will not - * be finished. - *

- * Project: envoy-client
- * File: InterruptEvent.java
- * Created: 23.07.2020
- * - * @author Leon Hofmeister - * @since Envoy Client v0.2-beta - */ -public class InterruptEvent extends Event { - - private static final long serialVersionUID = 1L; - - /** - * Creates a new {@link InterruptEvent}. - * - * @param reason the reason why this event exists -> allows finer sieving - * @since Envoy Client v0.2-beta - */ - public InterruptEvent(String reason) { super(reason); } - -} diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index 2d5be67..b561638 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -30,6 +30,7 @@ import javafx.util.Duration; import envoy.client.data.*; import envoy.client.data.audio.AudioRecorder; +import envoy.client.data.commands.SystemCommandBuilder; import envoy.client.data.commands.SystemCommandsMap; import envoy.client.event.MessageCreationEvent; import envoy.client.net.Client; @@ -129,8 +130,6 @@ public final class ChatScene implements Restorable { // Initialize message and user rendering messageList.setCellFactory(new ListCellFactory<>(MessageControl::new)); chatList.setCellFactory(new ListCellFactory<>(ChatControl::new)); - messageTextAreaCommands.addNoArgCommand("DABR", text -> { doABarrelRoll(); return null; }); - messageTextAreaCommands.addNoArgCommand("DoABarrelRoll", text -> { doABarrelRoll(); return null; }); settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE))); voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE))); @@ -208,6 +207,22 @@ public final class ChatScene implements Restorable { }); } + /** + * Initializes all {@code SystemCommands} used in {@code ChatScene}. + * + * @since Envoy Client v0.2-beta + */ + private void initializeSystemCommandsMap() { + final var builder = new SystemCommandBuilder(); + // Do A Barrel roll initialization + final var random = new Random(); + builder.setAction(text -> doABarrelRoll(Integer.parseInt(text.get(0)), Double.parseDouble(text.get(1)))) + .setDefaults(Integer.toString(random.nextInt(3) + 1), Double.toString(random.nextDouble() * 3 + 1)) + .setDescription("See for yourself :)") + .setNumberOfArguments(2); + messageTextAreaCommands.add("DABR", builder.build()); + } + /** * Initializes all necessary data via dependency injection- * @@ -230,6 +245,7 @@ public final class ChatScene implements Restorable { if (!client.isOnline()) updateInfoLabel("You are offline", "infoLabel-info"); recorder = new AudioRecorder(); + initializeSystemCommandsMap(); } @Override @@ -381,7 +397,8 @@ public final class ChatScene implements Restorable { } /** - * Rotates every element in our application by 360° in at most 2.75s. + * Rotates every element in our application by (at most 4 *) 360° in at most + * 2.75s. * * @since Envoy Client v0.1-beta */ @@ -498,9 +515,9 @@ public final class ChatScene implements Restorable { updateInfoLabel("You need to go online to send more messages", "infoLabel-error"); return; } - final var text = messageTextArea.getText().strip(); - messageTextAreaCommands.checkForCommands(text); - try { + final var text = messageTextArea.getText().strip(); + final var systemCommandPresent = messageTextAreaCommands.executeIfAnyPresent(text); + if (systemCommandPresent) {} else try { // Creating the message and its metadata final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator()) .setText(text);