mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-12-22 08:37:49 +01:00
Separate command handler into smaller parts (wip – doesn't compile)
- Move logic from CommandMapper to CommandHandler - Create "ResultStatus" on FoundCommandResult to precisely return the error case Naming and structure not final
This commit is contained in:
parent
550eecebdc
commit
ec9009d776
@ -13,6 +13,8 @@ import java.util.Collections;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.base.Objects.firstNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command description - defines which labels ("names") will lead to a command and points to the
|
* Command description - defines which labels ("names") will lead to a command and points to the
|
||||||
* {@link ExecutableCommand} implementation that executes the logic of the command.
|
* {@link ExecutableCommand} implementation that executes the logic of the command.
|
||||||
@ -99,38 +101,8 @@ public class CommandDescription {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void addChild(CommandDescription command) {
|
||||||
* Get the label most similar to the reference. The first label will be returned if no reference was supplied.
|
children.add(command);
|
||||||
*
|
|
||||||
* @param reference The command reference.
|
|
||||||
*
|
|
||||||
* @return The most similar label, or the first label. An empty label will be returned if no label was set.
|
|
||||||
*/
|
|
||||||
public String getLabel(CommandParts reference) {
|
|
||||||
// Ensure there's any item in the command list
|
|
||||||
if (this.labels.size() == 0)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
// Return the first label if we can't use the reference
|
|
||||||
if (reference == null)
|
|
||||||
return this.labels.get(0);
|
|
||||||
|
|
||||||
// Get the correct label from the reference
|
|
||||||
String preferred = reference.get(getParentCount());
|
|
||||||
|
|
||||||
// Check whether the preferred label is in the label list
|
|
||||||
double currentDifference = -1;
|
|
||||||
String currentLabel = this.labels.get(0);
|
|
||||||
for (String entry : this.labels) {
|
|
||||||
double entryDifference = StringUtils.getDifference(entry, preferred);
|
|
||||||
if (entryDifference < currentDifference || currentDifference < 0) {
|
|
||||||
currentDifference = entryDifference;
|
|
||||||
currentLabel = entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the most similar label
|
|
||||||
return currentLabel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,7 +122,7 @@ public class CommandDescription {
|
|||||||
* @return True if this command label equals to the param command.
|
* @return True if this command label equals to the param command.
|
||||||
*/
|
*/
|
||||||
public boolean hasLabel(String commandLabel) {
|
public boolean hasLabel(String commandLabel) {
|
||||||
for (String label : this.labels) {
|
for (String label : labels) {
|
||||||
if (label.equalsIgnoreCase(commandLabel)) {
|
if (label.equalsIgnoreCase(commandLabel)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -158,83 +130,6 @@ public class CommandDescription {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether this command label is applicable with a command reference. This doesn't check if the parent
|
|
||||||
* are suitable too.
|
|
||||||
*
|
|
||||||
* @param commandReference The command reference.
|
|
||||||
*
|
|
||||||
* @return True if the command reference is suitable to this command label, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean isSuitableLabel(CommandParts commandReference) {
|
|
||||||
// Get the parent count
|
|
||||||
//getParent() = getParent().getParentCount() + 1
|
|
||||||
String element = commandReference.get(getParentCount());
|
|
||||||
|
|
||||||
// Check whether this command description has this command label
|
|
||||||
for (String label : labels) {
|
|
||||||
if (label.equalsIgnoreCase(element)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the command reference.
|
|
||||||
*
|
|
||||||
* @param reference The reference to use as template, which is used to choose the most similar reference.
|
|
||||||
*
|
|
||||||
* @return Command reference.
|
|
||||||
*/
|
|
||||||
public CommandParts getCommandReference(CommandParts reference) {
|
|
||||||
// Build the reference
|
|
||||||
List<String> referenceList = new ArrayList<>();
|
|
||||||
|
|
||||||
// Check whether this command has a parent, if so, add the absolute parent command
|
|
||||||
if (getParent() != null) {
|
|
||||||
referenceList.addAll(getParent().getCommandReference(reference).getList());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current label
|
|
||||||
referenceList.add(getLabel(reference));
|
|
||||||
|
|
||||||
// Return the reference
|
|
||||||
return new CommandParts(referenceList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the difference between this command and another command reference.
|
|
||||||
*
|
|
||||||
* @param other The other command reference.
|
|
||||||
*
|
|
||||||
* @return The command difference. Zero if there's no difference. A negative number on error.
|
|
||||||
*/
|
|
||||||
public double getCommandDifference(CommandParts other) {
|
|
||||||
return getCommandDifference(other, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the difference between this command and another command reference.
|
|
||||||
*
|
|
||||||
* @param other The other command reference.
|
|
||||||
* @param fullCompare True to fully compare both command references.
|
|
||||||
*
|
|
||||||
* @return The command difference. Zero if there's no difference. A negative number on error.
|
|
||||||
*/
|
|
||||||
public double getCommandDifference(CommandParts other, boolean fullCompare) {
|
|
||||||
// Make sure the reference is valid
|
|
||||||
if (other == null)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Get the command reference
|
|
||||||
CommandParts reference = getCommandReference(other);
|
|
||||||
|
|
||||||
// Compare the two references, return the result
|
|
||||||
return CommandUtils.getDifference(reference.getList(),
|
|
||||||
CollectionUtils.getRange(other.getList(), 0, reference.getList().size()), fullCompare);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the executable command.
|
* Get the executable command.
|
||||||
*
|
*
|
||||||
@ -244,29 +139,6 @@ public class CommandDescription {
|
|||||||
return this.executableCommand;
|
return this.executableCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the executable command.
|
|
||||||
*
|
|
||||||
* @param executableCommand The executable command.
|
|
||||||
*/
|
|
||||||
public void setExecutableCommand(ExecutableCommand executableCommand) {
|
|
||||||
this.executableCommand = executableCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the command, if possible.
|
|
||||||
*
|
|
||||||
* @param sender The command sender that triggered the execution of this command.
|
|
||||||
* @param commandReference The command reference.
|
|
||||||
* @param commandArguments The command arguments.
|
|
||||||
*
|
|
||||||
* @return True on success, false on failure.
|
|
||||||
*/
|
|
||||||
public boolean execute(CommandSender sender, CommandParts commandReference, CommandParts commandArguments) {
|
|
||||||
// Execute the command, return the result
|
|
||||||
return getExecutableCommand().executeCommand(sender, commandReference, commandArguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parent command if this command description has a parent.
|
* Get the parent command if this command description has a parent.
|
||||||
*
|
*
|
||||||
@ -276,52 +148,6 @@ public class CommandDescription {
|
|||||||
return this.parent;
|
return this.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of parent this description has.
|
|
||||||
*
|
|
||||||
* @return The number of parents.
|
|
||||||
*/
|
|
||||||
public int getParentCount() {
|
|
||||||
// Check whether the this description has a parent
|
|
||||||
if (!hasParent())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Get the parent count of the parent, return the result
|
|
||||||
return getParent().getParentCount() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the parent command.
|
|
||||||
*
|
|
||||||
* @param parent Parent command.
|
|
||||||
*
|
|
||||||
* @return True on success, false on failure.
|
|
||||||
*/
|
|
||||||
public boolean setParent(CommandDescription parent) {
|
|
||||||
// Make sure the parent is different
|
|
||||||
if (this.parent == parent)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Set the parent
|
|
||||||
this.parent = parent;
|
|
||||||
|
|
||||||
// Make sure the parent isn't null
|
|
||||||
if (parent == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Add this description as a child to the parent
|
|
||||||
return parent.addChild(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the plugin description has a parent command.
|
|
||||||
*
|
|
||||||
* @return True if the description has a parent command, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean hasParent() {
|
|
||||||
return this.parent != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all command children.
|
* Get all command children.
|
||||||
*
|
*
|
||||||
@ -331,70 +157,6 @@ public class CommandDescription {
|
|||||||
return this.children;
|
return this.children;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a child to the command description.
|
|
||||||
*
|
|
||||||
* @param commandDescription The child to add.
|
|
||||||
*
|
|
||||||
* @return True on success, false on failure.
|
|
||||||
*/
|
|
||||||
public boolean addChild(CommandDescription commandDescription) {
|
|
||||||
// Make sure the description is valid
|
|
||||||
if (commandDescription == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Make sure the child doesn't exist already
|
|
||||||
if (isChild(commandDescription))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// The command description to add as a child
|
|
||||||
if (!this.children.add(commandDescription))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Set this description as parent on the child
|
|
||||||
return commandDescription.setParent(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether this command has any child labels.
|
|
||||||
*
|
|
||||||
* @return True if this command has any child labels.
|
|
||||||
*/
|
|
||||||
public boolean hasChildren() {
|
|
||||||
return (this.children.size() != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if this command description has a specific child.
|
|
||||||
*
|
|
||||||
* @param commandDescription The command description to check for.
|
|
||||||
*
|
|
||||||
* @return True if this command description has the specific child, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean isChild(CommandDescription commandDescription) {
|
|
||||||
// Make sure the description is valid
|
|
||||||
if (commandDescription == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check whether this child exists, return the result
|
|
||||||
return this.children.contains(commandDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an argument.
|
|
||||||
*
|
|
||||||
* @param argument The argument to add.
|
|
||||||
*
|
|
||||||
* @return True if succeed, false if failed.
|
|
||||||
*/
|
|
||||||
public boolean addArgument(CommandArgumentDescription argument) {
|
|
||||||
// Make sure the argument is valid
|
|
||||||
if (argument == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Add the argument, return the result
|
|
||||||
return this.arguments.add(argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all command arguments.
|
* Get all command arguments.
|
||||||
@ -432,104 +194,6 @@ public class CommandDescription {
|
|||||||
return detailedDescription;
|
return detailedDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the best suitable command for a query reference.
|
|
||||||
*
|
|
||||||
* @param queryReference The query reference to find a command for.
|
|
||||||
*
|
|
||||||
* @return The command found, or null.
|
|
||||||
*/
|
|
||||||
public FoundCommandResult findCommand(final CommandParts queryReference) {
|
|
||||||
// Make sure the command reference is valid
|
|
||||||
List<String> queryRef = queryReference.getList();
|
|
||||||
if (queryRef.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether this description is for the last element in the command reference, if so return the current command
|
|
||||||
if (queryRef.size() <= getParentCount() + 1) {
|
|
||||||
return new FoundCommandResult(
|
|
||||||
this,
|
|
||||||
getCommandReference(queryReference),
|
|
||||||
new CommandParts(new ArrayList<String>()),
|
|
||||||
queryReference);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the new command reference and arguments
|
|
||||||
CommandParts newReference = new CommandParts(CollectionUtils.getRange(queryReference.getList(), 0, getParentCount() + 1));
|
|
||||||
CommandParts newArguments = new CommandParts(CollectionUtils.getRange(queryReference.getList(), getParentCount() + 1));
|
|
||||||
|
|
||||||
// Handle the child's, if this command has any
|
|
||||||
if (getChildren().size() > 0) {
|
|
||||||
// Get a new instance of the child's list, and sort them by their difference in comparison to the query reference
|
|
||||||
List<CommandDescription> commandChildren = new ArrayList<>(getChildren());
|
|
||||||
Collections.sort(commandChildren, new Comparator<CommandDescription>() {
|
|
||||||
@Override
|
|
||||||
public int compare(CommandDescription o1, CommandDescription o2) {
|
|
||||||
return Double.compare(
|
|
||||||
o1.getCommandDifference(queryReference),
|
|
||||||
o2.getCommandDifference(queryReference));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get the difference of the first child in the list
|
|
||||||
double firstChildDifference = commandChildren.get(0).getCommandDifference(queryReference, true);
|
|
||||||
|
|
||||||
// Check if the reference perfectly suits the arguments of the current command if it doesn't perfectly suits a child command
|
|
||||||
if (firstChildDifference > 0.0)
|
|
||||||
if (getSuitableArgumentsDifference(queryReference) == 0)
|
|
||||||
return new FoundCommandResult(this, newReference, newArguments, queryReference);
|
|
||||||
|
|
||||||
// Loop through each child
|
|
||||||
for (CommandDescription child : commandChildren) {
|
|
||||||
// Get the best suitable command
|
|
||||||
FoundCommandResult result = child.findCommand(queryReference);
|
|
||||||
if (result != null)
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the remaining command reference elements fit the arguments for this command
|
|
||||||
if (getSuitableArgumentsDifference(queryReference) >= 0)
|
|
||||||
return new FoundCommandResult(this, newReference, newArguments, queryReference);
|
|
||||||
|
|
||||||
// No command found, return null
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the remaining command reference elements are suitable with arguments of the current command description,
|
|
||||||
* and get the difference in argument count.
|
|
||||||
*
|
|
||||||
* @param commandReference The command reference.
|
|
||||||
*
|
|
||||||
* @return The difference in argument count between the reference and the actual command.
|
|
||||||
*/
|
|
||||||
public int getSuitableArgumentsDifference(CommandParts commandReference) {
|
|
||||||
// Make sure the command reference is valid
|
|
||||||
List<String> labels = commandReference.getList();
|
|
||||||
if (labels.isEmpty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the remaining command reference element count
|
|
||||||
int remainingElementCount = labels.size() - getParentCount() - 1;
|
|
||||||
|
|
||||||
// Check if there are too few arguments
|
|
||||||
int minArguments = CommandUtils.getMinNumberOfArguments(this);
|
|
||||||
if (minArguments > remainingElementCount) {
|
|
||||||
return Math.abs(minArguments - remainingElementCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there are too many arguments
|
|
||||||
int maxArguments = CommandUtils.getMaxNumberOfArguments(this);
|
|
||||||
if (maxArguments >= 0 && maxArguments < remainingElementCount) {
|
|
||||||
return Math.abs(remainingElementCount - maxArguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The argument count is the same
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the command permissions. Return null if the command doesn't require any permission.
|
* Get the command permissions. Return null if the command doesn't require any permission.
|
||||||
@ -562,6 +226,7 @@ public class CommandDescription {
|
|||||||
*
|
*
|
||||||
* @return The generated CommandDescription object
|
* @return The generated CommandDescription object
|
||||||
*/
|
*/
|
||||||
|
// TODO ljacqu 20151206 Move validation to the create instance method
|
||||||
public CommandDescription build() {
|
public CommandDescription build() {
|
||||||
return createInstance(
|
return createInstance(
|
||||||
getOrThrow(labels, "labels"),
|
getOrThrow(labels, "labels"),
|
||||||
@ -570,7 +235,7 @@ public class CommandDescription {
|
|||||||
getOrThrow(executableCommand, "executableCommand"),
|
getOrThrow(executableCommand, "executableCommand"),
|
||||||
firstNonNull(parent, null),
|
firstNonNull(parent, null),
|
||||||
arguments,
|
arguments,
|
||||||
firstNonNull(permissions, null)
|
permissions
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,10 +294,6 @@ public class CommandDescription {
|
|||||||
return new ArrayList<>(Arrays.asList(items));
|
return new ArrayList<>(Arrays.asList(items));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T firstNonNull(T first, T second) {
|
|
||||||
return first != null ? first : second;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> T getOrThrow(T element, String elementName) {
|
private static <T> T getOrThrow(T element, String elementName) {
|
||||||
if (!isEmpty(element)) {
|
if (!isEmpty(element)) {
|
||||||
return element;
|
return element;
|
||||||
|
@ -2,6 +2,7 @@ package fr.xephi.authme.command;
|
|||||||
|
|
||||||
import fr.xephi.authme.AuthMe;
|
import fr.xephi.authme.AuthMe;
|
||||||
import fr.xephi.authme.command.help.HelpProvider;
|
import fr.xephi.authme.command.help.HelpProvider;
|
||||||
|
import fr.xephi.authme.permission.PermissionsManager;
|
||||||
import fr.xephi.authme.util.CollectionUtils;
|
import fr.xephi.authme.util.CollectionUtils;
|
||||||
import fr.xephi.authme.util.StringUtils;
|
import fr.xephi.authme.util.StringUtils;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
@ -17,27 +18,23 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class CommandHandler {
|
public class CommandHandler {
|
||||||
|
|
||||||
/**
|
|
||||||
* The threshold for assuming an existing command. If the difference is below this value, we assume
|
|
||||||
* that the user meant the similar command and we will run it.
|
|
||||||
*/
|
|
||||||
private static final double ASSUME_COMMAND_THRESHOLD = 0.12;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The threshold for suggesting a similar command. If the difference is below this value, we will
|
* The threshold for suggesting a similar command. If the difference is below this value, we will
|
||||||
* ask the player whether he meant the similar command.
|
* ask the player whether he meant the similar command.
|
||||||
*/
|
*/
|
||||||
private static final double SUGGEST_COMMAND_THRESHOLD = 0.75;
|
private static final double SUGGEST_COMMAND_THRESHOLD = 0.75;
|
||||||
|
|
||||||
private final Set<CommandDescription> commands;
|
private final Set<CommandDescription> baseCommands;
|
||||||
|
private final PermissionsManager permissionsManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a command handler.
|
* Create a command handler.
|
||||||
*
|
*
|
||||||
* @param commands The collection of available AuthMe commands
|
* @param baseCommands The collection of available AuthMe base commands
|
||||||
*/
|
*/
|
||||||
public CommandHandler(Set<CommandDescription> commands) {
|
public CommandHandler(Set<CommandDescription> baseCommands, PermissionsManager permissionsManager) {
|
||||||
this.commands = commands;
|
this.baseCommands = baseCommands;
|
||||||
|
this.permissionsManager = permissionsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,43 +48,29 @@ public class CommandHandler {
|
|||||||
* @return True if the command was executed, false otherwise.
|
* @return True if the command was executed, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean processCommand(CommandSender sender, String bukkitCommandLabel, String[] bukkitArgs) {
|
public boolean processCommand(CommandSender sender, String bukkitCommandLabel, String[] bukkitArgs) {
|
||||||
List<String> commandArgs = skipEmptyArguments(bukkitArgs);
|
// Add the Bukkit command label to the front so we get a list like [authme, register, bobby, mysecret]
|
||||||
// Add the Bukkit command label to the front so we get a list like [authme, register, pass, passConfirm]
|
List<String> parts = skipEmptyArguments(bukkitArgs);
|
||||||
commandArgs.add(0, bukkitCommandLabel);
|
parts.add(0, bukkitCommandLabel);
|
||||||
|
|
||||||
// TODO: remove commandParts
|
// Get the base command of the result, e.g. authme for [authme, register, bobby, mysecret]
|
||||||
CommandParts commandReference = new CommandParts(commandArgs);
|
FoundCommandResult result = mapPartsToCommand(parts);
|
||||||
|
switch (result.getResultStatus()) {
|
||||||
// Get a suitable command for this reference, and make sure it isn't null
|
case SUCCESS:
|
||||||
FoundCommandResult result = findCommand(commandReference);
|
// Check perms + process
|
||||||
if (result == null) {
|
break;
|
||||||
// TODO ljacqu 20151204: Log more information to the console (bukkitCommandLabel)
|
case MISSING_BASE_COMMAND:
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "Failed to parse " + AuthMe.getPluginName() + " command!");
|
sender.sendMessage(ChatColor.DARK_RED + "Failed to parse " + AuthMe.getPluginName() + " command!");
|
||||||
return false;
|
return false;
|
||||||
|
case INCORRECT_ARGUMENTS:
|
||||||
|
// sendImproperArgumentsMessage(sender, result);
|
||||||
|
break;
|
||||||
|
case UNKNOWN_LABEL:
|
||||||
|
// sendUnknownCommandMessage(sender);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unknown result '" + result.getResultStatus() + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String baseCommand = commandArgs.get(0);
|
|
||||||
|
|
||||||
// Make sure the difference between the command reference and the actual command isn't too big
|
|
||||||
final double commandDifference = result.getDifference();
|
|
||||||
if (commandDifference <= ASSUME_COMMAND_THRESHOLD) {
|
|
||||||
|
|
||||||
// Show a message when the command handler is assuming a command
|
|
||||||
if (commandDifference > 0) {
|
|
||||||
sendCommandAssumptionMessage(sender, result, commandReference);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.hasPermission(sender)) {
|
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "You don't have permission to use this command!");
|
|
||||||
} else if (!result.hasProperArguments()) {
|
|
||||||
sendImproperArgumentsMessage(sender, result, commandReference, baseCommand);
|
|
||||||
} else {
|
|
||||||
return result.executeCommand(sender);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sendUnknownCommandMessage(sender, commandDifference, result, baseCommand);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,101 +91,22 @@ public class CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static CommandDescription mapToBase(String commandLabel) {
|
|
||||||
for (CommandDescription command : CommandInitializer.getBaseCommands()) {
|
|
||||||
if (command.getLabels().contains(commandLabel)) {
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the best suitable command for the specified reference.
|
|
||||||
*
|
|
||||||
* @param queryReference The query reference to find a command for.
|
|
||||||
*
|
|
||||||
* @return The command found, or null.
|
|
||||||
*/
|
|
||||||
public FoundCommandResult findCommand(CommandParts queryReference) {
|
|
||||||
// Make sure the command reference is valid
|
|
||||||
List<String> labels = queryReference.getList();
|
|
||||||
if (labels.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (CommandDescription commandDescription : commands) {
|
|
||||||
// Check whether there's a command description available for the
|
|
||||||
// current command
|
|
||||||
if (!commandDescription.isSuitableLabel(queryReference))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Find the command reference, return the result
|
|
||||||
return commandDescription.findCommand(queryReference);
|
|
||||||
}
|
|
||||||
|
|
||||||
// No applicable command description found, return false
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the best suitable command for the specified reference.
|
|
||||||
*
|
|
||||||
* @param commandParts The query reference to find a command for.
|
|
||||||
*
|
|
||||||
* @return The command found, or null.
|
|
||||||
*/
|
|
||||||
private CommandDescription findCommand(List<String> commandParts) {
|
|
||||||
// Make sure the command reference is valid
|
|
||||||
if (commandParts.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO ljacqu 20151129: Since we only use .contains() on the CommandDescription#labels after init, change
|
|
||||||
// the type to set for faster lookup
|
|
||||||
Iterable<CommandDescription> commandsToScan = CommandInitializer.getBaseCommands();
|
|
||||||
CommandDescription result = null;
|
|
||||||
for (String label : commandParts) {
|
|
||||||
result = findLabel(label, commandsToScan);
|
|
||||||
if (result == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
commandsToScan = result.getChildren();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CommandDescription findLabel(String label, Iterable<CommandDescription> commands) {
|
|
||||||
if (commands == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (CommandDescription command : commands) {
|
|
||||||
if (command.getLabels().contains(label)) { // TODO ljacqu should be case-insensitive
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show an "unknown command" to the user and suggests an existing command if its similarity is within
|
* Show an "unknown command" to the user and suggests an existing command if its similarity is within
|
||||||
* the defined threshold.
|
* the defined threshold.
|
||||||
*
|
*
|
||||||
* @param sender The command sender
|
* @param sender The command sender
|
||||||
* @param commandDifference The difference between the invoked command and the existing one
|
|
||||||
* @param result The command that was found during the mapping process
|
* @param result The command that was found during the mapping process
|
||||||
* @param baseCommand The base command (TODO: This is probably already in FoundCommandResult)
|
* @param baseCommand The base command
|
||||||
*/
|
*/
|
||||||
private static void sendUnknownCommandMessage(CommandSender sender, double commandDifference,
|
private static void sendUnknownCommandMessage(CommandSender sender, FoundCommandResult result, String baseCommand) {
|
||||||
FoundCommandResult result, String baseCommand) {
|
|
||||||
CommandParts commandReference = result.getCommandReference();
|
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "Unknown command!");
|
sender.sendMessage(ChatColor.DARK_RED + "Unknown command!");
|
||||||
|
|
||||||
|
|
||||||
// Show a command suggestion if available and the difference isn't too big
|
// Show a command suggestion if available and the difference isn't too big
|
||||||
if (commandDifference < SUGGEST_COMMAND_THRESHOLD && result.getCommandDescription() != null) {
|
if (result.getDifference() < SUGGEST_COMMAND_THRESHOLD && result.getCommandDescription() != null) {
|
||||||
sender.sendMessage(ChatColor.YELLOW + "Did you mean " + ChatColor.GOLD + "/"
|
sender.sendMessage(ChatColor.YELLOW + "Did you mean " + ChatColor.GOLD + "/"
|
||||||
+ result.getCommandDescription().getCommandReference(commandReference) + ChatColor.YELLOW + "?");
|
+ result.getCommandDescription() + ChatColor.YELLOW + "?");
|
||||||
|
// TODO: Define a proper string representation of command description
|
||||||
}
|
}
|
||||||
|
|
||||||
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/" + baseCommand + " help"
|
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/" + baseCommand + " help"
|
||||||
@ -212,28 +116,115 @@ public class CommandHandler {
|
|||||||
private static void sendImproperArgumentsMessage(CommandSender sender, FoundCommandResult result,
|
private static void sendImproperArgumentsMessage(CommandSender sender, FoundCommandResult result,
|
||||||
CommandParts commandReference, String baseCommand) {
|
CommandParts commandReference, String baseCommand) {
|
||||||
// Get the command and the suggested command reference
|
// Get the command and the suggested command reference
|
||||||
List<String> suggestedCommandReference =
|
// FIXME List<String> suggestedCommandReference =
|
||||||
result.getCommandDescription().getCommandReference(commandReference).getList();
|
// result.getCommandDescription().getCommandReference(commandReference).getList();
|
||||||
List<String> helpCommandReference = CollectionUtils.getRange(suggestedCommandReference, 1);
|
// List<String> helpCommandReference = CollectionUtils.getRange(suggestedCommandReference, 1);
|
||||||
|
|
||||||
// Show the invalid arguments warning
|
// Show the invalid arguments warning
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "Incorrect command arguments!");
|
sender.sendMessage(ChatColor.DARK_RED + "Incorrect command arguments!");
|
||||||
|
|
||||||
// Show the command argument help
|
// Show the command argument help
|
||||||
HelpProvider.showHelp(sender, commandReference, new CommandParts(suggestedCommandReference),
|
// HelpProvider.showHelp(sender, commandReference, new CommandParts(suggestedCommandReference),
|
||||||
true, false, true, false, false, false);
|
// true, false, true, false, false, false);
|
||||||
|
|
||||||
// Show the command to use for detailed help
|
// Show the command to use for detailed help
|
||||||
sender.sendMessage(ChatColor.GOLD + "Detailed help: " + ChatColor.WHITE + "/" + baseCommand
|
// sender.sendMessage(ChatColor.GOLD + "Detailed help: " + ChatColor.WHITE + "/" + baseCommand
|
||||||
+ " help " + CommandUtils.labelsToString(helpCommandReference));
|
// + " help " + CommandUtils.labelsToString(helpCommandReference));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendCommandAssumptionMessage(CommandSender sender, FoundCommandResult result,
|
public FoundCommandResult mapPartsToCommand(final List<String> parts) {
|
||||||
CommandParts commandReference) {
|
if (CollectionUtils.isEmpty(parts)) {
|
||||||
List<String> assumedCommandParts =
|
return new FoundCommandResult(null, parts, null, 0.0, FoundCommandResult.ResultStatus.MISSING_BASE_COMMAND);
|
||||||
result.getCommandDescription().getCommandReference(commandReference).getList();
|
|
||||||
|
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "Unknown command, assuming " + ChatColor.GOLD + "/"
|
|
||||||
+ CommandUtils.labelsToString(assumedCommandParts) + ChatColor.DARK_RED + "!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommandDescription base = getBaseCommand(parts.get(0));
|
||||||
|
if (base == null) {
|
||||||
|
return new FoundCommandResult(null, parts, null, 0.0, FoundCommandResult.ResultStatus.MISSING_BASE_COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer labels: /register help goes to "Help command", not "Register command" with argument 'help'
|
||||||
|
List<String> remaining = parts.subList(1, parts.size());
|
||||||
|
CommandDescription childCommand = returnSuitableChild(base, remaining);
|
||||||
|
if (childCommand != null) {
|
||||||
|
return new FoundCommandResult(childCommand, parts.subList(2, parts.size()), parts.subList(0, 2));
|
||||||
|
} else if (isSuitableArgumentCount(base, remaining.size())) {
|
||||||
|
return new FoundCommandResult(base, parts.subList(1, parts.size()), parts.subList(0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: return getCommandWithSmallestDifference()
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Return FoundCommandDescription immediately
|
||||||
|
private CommandDescription getCommandWithSmallestDifference(CommandDescription base, List<String> parts) {
|
||||||
|
final String label = parts.get(0);
|
||||||
|
final int argumentCount = parts.size() - 1;
|
||||||
|
|
||||||
|
double minDifference = Double.POSITIVE_INFINITY;
|
||||||
|
CommandDescription closestCommand = null;
|
||||||
|
for (CommandDescription child : base.getChildren()) {
|
||||||
|
double argumentDifference = getArgumentCountDifference(child, argumentCount);
|
||||||
|
double labelDifference = getLabelDifference(child, label);
|
||||||
|
// Weigh argument difference less
|
||||||
|
double difference = labelDifference + argumentCount / 2;
|
||||||
|
if (difference < minDifference) {
|
||||||
|
minDifference = difference;
|
||||||
|
closestCommand = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSuitableArgumentCount(CommandDescription command, int argumentCount) {
|
||||||
|
int minArgs = CommandUtils.getMinNumberOfArguments(command);
|
||||||
|
int maxArgs = CommandUtils.getMaxNumberOfArguments(command);
|
||||||
|
|
||||||
|
return argumentCount >= minArgs && argumentCount <= maxArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getLabelDifference(CommandDescription command, String givenLabel) {
|
||||||
|
double minDifference = Double.POSITIVE_INFINITY;
|
||||||
|
for (String commandLabel : command.getLabels()) {
|
||||||
|
double difference = StringUtils.getDifference(commandLabel, givenLabel);
|
||||||
|
if (difference < minDifference) {
|
||||||
|
minDifference = difference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minDifference;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getArgumentCountDifference(CommandDescription commandDescription, int givenArgumentsCount) {
|
||||||
|
return Math.min(
|
||||||
|
Math.abs(givenArgumentsCount - CommandUtils.getMinNumberOfArguments(commandDescription)),
|
||||||
|
Math.abs(givenArgumentsCount - CommandUtils.getMaxNumberOfArguments(commandDescription)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the given command a suitable match for the given parts? parts is for example [changepassword, newpw, newpw]
|
||||||
|
public CommandDescription returnSuitableChild(CommandDescription baseCommand, List<String> parts) {
|
||||||
|
if (CollectionUtils.isEmpty(parts)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String label = parts.get(0).toLowerCase();
|
||||||
|
final int argumentCount = parts.size() - 1;
|
||||||
|
|
||||||
|
for (CommandDescription child : baseCommand.getChildren()) {
|
||||||
|
if (child.hasLabel(label) && isSuitableArgumentCount(child, argumentCount)) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandDescription getBaseCommand(String label) {
|
||||||
|
String baseLabel = label.toLowerCase();
|
||||||
|
for (CommandDescription command : baseCommands) {
|
||||||
|
if (command.hasLabel(baseLabel)) {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
package fr.xephi.authme.command;
|
|
||||||
|
|
||||||
import fr.xephi.authme.util.CollectionUtils;
|
|
||||||
import fr.xephi.authme.util.StringUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class responsible for mapping incoming arguments to a {@link CommandDescription}.
|
|
||||||
*/
|
|
||||||
public class CommandMapper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The threshold for assuming an existing command. If the difference is below this value, we assume
|
|
||||||
* that the user meant the similar command and we will run it.
|
|
||||||
*/
|
|
||||||
private static final double ASSUME_COMMAND_THRESHOLD = 0.12;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The threshold for suggesting a similar command. If the difference is below this value, we will
|
|
||||||
* ask the player whether he meant the similar command.
|
|
||||||
*/
|
|
||||||
private static final double SUGGEST_COMMAND_THRESHOLD = 0.75;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map incoming command parts to an actual command.
|
|
||||||
*
|
|
||||||
* @param parts The parts to process
|
|
||||||
* @return The generated result
|
|
||||||
*/
|
|
||||||
public FoundCommandResult mapPartsToCommand(final List<String> parts) {
|
|
||||||
if (CollectionUtils.isEmpty(parts)) {
|
|
||||||
return null; // TODO pass on the information that the base could not be mapped
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandDescription base = getBaseCommand(parts.get(0));
|
|
||||||
if (base == null) {
|
|
||||||
return null; // TODO Pass on the information that base could not be mapped
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> remaining = parts.subList(1, parts.size());
|
|
||||||
|
|
||||||
// Prefer labels: /register help goes to "Help command", not "Register command" with argument 'help'
|
|
||||||
CommandDescription childCommand = returnSuitableChild(base, remaining);
|
|
||||||
if (childCommand != null) {
|
|
||||||
// return childcommand: it's valid...
|
|
||||||
} else if (isSuitableArgumentCount(base, remaining.size())) {
|
|
||||||
// return base... it's valid
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: return getCommandWithSmallestDifference()
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Return FoundCommandDescription immediately
|
|
||||||
private CommandDescription getCommandWithSmallestDifference(CommandDescription base, List<String> parts) {
|
|
||||||
final String label = parts.get(0);
|
|
||||||
final int argumentCount = parts.size() - 1;
|
|
||||||
|
|
||||||
double minDifference = Double.POSITIVE_INFINITY;
|
|
||||||
CommandDescription closestCommand = null;
|
|
||||||
for (CommandDescription child : base.getChildren()) {
|
|
||||||
double argumentDifference = getArgumentCountDifference(child, argumentCount);
|
|
||||||
double labelDifference = getLabelDifference(child, label);
|
|
||||||
// Weigh argument difference less
|
|
||||||
double difference = labelDifference + argumentCount / 2;
|
|
||||||
if (difference < minDifference) {
|
|
||||||
minDifference = difference;
|
|
||||||
closestCommand = child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return closestCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isSuitableArgumentCount(CommandDescription command, int argumentCount) {
|
|
||||||
int minArgs = CommandUtils.getMinNumberOfArguments(command);
|
|
||||||
int maxArgs = CommandUtils.getMaxNumberOfArguments(command);
|
|
||||||
|
|
||||||
return argumentCount >= minArgs && argumentCount <= maxArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double getLabelDifference(CommandDescription command, String givenLabel) {
|
|
||||||
double minDifference = Double.POSITIVE_INFINITY;
|
|
||||||
for (String commandLabel : command.getLabels()) {
|
|
||||||
double difference = StringUtils.getDifference(commandLabel, givenLabel);
|
|
||||||
if (difference < minDifference) {
|
|
||||||
minDifference = difference;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return minDifference;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getArgumentCountDifference(CommandDescription commandDescription, int givenArgumentsCount) {
|
|
||||||
return Math.min(
|
|
||||||
Math.abs(givenArgumentsCount - CommandUtils.getMinNumberOfArguments(commandDescription)),
|
|
||||||
Math.abs(givenArgumentsCount - CommandUtils.getMaxNumberOfArguments(commandDescription)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is the given command a suitable match for the given parts? parts is for example [changepassword, newpw, newpw]
|
|
||||||
public CommandDescription returnSuitableChild(CommandDescription baseCommand, List<String> parts) {
|
|
||||||
if (CollectionUtils.isEmpty(parts)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String label = parts.get(0).toLowerCase();
|
|
||||||
final int argumentCount = parts.size() - 1;
|
|
||||||
|
|
||||||
for (CommandDescription child : baseCommand.getChildren()) {
|
|
||||||
if (child.getLabels().contains(label) && isSuitableArgumentCount(child, argumentCount)) {
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandDescription getBaseCommand(String label) {
|
|
||||||
String baseLabel = label.toLowerCase();
|
|
||||||
for (CommandDescription command : CommandInitializer.getBaseCommands()) {
|
|
||||||
if (command.getLabels().contains(baseLabel)) {
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user