Update SWCommand system to CommandPart System for better usages and extension possibilities
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Dieser Commit ist enthalten in:
Ursprung
cd8d2a8b42
Commit
415a002cad
@ -174,8 +174,7 @@ public abstract class SWCommand {
|
||||
if (compare != 0) {
|
||||
return compare;
|
||||
} else {
|
||||
return Integer.compare(o1.varArgType != null ? Integer.MAX_VALUE : o1.arguments.length,
|
||||
o2.varArgType != null ? Integer.MAX_VALUE : o2.arguments.length);
|
||||
return Integer.compare(o1.comparableValue, o2.comparableValue);
|
||||
}
|
||||
});
|
||||
commandHelpList.sort((o1, o2) -> {
|
||||
|
@ -29,6 +29,7 @@ import org.bukkit.entity.Player;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
@ -43,15 +44,6 @@ public class SWCommandUtils {
|
||||
static final Map<String, TypeMapper<?>> MAPPER_FUNCTIONS = new HashMap<>();
|
||||
static final Map<String, GuardChecker> GUARD_FUNCTIONS = new HashMap<>();
|
||||
|
||||
static final TypeMapper<?> ERROR_FUNCTION = createMapper(s -> {
|
||||
throw new SecurityException();
|
||||
}, s -> Collections.emptyList());
|
||||
|
||||
static final BiFunction<Class<Enum<?>>, String, Enum<?>> ENUM_MAPPER = (enumClass, s) -> {
|
||||
Enum<?>[] enums = enumClass.getEnumConstants();
|
||||
return Arrays.stream(enums).filter(e -> e.name().equalsIgnoreCase(s)).findFirst().orElse(null);
|
||||
};
|
||||
|
||||
static {
|
||||
addMapper(boolean.class, Boolean.class, createMapper(Boolean::parseBoolean, s -> Arrays.asList("true", "false")));
|
||||
addMapper(float.class, Float.class, createMapper(numberMapper(Float::parseFloat), numberCompleter(Float::parseFloat)));
|
||||
@ -93,6 +85,93 @@ public class SWCommandUtils {
|
||||
MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper);
|
||||
}
|
||||
|
||||
static CommandPart generateCommandPart(boolean help, String[] subCommand, Parameter[] parameters, Map<String, TypeMapper<?>> localTypeMapper, Map<String, GuardChecker> localGuardChecker) {
|
||||
CommandPart current = null;
|
||||
for (String s : subCommand) {
|
||||
CommandPart commandPart = new CommandPart(createMapper(s), null, null, null, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND);
|
||||
if (current != null) {
|
||||
current.setNext(commandPart);
|
||||
}
|
||||
current = commandPart;
|
||||
}
|
||||
for (int i = 1; i < parameters.length; i++) {
|
||||
Parameter parameter = parameters[i];
|
||||
TypeMapper<?> typeMapper = getTypeMapper(parameter, localTypeMapper);
|
||||
GuardChecker guardChecker = getGuardChecker(parameter, localGuardChecker);
|
||||
Class<?> varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null;
|
||||
SWCommand.OptionalValue optionalValue = parameter.getAnnotation(SWCommand.OptionalValue.class);
|
||||
|
||||
CommandPart commandPart = new CommandPart(typeMapper, guardChecker, varArgType, optionalValue != null ? optionalValue.value() : null, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND);
|
||||
if (current != null) {
|
||||
current.setNext(commandPart);
|
||||
}
|
||||
current = commandPart;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
static TypeMapper<?> getTypeMapper(Parameter parameter, Map<String, TypeMapper<?>> localTypeMapper) {
|
||||
Class<?> clazz = parameter.getType();
|
||||
if (parameter.isVarArgs()) {
|
||||
clazz = clazz.getComponentType();
|
||||
}
|
||||
|
||||
SWCommand.ClassMapper classMapper = clazz.getAnnotation(SWCommand.ClassMapper.class);
|
||||
SWCommand.Mapper mapper = clazz.getAnnotation(SWCommand.Mapper.class);
|
||||
if (clazz.isEnum() && classMapper == null && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) {
|
||||
return createEnumMapper((Class<Enum<?>>) clazz);
|
||||
}
|
||||
|
||||
String name = clazz.getTypeName();
|
||||
if (classMapper != null) {
|
||||
name = classMapper.value().getTypeName();
|
||||
} else if (mapper != null) {
|
||||
name = mapper.value();
|
||||
} else {
|
||||
SWCommand.StaticValue staticValue = parameter.getAnnotation(SWCommand.StaticValue.class);
|
||||
if (staticValue != null && parameter.getType() == String.class) {
|
||||
return createMapper(staticValue.value());
|
||||
}
|
||||
}
|
||||
TypeMapper<?> typeMapper = localTypeMapper.getOrDefault(name, MAPPER_FUNCTIONS.getOrDefault(name, null));
|
||||
if (typeMapper == null) {
|
||||
throw new IllegalArgumentException("No mapper found for " + name);
|
||||
}
|
||||
return typeMapper;
|
||||
}
|
||||
|
||||
static GuardChecker getGuardChecker(Parameter parameter, Map<String, GuardChecker> localGuardChecker) {
|
||||
Class<?> clazz = parameter.getType();
|
||||
if (parameter.isVarArgs()) {
|
||||
clazz = clazz.getComponentType();
|
||||
}
|
||||
|
||||
SWCommand.ClassGuard classGuard = clazz.getAnnotation(SWCommand.ClassGuard.class);
|
||||
if (classGuard != null) {
|
||||
if (classGuard.value() != null) {
|
||||
return getGuardChecker(classGuard.value().getTypeName(), localGuardChecker);
|
||||
}
|
||||
return getGuardChecker(clazz.getTypeName(), localGuardChecker);
|
||||
}
|
||||
|
||||
SWCommand.Guard guard = clazz.getAnnotation(SWCommand.Guard.class);
|
||||
if (guard != null) {
|
||||
if (guard.value() != null) {
|
||||
return getGuardChecker(guard.value(), localGuardChecker);
|
||||
}
|
||||
return getGuardChecker(clazz.getTypeName(), localGuardChecker);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static GuardChecker getGuardChecker(String s, Map<String, GuardChecker> localGuardChecker) {
|
||||
GuardChecker guardChecker = localGuardChecker.getOrDefault(s, GUARD_FUNCTIONS.getOrDefault(s, null));
|
||||
if (guardChecker == null) {
|
||||
throw new IllegalArgumentException("No guard found for " + s);
|
||||
}
|
||||
return guardChecker;
|
||||
}
|
||||
|
||||
static Object[] generateArgumentArray(CommandSender commandSender, TypeMapper<?>[] parameters, GuardChecker[] guards, String[] args, Class<?> varArgType, String[] subCommand) throws CommandParseException {
|
||||
Object[] arguments = new Object[parameters.length + 1];
|
||||
int index = 0;
|
||||
@ -166,6 +245,25 @@ public class SWCommandUtils {
|
||||
};
|
||||
}
|
||||
|
||||
public static TypeMapper<Enum<?>> createEnumMapper(Class<Enum<?>> enumClass) {
|
||||
Enum<?>[] enums = enumClass.getEnumConstants();
|
||||
List<String> strings = Arrays.stream(enums).map(Enum::name).collect(Collectors.toList());
|
||||
return new TypeMapper<Enum<?>>() {
|
||||
@Override
|
||||
public Enum<?> map(CommandSender commandSender, String[] previousArguments, String s) {
|
||||
for (Enum<?> e : enums) {
|
||||
if (e.name().equalsIgnoreCase(s)) return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabCompletes(CommandSender commandSender, String[] previousArguments, String s) {
|
||||
return strings;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static <T> Function<String, T> numberMapper(Function<String, T> mapper) {
|
||||
return s -> {
|
||||
try {
|
||||
|
@ -19,18 +19,16 @@
|
||||
|
||||
package de.steamwar.command;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static de.steamwar.command.SWCommandUtils.*;
|
||||
|
||||
class SubCommand {
|
||||
|
||||
@ -38,145 +36,62 @@ class SubCommand {
|
||||
Method method;
|
||||
String[] description;
|
||||
String[] subCommand;
|
||||
TypeMapper<?>[] arguments;
|
||||
GuardChecker[] guards;
|
||||
private Predicate<CommandSender> commandSenderPredicate;
|
||||
private Function<CommandSender, ?> commandSenderFunction;
|
||||
GuardChecker guardChecker;
|
||||
Class<?> varArgType = null;
|
||||
private boolean help;
|
||||
boolean noTabComplete;
|
||||
int comparableValue;
|
||||
|
||||
private CommandPart commandPart;
|
||||
|
||||
SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map<String, TypeMapper<?>> localTypeMapper, Map<String, GuardChecker> localGuardChecker, boolean help, String[] description, boolean noTabComplete) {
|
||||
this.swCommand = swCommand;
|
||||
this.method = method;
|
||||
this.help = help;
|
||||
this.description = description;
|
||||
this.noTabComplete = noTabComplete;
|
||||
this.subCommand = subCommand;
|
||||
|
||||
Parameter[] parameters = method.getParameters();
|
||||
comparableValue = parameters[parameters.length - 1].isVarArgs() ? Integer.MAX_VALUE : subCommand.length;
|
||||
|
||||
guardChecker = SWCommandUtils.getGuardChecker(parameters[0], localGuardChecker);
|
||||
|
||||
commandPart = SWCommandUtils.generateCommandPart(help, subCommand, parameters, localTypeMapper, localGuardChecker);
|
||||
commandSenderPredicate = sender -> parameters[0].getType().isAssignableFrom(sender.getClass());
|
||||
commandSenderFunction = sender -> parameters[0].getType().cast(sender);
|
||||
this.subCommand = subCommand;
|
||||
guardChecker = getGuardChecker(parameters[0], localGuardChecker);
|
||||
|
||||
arguments = new TypeMapper[parameters.length - 1];
|
||||
guards = new GuardChecker[parameters.length - 1];
|
||||
for (int i = 1; i < parameters.length; i++) {
|
||||
Parameter parameter = parameters[i];
|
||||
Class<?> clazz = parameter.getType();
|
||||
if (parameter.isVarArgs()) {
|
||||
clazz = clazz.getComponentType();
|
||||
varArgType = clazz;
|
||||
}
|
||||
|
||||
SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class);
|
||||
SWCommand.ClassMapper classMapper = parameter.getAnnotation(SWCommand.ClassMapper.class);
|
||||
if (clazz.isEnum() && mapper == null && classMapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) {
|
||||
Class<Enum<?>> enumClass = (Class<Enum<?>>) clazz;
|
||||
List<String> tabCompletes = new ArrayList<>();
|
||||
for (Enum<?> enumConstant : enumClass.getEnumConstants()) {
|
||||
tabCompletes.add(enumConstant.name().toLowerCase());
|
||||
}
|
||||
arguments[i - 1] = SWCommandUtils.createMapper(s -> ENUM_MAPPER.apply(enumClass, s), s -> tabCompletes);
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = clazz.getTypeName();
|
||||
if (classMapper != null) {
|
||||
name = classMapper.value().getTypeName();
|
||||
} else if (mapper != null) {
|
||||
name = mapper.value();
|
||||
} else {
|
||||
SWCommand.StaticValue staticValue = parameter.getAnnotation(SWCommand.StaticValue.class);
|
||||
if (staticValue != null && parameter.getType() == String.class) {
|
||||
arguments[i - 1] = SWCommandUtils.createMapper(staticValue.value());
|
||||
guards[i - 1] = getGuardChecker(parameter, localGuardChecker);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
arguments[i - 1] = localTypeMapper.containsKey(name)
|
||||
? localTypeMapper.get(name)
|
||||
: MAPPER_FUNCTIONS.getOrDefault(name, ERROR_FUNCTION);
|
||||
guards[i - 1] = getGuardChecker(parameter, localGuardChecker);
|
||||
}
|
||||
}
|
||||
|
||||
private GuardChecker getGuardChecker(Parameter parameter, Map<String, GuardChecker> localGuardChecker) {
|
||||
SWCommand.ClassGuard classGuard = parameter.getAnnotation(SWCommand.ClassGuard.class);
|
||||
if (classGuard != null) {
|
||||
if (classGuard.value() == null) {
|
||||
String s = parameter.getType().getTypeName();
|
||||
if (parameter.isVarArgs()) {
|
||||
s = parameter.getType().getComponentType().getTypeName();
|
||||
}
|
||||
return localGuardChecker.getOrDefault(s, GUARD_FUNCTIONS.getOrDefault(s, null));
|
||||
}
|
||||
GuardChecker current = localGuardChecker.getOrDefault(classGuard.value().getTypeName(), GUARD_FUNCTIONS.getOrDefault(classGuard.value().getTypeName(), null));
|
||||
if (current == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, () -> "The guard checker with name '" + classGuard.value().getTypeName() + "' is neither a local guard checker nor a global one");
|
||||
}
|
||||
return current;
|
||||
}
|
||||
SWCommand.Guard guard = parameter.getAnnotation(SWCommand.Guard.class);
|
||||
if (guard != null) {
|
||||
if (guard.value() == null || guard.value().isEmpty()) {
|
||||
String s = parameter.getType().getTypeName();
|
||||
if (parameter.isVarArgs()) {
|
||||
s = parameter.getType().getComponentType().getTypeName();
|
||||
}
|
||||
return localGuardChecker.getOrDefault(s, GUARD_FUNCTIONS.getOrDefault(s, null));
|
||||
}
|
||||
GuardChecker current = localGuardChecker.getOrDefault(guard.value(), GUARD_FUNCTIONS.getOrDefault(guard.value(), null));
|
||||
if (current == null) {
|
||||
Bukkit.getLogger().log(Level.WARNING, () -> "The guard checker with name '" + guard.value() + "' is neither a local guard checker nor a global one");
|
||||
}
|
||||
return current;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean invoke(CommandSender commandSender, String[] args) {
|
||||
if (args.length < arguments.length + subCommand.length - (varArgType != null ? 1 : 0)) {
|
||||
return false;
|
||||
}
|
||||
if (varArgType == null && args.length > arguments.length + subCommand.length) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (!commandSenderPredicate.test(commandSender)) {
|
||||
return false;
|
||||
}
|
||||
Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, arguments, guards, args, varArgType, subCommand);
|
||||
objects[0] = commandSenderFunction.apply(commandSender);
|
||||
for (int i = subCommand.length; i < args.length; i++) {
|
||||
GuardChecker current;
|
||||
if (i == subCommand.length) {
|
||||
current = guardChecker;
|
||||
} else {
|
||||
if (i >= objects.length + subCommand.length) {
|
||||
current = guards[guards.length - 1];
|
||||
} else {
|
||||
current = guards[i - 1 - subCommand.length];
|
||||
}
|
||||
}
|
||||
if (current != null) {
|
||||
GuardResult guardResult;
|
||||
if (i == 0) {
|
||||
guardResult = current.guard(commandSender, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND, new String[0], null);
|
||||
} else {
|
||||
guardResult = current.guard(commandSender, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND, Arrays.copyOf(args, i - 1), args[i - 1]);
|
||||
}
|
||||
if (guardResult != GuardResult.ALLOWED) {
|
||||
if (guardResult == GuardResult.DENIED) {
|
||||
throw new CommandNoHelpException();
|
||||
}
|
||||
|
||||
if (commandPart == null) {
|
||||
if (args.length != 0) {
|
||||
return false;
|
||||
}
|
||||
method.setAccessible(true);
|
||||
method.invoke(swCommand, commandSenderFunction.apply(commandSender));
|
||||
} else {
|
||||
List<Object> objects = new ArrayList<>();
|
||||
commandPart.generateArgumentArray(objects, commandSender, args, 0);
|
||||
if (guardChecker != null) {
|
||||
GuardResult guardResult = guardChecker.guard(commandSender, GuardCheckType.COMMAND, new String[0], null);
|
||||
switch (guardResult) {
|
||||
case ALLOWED:
|
||||
break;
|
||||
case DENIED:
|
||||
throw new CommandNoHelpException();
|
||||
case DENIED_WITH_HELP:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
commandPart.guardCheck(commandSender, args, 0);
|
||||
method.setAccessible(true);
|
||||
method.invoke(swCommand, objects);
|
||||
}
|
||||
} catch (CommandNoHelpException e) {
|
||||
throw e;
|
||||
} catch (CommandParseException e) {
|
||||
@ -188,64 +103,14 @@ class SubCommand {
|
||||
}
|
||||
|
||||
List<String> tabComplete(CommandSender commandSender, String[] args) {
|
||||
if (varArgType == null && args.length > arguments.length + subCommand.length) {
|
||||
return null;
|
||||
}
|
||||
if (guardChecker != null && guardChecker.guard(commandSender, GuardCheckType.TAB_COMPLETE, new String[0], null) != GuardResult.ALLOWED) {
|
||||
return null;
|
||||
}
|
||||
int index = 0;
|
||||
List<String> argsList = new LinkedList<>(Arrays.asList(args));
|
||||
for (String value : subCommand) {
|
||||
String s = argsList.remove(0);
|
||||
if (argsList.isEmpty()) return Collections.singletonList(value);
|
||||
if (!value.equalsIgnoreCase(s)) return null;
|
||||
index++;
|
||||
}
|
||||
int guardIndex = 0;
|
||||
for (TypeMapper<?> argument : arguments) {
|
||||
String s = argsList.remove(0);
|
||||
if (argsList.isEmpty()) {
|
||||
if (guards[guardIndex] != null && guards[guardIndex].guard(commandSender, GuardCheckType.TAB_COMPLETE, Arrays.copyOf(args, args.length - 1), s) != GuardResult.ALLOWED) {
|
||||
if (commandPart == null) {
|
||||
return null;
|
||||
}
|
||||
return argument.tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s);
|
||||
}
|
||||
try {
|
||||
if (guards[guardIndex] != null && guards[guardIndex].guard(commandSender, GuardCheckType.TAB_COMPLETE, Arrays.copyOf(args, index), s) != GuardResult.ALLOWED) {
|
||||
return null;
|
||||
}
|
||||
if (argument.map(commandSender, Arrays.copyOf(args, index), s) == null) {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
index++;
|
||||
guardIndex++;
|
||||
}
|
||||
if (varArgType != null && !argsList.isEmpty()) {
|
||||
while (!argsList.isEmpty()) {
|
||||
String s = argsList.remove(0);
|
||||
if (argsList.isEmpty()) {
|
||||
if (guards[guards.length - 1] != null && guards[guards.length - 1].guard(commandSender, GuardCheckType.TAB_COMPLETE, Arrays.copyOf(args, args.length - 1), s) != GuardResult.ALLOWED) {
|
||||
return null;
|
||||
}
|
||||
return arguments[arguments.length - 1].tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s);
|
||||
}
|
||||
try {
|
||||
if (guards[guards.length - 1] != null && guards[guards.length - 1].guard(commandSender, GuardCheckType.TAB_COMPLETE, Arrays.copyOf(args, index), s) != GuardResult.ALLOWED) {
|
||||
return null;
|
||||
}
|
||||
if (arguments[arguments.length - 1].map(commandSender, Arrays.copyOf(args, index), s) == null) {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
List<String> list = new ArrayList<>();
|
||||
commandPart.generateTabComplete(list, commandSender, args, 0);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ public class SimpleCommandTest {
|
||||
try {
|
||||
simpleCommand.execute(new TestCommandSender(), "", new String[]{"unknown"});
|
||||
} catch (SecurityException securityException) {
|
||||
securityException.printStackTrace();
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren