package envoy.client.data.commands; import java.util.*; import java.util.function.Consumer; import java.util.logging.*; 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
* Usage example:
* 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
* 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:
* File: SystemCommandsMap.java
* Created: 17.07.2020
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
*/
public final class SystemCommandsMap {
private final Map
* {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}
* {@code Button button = new Button();}
* {@code systemCommands.add("example", text -> button.setText(text.get(0), 1);}
* {@code ....}
* user input: {@code "/example xyz ..."}
* {@code systemCommands.get("example xyz ...")} or
* {@code systemCommands.get("/example xyz ...")}
* result: {@code Optional
* 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) {
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_:!()?.,;-)
*
* {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}
* {@code Button button = new Button();}
* {@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
* @return whether a command could be found
* @since Envoy Client v0.2-beta
*/
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 arguments = extractArguments(input, systemCommand);
// Executing the function
try {
systemCommand.getAction().accept(arguments);
systemCommand.onCall();
} catch (final 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
* 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 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, Consumer
* 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
* 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