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"); } }