Revised SystemCommand mechanism and implemented theoretical execution
This commit is contained in:
		@@ -1,26 +0,0 @@
 | 
			
		||||
package envoy.client.data.commands;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class is the base class for all {@link SystemCommand}s that do not need
 | 
			
		||||
 * another argument to parse their function.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>NoArgSystemCommand.java</strong><br>
 | 
			
		||||
 * Created: <strong>16.07.2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Leon Hofmeister
 | 
			
		||||
 * @since Envoy Client v0.1-beta
 | 
			
		||||
 */
 | 
			
		||||
public class NoArgSystemCommand extends SystemCommand<Void> {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs a new {@code NoArgSystemCommand} that takes no arguments.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param command the string that must be inputted to execute the given action
 | 
			
		||||
	 * @param action  the action that should be performed
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public NoArgSystemCommand(String command, Function<Void, Void> action) { super(command, action); }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
package envoy.client.data.commands;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class is the base class for all {@link SystemCommand}s that need a
 | 
			
		||||
 * String to parse their function.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>StringArgSystemCommand.java</strong><br>
 | 
			
		||||
 * Created: <strong>16.07.2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Leon Hofmeister
 | 
			
		||||
 * @since Envoy Client v0.1-beta
 | 
			
		||||
 */
 | 
			
		||||
public class StringArgSystemCommand extends SystemCommand<String> {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs a new {@code NoArgSystemCommand} that takes a String argument.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param command the string that must be inputted to execute the given action
 | 
			
		||||
	 * @param action  the action that should be performed
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public StringArgSystemCommand(String command, Function<String, Void> action) { super(command, action); }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,48 +4,62 @@ import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class is the base class of all {@code SystemCommands} and contains an
 | 
			
		||||
 * action and a command that needs to be inputted to execute the given action.
 | 
			
		||||
 * 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
 | 
			
		||||
 * that the words following the indicator String can be used as input of the
 | 
			
		||||
 * function. This approach has one limitation:<br>
 | 
			
		||||
 * <b>Order matters!</b> Changing the order of arguments will likely result in
 | 
			
		||||
 * unexpected behavior.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>SystemCommand.java</strong><br>
 | 
			
		||||
 * Created: <strong>16.07.2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Leon Hofmeister
 | 
			
		||||
 * @param <T> the type of argument needed
 | 
			
		||||
 * @since Envoy Client v0.1-beta
 | 
			
		||||
 */
 | 
			
		||||
public abstract class SystemCommand<T> {
 | 
			
		||||
public final class SystemCommand {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * This variable stores the command that should be inputted to execute the given
 | 
			
		||||
	 * action
 | 
			
		||||
	 * This variable stores the amount of arguments that need to be parsed for the
 | 
			
		||||
	 * underlying function.
 | 
			
		||||
	 */
 | 
			
		||||
	protected final String command;
 | 
			
		||||
	protected final int numberOfArguments;
 | 
			
		||||
 | 
			
		||||
	protected final Function<T, Void> action;
 | 
			
		||||
	/**
 | 
			
		||||
	 * This Function takes a {@code String[]} as argument because automatically
 | 
			
		||||
	 * {@code SystemCommand#numberOfArguments} words following the necessary command
 | 
			
		||||
	 * will be put into this array.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @see String#split(String)
 | 
			
		||||
	 */
 | 
			
		||||
	protected final Function<String[], Void> action;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs a new {@code NoArgSystemCommand} that takes no arguments.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param command the string that must be inputted to execute the given action
 | 
			
		||||
	 * @param action  the action that should be performed
 | 
			
		||||
	 * @param action            the action that should be performed
 | 
			
		||||
	 * @param numberOfArguments the amount of arguments that need to be parsed for
 | 
			
		||||
	 *                          the underlying function
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public SystemCommand(String command, Function<T, Void> action) {
 | 
			
		||||
		this.command	= command;
 | 
			
		||||
		this.action		= action;
 | 
			
		||||
	public SystemCommand(Function<String[], Void> action, int numberOfArguments) {
 | 
			
		||||
		this.numberOfArguments	= numberOfArguments;
 | 
			
		||||
		this.action				= action;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the command that must be inputted to execute the given action
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public String getCommand() { return command; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the action that should be performed
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public Function<T, Void> getAction() { return action; }
 | 
			
		||||
	public Function<String[], Void> getAction() { return action; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the amount of arguments that need to be parsed for
 | 
			
		||||
	 *         the underlying function
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public int getNumberOfArguments() { return numberOfArguments; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,159 @@
 | 
			
		||||
package envoy.client.data.commands;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
 | 
			
		||||
import envoy.util.EnvoyLog;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class stores all {@link SystemCommand}s used.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>SystemCommandsMap.java</strong><br>
 | 
			
		||||
 * Created: <strong>17.07.2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @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<String, SystemCommand>	systemCommands	= new HashMap<>();
 | 
			
		||||
	private boolean									rethrowFailure	= true;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @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
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public void addCommand(String command, Function<String[], Void> action, int numberOfArguments) {
 | 
			
		||||
		systemCommands.put(command, new SystemCommand(action, numberOfArguments));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Adds a new {@link SystemCommand} to the map that does not depend upon
 | 
			
		||||
	 * arguments in the given String
 | 
			
		||||
	 *
 | 
			
		||||
	 * @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}
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public void addNoArgCommand(String command, Function<String[], Void> action) { addCommand(command, action, 0); }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @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
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public void add(String command, Function<String[], Void> action, int numberOfArguments) {
 | 
			
		||||
		systemCommands.put(command, new SystemCommand(action, numberOfArguments));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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).
 | 
			
		||||
	 * <p>
 | 
			
		||||
	 * Usage example:<br>
 | 
			
		||||
	 * {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}<br>
 | 
			
		||||
	 * {@code systemCommands.add("example", Function.identity, 1);}<br>
 | 
			
		||||
	 * {@code ....}<br>
 | 
			
		||||
	 * user input: {@code "/example xyz ..."}<br>
 | 
			
		||||
	 * {@code systemCommands.checkPresent("example xyz ...")}
 | 
			
		||||
	 * result: {@code SystemCommand[action=Function.identity, numberOfArguments=1]}
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param input the input string given by the user, <b>excluding the "/"</b>
 | 
			
		||||
	 * @return the wrapped system command, if present
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public Optional<SystemCommand> checkPresent(String input) {
 | 
			
		||||
		// TODO: Is String.indexOf inclusive or exclusive?
 | 
			
		||||
		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).
 | 
			
		||||
	 * <p>
 | 
			
		||||
	 * Usage example:<br>
 | 
			
		||||
	 * {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}<br>
 | 
			
		||||
	 * {@code systemCommands.add("example", (words)-> <Button>.setText(words[0]), 1);}<br>
 | 
			
		||||
	 * {@code ....}<br>
 | 
			
		||||
	 * user input: {@code "/example xyz ..."}<br>
 | 
			
		||||
	 * {@code systemCommands.executeIfPresent("example xyz ...")}
 | 
			
		||||
	 * result: {@code <Button>.getText()=="xyz"}
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param input the input string given by the user, <b>excluding the "/"</b>
 | 
			
		||||
	 * @since Envoy Client v0.1-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
 | 
			
		||||
			// TODO: Is String.indexOf inclusive or exclusive?
 | 
			
		||||
			final var remainingString = input.substring(input.indexOf(" ") + 1);
 | 
			
		||||
			// TODO: Is Arrays.copyOfRange inclusive or exclusive in the "to" parameter?
 | 
			
		||||
			// TODO: Current implementation will fail in certain cases, i.e. two characters
 | 
			
		||||
			// behind each other (" "), not enough words, ...
 | 
			
		||||
			// TODO: Another way of failure is the fact that in the current implementation,
 | 
			
		||||
			// it is assumed that a String is already present in finished form and not that
 | 
			
		||||
			// it will be finished later on, or does it?
 | 
			
		||||
			final var arguments = Arrays.copyOfRange(remainingString.split(" "), 0, systemCommand.getNumberOfArguments());
 | 
			
		||||
			// Executing the function
 | 
			
		||||
			try {
 | 
			
		||||
				systemCommand.getAction().apply(arguments);
 | 
			
		||||
			} catch (final Exception e) {
 | 
			
		||||
				EnvoyLog.getLogger(SystemCommandsMap.class).log(Level.WARNING, "The system command " +
 | 
			
		||||
				// detected command
 | 
			
		||||
				input.substring(0, input.indexOf(" ")) + " threw an exception: ", e);
 | 
			
		||||
				if (rethrowFailure) throw e;
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return whether failures of {@link SystemCommand}s should be rethrown
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean isRethrowFailure() { return rethrowFailure; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param rethrowFailure whether failures of {@link SystemCommand}s should be
 | 
			
		||||
	 *                       rethrown
 | 
			
		||||
	 * @return this instance of a {@code SystemCommandsMap}
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public SystemCommandsMap setRethrowFailure(boolean rethrowFailure) {
 | 
			
		||||
		this.rethrowFailure = rethrowFailure;
 | 
			
		||||
		return this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the systemCommands
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public HashMap<String, SystemCommand> getSystemCommands() { return systemCommands; }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user