Merge pull request #26 from informatik-ag-ngl/f/system_commands
Added system commands ( features: custom argument number, default values, system command builder, ...). Fixed bug not copying attachment when using copy and send.
This commit is contained in:
commit
9d7f85c58d
File diff suppressed because one or more lines are too long
34
client/src/main/java/envoy/client/data/commands/OnCall.java
Normal file
34
client/src/main/java/envoy/client/data/commands/OnCall.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package envoy.client.data.commands;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface defines an action that should be performed when a system
|
||||||
|
* command gets called.
|
||||||
|
* <p>
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>OnCall.java</strong><br>
|
||||||
|
* Created: <strong>23.07.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Leon Hofmeister
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public interface OnCall {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs class specific actions when a {@link SystemCommand} has been called.
|
||||||
|
*
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
void onCall();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs actions that can only be performed by classes that are not
|
||||||
|
* {@link SystemCommand}s when a SystemCommand has been called.
|
||||||
|
*
|
||||||
|
* @param consumer the action to perform when this {@link SystemCommand} has
|
||||||
|
* been called
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
void onCall(Supplier<Void> consumer);
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package envoy.client.data.commands;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is the base class of all {@code SystemCommands} and contains an
|
||||||
|
* 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 List<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
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public class SystemCommand implements OnCall {
|
||||||
|
|
||||||
|
protected int relevance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The argument count of the command.
|
||||||
|
*/
|
||||||
|
protected final int numberOfArguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function takes a {@code List<String>} as argument because automatically
|
||||||
|
* {@code SystemCommand#numberOfArguments} words following the necessary command
|
||||||
|
* will be put into this list.
|
||||||
|
*
|
||||||
|
* @see String#split(String)
|
||||||
|
*/
|
||||||
|
protected final Consumer<List<String>> action;
|
||||||
|
|
||||||
|
protected final String description;
|
||||||
|
|
||||||
|
protected final List<String> defaults;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@code SystemCommand}.
|
||||||
|
*
|
||||||
|
* @param action the action performed by the command
|
||||||
|
* @param numberOfArguments the argument count accepted by the action
|
||||||
|
* @param defaults the default values for the corresponding arguments
|
||||||
|
* @param description the description of this {@code SystemCommand}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommand(Consumer<List<String>> action, int numberOfArguments, List<String> defaults, String description) {
|
||||||
|
this.numberOfArguments = numberOfArguments;
|
||||||
|
this.action = action;
|
||||||
|
this.defaults = defaults == null ? new ArrayList<>() : defaults;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the action that should be performed
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public Consumer<List<String>> getAction() { return action; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the argument count of the command
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public int getNumberOfArguments() { return numberOfArguments; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the description
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public String getDescription() { return description; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the relevance
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public int getRelevance() { return relevance; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param relevance the relevance to set
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public void setRelevance(int relevance) { this.relevance = relevance; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the relevance of this {@code SystemCommand}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onCall() { relevance++; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the relevance of this {@code SystemCommand} and executes the
|
||||||
|
* supplier.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onCall(Supplier<Void> consumer) {
|
||||||
|
onCall();
|
||||||
|
consumer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the defaults
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public List<String> getDefaults() { return defaults; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() { return Objects.hash(action); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) return true;
|
||||||
|
if (obj == null) return false;
|
||||||
|
if (getClass() != obj.getClass()) return false;
|
||||||
|
final SystemCommand other = (SystemCommand) obj;
|
||||||
|
return Objects.equals(action, other.action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SystemCommand [relevance=" + relevance + ", numberOfArguments=" + numberOfArguments + ", "
|
||||||
|
+ (action != null ? "action=" + action + ", " : "") + (description != null ? "description=" + description + ", " : "")
|
||||||
|
+ (defaults != null ? "defaults=" + defaults : "") + "]";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
package envoy.client.data.commands;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class acts as a builder for {@link SystemCommand}s.
|
||||||
|
* <p>
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>SystemCommandBuilder.java</strong><br>
|
||||||
|
* Created: <strong>23.07.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Leon Hofmeister
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public class SystemCommandBuilder {
|
||||||
|
|
||||||
|
private int numberOfArguments;
|
||||||
|
private Consumer<List<String>> action;
|
||||||
|
private List<String> defaults;
|
||||||
|
private String description;
|
||||||
|
private int relevance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numberOfArguments the numberOfArguments to set
|
||||||
|
* @return this {@code SystemCommandBuilder}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommandBuilder setNumberOfArguments(int numberOfArguments) {
|
||||||
|
this.numberOfArguments = numberOfArguments;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param action the action to set
|
||||||
|
* @return this {@code SystemCommandBuilder}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommandBuilder setAction(Consumer<List<String>> action) {
|
||||||
|
this.action = action;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param description the description to set
|
||||||
|
* @return this {@code SystemCommandBuilder}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommandBuilder setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param relevance the relevance to set
|
||||||
|
* @return this {@code SystemCommandBuilder}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommandBuilder setRelevance(int relevance) {
|
||||||
|
this.relevance = relevance;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param defaults the defaults to set
|
||||||
|
* @return this {@code SystemCommandBuilder}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommandBuilder setDefaults(String... defaults) {
|
||||||
|
this.defaults = List.of(defaults);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all values stored.
|
||||||
|
*
|
||||||
|
* @return this {@code SystemCommandBuilder}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommandBuilder reset() {
|
||||||
|
numberOfArguments = 0;
|
||||||
|
action = null;
|
||||||
|
defaults = new ArrayList<>();
|
||||||
|
description = "";
|
||||||
|
relevance = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a {@code SystemCommand} based upon the previously entered data.
|
||||||
|
*
|
||||||
|
* @return the built {@code SystemCommand}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommand build() { return build(true); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
|
||||||
|
* {@code SystemCommand#numberOfArguments} will be set to 0, regardless of the
|
||||||
|
* previous value.<br>
|
||||||
|
* At the end, this {@code SystemCommandBuilder} will be reset.
|
||||||
|
*
|
||||||
|
* @return the built {@code SystemCommand}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommand buildNoArg() {
|
||||||
|
numberOfArguments = 0;
|
||||||
|
return build(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
|
||||||
|
* {@code SystemCommand#numberOfArguments} will be set to use the rest of the
|
||||||
|
* string as argument, regardless of the previous value.<br>
|
||||||
|
* At the end, this {@code SystemCommandBuilder} will be reset.
|
||||||
|
*
|
||||||
|
* @return the built {@code SystemCommand}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommand buildRemainingArg() {
|
||||||
|
numberOfArguments = -1;
|
||||||
|
return build(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
|
||||||
|
* At the end, this {@code SystemCommandBuilder} <b>can</b> be reset but must
|
||||||
|
* not be.
|
||||||
|
*
|
||||||
|
* @param reset whether this {@code SystemCommandBuilder} should be reset
|
||||||
|
* afterwards.<br>
|
||||||
|
* This can be useful if another command wants to execute something
|
||||||
|
* similar
|
||||||
|
* @return the built {@code SystemCommand}
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public SystemCommand build(boolean reset) {
|
||||||
|
final var sc = new SystemCommand(action, numberOfArguments, defaults, description);
|
||||||
|
sc.setRelevance(relevance);
|
||||||
|
if (reset) reset();
|
||||||
|
return sc;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,263 @@
|
|||||||
|
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.
|
||||||
|
* <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.2-beta
|
||||||
|
*/
|
||||||
|
public final class SystemCommandsMap {
|
||||||
|
|
||||||
|
private final Map<String, SystemCommand> systemCommands = new HashMap<>();
|
||||||
|
|
||||||
|
private final Pattern commandPattern = Pattern.compile("^[a-zA-Z0-9_:!\\(\\)\\?\\.\\,\\;\\-]+$");
|
||||||
|
|
||||||
|
private static final Logger logger = EnvoyLog.getLogger(SystemCommandsMap.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new command to the map if the command name is valid.
|
||||||
|
*
|
||||||
|
* @param command the input string to execute the
|
||||||
|
* given action
|
||||||
|
* @param systemCommand the command to add - can be built using
|
||||||
|
* {@link SystemCommandBuilder}
|
||||||
|
* @see SystemCommandsMap#isValidKey(String)
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public void add(String command, SystemCommand systemCommand) { if (isValidKey(command)) systemCommands.put(command, systemCommand); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks if the input String is a key in the map and returns the
|
||||||
|
* wrapped System command if present.
|
||||||
|
* 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 Button button = new Button();}
|
||||||
|
* {@code systemCommands.add("example", text -> button.setText(text.get(0), 1);}<br>
|
||||||
|
* {@code ....}<br>
|
||||||
|
* user input: {@code "/example xyz ..."}<br>
|
||||||
|
* {@code systemCommands.get("example xyz ...")} or
|
||||||
|
* {@code systemCommands.get("/example xyz ...")}
|
||||||
|
* result: {@code Optional<SystemCommand>}
|
||||||
|
*
|
||||||
|
* @param input the input string given by the user
|
||||||
|
* @return the wrapped system command, if present
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public Optional<SystemCommand> get(String input) { return Optional.ofNullable(systemCommands.get(getCommand(input))); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method ensures that the "/" of a {@link SystemCommand} is stripped.<br>
|
||||||
|
* It returns the command as (most likely) entered as key in the map for the
|
||||||
|
* first word of the text.<br>
|
||||||
|
* 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.<br>
|
||||||
|
* (allowed chars are <b>a-zA-Z0-9_:!()?.,;-</b>)
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @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).
|
||||||
|
* <p>
|
||||||
|
* Usage example:<br>
|
||||||
|
* {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}<br>
|
||||||
|
* {@code Button button = new Button();}<br>
|
||||||
|
* {@code systemCommands.add("example", (words)-> button.setText(words.get(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
|
||||||
|
* @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<String> extractArguments(String input, SystemCommand systemCommand) {
|
||||||
|
// no more arguments follow after the command (e.g. text = "/DABR")
|
||||||
|
final var indexOfSpace = input.indexOf(" ");
|
||||||
|
if (indexOfSpace < 0) return supplementDefaults(new String[] {}, systemCommand);
|
||||||
|
// the arguments behind a system command
|
||||||
|
final var remainingString = input.substring(indexOfSpace + 1);
|
||||||
|
final var numberOfArguments = systemCommand.getNumberOfArguments();
|
||||||
|
// splitting those arguments and supplying default values
|
||||||
|
final var textArguments = remainingString.split(" ", -1);
|
||||||
|
final var originalArguments = numberOfArguments >= 0 ? Arrays.copyOfRange(textArguments, 0, numberOfArguments) : textArguments;
|
||||||
|
final var arguments = supplementDefaults(originalArguments, systemCommand);
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the recommendations based on the current input entered.<br>
|
||||||
|
* The first word is used for the recommendations and
|
||||||
|
* it does not matter if the "/" is at its beginning or not.<br>
|
||||||
|
* If none are present, nothing will be done.<br>
|
||||||
|
* Otherwise the given function will be executed on the recommendations.<br>
|
||||||
|
*
|
||||||
|
* @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, Function<Set<String>, Void> action) {
|
||||||
|
final var partialCommand = getCommand(input);
|
||||||
|
// Get the expected commands
|
||||||
|
final var recommendations = recommendCommands(partialCommand);
|
||||||
|
if (recommendations.isEmpty()) return;
|
||||||
|
// Execute the given action
|
||||||
|
else action.apply(recommendations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recommends commands based upon the currently entered input.<br>
|
||||||
|
* 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<String> recommendCommands(String partialCommand) {
|
||||||
|
// current implementation only looks if input is contained within a command,
|
||||||
|
// might be updated
|
||||||
|
return systemCommands.keySet()
|
||||||
|
.stream()
|
||||||
|
.filter(command -> command.contains(partialCommand))
|
||||||
|
.sorted((command1, command2) -> Integer.compare(systemCommands.get(command1).getRelevance(), systemCommands.get(command2).getRelevance()))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Supplies the default values for arguments if none are present in the text for
|
||||||
|
* any argument. <br>
|
||||||
|
* 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<String> supplementDefaults(String[] textArguments, SystemCommand toEvaluate) {
|
||||||
|
final var defaults = toEvaluate.getDefaults();
|
||||||
|
final var numberOfArguments = toEvaluate.getNumberOfArguments();
|
||||||
|
final List<String> result = new ArrayList<>();
|
||||||
|
|
||||||
|
if (toEvaluate.getNumberOfArguments() > 0) for (int index = 0; index < numberOfArguments; index++) {
|
||||||
|
String textArg = null;
|
||||||
|
if (index < textArguments.length) textArg = textArguments[index];
|
||||||
|
// Set the argument at position index to the current argument of the text, if it
|
||||||
|
// is present. Otherwise the default for that argument will be taken if present.
|
||||||
|
// In the worst case, an empty String will be used.
|
||||||
|
result.add(!(textArg == null) && !textArg.isBlank() ? textArg : index < defaults.size() ? defaults.get(index) : "");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return all {@link SystemCommand}s used with the underlying command as key
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
public Map<String, SystemCommand> getSystemCommands() { return systemCommands; }
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* This package contains all classes that can be used as system commands.<br>
|
||||||
|
* Every system command can be called using a specific syntax:"/<command>"
|
||||||
|
* <p>
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>package-info.java</strong><br>
|
||||||
|
* Created: <strong>16.07.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Leon Hofmeister
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
package envoy.client.data.commands;
|
@ -30,6 +30,8 @@ import javafx.util.Duration;
|
|||||||
|
|
||||||
import envoy.client.data.*;
|
import envoy.client.data.*;
|
||||||
import envoy.client.data.audio.AudioRecorder;
|
import envoy.client.data.audio.AudioRecorder;
|
||||||
|
import envoy.client.data.commands.SystemCommandBuilder;
|
||||||
|
import envoy.client.data.commands.SystemCommandsMap;
|
||||||
import envoy.client.event.MessageCreationEvent;
|
import envoy.client.event.MessageCreationEvent;
|
||||||
import envoy.client.net.Client;
|
import envoy.client.net.Client;
|
||||||
import envoy.client.net.WriteProxy;
|
import envoy.client.net.WriteProxy;
|
||||||
@ -107,6 +109,8 @@ public final class ChatScene implements Restorable {
|
|||||||
private Attachment pendingAttachment;
|
private Attachment pendingAttachment;
|
||||||
private boolean postingPermanentlyDisabled;
|
private boolean postingPermanentlyDisabled;
|
||||||
|
|
||||||
|
private final SystemCommandsMap messageTextAreaCommands = new SystemCommandsMap();
|
||||||
|
|
||||||
private static final Settings settings = Settings.getInstance();
|
private static final Settings settings = Settings.getInstance();
|
||||||
private static final EventBus eventBus = EventBus.getInstance();
|
private static final EventBus eventBus = EventBus.getInstance();
|
||||||
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
|
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
|
||||||
@ -193,7 +197,7 @@ public final class ChatScene implements Restorable {
|
|||||||
switch (e.getOperationType()) {
|
switch (e.getOperationType()) {
|
||||||
case ADD:
|
case ADD:
|
||||||
if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact);
|
if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact);
|
||||||
Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
|
final Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
|
||||||
Platform.runLater(() -> chatList.getItems().add(chat));
|
Platform.runLater(() -> chatList.getItems().add(chat));
|
||||||
break;
|
break;
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
@ -203,6 +207,22 @@ public final class ChatScene implements Restorable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes all {@code SystemCommands} used in {@code ChatScene}.
|
||||||
|
*
|
||||||
|
* @since Envoy Client v0.2-beta
|
||||||
|
*/
|
||||||
|
private void initializeSystemCommandsMap() {
|
||||||
|
final var builder = new SystemCommandBuilder();
|
||||||
|
// Do A Barrel roll initialization
|
||||||
|
final var random = new Random();
|
||||||
|
builder.setAction(text -> doABarrelRoll(Integer.parseInt(text.get(0)), Double.parseDouble(text.get(1))))
|
||||||
|
.setDefaults(Integer.toString(random.nextInt(3) + 1), Double.toString(random.nextDouble() * 3 + 1))
|
||||||
|
.setDescription("See for yourself :)")
|
||||||
|
.setNumberOfArguments(2);
|
||||||
|
messageTextAreaCommands.add("DABR", builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes all necessary data via dependency injection-
|
* Initializes all necessary data via dependency injection-
|
||||||
*
|
*
|
||||||
@ -225,6 +245,7 @@ public final class ChatScene implements Restorable {
|
|||||||
if (!client.isOnline()) updateInfoLabel("You are offline", "infoLabel-info");
|
if (!client.isOnline()) updateInfoLabel("You are offline", "infoLabel-info");
|
||||||
|
|
||||||
recorder = new AudioRecorder();
|
recorder = new AudioRecorder();
|
||||||
|
initializeSystemCommandsMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -376,20 +397,34 @@ public final class ChatScene implements Restorable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotates every element in our application by 360° in at most 2.75s.
|
* Rotates every element in our application by (at most 4 *) 360° in at most
|
||||||
|
* 2.75s.
|
||||||
*
|
*
|
||||||
* @since Envoy Client v0.1-beta
|
* @since Envoy Client v0.1-beta
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void doABarrelRoll() {
|
private void doABarrelRoll() {
|
||||||
// contains all Node objects in ChatScene in alphabetical order
|
|
||||||
final var rotatableNodes = new Node[] { attachmentButton, attachmentView, contactLabel, infoLabel, messageList, messageTextArea,
|
|
||||||
postButton, remainingChars, rotateButton, scene, settingsButton, chatList, voiceButton };
|
|
||||||
final var random = new Random();
|
final var random = new Random();
|
||||||
|
doABarrelRoll(random.nextInt(3) + 1, random.nextDouble() * 3 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates every element in our application by {@code rotations}*360° in
|
||||||
|
* {@code an}.
|
||||||
|
*
|
||||||
|
* @param rotations the amount of times the scene is rotated by 360°
|
||||||
|
* @param animationTime the time in seconds that this animation lasts
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
private void doABarrelRoll(int rotations, double animationTime) {
|
||||||
|
// contains all Node objects in ChatScene in alphabetical order
|
||||||
|
final var rotatableNodes = new Node[] { attachmentButton, attachmentView, contactLabel, infoLabel, messageList, messageTextArea, postButton,
|
||||||
|
remainingChars, rotateButton, scene, settingsButton, chatList, voiceButton };
|
||||||
for (final var node : rotatableNodes) {
|
for (final var node : rotatableNodes) {
|
||||||
// Defines at most four whole rotation in at most 4s
|
// Sets the animation duration to {animationTime}
|
||||||
final var rotateTransition = new RotateTransition(Duration.seconds(random.nextDouble() * 3 + 1), node);
|
final var rotateTransition = new RotateTransition(Duration.seconds(animationTime), node);
|
||||||
rotateTransition.setByAngle((random.nextInt(3) + 1) * 360);
|
// rotates every element {rotations} times
|
||||||
|
rotateTransition.setByAngle(rotations * 360);
|
||||||
rotateTransition.play();
|
rotateTransition.play();
|
||||||
// This is needed as for some strange reason objects could stop before being
|
// This is needed as for some strange reason objects could stop before being
|
||||||
// rotated back to 0°
|
// rotated back to 0°
|
||||||
@ -481,7 +516,7 @@ public final class ChatScene implements Restorable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final var text = messageTextArea.getText().strip();
|
final var text = messageTextArea.getText().strip();
|
||||||
try {
|
if (!messageTextAreaCommands.executeIfAnyPresent(text)) try {
|
||||||
// Creating the message and its metadata
|
// Creating the message and its metadata
|
||||||
final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
||||||
.setText(text);
|
.setText(text);
|
||||||
@ -569,9 +604,14 @@ public final class ChatScene implements Restorable {
|
|||||||
private void copyAndPostMessage() {
|
private void copyAndPostMessage() {
|
||||||
final var messageText = messageTextArea.getText();
|
final var messageText = messageTextArea.getText();
|
||||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(messageText), null);
|
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(messageText), null);
|
||||||
|
final var image = attachmentView.getImage();
|
||||||
|
final var messageAttachment = pendingAttachment;
|
||||||
postMessage();
|
postMessage();
|
||||||
messageTextArea.setText(messageText);
|
messageTextArea.setText(messageText);
|
||||||
updateRemainingCharsLabel();
|
updateRemainingCharsLabel();
|
||||||
postButton.setDisable(messageText.isBlank());
|
postButton.setDisable(messageText.isBlank());
|
||||||
|
attachmentView.setImage(image);
|
||||||
|
if (attachmentView.getImage() != null) attachmentView.setVisible(true);
|
||||||
|
pendingAttachment = messageAttachment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user