package envoy.client.data.commands; import java.util.*; import java.util.function.Function; import java.util.logging.Level; import java.util.regex.Pattern; import java.util.stream.Collectors; import envoy.util.EnvoyLog; /** * This class stores all {@link SystemCommand}s used. *

* Project: envoy-client
* File: SystemCommandsMap.java
* Created: 17.07.2020
* * @author Leon Hofmeister * @since Envoy Client v0.1-beta * @apiNote Please refrain from using the "/"-char in keys of this map. * Unexpected behavior will occur. */ public class SystemCommandsMap { private final HashMap systemCommands = new HashMap<>(); private final Pattern commandBounds = Pattern.compile("^[a-zA-Z0-9_:!\\(\\)\\?\\.\\,\\;\\-]+$"); /** * 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 * @param numberOfArguments the amount of arguments that need to be parsed for * the underlying function * @see SystemCommandsMap#isValidKey(String) * @since Envoy Client v0.1-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.1-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.1-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.1-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.1-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.1-beta */ public final boolean isValidKey(String command) { final boolean valid = commandBounds.matcher(command).matches(); if (!valid) EnvoyLog.getLogger(SystemCommandsMap.class) .log(Level.WARNING, "The command \"" + command + "\" is not valid. It will cause problems in execution: Only the characters " + commandBounds + "are allowed"); return valid; } /** * 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 ....}
* user input: {@code "/example xyz ..."}
* {@code systemCommands.checkPresent("example xyz ...")} * result: {@code SystemCommand[action=Function.identity, numberOfArguments=1]} * * @param input the input string given by the user, excluding the "/" * @return the wrapped system command, if present * @since Envoy Client v0.1-beta */ public Optional checkPresent(String input) { final var firstWord = input.substring(0, input.indexOf(" ")); return Optional.ofNullable(systemCommands.get(firstWord)); } /** * This method checks if the input String is a key in the map and executes the * wrapped System command if present. * Its intended usage is after a "/" has been detected in the input String. * It will do nothing 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", (words)-> <Button>.setText(words[0]), 1);}
* {@code ....}
* user input: {@code "/example xyz ..."}
* {@code systemCommands.executeIfPresent("example xyz ...")} * result: {@code