From 6cbee16c19bd60e3fa11c5bc24c83d5c27d156b0 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 22 Jan 2023 13:45:22 +0100 Subject: [PATCH 1/3] Remove Quotable because it was a dumb idea to add Add AbstractSWCommand.Length and AbstractSWCommand.ArrayLength --- .../steamwar/command/AbstractSWCommand.java | 177 +++++++++++------- src/de/steamwar/command/CommandMetaData.java | 8 +- src/de/steamwar/command/SWCommandUtils.java | 10 +- src/de/steamwar/command/SubCommand.java | 8 - .../de/steamwar/command/ArgumentCommand.java | 2 +- .../steamwar/command/ArgumentCommandTest.java | 23 --- 6 files changed, 117 insertions(+), 111 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 86a00f5..9b0264f 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -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 { protected void sendMessage(T sender, String message, Object[] args) {} - private String[] quote(String[] args) { - List 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 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 { protected final List 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 { 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 { 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>) 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[]> toApplicableTypes, String warning) { + private boolean checkType(Annotation[] annotations, Class clazz, Function>> 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> 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 { 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 { 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 implements AbstractTypeMapper { + + private int min; + private int max; + private AbstractTypeMapper inner; + + public Handler(Length length, AbstractTypeMapper 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 tabCompletes(T sender, PreviousArguments previousArguments, String s) { + List 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> { + @Override + public boolean test(Class clazz) { + return clazz.isArray(); + } + } + + class Handler implements AbstractValidator { + + 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; + } + } + } } diff --git a/src/de/steamwar/command/CommandMetaData.java b/src/de/steamwar/command/CommandMetaData.java index fdb1ea2..33909ac 100644 --- a/src/de/steamwar/command/CommandMetaData.java +++ b/src/de/steamwar/command/CommandMetaData.java @@ -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; } /** diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 32b7fcc..c06d584 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -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 , K, V> void init(SWTypeMapperCreator swTypeMapperCreator) { diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 961cf29..d74423b 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -150,9 +150,6 @@ public class SubCommand { CommandPart 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) STRING_SPACE_FILTER); - } handleImplicitTypeValidator(parameter, commandPart, localValidator); if (parameter.getAnnotation(AbstractSWCommand.AllowNull.class) == null) { commandPart.addValidator((AbstractValidator) NULL_FILTER); @@ -270,9 +267,4 @@ public class SubCommand { } private static final AbstractValidator NULL_FILTER = (sender, value, messageSender) -> value != null; - - private static final AbstractValidator STRING_SPACE_FILTER = (sender, value, messageSender) -> { - if (!(value instanceof String)) return true; - return !((String) value).contains(" "); - }; } diff --git a/testsrc/de/steamwar/command/ArgumentCommand.java b/testsrc/de/steamwar/command/ArgumentCommand.java index ad1c1ec..a107270 100644 --- a/testsrc/de/steamwar/command/ArgumentCommand.java +++ b/testsrc/de/steamwar/command/ArgumentCommand.java @@ -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"); } } diff --git a/testsrc/de/steamwar/command/ArgumentCommandTest.java b/testsrc/de/steamwar/command/ArgumentCommandTest.java index fd4fb5f..0af9327 100644 --- a/testsrc/de/steamwar/command/ArgumentCommandTest.java +++ b/testsrc/de/steamwar/command/ArgumentCommandTest.java @@ -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(); From c9d915b01e7ab4d065cc08c6e5e297d1c6181d0b Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 22 Jan 2023 15:41:05 +0100 Subject: [PATCH 2/3] Fix stuff and update tests --- .../steamwar/command/AbstractSWCommand.java | 53 +++++++++++++------ src/de/steamwar/command/CommandPart.java | 6 ++- .../de/steamwar/command/ArgumentCommand.java | 15 ++++++ .../steamwar/command/ArgumentCommandTest.java | 17 +++++- 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 9b0264f..892d804 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -67,7 +67,8 @@ public abstract class AbstractSWCommand { System.out.println(message.get()); } - protected void sendMessage(T sender, String message, Object[] args) {} + protected void sendMessage(T sender, String message, Object[] args) { + } protected final void execute(T sender, String alias, String[] args) { initialize(); @@ -91,7 +92,7 @@ public abstract class AbstractSWCommand { .filter(Objects::nonNull) .flatMap(Collection::stream) .filter(s -> !s.isEmpty()) - .filter(s -> s.toLowerCase().startsWith(string)) + .filter(s -> s.toLowerCase().startsWith(string) || string.startsWith(s.toLowerCase())) .distinct() .collect(Collectors.toList()); } @@ -150,7 +151,8 @@ public abstract class AbstractSWCommand { if (!checkType(method.getAnnotations(), method.getReturnType(), annotation -> { CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class); if (methodMetaData == null) return aClass -> true; - if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) return aClass -> false; + if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) + return aClass -> false; return aClass -> { Class[] types = methodMetaData.value(); if (types == null) return true; @@ -171,7 +173,8 @@ public abstract class AbstractSWCommand { if (Predicate.class.isAssignableFrom(handler)) { try { return (Predicate>) handler.getConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { } } return aClass -> { @@ -318,7 +321,9 @@ public abstract class AbstractSWCommand { @CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) protected @interface Cached { long cacheDuration() default 5; + TimeUnit timeUnit() default TimeUnit.SECONDS; + boolean global() default false; } @@ -378,7 +383,7 @@ public abstract class AbstractSWCommand { */ boolean allowISE() default false; - int[] falseValues() default { 0 }; + int[] falseValues() default {0}; class Handler implements AbstractTypeMapper { @@ -416,11 +421,6 @@ public abstract class AbstractSWCommand { return inner.map(sender, previousArguments, s); } - @Override - public boolean validate(T sender, Object value, MessageSender messageSender) { - return inner.validate(sender, value, messageSender); - } - @Override public Collection tabCompletes(T sender, PreviousArguments previousArguments, String s) { return inner.tabCompletes(sender, previousArguments, s); @@ -446,7 +446,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = 1) + @CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = 2) protected @interface ErrorMessage { /** * Error message to be displayed when the parameter is invalid. @@ -486,11 +486,14 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) @CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) - @CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 2) + @CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 3) protected @interface Min { int intValue() default Integer.MIN_VALUE; + long longValue() default Long.MIN_VALUE; + float floatValue() default Float.MIN_VALUE; + double doubleValue() default Double.MIN_VALUE; boolean inclusive() default true; @@ -507,6 +510,7 @@ public abstract class AbstractSWCommand { @Override public boolean validate(T sender, Number value, MessageSender messageSender) { + if (value == null) return true; return (comparator.apply(value).intValue()) >= this.value; } } @@ -515,11 +519,14 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) @CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) - @CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 2) + @CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 3) protected @interface Max { int intValue() default Integer.MAX_VALUE; + long longValue() default Long.MAX_VALUE; + float floatValue() default Float.MAX_VALUE; + double doubleValue() default Double.MAX_VALUE; boolean inclusive() default true; @@ -536,6 +543,7 @@ public abstract class AbstractSWCommand { @Override public boolean validate(T sender, Number value, MessageSender messageSender) { + if (value == null) return true; return (comparator.apply(value).intValue()) <= this.value; } } @@ -560,6 +568,7 @@ public abstract class AbstractSWCommand { @CommandMetaData.ImplicitTypeMapper(handler = Length.Handler.class) protected @interface Length { int min() default 0; + int max() default Integer.MAX_VALUE; class Handler implements AbstractTypeMapper { @@ -603,11 +612,14 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) @CommandMetaData.Parameter(handler = ArrayLength.Type.class) - @CommandMetaData.ImplicitValidator(handler = ArrayLength.Handler.class, order = 2) + @CommandMetaData.ImplicitValidator(handler = ArrayLength.Handler.class, order = 1) protected @interface ArrayLength { int min() default 0; + int max() default Integer.MAX_VALUE; + String errorMessage() default ""; + class Type implements Predicate> { @Override public boolean test(Class clazz) { @@ -619,17 +631,26 @@ public abstract class AbstractSWCommand { private int min; private int max; + private String errorMessage; public Handler(ArrayLength arrayLength) { this.min = arrayLength.min(); this.max = arrayLength.max(); + this.errorMessage = arrayLength.errorMessage(); + if (this.errorMessage.isEmpty()) { + this.errorMessage = null; + } } @Override public boolean validate(T sender, Object value, MessageSender messageSender) { - if (value == null) return false; + if (value == null) return true; int length = Array.getLength(value); - return length >= min && length <= max; + boolean valid = length >= min && length <= max; + if (errorMessage != null) { + messageSender.send(!valid, errorMessage); + } + return valid; } } } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index b902892..8a45791 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -181,6 +181,7 @@ class CommandPart { } catch (Exception e) { return new CheckArgumentResult(false, null); } + boolean success = true; for (AbstractValidator validator : validators) { try { if (!validator.validate(sender, value, (s, objects) -> { @@ -188,13 +189,14 @@ class CommandPart { command.sendMessage(sender, s, objects); }); })) { - return new CheckArgumentResult(false, null); + success = false; + value = null; } } catch (Throwable e) { throw CommandFrameworkException.commandPartExceptions("validating", e, args[index], (varArgType != null ? varArgType : parameter.getType()), parameter.getDeclaringExecutable(), parameterIndex); } } - return new CheckArgumentResult(true, value); + return new CheckArgumentResult(success, value); } public Class getType() { diff --git a/testsrc/de/steamwar/command/ArgumentCommand.java b/testsrc/de/steamwar/command/ArgumentCommand.java index a107270..48f9b40 100644 --- a/testsrc/de/steamwar/command/ArgumentCommand.java +++ b/testsrc/de/steamwar/command/ArgumentCommand.java @@ -57,4 +57,19 @@ public class ArgumentCommand extends TestSWCommand { public void argument(String sender, String arg) { throw new ExecutionIdentifier("RunArgument with String"); } + + @Register + public void minLengthArgument(String sender, @Length(min = 3) @StaticValue({"he", "hello"}) String arg) { + throw new ExecutionIdentifier("RunLengthArgument with String"); + } + + @Register + public void minAndMaxLengthArgument(String sender, @Length(min = 3, max = 3) @StaticValue({"wo", "world"}) String arg) { + throw new ExecutionIdentifier("RunLengthArgument with String"); + } + + @Register + public void arrayLengthArgument(String sender, @ArrayLength(min = 2) int... args) { + throw new ExecutionIdentifier("RunArrayLengthArgument with Integer"); + } } diff --git a/testsrc/de/steamwar/command/ArgumentCommandTest.java b/testsrc/de/steamwar/command/ArgumentCommandTest.java index 0af9327..5450c6e 100644 --- a/testsrc/de/steamwar/command/ArgumentCommandTest.java +++ b/testsrc/de/steamwar/command/ArgumentCommandTest.java @@ -98,7 +98,7 @@ public class ArgumentCommandTest { public void testTabComplete() { ArgumentCommand cmd = new ArgumentCommand(); List strings = cmd.tabComplete("test", "", new String[]{""}); - assertTabCompletes(strings, "true", "false"); + assertTabCompletes(strings, "true", "false", "hello", "wor"); } @Test @@ -106,5 +106,20 @@ public class ArgumentCommandTest { ArgumentCommand cmd = new ArgumentCommand(); List strings = cmd.tabComplete("test", "", new String[]{"t"}); assertTabCompletes(strings, "true", "t"); + + strings = cmd.tabComplete("test", "", new String[]{"h"}); + assertTabCompletes(strings, "h", "hello"); + + strings = cmd.tabComplete("test", "", new String[]{"hel"}); + assertTabCompletes(strings, "hel", "hello"); + + strings = cmd.tabComplete("test", "", new String[]{"w"}); + assertTabCompletes(strings, "w", "wor"); + + strings = cmd.tabComplete("test", "", new String[]{"wor"}); + assertTabCompletes(strings, "wor"); + + strings = cmd.tabComplete("test", "", new String[]{"worl"}); + assertTabCompletes(strings, "wor", "worl"); } } From e31534632e1d6b9278cc03de2691d21d9342cccd Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 22 Jan 2023 16:04:35 +0100 Subject: [PATCH 3/3] Fix stuff and update tests --- .../steamwar/command/AbstractSWCommand.java | 55 +++++++------------ .../de/steamwar/command/ArgumentCommand.java | 5 -- 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 892d804..5cdb213 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -26,10 +26,7 @@ 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.function.*; import java.util.stream.Collectors; public abstract class AbstractSWCommand { @@ -148,12 +145,12 @@ public abstract class AbstractSWCommand { } private boolean validateMethod(Method method) { - if (!checkType(method.getAnnotations(), method.getReturnType(), annotation -> { + if (!checkType(method.getAnnotations(), method.getReturnType(), false, annotation -> { CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class); - if (methodMetaData == null) return aClass -> true; + if (methodMetaData == null) return (aClass, varArg) -> true; if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) - return aClass -> false; - return aClass -> { + return (aClass, varArg) -> false; + return (aClass, varArg) -> { Class[] types = methodMetaData.value(); if (types == null) return true; for (Class type : types) { @@ -164,20 +161,19 @@ public abstract class AbstractSWCommand { }, "The method '" + method + "'")) return false; boolean valid = true; for (Parameter parameter : method.getParameters()) { - Class type = parameter.getType(); - if (parameter.isVarArgs()) type = type.getComponentType(); - if (!checkType(parameter.getAnnotations(), type, annotation -> { + if (!checkType(parameter.getAnnotations(), parameter.getType(), parameter.isVarArgs(), annotation -> { CommandMetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Parameter.class); - if (parameterMetaData == null) return aClass -> true; + if (parameterMetaData == null) return (aClass, varArg) -> true; Class handler = parameterMetaData.handler(); - if (Predicate.class.isAssignableFrom(handler)) { + if (BiPredicate.class.isAssignableFrom(handler)) { try { - return (Predicate>) handler.getConstructor().newInstance(); + return (BiPredicate, Boolean>) handler.getConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { } } - return aClass -> { + return (aClass, varArg) -> { + if (varArg) aClass = aClass.getComponentType(); Class[] types = parameterMetaData.value(); if (types == null) return true; for (Class current : types) { @@ -190,11 +186,11 @@ public abstract class AbstractSWCommand { return valid; } - private boolean checkType(Annotation[] annotations, Class clazz, Function>> toApplicableTypes, String warning) { + private boolean checkType(Annotation[] annotations, Class clazz, boolean varArg, Function, Boolean>> toApplicableTypes, String warning) { boolean valid = true; for (Annotation annotation : annotations) { - Predicate> predicate = toApplicableTypes.apply(annotation); - if (!predicate.test(clazz)) { + BiPredicate, Boolean> predicate = toApplicableTypes.apply(annotation); + if (!predicate.test(clazz, varArg)) { commandSystemWarning(() -> warning + " is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'"); valid = false; } @@ -446,7 +442,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = 2) + @CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = Integer.MAX_VALUE) protected @interface ErrorMessage { /** * Error message to be displayed when the parameter is invalid. @@ -486,7 +482,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) @CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) - @CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 3) + @CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 2) protected @interface Min { int intValue() default Integer.MIN_VALUE; @@ -519,7 +515,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) @CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) - @CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 3) + @CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 2) protected @interface Max { int intValue() default Integer.MAX_VALUE; @@ -618,11 +614,9 @@ public abstract class AbstractSWCommand { int max() default Integer.MAX_VALUE; - String errorMessage() default ""; - - class Type implements Predicate> { + class Type implements BiPredicate, Boolean> { @Override - public boolean test(Class clazz) { + public boolean test(Class clazz, Boolean isVarArgs) { return clazz.isArray(); } } @@ -631,26 +625,17 @@ public abstract class AbstractSWCommand { private int min; private int max; - private String errorMessage; public Handler(ArrayLength arrayLength) { this.min = arrayLength.min(); this.max = arrayLength.max(); - this.errorMessage = arrayLength.errorMessage(); - if (this.errorMessage.isEmpty()) { - this.errorMessage = null; - } } @Override public boolean validate(T sender, Object value, MessageSender messageSender) { if (value == null) return true; int length = Array.getLength(value); - boolean valid = length >= min && length <= max; - if (errorMessage != null) { - messageSender.send(!valid, errorMessage); - } - return valid; + return length >= min && length <= max; } } } diff --git a/testsrc/de/steamwar/command/ArgumentCommand.java b/testsrc/de/steamwar/command/ArgumentCommand.java index 48f9b40..451d6e9 100644 --- a/testsrc/de/steamwar/command/ArgumentCommand.java +++ b/testsrc/de/steamwar/command/ArgumentCommand.java @@ -67,9 +67,4 @@ public class ArgumentCommand extends TestSWCommand { public void minAndMaxLengthArgument(String sender, @Length(min = 3, max = 3) @StaticValue({"wo", "world"}) String arg) { throw new ExecutionIdentifier("RunLengthArgument with String"); } - - @Register - public void arrayLengthArgument(String sender, @ArrayLength(min = 2) int... args) { - throw new ExecutionIdentifier("RunArrayLengthArgument with Integer"); - } }