CMDAPIChangesForTeamCommand #40

Zusammengeführt
Lixfel hat 3 Commits von CMDAPIChangesForTeamCommand nach master 2023-01-22 19:10:58 +01:00 zusammengeführt
6 geänderte Dateien mit 117 neuen und 111 gelöschten Zeilen
Nur Änderungen aus Commit 6cbee16c19 werden angezeigt - Alle Commits anzeigen

Datei anzeigen

@ -21,12 +21,14 @@ package de.steamwar.command;
import java.lang.annotation.*;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@ -67,48 +69,11 @@ public abstract class AbstractSWCommand<T> {
protected void sendMessage(T sender, String message, Object[] args) {}
private String[] quote(String[] args) {
List<String> list = new ArrayList<>();
StringBuilder builder = new StringBuilder();
boolean quote = false;
for (String arg : args) {
if (arg.startsWith("\"") && arg.endsWith("\"")) {
list.add(arg);
continue;
}
if (arg.startsWith("\"")) {
quote = true;
builder.append(arg);
continue;
}
if (arg.endsWith("\"")) {
quote = false;
builder.append(" ").append(arg);
list.add(builder.toString());
builder = new StringBuilder();
continue;
}
if (quote) {
builder.append(" ").append(arg);
continue;
}
list.add(arg);
}
if (quote) {
builder.append("\"");
}
if (builder.length() > 0) {
list.add(builder.toString());
}
return list.toArray(new String[0]);
}
protected final void execute(T sender, String alias, String[] args) {
initialize();
String[] finalArgs = quote(args);
List<Runnable> errors = new ArrayList<>();
try {
if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, finalArgs))) {
if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, args))) {
errors.forEach(Runnable::run);
}
} catch (CommandFrameworkException e) {
@ -119,11 +84,10 @@ public abstract class AbstractSWCommand<T> {
protected final List<String> tabComplete(T sender, String alias, String[] args) throws IllegalArgumentException {
initialize();
String[] finalArgs = quote(args);
String string = args[args.length - 1].toLowerCase();
return commandList.stream()
.filter(s -> !s.noTabComplete)
.map(s -> s.tabComplete(sender, finalArgs))
.map(s -> s.tabComplete(sender, args))
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(s -> !s.isEmpty())
@ -185,9 +149,16 @@ public abstract class AbstractSWCommand<T> {
private boolean validateMethod(Method method) {
if (!checkType(method.getAnnotations(), method.getReturnType(), annotation -> {
CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class);
if (methodMetaData == null) return null;
if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) return new Class[0];
return methodMetaData.value();
if (methodMetaData == null) return aClass -> true;
if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) return aClass -> false;
return aClass -> {
Class<?>[] types = methodMetaData.value();
if (types == null) return true;
for (Class<?> type : types) {
if (type.isAssignableFrom(aClass)) return true;
}
return false;
};
}, "The method '" + method + "'")) return false;
boolean valid = true;
for (Parameter parameter : method.getParameters()) {
@ -195,26 +166,32 @@ public abstract class AbstractSWCommand<T> {
if (parameter.isVarArgs()) type = type.getComponentType();
if (!checkType(parameter.getAnnotations(), type, annotation -> {
CommandMetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Parameter.class);
if (parameterMetaData == null) return null;
return parameterMetaData.value();
if (parameterMetaData == null) return aClass -> true;
Class<?> handler = parameterMetaData.handler();
if (Predicate.class.isAssignableFrom(handler)) {
try {
return (Predicate<Class<?>>) handler.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
}
}
return aClass -> {
Class<?>[] types = parameterMetaData.value();
if (types == null) return true;
for (Class<?> current : types) {
if (current.isAssignableFrom(aClass)) return true;
}
return false;
};
}, "The parameter '" + parameter + "'")) valid = false;
}
return valid;
}
private boolean checkType(Annotation[] annotations, Class<?> clazz, Function<Annotation, Class<?>[]> toApplicableTypes, String warning) {
private boolean checkType(Annotation[] annotations, Class<?> clazz, Function<Annotation, Predicate<Class<?>>> toApplicableTypes, String warning) {
boolean valid = true;
for (Annotation annotation : annotations) {
Class<?>[] types = toApplicableTypes.apply(annotation);
if (types == null) continue;
boolean applicable = false;
for (Class<?> type : types) {
if (type.isAssignableFrom(clazz)) {
applicable = true;
break;
}
}
if (!applicable) {
Predicate<Class<?>> predicate = toApplicableTypes.apply(annotation);
if (!predicate.test(clazz)) {
commandSystemWarning(() -> warning + " is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'");
valid = false;
}
@ -506,15 +483,6 @@ public abstract class AbstractSWCommand<T> {
protected @interface AllowNull {
}
/**
* This annotation is used to mark a String to be quotable with multiple words.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter({String.class})
protected @interface Quotable {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class})
@ -586,4 +554,83 @@ public abstract class AbstractSWCommand<T> {
throw new IllegalArgumentException(type + " annotation is not supported for " + clazz);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.ImplicitTypeMapper(handler = Length.Handler.class)
protected @interface Length {
int min() default 0;
int max() default Integer.MAX_VALUE;
class Handler<T> implements AbstractTypeMapper<T, Object> {
private int min;
private int max;
private AbstractTypeMapper<T, Object> inner;
public Handler(Length length, AbstractTypeMapper<T, Object> inner) {
this.min = length.min();
this.max = length.max();
this.inner = inner;
}
@Override
public Object map(T sender, PreviousArguments previousArguments, String s) {
if (s.length() < min || s.length() > max) return null;
return inner.map(sender, previousArguments, s);
}
@Override
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
List<String> tabCompletes = inner.tabCompletes(sender, previousArguments, s)
.stream()
.filter(str -> str.length() >= min)
.map(str -> str.substring(0, Math.min(str.length(), max)))
.collect(Collectors.toList());
if (s.length() < min) {
tabCompletes.add(0, s);
}
return tabCompletes;
}
@Override
public String normalize(T sender, String s) {
return inner.normalize(sender, s);
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter(handler = ArrayLength.Type.class)
@CommandMetaData.ImplicitValidator(handler = ArrayLength.Handler.class, order = 2)
protected @interface ArrayLength {
int min() default 0;
int max() default Integer.MAX_VALUE;
class Type implements Predicate<Class<?>> {
@Override
public boolean test(Class<?> clazz) {
return clazz.isArray();
}
}
class Handler<T> implements AbstractValidator<T, Object> {
private int min;
private int max;
public Handler(ArrayLength arrayLength) {
this.min = arrayLength.min();
this.max = arrayLength.max();
}
@Override
public boolean validate(T sender, Object value, MessageSender messageSender) {
if (value == null) return false;
int length = Array.getLength(value);
return length >= min && length <= max;
}
}
}
}

Datei anzeigen

@ -19,10 +19,7 @@
package de.steamwar.command;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.*;
public @interface CommandMetaData {
@ -43,7 +40,8 @@ public @interface CommandMetaData {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface Parameter {
Class<?>[] value();
Class<?>[] value() default {};
Class<?> handler() default void.class;
}
/**

Datei anzeigen

@ -61,15 +61,7 @@ public class SWCommandUtils {
addMapper(double.class, Double.class, createMapper(numberMapper(Double::parseDouble), numberCompleter(Double::parseDouble, true)));
addMapper(int.class, Integer.class, createMapper(numberMapper(Integer::parseInt), numberCompleter(Integer::parseInt, false)));
addMapper(long.class, Long.class, createMapper(numberMapper(Long::parseLong), numberCompleter(Long::parseLong, false)));
MAPPER_FUNCTIONS.put(String.class.getTypeName(), createMapper(s -> {
if (s.startsWith("\"") && s.endsWith("\"")) {
return s.substring(1, s.length() - 1);
}
if (s.startsWith("'") && s.endsWith("'")) {
return s.substring(1, s.length() - 1);
}
return s;
}, Collections::singletonList));
MAPPER_FUNCTIONS.put(String.class.getTypeName(), createMapper(s -> s, Collections::singletonList));
}
public static <T extends AbstractTypeMapper<K, V>, K, V> void init(SWTypeMapperCreator<T, K, V> swTypeMapperCreator) {

Datei anzeigen

@ -150,9 +150,6 @@ public class SubCommand<T> {
CommandPart<T> commandPart = new CommandPart<>(command, typeMapper, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i);
commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG());
if (parameter.getAnnotation(AbstractSWCommand.Quotable.class) == null) {
commandPart.addValidator((AbstractValidator<T, Object>) STRING_SPACE_FILTER);
}
handleImplicitTypeValidator(parameter, commandPart, localValidator);
if (parameter.getAnnotation(AbstractSWCommand.AllowNull.class) == null) {
commandPart.addValidator((AbstractValidator<T, Object>) NULL_FILTER);
@ -270,9 +267,4 @@ public class SubCommand<T> {
}
private static final AbstractValidator<?, Object> NULL_FILTER = (sender, value, messageSender) -> value != null;
private static final AbstractValidator<?, Object> STRING_SPACE_FILTER = (sender, value, messageSender) -> {
if (!(value instanceof String)) return true;
return !((String) value).contains(" ");
};
}

Datei anzeigen

@ -54,7 +54,7 @@ public class ArgumentCommand extends TestSWCommand {
}
@Register
public void argument(String sender, @Quotable String arg) {
public void argument(String sender, String arg) {
throw new ExecutionIdentifier("RunArgument with String");
}
}

Datei anzeigen

@ -94,29 +94,6 @@ public class ArgumentCommandTest {
}
}
@Test
public void testString() {
ArgumentCommand cmd = new ArgumentCommand();
try {
cmd.execute("test", "", new String[]{"Hello World"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String");
}
try {
cmd.execute("test", "", new String[]{"\"Hello World\""});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String");
}
try {
cmd.execute("test", "", new String[]{"\"Hello", "World\""});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String");
}
}
@Test
public void testTabComplete() {
ArgumentCommand cmd = new ArgumentCommand();