package envoy.client.data.commands; import java.util.*; import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; 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
* 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
* 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 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. As it will cause problems in execution, it will not be entered into the map. 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:
* 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.
* Usage example:
* File: SystemCommandsMap.java
* Created: 17.07.2020
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
*/
public class SystemCommandsMap {
private final HashMap
* (allowed chars are a-zA-Z0-9_:!()?.,;-)
*
* {@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.2-beta
*/
public Optional
* 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);
}
/**
* This method ensures that the "/" of a {@link SystemCommand} is stripped.
* It returns the command as (most likely) entered as key in the map for the
* first word of the text.
* It should only be called on strings that contain a "/" at position 0/-1.
*
* @param raw the input
* @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) { return getCommand(raw, 0); }
/**
* 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).
*
* {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}
* {@code Button button = new Button();}
* {@code systemCommands.add("example", (words)-> button.setText(words[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 "/"
* @since Envoy Client v0.2-beta
*/
public void executeIfPresent(String input) {
checkPresent(input).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());
// Executing the function
try {
systemCommand.getAction().apply(arguments);
commandExecuted = true;
systemCommand.onCall();
} catch (final Exception e) {
logger.log(Level.WARNING, "The system command " + getCommand(input) + " threw an exception: ", e);
}
});
}
/**
* Retrieves the recommendations based on the current input entered.
* The first word 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 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
* 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
* In the current implementation, all we check is whether a key contains this
* input. This might be updated later on.
*
* @param partialCommand the partially entered command
* @return a set of all commands that match this input
* @since Envoy Client v0.2-beta
*/
private Set