From 96a5ec6b2fd249ec8105470775e78b92596ad9be Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 30 May 2022 14:32:38 +0200 Subject: [PATCH 01/29] Fix enum mapper --- src/de/steamwar/command/SWCommandUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index e038e23..73468fa 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -218,12 +218,12 @@ public class SWCommandUtils { public static >, K> T createEnumMapper(Class> enumClass) { Map> enumMap = new HashMap<>(); for (Enum e : enumClass.getEnumConstants()) { - enumMap.put(e.name(), e); + enumMap.put(e.name().toLowerCase(), e); } return (T) new AbstractTypeMapper>() { @Override public Enum map(Object commandSender, String[] previousArguments, String s) { - return enumMap.get(s); + return enumMap.get(s.toLowerCase()); } @Override From 0674c951aa70482bfb9238a5b87fc5ce9b2ca078 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 30 May 2022 14:52:21 +0200 Subject: [PATCH 02/29] Fix SWCommandUtils Add SWTypeMapperCreator --- src/de/steamwar/command/SWCommandUtils.java | 36 ++++++------------- .../steamwar/command/SWTypeMapperCreator.java | 28 +++++++++++++++ 2 files changed, 38 insertions(+), 26 deletions(-) create mode 100644 src/de/steamwar/command/SWTypeMapperCreator.java diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 73468fa..331b9f9 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -38,7 +38,11 @@ public class SWCommandUtils { @Getter private final Map> GUARD_FUNCTIONS = new HashMap<>(); - static { + private SWTypeMapperCreator swTypeMapperCreator; + + public static , K, V> void init(SWTypeMapperCreator swTypeMapperCreator) { + SWCommandUtils.swTypeMapperCreator = swTypeMapperCreator; + addMapper(boolean.class, Boolean.class, createMapper(s -> { if (s.equalsIgnoreCase("true")) return true; if (s.equalsIgnoreCase("false")) return false; @@ -197,22 +201,12 @@ public class SWCommandUtils { return createMapper((s) -> strings.contains(s) ? s : null, s -> strings); } - public static , K, V> T createMapper(Function mapper, Function> tabCompleter) { + public static , K, V> T createMapper(Function mapper, Function> tabCompleter) { return createMapper(mapper, (commandSender, s) -> tabCompleter.apply(s)); } - public static , K, V> T createMapper(Function mapper, BiFunction> tabCompleter) { - return (T) new AbstractTypeMapper() { - @Override - public V map(K commandSender, String[] previousArguments, String s) { - return mapper.apply(s); - } - - @Override - public List tabCompletes(K commandSender, String[] previous, String s) { - return tabCompleter.apply(commandSender, s); - } - }; + public static , K, V> T createMapper(Function mapper, BiFunction> tabCompleter) { + return (T) swTypeMapperCreator.createTypeMapper(mapper, tabCompleter); } public static >, K> T createEnumMapper(Class> enumClass) { @@ -220,17 +214,7 @@ public class SWCommandUtils { for (Enum e : enumClass.getEnumConstants()) { enumMap.put(e.name().toLowerCase(), e); } - return (T) new AbstractTypeMapper>() { - @Override - public Enum map(Object commandSender, String[] previousArguments, String s) { - return enumMap.get(s.toLowerCase()); - } - - @Override - public Collection tabCompletes(Object commandSender, String[] previousArguments, String s) { - return enumMap.keySet(); - } - }; + return createMapper(s -> enumMap.get(s.toLowerCase()), (k, s) -> enumMap.keySet()); } private static Function numberMapper(Function mapper) { @@ -249,7 +233,7 @@ public class SWCommandUtils { }; } - private static Function> numberCompleter(Function mapper) { + private static Function> numberCompleter(Function mapper) { return s -> numberMapper(mapper).apply(s) != null ? Collections.singletonList(s) : Collections.emptyList(); diff --git a/src/de/steamwar/command/SWTypeMapperCreator.java b/src/de/steamwar/command/SWTypeMapperCreator.java new file mode 100644 index 0000000..33c5dcb --- /dev/null +++ b/src/de/steamwar/command/SWTypeMapperCreator.java @@ -0,0 +1,28 @@ +/* + * 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 java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface SWTypeMapperCreator, K, V> { + T createTypeMapper(Function mapper, BiFunction> tabCompleter); +} From b0d74ed5a347ef97e8b418deb374304f0d3da593 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 30 May 2022 14:59:00 +0200 Subject: [PATCH 03/29] Fix SWCommandUtils --- src/de/steamwar/command/SWCommandUtils.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 331b9f9..6e486b1 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -38,11 +38,19 @@ public class SWCommandUtils { @Getter private final Map> GUARD_FUNCTIONS = new HashMap<>(); - private SWTypeMapperCreator swTypeMapperCreator; + private SWTypeMapperCreator swTypeMapperCreator = (mapper, tabCompleter) -> new AbstractTypeMapper() { + @Override + public Object map(Object sender, String[] previousArguments, String s) { + return mapper.apply(s); + } - public static , K, V> void init(SWTypeMapperCreator swTypeMapperCreator) { - SWCommandUtils.swTypeMapperCreator = swTypeMapperCreator; + @Override + public Collection tabCompletes(Object sender, String[] previousArguments, String s) { + return tabCompleter.apply(sender, s); + } + }; + static { addMapper(boolean.class, Boolean.class, createMapper(s -> { if (s.equalsIgnoreCase("true")) return true; if (s.equalsIgnoreCase("false")) return false; @@ -55,6 +63,10 @@ public class SWCommandUtils { MAPPER_FUNCTIONS.put(String.class.getTypeName(), createMapper(s -> s, Collections::singletonList)); } + public static , K, V> void init(SWTypeMapperCreator swTypeMapperCreator) { + SWCommandUtils.swTypeMapperCreator = swTypeMapperCreator; + } + private static void addMapper(Class clazz, Class alternativeClazz, AbstractTypeMapper mapper) { MAPPER_FUNCTIONS.put(clazz.getTypeName(), mapper); MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper); From 919c4d525ea39756b24bd961bb72d8d58bdd5bd4 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 30 May 2022 15:04:51 +0200 Subject: [PATCH 04/29] Fix SWCommandUtils --- src/de/steamwar/command/SWCommandUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 6e486b1..aaf0c7a 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -46,7 +46,7 @@ public class SWCommandUtils { @Override public Collection tabCompletes(Object sender, String[] previousArguments, String s) { - return tabCompleter.apply(sender, s); + return ((BiFunction>) tabCompleter).apply(sender, s); } }; From 46776e718d10c28710d43bafc7ad8730e7c7951e Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 7 Jun 2022 20:18:57 +0200 Subject: [PATCH 05/29] locale Packet --- .../server/LocaleInvalidationPacket.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/de/steamwar/network/packets/server/LocaleInvalidationPacket.java diff --git a/src/de/steamwar/network/packets/server/LocaleInvalidationPacket.java b/src/de/steamwar/network/packets/server/LocaleInvalidationPacket.java new file mode 100644 index 0000000..1e24547 --- /dev/null +++ b/src/de/steamwar/network/packets/server/LocaleInvalidationPacket.java @@ -0,0 +1,35 @@ +/* + * 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.network.packets.server; + +import de.steamwar.network.packets.NetworkPacket; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@Getter +public class LocaleInvalidationPacket extends NetworkPacket { + private static final long serialVersionUID = 1113963147008168582L; + private int playerId; +} From cf59dc97205dafa77f951060510f60fb0ed647c5 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Tue, 14 Jun 2022 09:47:45 +0200 Subject: [PATCH 06/29] Add MetaInfos --- .../steamwar/network/packets/MetaInfos.java | 23 ++++++++++++++ .../network/packets/NetworkPacket.java | 4 +++ .../network/packets/PacketHandler.java | 31 ++++++++++++++----- 3 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 src/de/steamwar/network/packets/MetaInfos.java diff --git a/src/de/steamwar/network/packets/MetaInfos.java b/src/de/steamwar/network/packets/MetaInfos.java new file mode 100644 index 0000000..61632d7 --- /dev/null +++ b/src/de/steamwar/network/packets/MetaInfos.java @@ -0,0 +1,23 @@ +/* + * 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.network.packets; + +public interface MetaInfos { +} diff --git a/src/de/steamwar/network/packets/NetworkPacket.java b/src/de/steamwar/network/packets/NetworkPacket.java index b1d6bf3..35c68c4 100644 --- a/src/de/steamwar/network/packets/NetworkPacket.java +++ b/src/de/steamwar/network/packets/NetworkPacket.java @@ -40,6 +40,10 @@ public abstract class NetworkPacket implements Serializable { PacketHandler.handlePacket(deserialize(data)); } + public static void handle(MetaInfos metaInfos, byte[] data) { + PacketHandler.handlePacket(metaInfos, deserialize(data)); + } + @SneakyThrows public static NetworkPacket deserialize(byte[] data) { ByteArrayInputStream bais = new ByteArrayInputStream(data); diff --git a/src/de/steamwar/network/packets/PacketHandler.java b/src/de/steamwar/network/packets/PacketHandler.java index f23afe5..11b9bcf 100644 --- a/src/de/steamwar/network/packets/PacketHandler.java +++ b/src/de/steamwar/network/packets/PacketHandler.java @@ -36,21 +36,34 @@ public abstract class PacketHandler { private final Map, Method> HANDLER_MAP = new HashMap<>(); public static void handlePacket(NetworkPacket packet) { + handlePacket(null, packet); + } + + public static void handlePacket(MetaInfos metaInfos, NetworkPacket packet) { for (PacketHandler handler : PACKET_HANDLERS) { - handler.handle(packet); + handler.handle(metaInfos, packet); } } protected PacketHandler() { Method[] methods = getClass().getMethods(); for (Method method : methods) { - if(method.getParameterCount() != 1 || !NetworkPacket.class.isAssignableFrom(method.getParameterTypes()[0])) { + Handler handler = method.getAnnotation(Handler.class); + if (handler == null) { continue; } - if (method.isAnnotationPresent(Handler.class)) { - Class packetClass = (Class) method.getParameterTypes()[0]; - HANDLER_MAP.put(packetClass, method); + if (!(method.getParameterCount() > 0 && method.getParameterCount() < 3)) { + continue; } + if (!NetworkPacket.class.isAssignableFrom(method.getParameterTypes()[0])) { + continue; + } + if (method.getParameterCount() == 2 && !MetaInfos.class.isAssignableFrom(method.getParameterTypes()[1])) { + continue; + } + + Class packetClass = (Class) method.getParameterTypes()[0]; + HANDLER_MAP.put(packetClass, method); } } @@ -63,12 +76,16 @@ public abstract class PacketHandler { } @SneakyThrows - public void handle(NetworkPacket packet) { + public void handle(MetaInfos metaInfos, NetworkPacket packet) { Method method = HANDLER_MAP.get(packet.getClass()); if (method == null) { return; } - method.invoke(this, packet); + if (method.getParameterCount() == 2) { + method.invoke(this, packet, metaInfos); + } else { + method.invoke(this, packet); + } } @Retention(RetentionPolicy.RUNTIME) From 2ddfa9e80f55ff02d85509aaa28f97ab2dffeeaf Mon Sep 17 00:00:00 2001 From: yoyosource Date: Tue, 14 Jun 2022 13:53:26 +0200 Subject: [PATCH 07/29] Fix stuff --- src/de/steamwar/network/packets/PacketHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/network/packets/PacketHandler.java b/src/de/steamwar/network/packets/PacketHandler.java index 11b9bcf..333bec6 100644 --- a/src/de/steamwar/network/packets/PacketHandler.java +++ b/src/de/steamwar/network/packets/PacketHandler.java @@ -82,7 +82,11 @@ public abstract class PacketHandler { return; } if (method.getParameterCount() == 2) { - method.invoke(this, packet, metaInfos); + if (metaInfos == null || method.getParameterTypes()[1].isAssignableFrom(metaInfos.getClass())) { + method.invoke(this, packet, metaInfos); + } else { + throw new IllegalArgumentException("MetaInfos is not assignable to " + method.getParameterTypes()[1]); + } } else { method.invoke(this, packet); } From ea9a4c2a81805268451a9c113e2a9993b1c5b5ad Mon Sep 17 00:00:00 2001 From: yoyosource Date: Tue, 14 Jun 2022 14:57:39 +0200 Subject: [PATCH 08/29] Optimize meta infos even more --- .../network/packets/NetworkPacket.java | 6 +++- .../network/packets/PacketHandler.java | 36 +++++-------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/de/steamwar/network/packets/NetworkPacket.java b/src/de/steamwar/network/packets/NetworkPacket.java index 35c68c4..2a50056 100644 --- a/src/de/steamwar/network/packets/NetworkPacket.java +++ b/src/de/steamwar/network/packets/NetworkPacket.java @@ -27,6 +27,8 @@ import java.io.*; @EqualsAndHashCode public abstract class NetworkPacket implements Serializable { + public transient MetaInfos metaInfos; + @SneakyThrows public byte[] serialize() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -41,7 +43,9 @@ public abstract class NetworkPacket implements Serializable { } public static void handle(MetaInfos metaInfos, byte[] data) { - PacketHandler.handlePacket(metaInfos, deserialize(data)); + NetworkPacket networkPacket = deserialize(data); + networkPacket.metaInfos = metaInfos; + PacketHandler.handlePacket(networkPacket); } @SneakyThrows diff --git a/src/de/steamwar/network/packets/PacketHandler.java b/src/de/steamwar/network/packets/PacketHandler.java index 333bec6..671eb46 100644 --- a/src/de/steamwar/network/packets/PacketHandler.java +++ b/src/de/steamwar/network/packets/PacketHandler.java @@ -36,34 +36,22 @@ public abstract class PacketHandler { private final Map, Method> HANDLER_MAP = new HashMap<>(); public static void handlePacket(NetworkPacket packet) { - handlePacket(null, packet); - } - - public static void handlePacket(MetaInfos metaInfos, NetworkPacket packet) { for (PacketHandler handler : PACKET_HANDLERS) { - handler.handle(metaInfos, packet); + handler.handle(packet); } } protected PacketHandler() { Method[] methods = getClass().getMethods(); for (Method method : methods) { - Handler handler = method.getAnnotation(Handler.class); - if (handler == null) { - continue; - } - if (!(method.getParameterCount() > 0 && method.getParameterCount() < 3)) { - continue; - } - if (!NetworkPacket.class.isAssignableFrom(method.getParameterTypes()[0])) { - continue; - } - if (method.getParameterCount() == 2 && !MetaInfos.class.isAssignableFrom(method.getParameterTypes()[1])) { + if(method.getParameterCount() != 1 || !NetworkPacket.class.isAssignableFrom(method.getParameterTypes()[0])) { continue; } - Class packetClass = (Class) method.getParameterTypes()[0]; - HANDLER_MAP.put(packetClass, method); + if (method.isAnnotationPresent(Handler.class)) { + Class packetClass = (Class) method.getParameterTypes()[0]; + HANDLER_MAP.put(packetClass, method); + } } } @@ -76,20 +64,12 @@ public abstract class PacketHandler { } @SneakyThrows - public void handle(MetaInfos metaInfos, NetworkPacket packet) { + public void handle(NetworkPacket packet) { Method method = HANDLER_MAP.get(packet.getClass()); if (method == null) { return; } - if (method.getParameterCount() == 2) { - if (metaInfos == null || method.getParameterTypes()[1].isAssignableFrom(metaInfos.getClass())) { - method.invoke(this, packet, metaInfos); - } else { - throw new IllegalArgumentException("MetaInfos is not assignable to " + method.getParameterTypes()[1]); - } - } else { - method.invoke(this, packet); - } + method.invoke(this, packet); } @Retention(RetentionPolicy.RUNTIME) From 62d2b50207001d630b736024eb5a50d997e57c94 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Tue, 14 Jun 2022 15:12:07 +0200 Subject: [PATCH 09/29] Optimize meta infos even more --- src/de/steamwar/network/packets/NetworkPacket.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/network/packets/NetworkPacket.java b/src/de/steamwar/network/packets/NetworkPacket.java index 2a50056..14797fc 100644 --- a/src/de/steamwar/network/packets/NetworkPacket.java +++ b/src/de/steamwar/network/packets/NetworkPacket.java @@ -27,7 +27,11 @@ import java.io.*; @EqualsAndHashCode public abstract class NetworkPacket implements Serializable { - public transient MetaInfos metaInfos; + private transient MetaInfos metaInfos; + + public MetaInfos getMetaInfos() { + return metaInfos; + } @SneakyThrows public byte[] serialize() { From 8dbab5b132d2fd84db202d691fd1f817f91579d6 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 14 Jun 2022 19:41:14 +0200 Subject: [PATCH 10/29] hotfix: Networking --- src/de/steamwar/network/packets/NetworkPacket.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/de/steamwar/network/packets/NetworkPacket.java b/src/de/steamwar/network/packets/NetworkPacket.java index 14797fc..987e1ef 100644 --- a/src/de/steamwar/network/packets/NetworkPacket.java +++ b/src/de/steamwar/network/packets/NetworkPacket.java @@ -27,6 +27,7 @@ import java.io.*; @EqualsAndHashCode public abstract class NetworkPacket implements Serializable { + private static final long serialVersionUID = -3168992457669156473L; private transient MetaInfos metaInfos; public MetaInfos getMetaInfos() { From b8d659d1c6d9d51475d37ba1ba402d1670a453c1 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 13:18:14 +0200 Subject: [PATCH 11/29] Add AbstractValidator Deprecate AbstractGuardChecker --- .../command/AbstractGuardChecker.java | 1 + .../steamwar/command/AbstractSWCommand.java | 53 +++++++++++++++++++ .../steamwar/command/AbstractTypeMapper.java | 2 +- .../steamwar/command/AbstractValidator.java | 26 +++++++++ src/de/steamwar/command/GuardCheckType.java | 1 + src/de/steamwar/command/GuardResult.java | 1 + src/de/steamwar/command/SWCommandUtils.java | 10 +++- 7 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 src/de/steamwar/command/AbstractValidator.java diff --git a/src/de/steamwar/command/AbstractGuardChecker.java b/src/de/steamwar/command/AbstractGuardChecker.java index f5f2597..dd4c555 100644 --- a/src/de/steamwar/command/AbstractGuardChecker.java +++ b/src/de/steamwar/command/AbstractGuardChecker.java @@ -19,6 +19,7 @@ package de.steamwar.command; +@Deprecated @FunctionalInterface public interface AbstractGuardChecker { /** diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index d654df7..2500bd1 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -38,6 +38,7 @@ public abstract class AbstractSWCommand { private final Map> localTypeMapper = new HashMap<>(); private final Map> localGuardChecker = new HashMap<>(); + private final Map> localValidators = new HashMap<>(); protected AbstractSWCommand(Class clazz, String command) { this(clazz, command, new String[0]); @@ -127,6 +128,20 @@ public abstract class AbstractSWCommand { SWCommandUtils.getGUARD_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), guardChecker); } }); + addValidator(Validator.class, method, i -> i == 0, false, AbstractValidator.class, (anno, validator) -> { + if (anno.local()) { + localValidators.putIfAbsent(anno.value(), (AbstractValidator) validator); + } else { + SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value(), validator); + } + }); + addValidator(ClassValidator.class, method, i -> i == 0, false, AbstractValidator.class, (anno, validator) -> { + if (anno.local()) { + localValidators.putIfAbsent(anno.value().getTypeName(), (AbstractValidator) validator); + } else { + SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), validator); + } + }); } for (Method method : methods) { add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> { @@ -228,6 +243,17 @@ public abstract class AbstractSWCommand { }); } + private void addValidator(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer> consumer) { + add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> { + try { + method.setAccessible(true); + consumer.accept(anno, (AbstractValidator) method.invoke(this)); + } catch (Exception e) { + throw new SecurityException(e.getMessage(), e); + } + }); + } + // TODO: Implement this when Message System is ready /* public void addDefaultHelpMessage(String message) { @@ -280,6 +306,7 @@ public abstract class AbstractSWCommand { boolean local() default false; } + @Deprecated @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.METHOD}) protected @interface Guard { @@ -288,6 +315,7 @@ public abstract class AbstractSWCommand { boolean local() default false; } + @Deprecated @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) protected @interface ClassGuard { @@ -296,6 +324,22 @@ public abstract class AbstractSWCommand { boolean local() default false; } + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER, ElementType.METHOD}) + protected @interface Validator { + String value() default ""; + + boolean local() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + protected @interface ClassValidator { + Class value(); + + boolean local() default false; + } + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) protected @interface StaticValue { @@ -320,4 +364,13 @@ public abstract class AbstractSWCommand { */ String value(); } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + protected @interface ErrorMessage { + /** + * Error message to be displayed when the parameter is invalid. + */ + String value(); + } } diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java index 744b726..a9174dc 100644 --- a/src/de/steamwar/command/AbstractTypeMapper.java +++ b/src/de/steamwar/command/AbstractTypeMapper.java @@ -21,7 +21,7 @@ package de.steamwar.command; import java.util.Collection; -public interface AbstractTypeMapper { +public interface AbstractTypeMapper extends AbstractValidator { /** * The CommandSender can be null! */ diff --git a/src/de/steamwar/command/AbstractValidator.java b/src/de/steamwar/command/AbstractValidator.java new file mode 100644 index 0000000..c4df8ed --- /dev/null +++ b/src/de/steamwar/command/AbstractValidator.java @@ -0,0 +1,26 @@ +/* + * 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; + +public interface AbstractValidator { + default boolean validate(K sender, T value) { + return true; + } +} diff --git a/src/de/steamwar/command/GuardCheckType.java b/src/de/steamwar/command/GuardCheckType.java index 0f023b8..d2e6ed0 100644 --- a/src/de/steamwar/command/GuardCheckType.java +++ b/src/de/steamwar/command/GuardCheckType.java @@ -19,6 +19,7 @@ package de.steamwar.command; +@Deprecated public enum GuardCheckType { COMMAND, HELP_COMMAND, diff --git a/src/de/steamwar/command/GuardResult.java b/src/de/steamwar/command/GuardResult.java index 9ffbf77..3e6aafc 100644 --- a/src/de/steamwar/command/GuardResult.java +++ b/src/de/steamwar/command/GuardResult.java @@ -19,6 +19,7 @@ package de.steamwar.command; +@Deprecated public enum GuardResult { ALLOWED, DENIED_WITH_HELP, diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index aaf0c7a..4e57876 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -28,6 +28,7 @@ import java.lang.reflect.Parameter; import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.stream.Collectors; @UtilityClass public class SWCommandUtils { @@ -36,8 +37,12 @@ public class SWCommandUtils { private final Map> MAPPER_FUNCTIONS = new HashMap<>(); @Getter + @Deprecated private final Map> GUARD_FUNCTIONS = new HashMap<>(); + @Getter + private final Map> VALIDATOR_FUNCTIONS = new HashMap<>(); + private SWTypeMapperCreator swTypeMapperCreator = (mapper, tabCompleter) -> new AbstractTypeMapper() { @Override public Object map(Object sender, String[] previousArguments, String s) { @@ -209,8 +214,9 @@ public class SWCommandUtils { } public static , K> T createMapper(String... values) { - List strings = Arrays.asList(values); - return createMapper((s) -> strings.contains(s) ? s : null, s -> strings); + List strings = Arrays.stream(values).map(String::toLowerCase).collect(Collectors.toList()); + List tabCompletes = Arrays.asList(values); + return createMapper(s -> strings.contains(s.toLowerCase()) ? s : null, s -> tabCompletes); } public static , K, V> T createMapper(Function mapper, Function> tabCompleter) { From fd6612ac1b5bad4ddb365711b34dd1577ae8dad9 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 15:59:28 +0200 Subject: [PATCH 12/29] 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")); } } From a7d41ddc80a26b905081adfc5c64aa65c502eb7b Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 16:00:14 +0200 Subject: [PATCH 13/29] Update when messages and stuff is send --- src/de/steamwar/command/SubCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 6456bfd..4758433 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -74,7 +74,7 @@ public class SubCommand { } if (validator != null) { if (!validator.validate(sender, sender, (s, objectArgs) -> { - // TODO: implement + abstractSWCommand.sendMessage(sender, s, objectArgs); })) { throw new CommandNoHelpException(); } @@ -97,7 +97,7 @@ public class SubCommand { commandPart.generateArgumentArray(objects, sender, args, 0); if (validator != null) { if (!validator.validate(sender, sender, (s, objectArgs) -> { - // TODO: implement + abstractSWCommand.sendMessage(sender, s, objectArgs); })) { throw new CommandNoHelpException(); } @@ -134,7 +134,7 @@ public class SubCommand { List tabComplete(T sender, String[] args) { if (validator != null) { if (!validator.validate(sender, sender, (s, objects) -> { - // TODO: implement + // ignore })) { return null; } From a8880e53dbc03ada5eb2385487e43d9f7eb7dde8 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 16:46:22 +0200 Subject: [PATCH 14/29] Fix a bunch of errors --- .../steamwar/command/AbstractSWCommand.java | 6 +- .../steamwar/command/AbstractTypeMapper.java | 6 ++ .../steamwar/command/AbstractValidator.java | 5 +- src/de/steamwar/command/CommandPart.java | 30 +++++---- src/de/steamwar/command/SWCommandUtils.java | 11 ++-- src/de/steamwar/command/SubCommand.java | 5 +- .../de/steamwar/command/ValidatorCommand.java | 60 ++++++++++++++++++ .../command/ValidatorCommandTest.java | 63 +++++++++++++++++++ .../steamwar/command/dto/TestValidator.java | 25 ++++++++ 9 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 testsrc/de/steamwar/command/ValidatorCommand.java create mode 100644 testsrc/de/steamwar/command/ValidatorCommandTest.java create mode 100644 testsrc/de/steamwar/command/dto/TestValidator.java 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 { +} From d0150a23357ad7214097ce0743a3cb5715b20f34 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 16:55:35 +0200 Subject: [PATCH 15/29] Fix a bunch of errors --- src/de/steamwar/command/AbstractSWCommand.java | 9 +++++---- src/de/steamwar/command/SWCommandUtils.java | 12 ++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 5f3da93..16b764c 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -236,22 +236,23 @@ public abstract class AbstractSWCommand { }); } - private void addGuard(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer> consumer) { + private void addValidator(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer> consumer) { add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> { try { method.setAccessible(true); - consumer.accept(anno, (AbstractGuardChecker) method.invoke(this)); + consumer.accept(anno, (AbstractValidator) method.invoke(this)); } catch (Exception e) { throw new SecurityException(e.getMessage(), e); } }); } - private void addValidator(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer> consumer) { + @Deprecated + private void addGuard(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer> consumer) { add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> { try { method.setAccessible(true); - consumer.accept(anno, (AbstractValidator) method.invoke(this)); + consumer.accept(anno, (AbstractGuardChecker) method.invoke(this)); } catch (Exception e) { throw new SecurityException(e.getMessage(), e); } diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index a3ee3de..4756630 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -207,6 +207,7 @@ public class SWCommandUtils { return validator; } + @Deprecated public static AbstractGuardChecker getGuardChecker(Parameter parameter, Map> localGuardChecker) { Class clazz = parameter.getType(); if (parameter.isVarArgs()) { @@ -231,6 +232,7 @@ public class SWCommandUtils { return null; } + @Deprecated private static AbstractGuardChecker getGuardChecker(String s, Map> localGuardChecker) { AbstractGuardChecker guardChecker = localGuardChecker.getOrDefault(s, (AbstractGuardChecker) GUARD_FUNCTIONS.getOrDefault(s, null)); if (guardChecker == null) { @@ -247,10 +249,20 @@ public class SWCommandUtils { MAPPER_FUNCTIONS.putIfAbsent(name, mapper); } + public static void addValidator(Class clazz, AbstractValidator validator) { + addValidator(clazz.getTypeName(), validator); + } + + public static void addValidator(String name, AbstractValidator validator) { + VALIDATOR_FUNCTIONS.putIfAbsent(name, validator); + } + + @Deprecated public static void addGuard(Class clazz, AbstractGuardChecker guardChecker) { addGuard(clazz.getTypeName(), guardChecker); } + @Deprecated public static void addGuard(String name, AbstractGuardChecker guardChecker) { GUARD_FUNCTIONS.putIfAbsent(name, guardChecker); } From 10525d2a5cf24245a633dc35b8f8793da37fd1b1 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 17:28:14 +0200 Subject: [PATCH 16/29] Fix stuff --- src/de/steamwar/command/CommandPart.java | 24 ------------------------ src/de/steamwar/command/SubCommand.java | 1 - 2 files changed, 25 deletions(-) diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 1072c2a..2f7e075 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -123,30 +123,6 @@ 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++) { diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index f8e7014..d3a266c 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -114,7 +114,6 @@ public class SubCommand { return true; } } - commandPart.validate(sender, objects, 0); commandPart.guardCheck(sender, args, 0); objects.add(0, senderFunction.apply(sender)); method.setAccessible(true); From 3d7e619bf675a5e35dc12635aa4b39c50d7ea06c Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 20:51:51 +0200 Subject: [PATCH 17/29] Fix duplicates in tab complete --- src/de/steamwar/command/AbstractSWCommand.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 16b764c..d9a721f 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -93,6 +93,7 @@ public abstract class AbstractSWCommand { .flatMap(Collection::stream) .filter(s -> !s.isEmpty()) .filter(s -> s.toLowerCase().startsWith(string)) + .distinct() .collect(Collectors.toList()); } From ef49888699f70af0eb23906519219bb4758ead5a Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 22:16:13 +0200 Subject: [PATCH 18/29] Hotfix AbstractSWCommand --- src/de/steamwar/command/AbstractSWCommand.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index d9a721f..f3a0d4b 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -45,7 +45,12 @@ public abstract class AbstractSWCommand { } protected AbstractSWCommand(Class clazz, String command, String... aliases) { + this(clazz, command, () -> {}, aliases); + } + + protected AbstractSWCommand(Class clazz, String command, Runnable initializer, String... aliases) { this.clazz = clazz; + initializer.run(); createAndSafeCommand(command, aliases); unregister(); register(); From b1328695a1a30c062d066ff564a9112fde668b3e Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 22:24:16 +0200 Subject: [PATCH 19/29] Hotfix AbstractSWCommand --- src/de/steamwar/command/AbstractSWCommand.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index f3a0d4b..d9a721f 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -45,12 +45,7 @@ public abstract class AbstractSWCommand { } protected AbstractSWCommand(Class clazz, String command, String... aliases) { - this(clazz, command, () -> {}, aliases); - } - - protected AbstractSWCommand(Class clazz, String command, Runnable initializer, String... aliases) { this.clazz = clazz; - initializer.run(); createAndSafeCommand(command, aliases); unregister(); register(); From 09a9d8980b0186e7e5e17c14ef651a9684bc8651 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 22:34:24 +0200 Subject: [PATCH 20/29] Fix Validator api for varargs --- src/de/steamwar/command/SWCommandUtils.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 4756630..d6164cb 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -169,9 +169,6 @@ public class SWCommandUtils { 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) { From fc976acdd4670a9287552bc01bf2dd86c86ce488 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 23:02:13 +0200 Subject: [PATCH 21/29] Fix CommandPart Validator for varargs part --- src/de/steamwar/command/CommandPart.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 2f7e075..ef888ec 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -92,12 +92,17 @@ class CommandPart { if (varArgType != null) { Object array = Array.newInstance(varArgType, args.length - startIndex); for (int i = startIndex; i < args.length; i++) { - CheckArgumentResult validArgument = checkArgument(errors, null, sender, args, i); + CheckArgumentResult validArgument = checkArgument(null, null, sender, args, i); if (!validArgument.success) { throw new CommandParseException(); } Array.set(array, i - startIndex, validArgument.value); } + if (validator != null && !validator.validate(sender, array, (s, objects) -> { + errors.accept(() -> command.sendMessage(sender, s, objects)); + })) { + throw new CommandParseException(); + } current.add(array); return; } @@ -195,7 +200,7 @@ class CommandPart { 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 (validator != null) { + if (validator != null && errors != null) { if (!validator.validate(sender, value, (s, objects) -> { errors.accept(() -> { command.sendMessage(sender, s, objects); From 43728d8143a8a64eaa9361fa84a5819e386560ca Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 23:06:53 +0200 Subject: [PATCH 22/29] Fix CommandPart Validator for varargs part --- src/de/steamwar/command/AbstractSWCommand.java | 5 ++++- src/de/steamwar/command/AbstractTypeMapper.java | 3 +-- src/de/steamwar/command/AbstractValidator.java | 9 ++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index d9a721f..012d96a 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -72,7 +72,10 @@ public abstract class AbstractSWCommand { List errors = new ArrayList<>(); try { if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, args))) { - errors.forEach(Runnable::run); + if (!errors.isEmpty()) { + errors.forEach(Runnable::run); + return; + } commandHelpList.stream().anyMatch(s -> s.invoke((ignore) -> {}, sender, alias, args)); } } catch (CommandNoHelpException e) { diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java index ca8c50b..4a340d8 100644 --- a/src/de/steamwar/command/AbstractTypeMapper.java +++ b/src/de/steamwar/command/AbstractTypeMapper.java @@ -20,7 +20,6 @@ package de.steamwar.command; import java.util.Collection; -import java.util.function.BiConsumer; public interface AbstractTypeMapper extends AbstractValidator { /** @@ -29,7 +28,7 @@ public interface AbstractTypeMapper extends AbstractValidator { T map(K sender, String[] previousArguments, String s); @Override - default boolean validate(K sender, T value, BiConsumer messageSender) { + default boolean validate(K sender, T value, MessageSender messageSender) { return true; } diff --git a/src/de/steamwar/command/AbstractValidator.java b/src/de/steamwar/command/AbstractValidator.java index a12c0e4..60efd19 100644 --- a/src/de/steamwar/command/AbstractValidator.java +++ b/src/de/steamwar/command/AbstractValidator.java @@ -19,8 +19,6 @@ package de.steamwar.command; -import java.util.function.BiConsumer; - @FunctionalInterface public interface AbstractValidator { @@ -32,5 +30,10 @@ 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. */ - boolean validate(K sender, T value, BiConsumer messageSender); + boolean validate(K sender, T value, MessageSender messageSender); + + @FunctionalInterface + interface MessageSender { + void send(String s, Object... args); + } } From c4ed8948ad5bb2cb8414ee2d3408e52928237908 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 16 Jun 2022 23:08:05 +0200 Subject: [PATCH 23/29] Fix CommandPart Validator for varargs part --- src/de/steamwar/command/SWCommandUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index d6164cb..cea1dd6 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -26,7 +26,6 @@ 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; @@ -189,7 +188,7 @@ public class SWCommandUtils { AbstractSWCommand.ErrorMessage errorMessage = parameter.getAnnotation(AbstractSWCommand.ErrorMessage.class); if (errorMessage != null) { return (AbstractValidator) (sender, value, messageSender) -> { - if (value == null) messageSender.accept(errorMessage.value(), new Object[0]); + if (value == null) messageSender.send(errorMessage.value(), new Object[0]); return value != null; }; } From 4e46e05a3b0c627a34631848874fc56d14ba56e4 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Fri, 17 Jun 2022 09:51:28 +0200 Subject: [PATCH 24/29] Update AbstractSWCommand.ErrorMessage --- src/de/steamwar/command/AbstractSWCommand.java | 5 +++++ src/de/steamwar/command/SWCommandUtils.java | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 012d96a..75feed9 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -381,5 +381,10 @@ public abstract class AbstractSWCommand { * Error message to be displayed when the parameter is invalid. */ String value(); + + /** + * This is the short form for 'allowEmptyArrays'. + */ + boolean allowEAs() default true; } } diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index cea1dd6..86716d2 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -23,6 +23,7 @@ import lombok.Getter; import lombok.experimental.UtilityClass; import java.lang.annotation.Annotation; +import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.*; @@ -187,8 +188,12 @@ public class SWCommandUtils { AbstractSWCommand.ErrorMessage errorMessage = parameter.getAnnotation(AbstractSWCommand.ErrorMessage.class); if (errorMessage != null) { - return (AbstractValidator) (sender, value, messageSender) -> { - if (value == null) messageSender.send(errorMessage.value(), new Object[0]); + return (AbstractValidator) (sender, value, messageSender) -> { + if (value == null) messageSender.send(errorMessage.value()); + if (!errorMessage.allowEAs() && value != null && value.getClass().isArray() && Array.getLength(value) == 0) { + messageSender.send(errorMessage.value()); + return false; + } return value != null; }; } From cfd9b4107cae7fda35546a6efc3937618288a7dc Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 20 Jun 2022 13:16:13 +0200 Subject: [PATCH 25/29] Add TabCompletionCache --- .../steamwar/command/AbstractSWCommand.java | 20 +++++++ src/de/steamwar/command/CommandPart.java | 8 ++- .../steamwar/command/TabCompletionCache.java | 57 +++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/de/steamwar/command/TabCompletionCache.java diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 75feed9..ad249cb 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -23,6 +23,7 @@ import java.lang.annotation.*; 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.IntPredicate; import java.util.function.Supplier; @@ -108,7 +109,9 @@ public abstract class AbstractSWCommand { private synchronized void createMapping() { List methods = methods(); for (Method method : methods) { + Cached cached = method.getAnnotation(Cached.class); addMapper(Mapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> { + addToCache(cached, typeMapper); if (anno.local()) { localTypeMapper.putIfAbsent(anno.value(), (AbstractTypeMapper) typeMapper); } else { @@ -116,6 +119,7 @@ public abstract class AbstractSWCommand { } }); addMapper(ClassMapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> { + addToCache(cached, typeMapper); if (anno.local()) { localTypeMapper.putIfAbsent(anno.value().getTypeName(), (AbstractTypeMapper) typeMapper); } else { @@ -209,6 +213,14 @@ public abstract class AbstractSWCommand { initialized = true; } + private void addToCache(Cached cached, AbstractTypeMapper typeMapper) { + if (cached != null) { + TabCompletionCache.cached.add(typeMapper); + if (cached.global()) TabCompletionCache.global.add(typeMapper); + TabCompletionCache.cacheDuration.put(typeMapper, cached.timeUnit().toMillis(cached.cacheDuration())); + } + } + private void add(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer consumer) { T[] anno = SWCommandUtils.getAnnotation(method, annotation); if (anno == null || anno.length == 0) return; @@ -315,6 +327,14 @@ public abstract class AbstractSWCommand { boolean local() default false; } + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + protected @interface Cached { + long cacheDuration() default 5; + TimeUnit timeUnit() default TimeUnit.SECONDS; + boolean global() default false; + } + @Deprecated @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.METHOD}) diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index ef888ec..283dd51 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -169,7 +169,7 @@ class CommandPart { return; } } - Collection strings = typeMapper.tabCompletes(sender, Arrays.copyOf(args, args.length - 1), args[args.length - 1]); + Collection strings = tabCompletes(sender, args, args.length - 1); if (strings != null) { current.addAll(strings); } @@ -197,6 +197,12 @@ class CommandPart { } } + private Collection tabCompletes(T sender, String[] args, int startIndex) { + return TabCompletionCache.tabComplete(sender, typeMapper, command, () -> { + return typeMapper.tabCompletes(sender, Arrays.copyOf(args, startIndex), args[startIndex]); + }); + } + 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]); diff --git a/src/de/steamwar/command/TabCompletionCache.java b/src/de/steamwar/command/TabCompletionCache.java new file mode 100644 index 0000000..e746155 --- /dev/null +++ b/src/de/steamwar/command/TabCompletionCache.java @@ -0,0 +1,57 @@ +package de.steamwar.command; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.experimental.UtilityClass; + +import java.util.*; +import java.util.function.Supplier; + +@UtilityClass +public class TabCompletionCache { + + private Map tabCompletionCache = new HashMap<>(); + Set> cached = new HashSet<>(); + Set> global = new HashSet<>(); + Map, Long> cacheDuration = new HashMap<>(); + + @EqualsAndHashCode + @AllArgsConstructor + private static class Key { + private Object sender; + private AbstractTypeMapper typeMapper; + } + + @AllArgsConstructor + private static class TabCompletions { + private AbstractSWCommand command; + private long timestamp; + private Collection tabCompletions; + } + + Collection tabComplete(Object sender, AbstractTypeMapper typeMapper, AbstractSWCommand command, Supplier> tabCompleteSupplier) { + if (!cached.contains(typeMapper)) return tabCompleteSupplier.get(); + Key key = global.contains(typeMapper) ? new Key(null, typeMapper) : new Key(sender, typeMapper); + TabCompletions tabCompletions = tabCompletionCache.computeIfAbsent(key, ignore -> { + return new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get()); + }); + if (tabCompletions.command != command) { + tabCompletions = new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get()); + tabCompletionCache.put(key, tabCompletions); + } + tabCompletions.timestamp = System.currentTimeMillis(); + return tabCompletions.tabCompletions; + } + + public void invalidateOldEntries() { + Set toRemove = new HashSet<>(); + for (Map.Entry tabCompletionsEntry : tabCompletionCache.entrySet()) { + if (System.currentTimeMillis() - tabCompletionsEntry.getValue().timestamp > cacheDuration.get(tabCompletionsEntry.getKey().typeMapper)) { + toRemove.add(tabCompletionsEntry.getKey()); + } + } + for (Key key : toRemove) { + tabCompletionCache.remove(key); + } + } +} From 6bd95ad84c44440f01515e56cacbf44b8ff0aa4a Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 20 Jun 2022 13:26:33 +0200 Subject: [PATCH 26/29] Fix one thing --- src/de/steamwar/command/TabCompletionCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/command/TabCompletionCache.java b/src/de/steamwar/command/TabCompletionCache.java index e746155..aa60cc6 100644 --- a/src/de/steamwar/command/TabCompletionCache.java +++ b/src/de/steamwar/command/TabCompletionCache.java @@ -35,7 +35,7 @@ public class TabCompletionCache { TabCompletions tabCompletions = tabCompletionCache.computeIfAbsent(key, ignore -> { return new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get()); }); - if (tabCompletions.command != command) { + if (tabCompletions.command != command || System.currentTimeMillis() - tabCompletions.timestamp > cacheDuration.get(typeMapper)) { tabCompletions = new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get()); tabCompletionCache.put(key, tabCompletions); } From 75f88d65ac1878a22b68eee8cd1ac7a3d9f4160d Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 20 Jun 2022 14:06:28 +0200 Subject: [PATCH 27/29] Fix stuff Add tests --- src/de/steamwar/command/CommandPart.java | 2 +- src/de/steamwar/command/SWCommandUtils.java | 2 +- testsrc/de/steamwar/command/CacheCommand.java | 38 +++++++++++++++++++ .../de/steamwar/command/CacheCommandTest.java | 28 ++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 testsrc/de/steamwar/command/CacheCommand.java create mode 100644 testsrc/de/steamwar/command/CacheCommandTest.java diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 283dd51..d5a5220 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -188,7 +188,7 @@ class CommandPart { return; } - Collection strings = typeMapper.tabCompletes(sender, Arrays.copyOf(args, startIndex), args[startIndex]); + Collection strings = tabCompletes(sender, args, startIndex); if (strings != null) { current.addAll(strings); } diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 86716d2..9620631 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -313,7 +313,7 @@ public class SWCommandUtils { } static T[] getAnnotation(Method method, Class annotation) { - if (method.getAnnotations().length != 1) return null; + if (method.getAnnotations().length == 0) return null; return method.getDeclaredAnnotationsByType(annotation); } } diff --git a/testsrc/de/steamwar/command/CacheCommand.java b/testsrc/de/steamwar/command/CacheCommand.java new file mode 100644 index 0000000..870d56d --- /dev/null +++ b/testsrc/de/steamwar/command/CacheCommand.java @@ -0,0 +1,38 @@ +package de.steamwar.command; + +import de.steamwar.command.dto.TestSWCommand; +import de.steamwar.command.dto.TestTypeMapper; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; + +public class CacheCommand extends TestSWCommand { + + public CacheCommand() { + super("typemapper"); + } + + @Register + public void test(String sender, int tabCompleteTest) { + } + + private AtomicInteger count = new AtomicInteger(); + + @Cached + @Mapper(value = "int", local = true) + public AbstractTypeMapper typeMapper() { + System.out.println("TypeMapper register"); + return new TestTypeMapper() { + @Override + public Integer map(String sender, String[] previousArguments, String s) { + return Integer.parseInt(s); + } + + @Override + public Collection tabCompletes(String sender, String[] previousArguments, String s) { + return Arrays.asList(count.getAndIncrement() + ""); + } + }; + } +} diff --git a/testsrc/de/steamwar/command/CacheCommandTest.java b/testsrc/de/steamwar/command/CacheCommandTest.java new file mode 100644 index 0000000..5152d0b --- /dev/null +++ b/testsrc/de/steamwar/command/CacheCommandTest.java @@ -0,0 +1,28 @@ +package de.steamwar.command; + +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +public class CacheCommandTest { + + @Test + public void testCaching() { + CacheCommand cmd = new CacheCommand(); + List tabCompletions1 = cmd.tabComplete("test", "", new String[]{""}); + List tabCompletions2 = cmd.tabComplete("test", "", new String[]{""}); + assertThat(tabCompletions1, is(equalTo(tabCompletions2))); + } + + @Test + public void testCachingWithDifferentSenders() { + CacheCommand cmd = new CacheCommand(); + List tabCompletions1 = cmd.tabComplete("test", "", new String[]{""}); + List tabCompletions2 = cmd.tabComplete("test2", "", new String[]{""}); + assertThat(tabCompletions1, is(not(equalTo(tabCompletions2)))); + } +} From 96953bdec40be6e8cc478e2eba0cd45d840fbd48 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 20 Jun 2022 19:35:02 +0200 Subject: [PATCH 28/29] Update stuff --- src/de/steamwar/command/AbstractSWCommand.java | 12 ++---------- src/de/steamwar/command/TabCompletionCache.java | 8 ++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index ad249cb..1de5785 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -111,7 +111,7 @@ public abstract class AbstractSWCommand { for (Method method : methods) { Cached cached = method.getAnnotation(Cached.class); addMapper(Mapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> { - addToCache(cached, typeMapper); + TabCompletionCache.add(typeMapper, cached); if (anno.local()) { localTypeMapper.putIfAbsent(anno.value(), (AbstractTypeMapper) typeMapper); } else { @@ -119,7 +119,7 @@ public abstract class AbstractSWCommand { } }); addMapper(ClassMapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> { - addToCache(cached, typeMapper); + TabCompletionCache.add(typeMapper, cached); if (anno.local()) { localTypeMapper.putIfAbsent(anno.value().getTypeName(), (AbstractTypeMapper) typeMapper); } else { @@ -213,14 +213,6 @@ public abstract class AbstractSWCommand { initialized = true; } - private void addToCache(Cached cached, AbstractTypeMapper typeMapper) { - if (cached != null) { - TabCompletionCache.cached.add(typeMapper); - if (cached.global()) TabCompletionCache.global.add(typeMapper); - TabCompletionCache.cacheDuration.put(typeMapper, cached.timeUnit().toMillis(cached.cacheDuration())); - } - } - private void add(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer consumer) { T[] anno = SWCommandUtils.getAnnotation(method, annotation); if (anno == null || anno.length == 0) return; diff --git a/src/de/steamwar/command/TabCompletionCache.java b/src/de/steamwar/command/TabCompletionCache.java index aa60cc6..258b253 100644 --- a/src/de/steamwar/command/TabCompletionCache.java +++ b/src/de/steamwar/command/TabCompletionCache.java @@ -15,6 +15,14 @@ public class TabCompletionCache { Set> global = new HashSet<>(); Map, Long> cacheDuration = new HashMap<>(); + void add(AbstractTypeMapper typeMapper, AbstractSWCommand.Cached cached) { + if (cached != null) { + TabCompletionCache.cached.add(typeMapper); + if (cached.global()) TabCompletionCache.global.add(typeMapper); + TabCompletionCache.cacheDuration.put(typeMapper, cached.timeUnit().toMillis(cached.cacheDuration())); + } + } + @EqualsAndHashCode @AllArgsConstructor private static class Key { From c06fad6593e64c64e6f74f26185cd78353abffc0 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 20 Jun 2022 21:20:42 +0200 Subject: [PATCH 29/29] Fixed copyright headers --- .../steamwar/command/TabCompletionCache.java | 19 ++++++++++++++++++ testsrc/de/steamwar/command/CacheCommand.java | 19 ++++++++++++++++++ .../de/steamwar/command/CacheCommandTest.java | 20 ++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/command/TabCompletionCache.java b/src/de/steamwar/command/TabCompletionCache.java index 258b253..5cf555c 100644 --- a/src/de/steamwar/command/TabCompletionCache.java +++ b/src/de/steamwar/command/TabCompletionCache.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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 lombok.AllArgsConstructor; diff --git a/testsrc/de/steamwar/command/CacheCommand.java b/testsrc/de/steamwar/command/CacheCommand.java index 870d56d..1771bca 100644 --- a/testsrc/de/steamwar/command/CacheCommand.java +++ b/testsrc/de/steamwar/command/CacheCommand.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.TestSWCommand; diff --git a/testsrc/de/steamwar/command/CacheCommandTest.java b/testsrc/de/steamwar/command/CacheCommandTest.java index 5152d0b..122528f 100644 --- a/testsrc/de/steamwar/command/CacheCommandTest.java +++ b/testsrc/de/steamwar/command/CacheCommandTest.java @@ -1,6 +1,24 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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 org.hamcrest.Matchers; import org.junit.Test; import java.util.List;