CommandDispatcher comments for maintenance purpose and fixed all code warnings

This commit is contained in:
themode 2020-10-29 19:51:10 +01:00
parent 384df073a8
commit c887392a91
3 changed files with 78 additions and 52 deletions

View File

@ -73,8 +73,9 @@ public class Command {
* It is called no matter the syntax used and can be used to check permissions or * It is called no matter the syntax used and can be used to check permissions or
* the {@link CommandSender} type. * the {@link CommandSender} type.
* *
* @return the command condition * @return the command condition, null if not any
*/ */
@Nullable
public CommandCondition getCondition() { public CommandCondition getCondition() {
return condition; return condition;
} }
@ -82,9 +83,9 @@ public class Command {
/** /**
* Sets the {@link CommandCondition}. * Sets the {@link CommandCondition}.
* *
* @param commandCondition the new command condition * @param commandCondition the new command condition, null to do not call anything
*/ */
public void setCondition(CommandCondition commandCondition) { public void setCondition(@Nullable CommandCondition commandCondition) {
this.condition = commandCondition; this.condition = commandCondition;
} }

View File

@ -3,6 +3,7 @@ package net.minestom.server.command.builder;
import net.minestom.server.command.CommandSender; import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.condition.CommandCondition; import net.minestom.server.command.builder.condition.CommandCondition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
@ -16,27 +17,33 @@ public class CommandDispatcher {
private final Map<String, Command> commandMap = new HashMap<>(); private final Map<String, Command> commandMap = new HashMap<>();
private final Set<Command> commands = new HashSet<>(); private final Set<Command> commands = new HashSet<>();
public void register(Command command) { public void register(@NotNull Command command) {
this.commandMap.put(command.getName().toLowerCase(), command); this.commandMap.put(command.getName().toLowerCase(), command);
for (String alias : command.getAliases()) {
this.commandMap.put(alias.toLowerCase(), command); // Register aliases
final String[] aliases = command.getAliases();
if (aliases != null) {
for (String alias : command.getAliases()) {
this.commandMap.put(alias.toLowerCase(), command);
}
} }
this.commands.add(command); this.commands.add(command);
} }
/** /**
* Parse the given command * Parses the given command.
* *
* @param commandString the command (containing the command name and the args if any) * @param commandString the command (containing the command name and the args if any)
* @return the result of the parsing, null if the command doesn't exist * @return the result of the parsing, null if the command doesn't exist
*/ */
public CommandResult parse(String commandString) { public CommandResult parse(@NotNull String commandString) {
commandString = commandString.trim(); commandString = commandString.trim();
// Split space // Split space
final String spaceRegex = " "; final String spaceRegex = " ";
final String[] splitted = commandString.split(spaceRegex); final String[] parts = commandString.split(spaceRegex);
final String commandName = splitted[0]; final String commandName = parts[0];
final String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(spaceRegex); final String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(spaceRegex);
@ -49,11 +56,12 @@ public class CommandDispatcher {
return findCommandResult(command, args); return findCommandResult(command, args);
} }
public void execute(CommandSender source, String commandString) { public void execute(@NotNull CommandSender source, @NotNull String commandString) {
CommandResult result = parse(commandString); CommandResult result = parse(commandString);
result.execute(source, commandString); result.execute(source, commandString);
} }
@NotNull
public Set<Command> getCommands() { public Set<Command> getCommands() {
return Collections.unmodifiableSet(commands); return Collections.unmodifiableSet(commands);
} }
@ -64,43 +72,50 @@ public class CommandDispatcher {
* @param commandName the command name * @param commandName the command name
* @return the {@link Command} associated with the name, null if not any * @return the {@link Command} associated with the name, null if not any
*/ */
public Command findCommand(String commandName) { @Nullable
public Command findCommand(@NotNull String commandName) {
commandName = commandName.toLowerCase(); commandName = commandName.toLowerCase();
return commandMap.getOrDefault(commandName, null); return commandMap.getOrDefault(commandName, null);
} }
private CommandResult findCommandResult(Command command, String[] args) { private CommandResult findCommandResult(@NotNull Command command, @NotNull String[] args) {
CommandResult result = new CommandResult(); CommandResult result = new CommandResult();
result.command = command; result.command = command;
Arguments executorArgs = new Arguments(); Arguments executorArgs = new Arguments();
// Default executor // The default executor should be used if no argument is provided
// Check if args array is empty
if (args[0].length() == 0) { if (args[0].length() == 0) {
result.executor = command.getDefaultExecutor(); result.executor = command.getDefaultExecutor();
result.arguments = executorArgs; result.arguments = executorArgs;
return result; return result;
} }
// SYNTAXES PARSING
// Find syntax // All the registered syntaxes of the command
final Collection<CommandSyntax> syntaxes = command.getSyntaxes(); final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
// Contains all the fully validated syntaxes (we later find the one with the most amount of arguments)
List<CommandSyntax> validSyntaxes = new ArrayList<>(); List<CommandSyntax> validSyntaxes = new ArrayList<>();
// Contains the raw string value of each argument that has been validated
// CommandSyntax - (Argument index/Raw string value)
Map<CommandSyntax, String[]> syntaxesValues = new HashMap<>(); Map<CommandSyntax, String[]> syntaxesValues = new HashMap<>();
// Contains all the syntaxes that are not fully correct, used to later, retrieve the "most correct syntax"
// Number of correct argument - The data about the failing argument
TreeMap<Integer, CommandSuggestionHolder> syntaxesSuggestions = new TreeMap<>(Collections.reverseOrder()); TreeMap<Integer, CommandSuggestionHolder> syntaxesSuggestions = new TreeMap<>(Collections.reverseOrder());
for (CommandSyntax syntax : syntaxes) { for (CommandSyntax syntax : syntaxes) {
final Argument[] arguments = syntax.getArguments(); final Argument<Object>[] arguments = syntax.getArguments();
final String[] argsValues = new String[arguments.length]; final String[] argsValues = new String[arguments.length];
boolean syntaxCorrect = true; boolean syntaxCorrect = true;
int argIndex = 0; int argIndex = 0;
boolean useRemaining = false; boolean useRemaining = false;
// Check the validity of the arguments...
for (int argCount = 0; argCount < syntax.getArguments().length; argCount++) { for (int argCount = 0; argCount < syntax.getArguments().length; argCount++) {
final Argument argument = syntax.getArguments()[argCount]; final Argument<Object> argument = syntax.getArguments()[argCount];
useRemaining = argument.useRemaining(); useRemaining = argument.useRemaining();
// the correction result of the argument // the correction result of the argument
@ -111,6 +126,7 @@ public class CommandDispatcher {
StringBuilder argValue = new StringBuilder(); StringBuilder argValue = new StringBuilder();
if (useRemaining) { if (useRemaining) {
// Argument is supposed to take the rest of the command input
for (int i = argIndex; i < args.length; i++) { for (int i = argIndex; i < args.length; i++) {
final String arg = args[i]; final String arg = args[i];
if (argValue.length() > 0) if (argValue.length() > 0)
@ -126,6 +142,7 @@ public class CommandDispatcher {
argsValues[argIndex] = argValueString; argsValues[argIndex] = argValueString;
} }
} else { } else {
// Argument is either single-word or can accept optional delimited space(s)
for (int i = argIndex; i < args.length; i++) { for (int i = argIndex; i < args.length; i++) {
final String arg = args[i]; final String arg = args[i];
@ -147,10 +164,7 @@ public class CommandDispatcher {
} }
} }
if (correct) { if (!correct) {
// Argument correct, check the next one
continue;
} else {
// Argument is not correct, add it to the syntax suggestion with the number // Argument is not correct, add it to the syntax suggestion with the number
// of correct argument(s) and do not check the next syntax argument // of correct argument(s) and do not check the next syntax argument
syntaxCorrect = false; syntaxCorrect = false;
@ -173,39 +187,43 @@ public class CommandDispatcher {
} }
} }
// Check if there is at least one correct syntax
if (!validSyntaxes.isEmpty()) { if (!validSyntaxes.isEmpty()) {
// Search the syntax with all perfect args // Search the syntax with all perfect args
final CommandSyntax finalSyntax = findMostCorrectSyntax(validSyntaxes, syntaxesValues, executorArgs, result); final CommandSyntax finalSyntax = findMostCorrectSyntax(validSyntaxes, syntaxesValues, executorArgs);
if (finalSyntax != null) { if (finalSyntax != null) {
// A fully correct syntax has been found, use it
result.executor = finalSyntax.getExecutor(); result.executor = finalSyntax.getExecutor();
result.arguments = executorArgs; result.arguments = executorArgs;
return result; return result;
} }
// Search the first syntax with an incorrect argument // Otherwise, search for the first syntax with an incorrect argument
for (CommandSyntax syntax : validSyntaxes) { for (CommandSyntax syntax : validSyntaxes) {
final Argument[] arguments = syntax.getArguments(); final Argument<Object>[] arguments = syntax.getArguments();
final String[] argsValues = syntaxesValues.get(syntax); final String[] argsValues = syntaxesValues.get(syntax);
for (int i = 0; i < arguments.length; i++) { for (int i = 0; i < arguments.length; i++) {
final Argument argument = arguments[i]; final Argument<Object> argument = arguments[i];
final String argValue = argsValues[i]; final String argValue = argsValues[i];
// Finally parse it // Finally parse it
final Object parsedValue = argument.parse(argValue); final Object parsedValue = argument.parse(argValue);
final int conditionResult = argument.getConditionResult(parsedValue); final int conditionResult = argument.getConditionResult(parsedValue);
if (conditionResult != Argument.SUCCESS) { if (conditionResult != Argument.SUCCESS) {
// Condition of an argument not correct, use the argument callback // Condition of an argument not correct, use the argument callback if any
result.callback = argument.getCallback(); if (argument.hasErrorCallback()) {
result.value = argValue; result.callback = argument.getCallback();
result.error = conditionResult; result.value = argValue;
result.error = conditionResult;
return result; return result;
}
} }
} }
} }
} }
// If command isn't correct, find the closest // No all-correct syntax, find the closest one to use the argument callback
{ {
// Get closest valid syntax // Get closest valid syntax
if (!syntaxesSuggestions.isEmpty()) { if (!syntaxesSuggestions.isEmpty()) {
@ -221,32 +239,38 @@ public class CommandDispatcher {
final int argIndex = suggestionHolder.argIndex; final int argIndex = suggestionHolder.argIndex;
// Found the closest syntax with at least 1 correct argument // Found the closest syntax with at least 1 correct argument
Argument argument = syntax.getArguments()[argIndex]; final Argument<Object> argument = syntax.getArguments()[argIndex];
result.callback = argument.getCallback(); if (argument.hasErrorCallback()) {
result.value = argValue; result.callback = argument.getCallback();
result.error = correctionResult; result.value = argValue;
} else { result.error = correctionResult;
// No argument correct, use the default executor
result.executor = command.getDefaultExecutor();
result.arguments = executorArgs;
}
return result; return result;
}
}
} }
} }
// Use the default executor // Use the default executor at last resort
result.executor = command.getDefaultExecutor(); result.executor = command.getDefaultExecutor();
result.arguments = executorArgs; result.arguments = executorArgs;
return result; return result;
} }
/**
* Retrieves from the valid syntax map the arguments condition result and get the one with the most
* valid arguments.
*
* @param validSyntaxes the list containing all the valid syntaxes
* @param syntaxesValues the map containing the argument raw string values
* @param executorArgs the recipient of the argument parsed values
* @return the command syntax with all of its arguments correct and with the most arguments count, null if not any
*/
@Nullable @Nullable
private CommandSyntax findMostCorrectSyntax(List<CommandSyntax> validSyntaxes, private CommandSyntax findMostCorrectSyntax(@NotNull List<CommandSyntax> validSyntaxes,
Map<CommandSyntax, String[]> syntaxesValues, @NotNull Map<CommandSyntax, String[]> syntaxesValues,
Arguments executorArgs, CommandResult result) { @NotNull Arguments executorArgs) {
Map<CommandSyntax, Arguments> argumentsValueMap = new HashMap<>(); Map<CommandSyntax, Arguments> argumentsValueMap = new HashMap<>();
CommandSyntax finalSyntax = null; CommandSyntax finalSyntax = null;
@ -255,10 +279,10 @@ public class CommandDispatcher {
Arguments syntaxValues = new Arguments(); Arguments syntaxValues = new Arguments();
boolean fullyCorrect = true; boolean fullyCorrect = true;
final Argument[] arguments = syntax.getArguments(); final Argument<Object>[] arguments = syntax.getArguments();
final String[] argsValues = syntaxesValues.get(syntax); final String[] argsValues = syntaxesValues.get(syntax);
for (int i = 0; i < arguments.length; i++) { for (int i = 0; i < arguments.length; i++) {
final Argument argument = arguments[i]; final Argument<Object> argument = arguments[i];
final String argValue = argsValues[i]; final String argValue = argsValues[i];
// Finally parse it // Finally parse it
final Object parsedValue = argument.parse(argValue); final Object parsedValue = argument.parse(argValue);
@ -266,6 +290,7 @@ public class CommandDispatcher {
if (conditionResult == Argument.SUCCESS) { if (conditionResult == Argument.SUCCESS) {
syntaxValues.setArg(argument.getId(), parsedValue); syntaxValues.setArg(argument.getId(), parsedValue);
} else { } else {
// One argument is incorrect, stop the whole syntax check
fullyCorrect = false; fullyCorrect = false;
break; break;
} }
@ -312,7 +337,7 @@ public class CommandDispatcher {
private int error; private int error;
/** /**
* Execute the command for the given source. * Executes the command for the given source.
* <p> * <p>
* The command will not be executed if {@link Command#getCondition()} * The command will not be executed if {@link Command#getCondition()}
* is not validated. * is not validated.
@ -320,7 +345,7 @@ public class CommandDispatcher {
* @param source the command source * @param source the command source
* @param commandString the command string * @param commandString the command string
*/ */
public void execute(CommandSender source, String commandString) { public void execute(@NotNull CommandSender source, @NotNull String commandString) {
// Global listener // Global listener
command.globalListener(source, arguments, commandString); command.globalListener(source, arguments, commandString);
// Condition check // Condition check

View File

@ -22,7 +22,7 @@ public class CommandSyntax {
* @return the required arguments * @return the required arguments
*/ */
@NotNull @NotNull
public Argument[] getArguments() { public Argument<Object>[] getArguments() {
return args; return args;
} }