From fd6612ac1b5bad4ddb365711b34dd1577ae8dad9 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 15:59:28 +0200 Subject: [PATCH] Update validators --- .../steamwar/command/AbstractSWCommand.java | 6 ++- .../steamwar/command/AbstractValidator.java | 13 ++++- src/de/steamwar/command/CommandPart.java | 39 +++++++++++++- src/de/steamwar/command/SWCommandUtils.java | 53 +++++++++++++++++-- src/de/steamwar/command/SubCommand.java | 51 ++++++++++++++---- .../de/steamwar/command/GuardCommandTest.java | 2 +- 6 files changed, 146 insertions(+), 18 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 2500bd1..9387a02 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -65,6 +65,8 @@ public abstract class AbstractSWCommand { System.out.println(message.get()); } + protected void sendMessage(T sender, String message, Object[] args) {} + protected final void execute(T sender, String alias, String[] args) { initialize(); try { @@ -156,7 +158,7 @@ public abstract class AbstractSWCommand { commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the varArgs parameters of type '" + String.class.getTypeName() + "' as last Argument"); return; } - commandHelpList.add(new SubCommand<>(this, method, anno.value(), new HashMap<>(), localGuardChecker, true, null, anno.noTabComplete())); + commandHelpList.add(new SubCommand<>(this, method, anno.value(), new HashMap<>(), new HashMap<>(), localGuardChecker, true, null, anno.noTabComplete())); }); add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> { @@ -177,7 +179,7 @@ public abstract class AbstractSWCommand { return; } } - commandList.add(new SubCommand<>(this, method, anno.value(), localTypeMapper, localGuardChecker, false, anno.description(), anno.noTabComplete())); + commandList.add(new SubCommand<>(this, method, anno.value(), localTypeMapper, localValidators, localGuardChecker, false, anno.description(), anno.noTabComplete())); }); } diff --git a/src/de/steamwar/command/AbstractValidator.java b/src/de/steamwar/command/AbstractValidator.java index c4df8ed..0f74382 100644 --- a/src/de/steamwar/command/AbstractValidator.java +++ b/src/de/steamwar/command/AbstractValidator.java @@ -19,8 +19,19 @@ package de.steamwar.command; +import java.util.function.BiConsumer; + public interface AbstractValidator { - default boolean validate(K sender, T value) { + + /** + * Validates the given value. + * + * @param sender The sender of the command. + * @param value The value to validate or null if mapping returned null. + * @param messageSender The message sender to send messages to the player. Never send messages directly to the player. + * @return The result of the validation. + */ + default boolean validate(K sender, T value, BiConsumer messageSender) { return true; } } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 0688caf..503a7af 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -37,7 +37,9 @@ class CommandPart { private final Object value; } + private AbstractSWCommand command; private AbstractTypeMapper typeMapper; + private AbstractValidator validator; private AbstractGuardChecker guardChecker; private Class varArgType; private String optional; @@ -48,8 +50,10 @@ class CommandPart { @Setter private boolean ignoreAsArgument = false; - public CommandPart(AbstractTypeMapper typeMapper, AbstractGuardChecker guardChecker, Class varArgType, String optional, GuardCheckType guardCheckType) { + public CommandPart(AbstractSWCommand command, AbstractTypeMapper typeMapper, AbstractValidator validator, AbstractGuardChecker guardChecker, Class varArgType, String optional, GuardCheckType guardCheckType) { + this.command = command; this.typeMapper = typeMapper; + this.validator = validator; this.guardChecker = guardChecker; this.varArgType = varArgType; this.optional = optional; @@ -117,6 +121,30 @@ class CommandPart { } } + public boolean validate(T sender, List values, int startIndex) { + if (varArgType != null) { + for (int i = startIndex; i < values.size(); i++) { + Object value = values.get(i); + if (validator != null && !validator.validate(sender, value, (s, objects) -> command.sendMessage(sender, s, objects))) { + return false; + } + } + return true; + } + + Object value = values.get(startIndex); + if (validator != null && !validator.validate(sender, value, (s, objects) -> command.sendMessage(sender, s, objects))) { + if (optional != null && next != null) { + return next.validate(sender, values, startIndex); + } + return false; + } + if (next != null) { + return next.validate(sender, values, startIndex + 1); + } + return true; + } + public boolean guardCheck(T sender, String[] args, int startIndex) { if (varArgType != null) { for (int i = startIndex; i < args.length; i++) { @@ -192,6 +220,15 @@ class CommandPart { if (value == null) { return new CheckArgumentResult(false, null); } + if (validator != null) { + if (!validator.validate(sender, value, (s, objects) -> { + // ignore + })) { + return new CheckArgumentResult(false, null); + } + return new CheckArgumentResult(true, value); + } + GuardResult guardResult = checkGuard(guardCheckType, sender, args, index); switch (guardResult) { case ALLOWED: diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 4e57876..81d759a 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -26,6 +26,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.*; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; @@ -77,11 +78,11 @@ public class SWCommandUtils { MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper); } - static CommandPart generateCommandPart(boolean help, String[] subCommand, Parameter[] parameters, Map> localTypeMapper, Map> localGuardChecker) { + static CommandPart generateCommandPart(AbstractSWCommand command, boolean help, String[] subCommand, Parameter[] parameters, Map> localTypeMapper, Map> localValidator, Map> localGuardChecker) { CommandPart first = null; CommandPart current = null; for (String s : subCommand) { - CommandPart commandPart = new CommandPart(createMapper(s), null, null, null, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND); + CommandPart commandPart = new CommandPart(command, createMapper(s), null, null, null, null, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND); commandPart.setIgnoreAsArgument(true); if (current != null) { current.setNext(commandPart); @@ -94,11 +95,12 @@ public class SWCommandUtils { for (int i = 1; i < parameters.length; i++) { Parameter parameter = parameters[i]; AbstractTypeMapper typeMapper = getTypeMapper(parameter, localTypeMapper); + AbstractValidator validator = (AbstractValidator) getValidator(parameter, localValidator); AbstractGuardChecker guardChecker = getGuardChecker(parameter, localGuardChecker); Class varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null; AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class); - CommandPart commandPart = new CommandPart<>(typeMapper, guardChecker, varArgType, optionalValue != null ? optionalValue.value() : null, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND); + CommandPart commandPart = new CommandPart<>(command, typeMapper, validator, guardChecker, varArgType, optionalValue != null ? optionalValue.value() : null, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND); if (current != null) { current.setNext(commandPart); } @@ -119,7 +121,7 @@ public class SWCommandUtils { AbstractSWCommand.ClassMapper classMapper = parameter.getAnnotation(AbstractSWCommand.ClassMapper.class); AbstractSWCommand.Mapper mapper = parameter.getAnnotation(AbstractSWCommand.Mapper.class); if (clazz.isEnum() && classMapper == null && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) { - return (AbstractTypeMapper) createEnumMapper((Class>) clazz); + return createEnumMapper((Class>) clazz); } String name = clazz.getTypeName(); @@ -165,6 +167,49 @@ public class SWCommandUtils { return typeMapper; } + public static AbstractValidator getValidator(Parameter parameter, Map> localValidator) { + Class clazz = parameter.getType(); + if (parameter.isVarArgs()) { + clazz = clazz.getComponentType(); + } + + AbstractSWCommand.ClassValidator classValidator = parameter.getAnnotation(AbstractSWCommand.ClassValidator.class); + if (classValidator != null) { + if (classValidator.value() != null) { + return getValidator(classValidator.value().getTypeName(), localValidator); + } + return getValidator(clazz.getTypeName(), localValidator); + } + + AbstractSWCommand.Validator validator = parameter.getAnnotation(AbstractSWCommand.Validator.class); + if (validator != null) { + if (validator.value() != null) { + return getValidator(validator.value(), localValidator); + } + return getValidator(clazz.getTypeName(), localValidator); + } + + AbstractSWCommand.ErrorMessage errorMessage = parameter.getAnnotation(AbstractSWCommand.ErrorMessage.class); + if (errorMessage != null) { + return new AbstractValidator() { + @Override + public boolean validate(T sender, String value, BiConsumer messageSender) { + if (value == null) messageSender.accept(errorMessage.value(), new Object[0]); + return value != null; + } + }; + } + return null; + } + + private static AbstractValidator getValidator(String s, Map> localGuardChecker) { + AbstractValidator validator = localGuardChecker.getOrDefault(s, (AbstractValidator) VALIDATOR_FUNCTIONS.getOrDefault(s, null)); + if (validator == null) { + throw new IllegalArgumentException("No validator found for " + s); + } + return validator; + } + public static AbstractGuardChecker getGuardChecker(Parameter parameter, Map> localGuardChecker) { Class clazz = parameter.getType(); if (parameter.isVarArgs()) { diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 603e6d3..6456bfd 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -36,13 +36,14 @@ public class SubCommand { String[] subCommand; private Predicate senderPredicate; private Function senderFunction; + AbstractValidator validator; AbstractGuardChecker guardChecker; boolean noTabComplete; int comparableValue; private CommandPart commandPart; - SubCommand(AbstractSWCommand abstractSWCommand, Method method, String[] subCommand, Map> localTypeMapper, Map> localGuardChecker, boolean help, String[] description, boolean noTabComplete) { + SubCommand(AbstractSWCommand abstractSWCommand, Method method, String[] subCommand, Map> localTypeMapper, Map> localValidator, Map> localGuardChecker, boolean help, String[] description, boolean noTabComplete) { this.abstractSWCommand = abstractSWCommand; this.method = method; this.subCommand = subCommand; @@ -52,9 +53,10 @@ public class SubCommand { Parameter[] parameters = method.getParameters(); comparableValue = parameters[parameters.length - 1].isVarArgs() ? Integer.MAX_VALUE : -parameters.length; + validator = (AbstractValidator) SWCommandUtils.getValidator(parameters[0], localValidator); guardChecker = SWCommandUtils.getGuardChecker(parameters[0], localGuardChecker); - commandPart = SWCommandUtils.generateCommandPart(help, subCommand, parameters, localTypeMapper, localGuardChecker); + commandPart = SWCommandUtils.generateCommandPart(abstractSWCommand, help, subCommand, parameters, localTypeMapper, localValidator, localGuardChecker); senderPredicate = t -> parameters[0].getType().isAssignableFrom(t.getClass()); senderFunction = t -> parameters[0].getType().cast(t); @@ -70,12 +72,13 @@ public class SubCommand { if (args.length != 0) { return false; } - method.setAccessible(true); - method.invoke(abstractSWCommand, senderFunction.apply(sender)); - } else { - List objects = new ArrayList<>(); - commandPart.generateArgumentArray(objects, sender, args, 0); - if (guardChecker != null) { + if (validator != null) { + if (!validator.validate(sender, sender, (s, objectArgs) -> { + // TODO: implement + })) { + throw new CommandNoHelpException(); + } + } else if (guardChecker != null) { GuardResult guardResult = guardChecker.guard(sender, GuardCheckType.COMMAND, new String[0], null); switch (guardResult) { case ALLOWED: @@ -87,6 +90,30 @@ public class SubCommand { return true; } } + method.setAccessible(true); + method.invoke(abstractSWCommand, senderFunction.apply(sender)); + } else { + List objects = new ArrayList<>(); + commandPart.generateArgumentArray(objects, sender, args, 0); + if (validator != null) { + if (!validator.validate(sender, sender, (s, objectArgs) -> { + // TODO: implement + })) { + throw new CommandNoHelpException(); + } + } else if (guardChecker != null) { + GuardResult guardResult = guardChecker.guard(sender, GuardCheckType.COMMAND, new String[0], null); + switch (guardResult) { + case ALLOWED: + break; + case DENIED: + throw new CommandNoHelpException(); + case DENIED_WITH_HELP: + default: + return true; + } + } + commandPart.validate(sender, objects, 0); commandPart.guardCheck(sender, args, 0); objects.add(0, senderFunction.apply(sender)); method.setAccessible(true); @@ -105,7 +132,13 @@ public class SubCommand { } List tabComplete(T sender, String[] args) { - if (guardChecker != null && guardChecker.guard(sender, GuardCheckType.TAB_COMPLETE, new String[0], null) != GuardResult.ALLOWED) { + if (validator != null) { + if (!validator.validate(sender, sender, (s, objects) -> { + // TODO: implement + })) { + return null; + } + } else if (guardChecker != null && guardChecker.guard(sender, GuardCheckType.TAB_COMPLETE, new String[0], null) != GuardResult.ALLOWED) { return null; } if (commandPart == null) { diff --git a/testsrc/de/steamwar/command/GuardCommandTest.java b/testsrc/de/steamwar/command/GuardCommandTest.java index 233333c..51eae0e 100644 --- a/testsrc/de/steamwar/command/GuardCommandTest.java +++ b/testsrc/de/steamwar/command/GuardCommandTest.java @@ -35,7 +35,7 @@ public class GuardCommandTest { try { cmd.execute("test", "", new String[0]); } catch (Exception e) { - assertCMDFramework(e, ExecutionIdentifier.class, "RunTypeMapper"); + assertThat(e.getMessage(), is("GuardChecker COMMAND")); } }