Aliases to the same command should be considered one alias for the puzzler
This commit is contained in:
parent
a3f675db24
commit
b7ddb71f04
@ -6,7 +6,6 @@ import com.massivecraft.massivecore.Lang;
|
|||||||
import com.massivecraft.massivecore.MassiveException;
|
import com.massivecraft.massivecore.MassiveException;
|
||||||
import com.massivecraft.massivecore.MassivePlugin;
|
import com.massivecraft.massivecore.MassivePlugin;
|
||||||
import com.massivecraft.massivecore.collections.MassiveList;
|
import com.massivecraft.massivecore.collections.MassiveList;
|
||||||
import com.massivecraft.massivecore.collections.MassiveMap;
|
|
||||||
import com.massivecraft.massivecore.collections.MassiveSet;
|
import com.massivecraft.massivecore.collections.MassiveSet;
|
||||||
import com.massivecraft.massivecore.command.requirement.Requirement;
|
import com.massivecraft.massivecore.command.requirement.Requirement;
|
||||||
import com.massivecraft.massivecore.command.requirement.RequirementAbstract;
|
import com.massivecraft.massivecore.command.requirement.RequirementAbstract;
|
||||||
@ -15,12 +14,13 @@ import com.massivecraft.massivecore.command.type.Type;
|
|||||||
import com.massivecraft.massivecore.command.type.enumeration.TypeEnum;
|
import com.massivecraft.massivecore.command.type.enumeration.TypeEnum;
|
||||||
import com.massivecraft.massivecore.mixin.MixinMessage;
|
import com.massivecraft.massivecore.mixin.MixinMessage;
|
||||||
import com.massivecraft.massivecore.mson.Mson;
|
import com.massivecraft.massivecore.mson.Mson;
|
||||||
|
import com.massivecraft.massivecore.predicate.Predicate;
|
||||||
|
import com.massivecraft.massivecore.predicate.PredicateLevenshteinClose;
|
||||||
import com.massivecraft.massivecore.predicate.PredicateStartsWithIgnoreCase;
|
import com.massivecraft.massivecore.predicate.PredicateStartsWithIgnoreCase;
|
||||||
import com.massivecraft.massivecore.util.MUtil;
|
import com.massivecraft.massivecore.util.MUtil;
|
||||||
import com.massivecraft.massivecore.util.PermissionUtil;
|
import com.massivecraft.massivecore.util.PermissionUtil;
|
||||||
import com.massivecraft.massivecore.util.ReflectionUtil;
|
import com.massivecraft.massivecore.util.ReflectionUtil;
|
||||||
import com.massivecraft.massivecore.util.Txt;
|
import com.massivecraft.massivecore.util.Txt;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.PluginIdentifiableCommand;
|
import org.bukkit.command.PluginIdentifiableCommand;
|
||||||
@ -35,8 +35,6 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class MassiveCommand implements Active, PluginIdentifiableCommand
|
public class MassiveCommand implements Active, PluginIdentifiableCommand
|
||||||
@ -392,66 +390,58 @@ public class MassiveCommand implements Active, PluginIdentifiableCommand
|
|||||||
// -------------------------------------------- //
|
// -------------------------------------------- //
|
||||||
// CHILDREN > GET
|
// CHILDREN > GET
|
||||||
// -------------------------------------------- //
|
// -------------------------------------------- //
|
||||||
|
|
||||||
// The full version of the child matcher method.
|
// The full version of the child matcher method.
|
||||||
// Returns a map from alias to command.
|
// Returns a set of child commands with similar aliases.
|
||||||
//
|
//
|
||||||
// token - the full alias or an alias prefix.
|
// token - the full alias or an alias prefix.
|
||||||
// levenshtein - should we use levenshtein instead of starts with?
|
// levenshtein - should we use levenshtein instead of starts with?
|
||||||
// prioritizeExact - return single entry map on full match.
|
// prioritizeExact - return single element set on full match.
|
||||||
//
|
//
|
||||||
// An empty map means no child was found.
|
// An empty set means no child was found.
|
||||||
// A single entry map means we found an unambiguous match.
|
// A single element set means we found an unambiguous match.
|
||||||
// A larger map means the token was ambiguous.
|
// A larger set means the token was ambiguous.
|
||||||
public Map<String, MassiveCommand> getChildMatches(String token, boolean levenshtein, CommandSender onlyRelevantToSender)
|
private Set<MassiveCommand> getChildren(String token, boolean levenshtein, CommandSender onlyRelevantToSender)
|
||||||
{
|
{
|
||||||
// Create Ret
|
// Create Ret
|
||||||
Map<String, MassiveCommand> ret = new MassiveMap<>();
|
Set<MassiveCommand> ret = new MassiveSet<>();
|
||||||
|
|
||||||
// Prepare
|
// Prepare
|
||||||
token = token.toLowerCase();
|
token = token.toLowerCase();
|
||||||
PredicateStartsWithIgnoreCase predicate = PredicateStartsWithIgnoreCase.get(token);
|
Predicate<String> predicate = levenshtein ? PredicateLevenshteinClose.get(token) : PredicateStartsWithIgnoreCase.get(token);
|
||||||
|
|
||||||
// Fill Ret
|
// Fill Ret
|
||||||
|
// Go through each child command
|
||||||
for (MassiveCommand child : this.getChildren())
|
for (MassiveCommand child : this.getChildren())
|
||||||
{
|
{
|
||||||
|
// See if any of the aliases has a match or close enough
|
||||||
|
// If there is a direct match, return that
|
||||||
for (String alias : child.getAliases())
|
for (String alias : child.getAliases())
|
||||||
{
|
{
|
||||||
// If this alias has not already been reserved ...
|
|
||||||
if (ret.containsKey(alias)) continue;
|
|
||||||
|
|
||||||
// ... consider exact priority ...
|
// ... consider exact priority ...
|
||||||
if (alias.equalsIgnoreCase(token))
|
if (alias.equalsIgnoreCase(token))
|
||||||
{
|
{
|
||||||
return new MassiveMap<>(alias, child);
|
return Collections.singleton(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret.contains(child)) continue;
|
||||||
|
|
||||||
// ... matches ...
|
// ... matches ...
|
||||||
if (levenshtein)
|
if (!predicate.apply(alias)) continue;
|
||||||
{
|
|
||||||
// ... is levenshteinish ...
|
|
||||||
if ( ! this.isLevenshteinClose(token, alias)) continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// ... the alias startsWithIgnoreCase the token ...
|
|
||||||
if ( ! predicate.apply(alias)) continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... and put in ret.
|
// ... and put in ret.
|
||||||
ret.put(alias, child);
|
ret.add(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only Relevant
|
// Only Relevant
|
||||||
if (onlyRelevantToSender != null)
|
if (onlyRelevantToSender != null)
|
||||||
{
|
{
|
||||||
Iterator<Entry<String, MassiveCommand>> iter = ret.entrySet().iterator();
|
for (Iterator<MassiveCommand> iterator = ret.iterator(); iterator.hasNext(); )
|
||||||
while (iter.hasNext())
|
|
||||||
{
|
{
|
||||||
Entry<String, MassiveCommand> entry = iter.next();
|
MassiveCommand command = iterator.next();
|
||||||
if (entry.getValue().isRelevant(onlyRelevantToSender)) continue;
|
if (command.isRelevant(onlyRelevantToSender)) continue;
|
||||||
iter.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,41 +452,23 @@ public class MassiveCommand implements Active, PluginIdentifiableCommand
|
|||||||
// A simplified version returning null on ambiguity and nothing found.
|
// A simplified version returning null on ambiguity and nothing found.
|
||||||
public MassiveCommand getChild(String token)
|
public MassiveCommand getChild(String token)
|
||||||
{
|
{
|
||||||
Map<String, MassiveCommand> childMatches = this.getChildMatches(token, false, null);
|
Set<MassiveCommand> children = this.getChildren(token, false, null);
|
||||||
|
|
||||||
if (childMatches.isEmpty()) return null;
|
if (children.isEmpty()) return null;
|
||||||
if (childMatches.size() > 1) return null;
|
if (children.size() > 1) return null;
|
||||||
|
return children.iterator().next();
|
||||||
return childMatches.entrySet().iterator().next().getValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isRelevant(CommandSender sender)
|
protected boolean isRelevant(CommandSender sender)
|
||||||
{
|
{
|
||||||
if (sender == null) return true;
|
if (sender == null) return true;
|
||||||
|
|
||||||
if ( ! this.isVisibleTo(sender)) return false;
|
if (!this.isVisibleTo(sender)) return false;
|
||||||
if ( ! this.isRequirementsMet(sender, false)) return false;
|
if (!this.isRequirementsMet(sender, false)) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLevenshteinClose(String argument, String alias)
|
|
||||||
{
|
|
||||||
int levenshteinDistanceMax = this.getLevenshteinMax(argument);
|
|
||||||
int distance = StringUtils.getLevenshteinDistance(argument, alias);
|
|
||||||
return distance <= levenshteinDistanceMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLevenshteinMax(String argument)
|
|
||||||
{
|
|
||||||
if (argument == null) return 0;
|
|
||||||
if (argument.length() <= 1) return 0; // When dealing with 1 character aliases, there is way too many options. So we don't suggest.
|
|
||||||
if (argument.length() <= 4) return 1; // When dealing with low length aliases, there too many options. So we won't suggest much
|
|
||||||
if (argument.length() <= 7) return 2; // 2 is default.
|
|
||||||
|
|
||||||
return 3; // If it were 8 characters or more, we end up here. Because many characters allow for more typos.
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------- //
|
// -------------------------------------------- //
|
||||||
// ALIASES
|
// ALIASES
|
||||||
// -------------------------------------------- //
|
// -------------------------------------------- //
|
||||||
@ -1103,19 +1075,20 @@ public class MassiveCommand implements Active, PluginIdentifiableCommand
|
|||||||
this.setArgs(args);
|
this.setArgs(args);
|
||||||
|
|
||||||
// Requirements
|
// Requirements
|
||||||
if ( ! this.isRequirementsMet(sender, true)) return;
|
if (!this.isRequirementsMet(sender, true)) return;
|
||||||
|
|
||||||
// Child Execution
|
// Child Execution
|
||||||
if (this.isParent() && args.size() > 0)
|
if (this.isParent() && args.size() > 0)
|
||||||
{
|
{
|
||||||
// Get matches
|
// Get matches
|
||||||
String token = args.get(0);
|
String token = args.get(0);
|
||||||
Map<String, MassiveCommand> matches = this.getChildMatches(token, false, null);
|
|
||||||
|
Set<MassiveCommand> matches = this.getChildren(token, false, null);
|
||||||
|
|
||||||
// Score!
|
// Score!
|
||||||
if (matches.size() == 1)
|
if (matches.size() == 1)
|
||||||
{
|
{
|
||||||
MassiveCommand child = matches.entrySet().iterator().next().getValue();
|
MassiveCommand child = matches.iterator().next();
|
||||||
args.remove(0);
|
args.remove(0);
|
||||||
child.execute(sender, args);
|
child.execute(sender, args);
|
||||||
}
|
}
|
||||||
@ -1128,12 +1101,12 @@ public class MassiveCommand implements Active, PluginIdentifiableCommand
|
|||||||
if (matches.isEmpty())
|
if (matches.isEmpty())
|
||||||
{
|
{
|
||||||
base = Lang.COMMAND_CHILD_NONE;
|
base = Lang.COMMAND_CHILD_NONE;
|
||||||
suggestions = this.getChildMatches(token, true, sender).values();
|
suggestions = this.getChildren(token, true, sender);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
base = Lang.COMMAND_CHILD_AMBIGUOUS;
|
base = Lang.COMMAND_CHILD_AMBIGUOUS;
|
||||||
suggestions = this.getChildMatches(token, false, sender).values();
|
suggestions = this.getChildren(token, false, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message: "The sub command X couldn't be found."
|
// Message: "The sub command X couldn't be found."
|
||||||
@ -1158,7 +1131,7 @@ public class MassiveCommand implements Active, PluginIdentifiableCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Self Execution > Arguments Valid
|
// Self Execution > Arguments Valid
|
||||||
if ( ! this.isArgsValid(this.getArgs(), this.sender)) return;
|
if (!this.isArgsValid(this.getArgs(), this.sender)) return;
|
||||||
|
|
||||||
// Self Execution > Perform
|
// Self Execution > Perform
|
||||||
this.perform();
|
this.perform();
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.massivecraft.massivecore.predicate;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
public class PredicateLevenshteinClose implements Predicate<String>
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// FIELDS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
private int levenshteinMax;
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// CONSTRUCT
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public static PredicateLevenshteinClose get(String token) { return new PredicateLevenshteinClose(token); }
|
||||||
|
public static PredicateLevenshteinClose get(String token, int levenshteinMax) { return new PredicateLevenshteinClose(token, levenshteinMax); }
|
||||||
|
|
||||||
|
public PredicateLevenshteinClose(String token)
|
||||||
|
{
|
||||||
|
this(token, getLevenshteinMax(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PredicateLevenshteinClose(String token, int levenshteinMax)
|
||||||
|
{
|
||||||
|
if (token == null) throw new NullPointerException("token");
|
||||||
|
|
||||||
|
this.token = token;
|
||||||
|
this.levenshteinMax = levenshteinMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// OVERRIDE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(String type)
|
||||||
|
{
|
||||||
|
if (type == null) return false;
|
||||||
|
int distance = StringUtils.getLevenshteinDistance(this.token, type);
|
||||||
|
return distance <= this.levenshteinMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// UTIL
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
// This is the standard distance we tolerate for command aliases
|
||||||
|
public static int getLevenshteinMax(String argument)
|
||||||
|
{
|
||||||
|
if (argument == null) return 0;
|
||||||
|
if (argument.length() <= 1) return 0; // When dealing with 1 character aliases, there is way too many options. So we don't suggest.
|
||||||
|
if (argument.length() <= 4) return 1; // When dealing with low length aliases, there too many options. So we won't suggest much
|
||||||
|
if (argument.length() <= 7) return 2; // 2 is default.
|
||||||
|
|
||||||
|
return 3; // If it were 8 characters or more, we end up here. Because many characters allow for more typos.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user