Add command similarity check to mapper (work in progress)

This commit is contained in:
ljacqu 2015-12-11 22:08:36 +01:00
parent 56c005587c
commit 550eecebdc

View File

@ -1,6 +1,7 @@
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;
@ -10,6 +11,18 @@ import java.util.List;
*/
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.
*
@ -26,25 +39,41 @@ public class CommandMapper {
return null; // TODO Pass on the information that base could not be mapped
}
//List<String> labels = CollectionUtils.getRange(parts, 0, 1);
List<String> remaining = parts.subList(1, parts.size());
// Prefer labels: /register help goes to "Help command", not "Register command" with 'help'
// 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...
}
// No child command found, check if parent is suitable
if (isSuitableArgumentCount(base, remaining.size())) {
} else if (isSuitableArgumentCount(base, remaining.size())) {
// return base... it's valid
}
// TODO: We don't have a suitable command for the given parts, so find the most similar one
// 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);
@ -52,13 +81,32 @@ public class CommandMapper {
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) {
// TODO: Validate list + make case-insensitive
if (CollectionUtils.isEmpty(parts)) {
return null;
}
final String label = parts.get(0).toLowerCase();
final int argumentCount = parts.size() - 1;
List<String> args = parts.subList(1, parts.size());
for (CommandDescription child : baseCommand.getChildren()) {
if (child.getLabels().contains(label) && isSuitableArgumentCount(child, argumentCount)) {
return child;