diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 9387a02..5f3da93 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -69,9 +69,11 @@ public abstract class AbstractSWCommand { protected final void execute(T sender, String alias, String[] args) { initialize(); + List errors = new ArrayList<>(); try { - if (!commandList.stream().anyMatch(s -> s.invoke(sender, alias, args))) { - commandHelpList.stream().anyMatch(s -> s.invoke(sender, alias, args)); + if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, args))) { + errors.forEach(Runnable::run); + commandHelpList.stream().anyMatch(s -> s.invoke((ignore) -> {}, sender, alias, args)); } } catch (CommandNoHelpException e) { // Ignored diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java index a9174dc..ca8c50b 100644 --- a/src/de/steamwar/command/AbstractTypeMapper.java +++ b/src/de/steamwar/command/AbstractTypeMapper.java @@ -20,6 +20,7 @@ package de.steamwar.command; import java.util.Collection; +import java.util.function.BiConsumer; public interface AbstractTypeMapper extends AbstractValidator { /** @@ -27,5 +28,10 @@ public interface AbstractTypeMapper extends AbstractValidator { */ T map(K sender, String[] previousArguments, String s); + @Override + default boolean validate(K sender, T value, BiConsumer messageSender) { + return true; + } + Collection tabCompletes(K sender, String[] previousArguments, String s); } diff --git a/src/de/steamwar/command/AbstractValidator.java b/src/de/steamwar/command/AbstractValidator.java index 0f74382..a12c0e4 100644 --- a/src/de/steamwar/command/AbstractValidator.java +++ b/src/de/steamwar/command/AbstractValidator.java @@ -21,6 +21,7 @@ package de.steamwar.command; import java.util.function.BiConsumer; +@FunctionalInterface public interface AbstractValidator { /** @@ -31,7 +32,5 @@ public interface AbstractValidator { * @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; - } + boolean validate(K sender, T value, BiConsumer messageSender); } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 503a7af..1072c2a 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -23,9 +23,11 @@ import lombok.AllArgsConstructor; import lombok.Setter; import java.lang.reflect.Array; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.function.Consumer; class CommandPart { @@ -86,11 +88,11 @@ class CommandPart { } } - public void generateArgumentArray(List current, T sender, String[] args, int startIndex) { + public void generateArgumentArray(Consumer errors, List current, T sender, String[] args, int startIndex) { if (varArgType != null) { Object array = Array.newInstance(varArgType, args.length - startIndex); for (int i = startIndex; i < args.length; i++) { - CheckArgumentResult validArgument = checkArgument(null, sender, args, i); + CheckArgumentResult validArgument = checkArgument(errors, null, sender, args, i); if (!validArgument.success) { throw new CommandParseException(); } @@ -100,7 +102,7 @@ class CommandPart { return; } - CheckArgumentResult validArgument = checkArgument(null, sender, args, startIndex); + CheckArgumentResult validArgument = checkArgument(errors, null, sender, args, startIndex); if (!validArgument.success && optional == null) { throw new CommandParseException(); } @@ -109,7 +111,7 @@ class CommandPart { current.add(typeMapper.map(sender, EMPTY_ARRAY, optional)); } if (next != null) { - next.generateArgumentArray(current, sender, args, startIndex); + next.generateArgumentArray(errors, current, sender, args, startIndex); } return; } @@ -117,7 +119,7 @@ class CommandPart { current.add(validArgument.value); } if (next != null) { - next.generateArgumentArray(current, sender, args, startIndex + 1); + next.generateArgumentArray(errors, current, sender, args, startIndex + 1); } } @@ -181,7 +183,7 @@ class CommandPart { public void generateTabComplete(List current, T sender, String[] args, int startIndex) { if (varArgType != null) { for (int i = startIndex; i < args.length - 1; i++) { - CheckArgumentResult validArgument = checkArgument(GuardCheckType.TAB_COMPLETE, sender, args, i); + CheckArgumentResult validArgument = checkArgument((ignore) -> {}, GuardCheckType.TAB_COMPLETE, sender, args, i); if (!validArgument.success) { return; } @@ -194,7 +196,7 @@ class CommandPart { } if (args.length - 1 > startIndex) { - CheckArgumentResult checkArgumentResult = checkArgument(GuardCheckType.TAB_COMPLETE, sender, args, startIndex); + CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, GuardCheckType.TAB_COMPLETE, sender, args, startIndex); if (checkArgumentResult.success && next != null) { next.generateTabComplete(current, sender, args, startIndex + 1); return; @@ -214,19 +216,21 @@ class CommandPart { } } - private CheckArgumentResult checkArgument(GuardCheckType guardCheckType, T sender, String[] args, int index) { + private CheckArgumentResult checkArgument(Consumer errors, GuardCheckType guardCheckType, T sender, String[] args, int index) { try { Object value = typeMapper.map(sender, Arrays.copyOf(args, index), args[index]); - if (value == null) { - return new CheckArgumentResult(false, null); - } if (validator != null) { if (!validator.validate(sender, value, (s, objects) -> { - // ignore + errors.accept(() -> { + command.sendMessage(sender, s, objects); + }); })) { return new CheckArgumentResult(false, null); } - return new CheckArgumentResult(true, value); + return new CheckArgumentResult(value != null, value); + } + if (value == null) { + return new CheckArgumentResult(false, null); } GuardResult guardResult = checkGuard(guardCheckType, sender, args, index); diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 81d759a..a3ee3de 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -183,7 +183,7 @@ public class SWCommandUtils { AbstractSWCommand.Validator validator = parameter.getAnnotation(AbstractSWCommand.Validator.class); if (validator != null) { - if (validator.value() != null) { + if (validator.value() != null && !validator.value().isEmpty()) { return getValidator(validator.value(), localValidator); } return getValidator(clazz.getTypeName(), localValidator); @@ -191,12 +191,9 @@ public class SWCommandUtils { 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 (AbstractValidator) (sender, value, messageSender) -> { + if (value == null) messageSender.accept(errorMessage.value(), new Object[0]); + return value != null; }; } return null; diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 4758433..f8e7014 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -25,6 +25,7 @@ import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -62,7 +63,7 @@ public class SubCommand { senderFunction = t -> parameters[0].getType().cast(t); } - boolean invoke(T sender, String alias, String[] args) { + boolean invoke(Consumer errors, T sender, String alias, String[] args) { try { if (!senderPredicate.test(sender)) { return false; @@ -94,7 +95,7 @@ public class SubCommand { method.invoke(abstractSWCommand, senderFunction.apply(sender)); } else { List objects = new ArrayList<>(); - commandPart.generateArgumentArray(objects, sender, args, 0); + commandPart.generateArgumentArray(errors, objects, sender, args, 0); if (validator != null) { if (!validator.validate(sender, sender, (s, objectArgs) -> { abstractSWCommand.sendMessage(sender, s, objectArgs); diff --git a/testsrc/de/steamwar/command/ValidatorCommand.java b/testsrc/de/steamwar/command/ValidatorCommand.java new file mode 100644 index 0000000..ea95a5e --- /dev/null +++ b/testsrc/de/steamwar/command/ValidatorCommand.java @@ -0,0 +1,60 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.command; + +import de.steamwar.command.dto.ExecutionIdentifier; +import de.steamwar.command.dto.TestSWCommand; +import de.steamwar.command.dto.TestValidator; + +public class ValidatorCommand extends TestSWCommand { + + public ValidatorCommand() { + super("testvalidator"); + } + + @Register + public void test(@Validator String sender) { + throw new ExecutionIdentifier("RunTest"); + } + + @Override + protected void sendMessage(String sender, String message, Object[] args) { + if (message.equals("Hello World")) { + throw new ExecutionIdentifier("RunSendMessageWithHelloWorldParameter"); + } + } + + @Register + public void onError(String sender, @ErrorMessage("Hello World") int error) { + throw new ExecutionIdentifier("RunOnError"); + } + + @Register + public void onError(String sender, double error) { + throw new ExecutionIdentifier("RunOnErrorDouble"); + } + + @ClassValidator(value = String.class, local = true) + public TestValidator validator() { + return (sender, value, messageSender) -> { + throw new ExecutionIdentifier("RunValidator"); + }; + } +} diff --git a/testsrc/de/steamwar/command/ValidatorCommandTest.java b/testsrc/de/steamwar/command/ValidatorCommandTest.java new file mode 100644 index 0000000..d9712e3 --- /dev/null +++ b/testsrc/de/steamwar/command/ValidatorCommandTest.java @@ -0,0 +1,63 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.command; + +import de.steamwar.command.dto.ExecutionIdentifier; +import org.junit.Test; + +import static de.steamwar.AssertionUtils.assertCMDFramework; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class ValidatorCommandTest { + + @Test + public void testValidator() { + ValidatorCommand cmd = new ValidatorCommand(); + try { + cmd.execute("test", "", new String[0]); + assertThat(true, is(false)); + } catch (Exception e) { + assertThat(e.getMessage(), is("RunValidator")); + } + } + + @Test + public void testErrorMessage() { + ValidatorCommand cmd = new ValidatorCommand(); + try { + cmd.execute("test", "", new String[]{"Hello"}); + assertThat(true, is(false)); + } catch (Exception e) { + assertThat(e.getMessage(), is("RunSendMessageWithHelloWorldParameter")); + } + } + + @Test + public void testErrorNoMessage() { + ValidatorCommand cmd = new ValidatorCommand(); + try { + cmd.execute("test", "", new String[]{"0.0"}); + assertThat(true, is(false)); + } catch (Exception e) { + assertCMDFramework(e, ExecutionIdentifier.class, "RunOnErrorDouble"); + } + } +} diff --git a/testsrc/de/steamwar/command/dto/TestValidator.java b/testsrc/de/steamwar/command/dto/TestValidator.java new file mode 100644 index 0000000..8cf0495 --- /dev/null +++ b/testsrc/de/steamwar/command/dto/TestValidator.java @@ -0,0 +1,25 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.command.dto; + +import de.steamwar.command.AbstractValidator; + +public interface TestValidator extends AbstractValidator { +}