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).
*