From 40eaa180c65625cde8abd1e208995500bf6a2302 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 21 Apr 2022 22:43:14 +0200 Subject: [PATCH 1/8] Add CommandFramework (needs Message System for completion)? --- .../command/AbstractGuardChecker.java | 9 + .../steamwar/command/AbstractSWCommand.java | 304 ++++++++++++++++++ .../steamwar/command/AbstractTypeMapper.java | 12 + .../command/CommandFrameworkException.java | 76 +++++ .../command/CommandNoHelpException.java | 25 ++ .../command/CommandParseException.java | 26 ++ src/de/steamwar/command/CommandPart.java | 196 +++++++++++ src/de/steamwar/command/GuardCheckType.java | 26 ++ src/de/steamwar/command/GuardResult.java | 26 ++ src/de/steamwar/command/SWCommandUtils.java | 241 ++++++++++++++ src/de/steamwar/command/SubCommand.java | 99 ++++++ 11 files changed, 1040 insertions(+) create mode 100644 src/de/steamwar/command/AbstractGuardChecker.java create mode 100644 src/de/steamwar/command/AbstractSWCommand.java create mode 100644 src/de/steamwar/command/AbstractTypeMapper.java create mode 100644 src/de/steamwar/command/CommandFrameworkException.java create mode 100644 src/de/steamwar/command/CommandNoHelpException.java create mode 100644 src/de/steamwar/command/CommandParseException.java create mode 100644 src/de/steamwar/command/CommandPart.java create mode 100644 src/de/steamwar/command/GuardCheckType.java create mode 100644 src/de/steamwar/command/GuardResult.java create mode 100644 src/de/steamwar/command/SWCommandUtils.java create mode 100644 src/de/steamwar/command/SubCommand.java diff --git a/src/de/steamwar/command/AbstractGuardChecker.java b/src/de/steamwar/command/AbstractGuardChecker.java new file mode 100644 index 0000000..8d1a269 --- /dev/null +++ b/src/de/steamwar/command/AbstractGuardChecker.java @@ -0,0 +1,9 @@ +package de.steamwar.command; + +@FunctionalInterface +public interface AbstractGuardChecker { + /** + * While guarding the first parameter of the command the parameter s of this method is {@code null} + */ + GuardResult guard(T t, GuardCheckType guardCheckType, String[] previousArguments, String s); +} diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java new file mode 100644 index 0000000..b13c745 --- /dev/null +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -0,0 +1,304 @@ +package de.steamwar.command; + +import java.lang.annotation.*; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.IntPredicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public abstract class AbstractSWCommand { + + private Class clazz; // This is used in createMappings() + + private boolean initialized = false; + protected final List> commandList = new ArrayList<>(); + protected final List> commandHelpList = new ArrayList<>(); + + private final Map> localTypeMapper = new HashMap<>(); + private final Map> localGuardChecker = new HashMap<>(); + + protected AbstractSWCommand(Class clazz, String command) { + this(clazz, command, new String[0]); + } + + protected AbstractSWCommand(Class clazz, String command, String[] aliases) { + this.clazz = clazz; + createAndSafeCommand(command, aliases); + unregister(); + register(); + } + + protected abstract void createAndSafeCommand(String command, String[] aliases); + + public abstract void unregister(); + + public abstract void register(); + + protected void commandSystemError(T sender, CommandFrameworkException e) { + e.printStackTrace(); + } + + protected void commandSystemWarning(Supplier message) { + System.out.println(message.get()); + } + + protected final void execute(T sender, String alias, String[] args) { + initialize(); + try { + if (!commandList.stream().anyMatch(s -> s.invoke(sender, alias, args))) { + commandHelpList.stream().anyMatch(s -> s.invoke(sender, alias, args)); + } + } catch (CommandNoHelpException e) { + // Ignored + } catch (CommandFrameworkException e) { + commandSystemError(sender, e); + throw e; + } + } + + protected final List tabComplete(T sender, String alias, String[] args) throws IllegalArgumentException { + initialize(); + String string = args[args.length - 1].toLowerCase(); + return commandList.stream() + .filter(s -> !s.noTabComplete) + .map(s -> s.tabComplete(sender, args)) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .filter(s -> !s.isEmpty()) + .filter(s -> s.toLowerCase().startsWith(string)) + .collect(Collectors.toList()); + } + + private void initialize() { + if (initialized) return; + createMapping(); + } + + private synchronized void createMapping() { + List methods = methods(); + for (Method method : methods) { + addMapper(Mapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> { + if (anno.local()) { + localTypeMapper.putIfAbsent(anno.value(), (AbstractTypeMapper) typeMapper); + } else { + SWCommandUtils.getMAPPER_FUNCTIONS().putIfAbsent(anno.value(), typeMapper); + } + }); + addMapper(ClassMapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> { + if (anno.local()) { + localTypeMapper.putIfAbsent(anno.value().getTypeName(), (AbstractTypeMapper) typeMapper); + } else { + SWCommandUtils.getMAPPER_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), typeMapper); + } + }); + addGuard(Guard.class, method, i -> i == 0, false, AbstractGuardChecker.class, (anno, guardChecker) -> { + if (anno.local()) { + localGuardChecker.putIfAbsent(anno.value(), (AbstractGuardChecker) guardChecker); + } else { + SWCommandUtils.getGUARD_FUNCTIONS().putIfAbsent(anno.value(), guardChecker); + } + }); + addGuard(ClassGuard.class, method, i -> i == 0, false, AbstractGuardChecker.class, (anno, guardChecker) -> { + if (anno.local()) { + localGuardChecker.putIfAbsent(anno.value().getTypeName(), (AbstractGuardChecker) guardChecker); + } else { + SWCommandUtils.getGUARD_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), guardChecker); + } + }); + + add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> { + if (!anno.help()) return; + if (parameters.length != 2) { + commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking parameters or has too many"); + } + if (!parameters[parameters.length - 1].isVarArgs()) { + commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the varArgs parameters as last Argument"); + } + if (parameters[parameters.length - 1].getType().getComponentType() != String.class) { + 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())); + }); + } + for (Method method : methods) { + add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> { + if (anno.help()) return; + for (int i = 1; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + Class clazz = parameter.getType(); + if (parameter.isVarArgs() && i == parameters.length - 1) { + clazz = parameter.getType().getComponentType(); + } + Mapper mapper = parameter.getAnnotation(Mapper.class); + if (clazz.isEnum() && mapper == null && !SWCommandUtils.getMAPPER_FUNCTIONS().containsKey(clazz.getTypeName())) { + continue; + } + String name = mapper != null ? mapper.value() : clazz.getTypeName(); + if (!SWCommandUtils.getMAPPER_FUNCTIONS().containsKey(name) && !localTypeMapper.containsKey(name)) { + commandSystemWarning(() -> "The parameter '" + parameter.toString() + "' is using an unsupported Mapper of type '" + name + "'"); + return; + } + } + commandList.add(new SubCommand(this, method, anno.value(), localTypeMapper, localGuardChecker, false, anno.description(), anno.noTabComplete())); + }); + + this.commandList.sort((o1, o2) -> { + int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length); + if (compare != 0) { + return compare; + } else { + return Integer.compare(o1.comparableValue, o2.comparableValue); + } + }); + commandHelpList.sort((o1, o2) -> { + int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length); + if (compare != 0) { + return compare; + } else { + return Integer.compare(o1.method.getDeclaringClass() == AbstractSWCommand.class ? 1 : 0, + o2.method.getDeclaringClass() == AbstractSWCommand.class ? 1 : 0); + } + }); + } + initialized = true; + } + + 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; + + Parameter[] parameters = method.getParameters(); + if (!parameterTester.test(parameters.length)) { + commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking parameters or has too many"); + return; + } + if (firstParameter && !clazz.isAssignableFrom(parameters[0].getType())) { + commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the first parameter of type '" + clazz.getTypeName() + "'"); + return; + } + if (returnType != null && method.getReturnType() != returnType) { + commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the desired return type '" + returnType.getTypeName() + "'"); + return; + } + Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters)); + } + + private void addMapper(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, (AbstractTypeMapper) method.invoke(this)); + } catch (Exception e) { + throw new SecurityException(e.getMessage(), e); + } + }); + } + + 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, (AbstractGuardChecker) 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) { + defaultHelpMessages.add(message); + } + */ + + private List methods() { + List methods = new ArrayList<>(); + Class current = getClass(); + while (current.getSuperclass() != AbstractSWCommand.class) { + methods.addAll(Arrays.asList(current.getDeclaredMethods())); + current = current.getSuperclass(); + } + return methods; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + @Repeatable(Register.Registeres.class) + protected @interface Register { + String[] value() default {}; + + boolean help() default false; + + String[] description() default {}; + + boolean noTabComplete() default false; + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + @interface Registeres { + Register[] value(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER, ElementType.METHOD}) + protected @interface Mapper { + String value(); + + boolean local() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + protected @interface ClassMapper { + Class value(); + + boolean local() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER, ElementType.METHOD}) + protected @interface Guard { + String value() default ""; + + boolean local() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + protected @interface ClassGuard { + Class value(); + + boolean local() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + protected @interface StaticValue { + String[] value(); + + /** + * This is the short form for 'allowImplicitSwitchExpressions' + * and can be set to true if you want to allow int as well as boolean as annotated parameter types. + * The value array needs to be at least 2 long for this flag to be considered. + * While using an int, the value will represent the index into the value array. + * While using a boolean, the value array must only be 2 long and the value will be {@code false} + * for the first index and {@code true} for the second index. + */ + boolean allowISE() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + protected @interface OptionalValue { + /** + * Will pe parsed against the TypeMapper specified by the parameter or annotation. + */ + String value(); + } +} diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java new file mode 100644 index 0000000..e5735fe --- /dev/null +++ b/src/de/steamwar/command/AbstractTypeMapper.java @@ -0,0 +1,12 @@ +package de.steamwar.command; + +import java.util.List; + +public interface AbstractTypeMapper { + /** + * The CommandSender can be null! + */ + T map(K sender, String[] previousArguments, String s); + + List tabCompletes(K sender, String[] previousArguments, String s); +} diff --git a/src/de/steamwar/command/CommandFrameworkException.java b/src/de/steamwar/command/CommandFrameworkException.java new file mode 100644 index 0000000..74db6d3 --- /dev/null +++ b/src/de/steamwar/command/CommandFrameworkException.java @@ -0,0 +1,76 @@ +/* + * 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 java.io.PrintStream; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; + +public class CommandFrameworkException extends RuntimeException { + + private InvocationTargetException invocationTargetException; + private String alias; + private String[] args; + + private String message; + + CommandFrameworkException(InvocationTargetException invocationTargetException, String alias, String[] args) { + super(invocationTargetException); + this.invocationTargetException = invocationTargetException; + this.alias = alias; + this.args = args; + } + + public synchronized String getBuildStackTrace() { + if (message != null) { + return message; + } + StackTraceElement[] stackTraceElements = invocationTargetException.getCause().getStackTrace(); + StringBuilder st = new StringBuilder(); + st.append(invocationTargetException.getCause().getClass().getTypeName()); + if (invocationTargetException.getCause().getMessage() != null) { + st.append(": ").append(invocationTargetException.getCause().getMessage()); + } + st.append("\n"); + if (alias != null && !alias.isEmpty()) { + st.append("Performed command: ").append(alias).append(" ").append(String.join(" ", args)).append("\n"); + } + for (int i = 0; i < stackTraceElements.length - invocationTargetException.getStackTrace().length; i++) { + st.append("\tat ").append(stackTraceElements[i].toString()).append("\n"); + } + message = st.toString(); + return message; + } + + @Override + public void printStackTrace() { + printStackTrace(System.err); + } + + @Override + public void printStackTrace(PrintStream s) { + s.print(getBuildStackTrace()); + } + + @Override + public void printStackTrace(PrintWriter s) { + s.print(getBuildStackTrace()); + } +} diff --git a/src/de/steamwar/command/CommandNoHelpException.java b/src/de/steamwar/command/CommandNoHelpException.java new file mode 100644 index 0000000..e3d476a --- /dev/null +++ b/src/de/steamwar/command/CommandNoHelpException.java @@ -0,0 +1,25 @@ +/* + * 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; + +class CommandNoHelpException extends RuntimeException { + + CommandNoHelpException() {} +} diff --git a/src/de/steamwar/command/CommandParseException.java b/src/de/steamwar/command/CommandParseException.java new file mode 100644 index 0000000..3d81ea6 --- /dev/null +++ b/src/de/steamwar/command/CommandParseException.java @@ -0,0 +1,26 @@ +/* + * 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; + +public class CommandParseException extends RuntimeException { + + public CommandParseException() { + } +} diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java new file mode 100644 index 0000000..6a8ff4b --- /dev/null +++ b/src/de/steamwar/command/CommandPart.java @@ -0,0 +1,196 @@ +package de.steamwar.command; + +import lombok.AllArgsConstructor; +import lombok.Setter; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.List; + +class CommandPart { + + private static final String[] EMPTY_ARRAY = new String[0]; + + @AllArgsConstructor + private static class CheckArgumentResult { + private final boolean success; + private final Object value; + } + + private AbstractTypeMapper typeMapper; + private AbstractGuardChecker guardChecker; + private Class varArgType; + private String optional; + private GuardCheckType guardCheckType; + + private CommandPart next = null; + + @Setter + private boolean ignoreAsArgument = false; + + public CommandPart(AbstractTypeMapper typeMapper, AbstractGuardChecker guardChecker, Class varArgType, String optional, GuardCheckType guardCheckType) { + this.typeMapper = typeMapper; + this.guardChecker = guardChecker; + this.varArgType = varArgType; + this.optional = optional; + this.guardCheckType = guardCheckType; + + validatePart(); + } + + public void setNext(CommandPart next) { + if (varArgType != null) { + throw new IllegalArgumentException("There can't be a next part if this is a vararg part!"); + } + this.next = next; + } + + private void validatePart() { + if (guardCheckType == GuardCheckType.TAB_COMPLETE) { + throw new IllegalArgumentException("Tab complete is not allowed as a guard check type!"); + } + if (optional != null && varArgType != null) { + throw new IllegalArgumentException("A vararg part can't have an optional part!"); + } + + if (optional != null) { + try { + typeMapper.map(null, EMPTY_ARRAY, optional); + } catch (Exception e) { + throw new IllegalArgumentException("The optional part is not valid!"); + } + } + } + + public void generateArgumentArray(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); + if (!validArgument.success) { + throw new CommandParseException(); + } + Array.set(array, i - startIndex, validArgument.value); + } + current.add(array); + return; + } + + CheckArgumentResult validArgument = checkArgument(null, sender, args, startIndex); + if (!validArgument.success && optional == null) { + throw new CommandParseException(); + } + if (!validArgument.success) { + if (!ignoreAsArgument) { + current.add(typeMapper.map(sender, EMPTY_ARRAY, optional)); + } + if (next != null) { + next.generateArgumentArray(current, sender, args, startIndex); + } + return; + } + if (!ignoreAsArgument) { + current.add(validArgument.value); + } + if (next != null) { + next.generateArgumentArray(current, sender, args, startIndex + 1); + } + } + + public boolean guardCheck(T sender, String[] args, int startIndex) { + if (varArgType != null) { + for (int i = startIndex; i < args.length; i++) { + GuardResult guardResult = checkGuard(guardCheckType, sender, args, i); + if (guardResult == GuardResult.DENIED) { + throw new CommandNoHelpException(); + } + if (guardResult == GuardResult.DENIED_WITH_HELP) { + return false; + } + } + return true; + } + + GuardResult guardResult = checkGuard(guardCheckType, sender, args, startIndex); + if (guardResult == GuardResult.DENIED) { + if (optional != null && next != null) { + return next.guardCheck(sender, args, startIndex); + } + throw new CommandNoHelpException(); + } + if (guardResult == GuardResult.DENIED_WITH_HELP) { + if (optional != null && next != null) { + return next.guardCheck(sender, args, startIndex); + } + return false; + } + if (next != null) { + return next.guardCheck(sender, args, startIndex + 1); + } + return true; + } + + 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(null, sender, args, i); + if (!validArgument.success) { + return; + } + } + List strings = typeMapper.tabCompletes(sender, Arrays.copyOf(args, args.length - 1), args[args.length - 1]); + if (strings != null) { + current.addAll(strings); + } + return; + } + + if (args.length - 1 > startIndex) { + CheckArgumentResult checkArgumentResult = checkArgument(GuardCheckType.TAB_COMPLETE, sender, args, startIndex); + if (checkArgumentResult.success && next != null) { + next.generateTabComplete(current, sender, args, startIndex + 1); + return; + } + if (optional != null && next != null) { + next.generateTabComplete(current, sender, args, startIndex); + } + return; + } + + List strings = typeMapper.tabCompletes(sender, Arrays.copyOf(args, startIndex), args[startIndex]); + if (strings != null) { + current.addAll(strings); + } + if (optional != null && next != null) { + next.generateTabComplete(current, sender, args, startIndex); + } + } + + private CheckArgumentResult checkArgument(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); + } + GuardResult guardResult = checkGuard(guardCheckType, sender, args, index); + switch (guardResult) { + case ALLOWED: + return new CheckArgumentResult(true, value); + case DENIED: + throw new CommandNoHelpException(); + case DENIED_WITH_HELP: + default: + return new CheckArgumentResult(false, null); + } + } catch (Exception e) { + return new CheckArgumentResult(false, null); + } + } + + private GuardResult checkGuard(GuardCheckType guardCheckType, T sender, String[] args, int index) { + if (guardChecker != null && guardCheckType != null) { + return guardChecker.guard(sender, guardCheckType, Arrays.copyOf(args, index), args[index]); + } + return GuardResult.ALLOWED; + } +} diff --git a/src/de/steamwar/command/GuardCheckType.java b/src/de/steamwar/command/GuardCheckType.java new file mode 100644 index 0000000..0f023b8 --- /dev/null +++ b/src/de/steamwar/command/GuardCheckType.java @@ -0,0 +1,26 @@ +/* + * 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; + +public enum GuardCheckType { + COMMAND, + HELP_COMMAND, + TAB_COMPLETE +} diff --git a/src/de/steamwar/command/GuardResult.java b/src/de/steamwar/command/GuardResult.java new file mode 100644 index 0000000..9ffbf77 --- /dev/null +++ b/src/de/steamwar/command/GuardResult.java @@ -0,0 +1,26 @@ +/* + * 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; + +public enum GuardResult { + ALLOWED, + DENIED_WITH_HELP, + DENIED +} diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java new file mode 100644 index 0000000..52e1d75 --- /dev/null +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -0,0 +1,241 @@ +package de.steamwar.command; + +import lombok.Getter; +import lombok.experimental.UtilityClass; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +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 { + + @Getter + private final Map> MAPPER_FUNCTIONS = new HashMap<>(); + + @Getter + private final Map> GUARD_FUNCTIONS = new HashMap<>(); + + static { + addMapper(boolean.class, Boolean.class, createMapper(Boolean::parseBoolean, s -> Arrays.asList("true", "false"))); + addMapper(float.class, Float.class, createMapper(numberMapper(Float::parseFloat), numberCompleter(Float::parseFloat))); + addMapper(double.class, Double.class, createMapper(numberMapper(Double::parseDouble), numberCompleter(Double::parseDouble))); + addMapper(int.class, Integer.class, createMapper(numberMapper(Integer::parseInt), numberCompleter(Integer::parseInt))); + addMapper(long.class, Long.class, createMapper(numberMapper(Long::parseLong), numberCompleter(Long::parseLong))); + MAPPER_FUNCTIONS.put(String.class.getTypeName(), createMapper(s -> s, Collections::singletonList)); + } + + private static void addMapper(Class clazz, Class alternativeClazz, AbstractTypeMapper mapper) { + MAPPER_FUNCTIONS.put(clazz.getTypeName(), mapper); + MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper); + } + + static CommandPart generateCommandPart(boolean help, String[] subCommand, Parameter[] parameters, Map> localTypeMapper, 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.setIgnoreAsArgument(true); + if (current != null) { + current.setNext(commandPart); + } + current = commandPart; + if (first == null) { + first = current; + } + } + for (int i = 1; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + AbstractTypeMapper typeMapper = getTypeMapper(parameter, localTypeMapper); + 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); + if (current != null) { + current.setNext(commandPart); + } + current = commandPart; + if (first == null) { + first = current; + } + } + return first; + } + + public static AbstractTypeMapper getTypeMapper(Parameter parameter, Map> localTypeMapper) { + Class clazz = parameter.getType(); + if (parameter.isVarArgs()) { + clazz = clazz.getComponentType(); + } + + 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); + } + + String name = clazz.getTypeName(); + if (classMapper != null) { + name = classMapper.value().getTypeName(); + } else if (mapper != null) { + name = mapper.value(); + } else { + AbstractSWCommand.StaticValue staticValue = parameter.getAnnotation(AbstractSWCommand.StaticValue.class); + if (staticValue != null) { + if (parameter.getType() == String.class) { + return createMapper(staticValue.value()); + } + if (staticValue.allowISE()) { + if ((parameter.getType() == boolean.class || parameter.getType() == Boolean.class) && staticValue.value().length == 2) { + List tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value())); + return createMapper(s -> { + int index = tabCompletes.indexOf(s); + return index == -1 ? null : index != 0; + }, (commandSender, s) -> tabCompletes); + } + if ((parameter.getType() == int.class || parameter.getType() == Integer.class) && staticValue.value().length >= 2) { + List tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value())); + return createMapper(s -> { + int index = tabCompletes.indexOf(s); + return index == -1 ? null : index; + }, (commandSender, s) -> tabCompletes); + } + if ((parameter.getType() == long.class || parameter.getType() == Long.class) && staticValue.value().length >= 2) { + List tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value())); + return createMapper(s -> { + long index = tabCompletes.indexOf(s); + return index == -1 ? null : index; + }, (commandSender, s) -> tabCompletes); + } + } + } + } + AbstractTypeMapper typeMapper = localTypeMapper.getOrDefault(name, (AbstractTypeMapper) MAPPER_FUNCTIONS.getOrDefault(name, null)); + if (typeMapper == null) { + throw new IllegalArgumentException("No mapper found for " + name); + } + return typeMapper; + } + + public static AbstractGuardChecker getGuardChecker(Parameter parameter, Map> localGuardChecker) { + Class clazz = parameter.getType(); + if (parameter.isVarArgs()) { + clazz = clazz.getComponentType(); + } + + AbstractSWCommand.ClassGuard classGuard = parameter.getAnnotation(AbstractSWCommand.ClassGuard.class); + if (classGuard != null) { + if (classGuard.value() != null) { + return getGuardChecker(classGuard.value().getTypeName(), localGuardChecker); + } + return getGuardChecker(clazz.getTypeName(), localGuardChecker); + } + + AbstractSWCommand.Guard guard = parameter.getAnnotation(AbstractSWCommand.Guard.class); + if (guard != null) { + if (guard.value() != null && !guard.value().isEmpty()) { + return getGuardChecker(guard.value(), localGuardChecker); + } + return getGuardChecker(clazz.getTypeName(), localGuardChecker); + } + return null; + } + + private static AbstractGuardChecker getGuardChecker(String s, Map> localGuardChecker) { + AbstractGuardChecker guardChecker = localGuardChecker.getOrDefault(s, (AbstractGuardChecker) GUARD_FUNCTIONS.getOrDefault(s, null)); + if (guardChecker == null) { + throw new IllegalArgumentException("No guard found for " + s); + } + return guardChecker; + } + + public static void addMapper(Class clazz, AbstractTypeMapper mapper) { + addMapper(clazz.getTypeName(), mapper); + } + + public static void addMapper(String name, AbstractTypeMapper mapper) { + MAPPER_FUNCTIONS.putIfAbsent(name, mapper); + } + + public static void addGuard(Class clazz, AbstractGuardChecker guardChecker) { + addGuard(clazz.getTypeName(), guardChecker); + } + + public static void addGuard(String name, AbstractGuardChecker guardChecker) { + GUARD_FUNCTIONS.putIfAbsent(name, guardChecker); + } + + public static AbstractTypeMapper createMapper(String... values) { + List strings = Arrays.asList(values); + return createMapper((s) -> strings.contains(s) ? s : null, s -> strings); + } + + public static AbstractTypeMapper createMapper(Function mapper, Function> tabCompleter) { + return createMapper(mapper, (commandSender, s) -> tabCompleter.apply(s)); + } + + public static AbstractTypeMapper createMapper(Function mapper, BiFunction> tabCompleter) { + return new AbstractTypeMapper() { + @Override + public T 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 AbstractTypeMapper> createEnumMapper(Class> enumClass) { + Enum[] enums = enumClass.getEnumConstants(); + List strings = Arrays.stream(enums).map(Enum::name).map(String::toLowerCase).collect(Collectors.toList()); + return new AbstractTypeMapper>() { + @Override + public Enum map(Object commandSender, String[] previousArguments, String s) { + for (Enum e : enums) { + if (e.name().equalsIgnoreCase(s)) return e; + } + return null; + } + + @Override + public List tabCompletes(Object commandSender, String[] previousArguments, String s) { + return strings; + } + }; + } + + private static Function numberMapper(Function mapper) { + return s -> { + if (s.equalsIgnoreCase("nan")) return null; + try { + return mapper.apply(s); + } catch (NumberFormatException e) { + // Ignored + } + try { + return mapper.apply(s.replace(',', '.')); + } catch (NumberFormatException e) { + return null; + } + }; + } + + private static Function> numberCompleter(Function mapper) { + return s -> numberMapper(mapper).apply(s) != null + ? Collections.singletonList(s) + : Collections.emptyList(); + } + + static T[] getAnnotation(Method method, Class annotation) { + if (method.getAnnotations().length != 1) return null; + return method.getDeclaredAnnotationsByType(annotation); + } +} diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java new file mode 100644 index 0000000..4b9a4a6 --- /dev/null +++ b/src/de/steamwar/command/SubCommand.java @@ -0,0 +1,99 @@ +package de.steamwar.command; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; + +public class SubCommand { + + private AbstractSWCommand abstractSWCommand; + Method method; + String[] description; + String[] subCommand; + private Predicate senderPredicate; + private Function senderFunction; + 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) { + this.abstractSWCommand = abstractSWCommand; + this.method = method; + this.subCommand = subCommand; + this.description = description; + this.noTabComplete = noTabComplete; + + Parameter[] parameters = method.getParameters(); + comparableValue = parameters[parameters.length - 1].isVarArgs() ? Integer.MAX_VALUE : -parameters.length; + + guardChecker = SWCommandUtils.getGuardChecker(parameters[0], localGuardChecker); + + commandPart = SWCommandUtils.generateCommandPart(help, subCommand, parameters, localTypeMapper, localGuardChecker); + + senderPredicate = t -> parameters[0].getType().isAssignableFrom(t.getClass()); + senderFunction = t -> parameters[0].getType().cast(t); + } + + boolean invoke(T sender, String alias, String[] args) { + try { + if (!senderPredicate.test(sender)) { + return false; + } + + if (commandPart == null) { + 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) { + 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.guardCheck(sender, args, 0); + objects.add(0, senderFunction.apply(sender)); + method.setAccessible(true); + method.invoke(abstractSWCommand, objects.toArray()); + } + } catch (CommandNoHelpException e) { + throw e; + } catch (CommandParseException e) { + return false; + } catch (InvocationTargetException e) { + throw new CommandFrameworkException(e, alias, args); + } catch (IllegalAccessException | RuntimeException e) { + throw new SecurityException(e.getMessage(), e); + } + return true; + } + + List tabComplete(T sender, String[] args) { + if (guardChecker != null && guardChecker.guard(sender, GuardCheckType.TAB_COMPLETE, new String[0], null) != GuardResult.ALLOWED) { + return null; + } + if (commandPart == null) { + return null; + } + List list = new ArrayList<>(); + commandPart.generateTabComplete(list, sender, args, 0); + return list; + } +} -- 2.39.2 From 598e8b406172bd7ac2f942b8525c74e573ce573c Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 21 Apr 2022 22:58:45 +0200 Subject: [PATCH 2/8] Fix return type for guard methods and typemapper methods --- src/de/steamwar/command/AbstractSWCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index b13c745..da80f23 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -180,7 +180,7 @@ public abstract class AbstractSWCommand { commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the first parameter of type '" + clazz.getTypeName() + "'"); return; } - if (returnType != null && method.getReturnType() != returnType) { + if (returnType != null && !method.getReturnType().isAssignableFrom(returnType)) { commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the desired return type '" + returnType.getTypeName() + "'"); return; } -- 2.39.2 From af7cd5193711784448ae6e4574e11ac920814ca4 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Fri, 22 Apr 2022 10:16:25 +0200 Subject: [PATCH 3/8] Add some simple tests. More to come --- build.gradle | 13 +++ src/de/steamwar/command/SWCommandUtils.java | 6 +- src/de/steamwar/command/SubCommand.java | 1 + .../de/steamwar/command/ArgumentCommand.java | 41 +++++++++ .../steamwar/command/ArgumentCommandTest.java | 87 +++++++++++++++++++ .../de/steamwar/command/SimpleCommand.java | 16 ++++ .../steamwar/command/SimpleCommandTest.java | 32 +++++++ .../command/dto/ExecutionIdentifier.java | 7 ++ .../command/dto/TestGuardChecker.java | 6 ++ .../steamwar/command/dto/TestSWCommand.java | 40 +++++++++ .../steamwar/command/dto/TestTypeChecker.java | 6 ++ 11 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 testsrc/de/steamwar/command/ArgumentCommand.java create mode 100644 testsrc/de/steamwar/command/ArgumentCommandTest.java create mode 100644 testsrc/de/steamwar/command/SimpleCommand.java create mode 100644 testsrc/de/steamwar/command/SimpleCommandTest.java create mode 100644 testsrc/de/steamwar/command/dto/ExecutionIdentifier.java create mode 100644 testsrc/de/steamwar/command/dto/TestGuardChecker.java create mode 100644 testsrc/de/steamwar/command/dto/TestSWCommand.java create mode 100644 testsrc/de/steamwar/command/dto/TestTypeChecker.java diff --git a/build.gradle b/build.gradle index 7397719..b625ffd 100644 --- a/build.gradle +++ b/build.gradle @@ -62,6 +62,16 @@ sourceSets { exclude '**/*.java', '**/*.kt' } } + + test { + java { + srcDirs = ['testsrc'] + } + resources { + srcDirs = ['testsrc'] + exclude '**/*.java', '**/*.kt' + } + } } repositories { @@ -73,6 +83,9 @@ dependencies { testCompileOnly 'org.projectlombok:lombok:1.18.22' annotationProcessor 'org.projectlombok:lombok:1.18.22' testAnnotationProcessor 'org.projectlombok:lombok:1.18.22' + + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.hamcrest:hamcrest:2.2' } task buildProject { diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 52e1d75..4a6088a 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -21,7 +21,11 @@ public class SWCommandUtils { private final Map> GUARD_FUNCTIONS = new HashMap<>(); static { - addMapper(boolean.class, Boolean.class, createMapper(Boolean::parseBoolean, s -> Arrays.asList("true", "false"))); + addMapper(boolean.class, Boolean.class, createMapper(s -> { + if (s.equalsIgnoreCase("true")) return true; + if (s.equalsIgnoreCase("false")) return false; + return null; + }, s -> Arrays.asList("true", "false"))); addMapper(float.class, Float.class, createMapper(numberMapper(Float::parseFloat), numberCompleter(Float::parseFloat))); addMapper(double.class, Double.class, createMapper(numberMapper(Double::parseDouble), numberCompleter(Double::parseDouble))); addMapper(int.class, Integer.class, createMapper(numberMapper(Integer::parseInt), numberCompleter(Integer::parseInt))); diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 4b9a4a6..2f41791 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -4,6 +4,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; diff --git a/testsrc/de/steamwar/command/ArgumentCommand.java b/testsrc/de/steamwar/command/ArgumentCommand.java new file mode 100644 index 0000000..c630a93 --- /dev/null +++ b/testsrc/de/steamwar/command/ArgumentCommand.java @@ -0,0 +1,41 @@ +package de.steamwar.command; + +import de.steamwar.command.dto.ExecutionIdentifier; +import de.steamwar.command.dto.TestSWCommand; + +public class ArgumentCommand extends TestSWCommand { + + public ArgumentCommand() { + super("argument"); + } + + @Register + public void argument(String sender, boolean b, boolean b2) { + throw new ExecutionIdentifier("RunArgument with Boolean"); + } + + @Register + public void argument(String sender, float f, float f2, float f3) { + throw new ExecutionIdentifier("RunArgument with Float"); + } + + @Register + public void argument(String sender, double d, double d2) { + throw new ExecutionIdentifier("RunArgument with Double"); + } + + @Register + public void argument(String sender, int i) { + throw new ExecutionIdentifier("RunArgument with Integer"); + } + + @Register + public void argument(String sender, long l, long l2) { + throw new ExecutionIdentifier("RunArgument with Long"); + } + + @Register + public void argument(String sender, String arg) { + throw new ExecutionIdentifier("RunArgument with String"); + } +} diff --git a/testsrc/de/steamwar/command/ArgumentCommandTest.java b/testsrc/de/steamwar/command/ArgumentCommandTest.java new file mode 100644 index 0000000..d7a24f1 --- /dev/null +++ b/testsrc/de/steamwar/command/ArgumentCommandTest.java @@ -0,0 +1,87 @@ +package de.steamwar.command; + +import de.steamwar.command.dto.ExecutionIdentifier; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +public class ArgumentCommandTest { + + @Test + public void testNoArgs() { + ArgumentCommand cmd = new ArgumentCommand(); + try { + cmd.execute("test", "", new String[0]); + } catch (Exception e) { + throw new AssertionError("No exception expected"); + } + } + + @Test + public void testBoolean() { + ArgumentCommand cmd = new ArgumentCommand(); + try { + cmd.execute("test", "", new String[]{"true", "false"}); + } catch (Exception e) { + assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); + assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Boolean")); + } + } + + @Test + public void testFloat() { + ArgumentCommand cmd = new ArgumentCommand(); + try { + cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0"}); + } catch (Exception e) { + assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); + assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Float")); + } + } + + @Test + public void testDouble() { + ArgumentCommand cmd = new ArgumentCommand(); + try { + cmd.execute("test", "", new String[]{"0.0", "0.0"}); + } catch (Exception e) { + assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); + assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Double")); + } + } + + @Test + public void testInt() { + ArgumentCommand cmd = new ArgumentCommand(); + try { + cmd.execute("test", "", new String[]{"0"}); + } catch (Exception e) { + assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); + assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Integer")); + } + } + + @Test + public void testLong() { + ArgumentCommand cmd = new ArgumentCommand(); + try { + cmd.execute("test", "", new String[]{"0", "0"}); + } catch (Exception e) { + assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); + assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Long")); + } + } + + @Test + public void testString() { + ArgumentCommand cmd = new ArgumentCommand(); + try { + cmd.execute("test", "", new String[]{"Hello World"}); + } catch (Exception e) { + assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); + assertThat(e.getCause().getCause().getMessage(), is("RunArgument with String")); + } + } +} diff --git a/testsrc/de/steamwar/command/SimpleCommand.java b/testsrc/de/steamwar/command/SimpleCommand.java new file mode 100644 index 0000000..88d0bd6 --- /dev/null +++ b/testsrc/de/steamwar/command/SimpleCommand.java @@ -0,0 +1,16 @@ +package de.steamwar.command; + +import de.steamwar.command.dto.ExecutionIdentifier; +import de.steamwar.command.dto.TestSWCommand; + +public class SimpleCommand extends TestSWCommand { + + public SimpleCommand() { + super("simple"); + } + + @Register + public void simple(String s) { + throw new ExecutionIdentifier("RunSimple with noArgs"); + } +} diff --git a/testsrc/de/steamwar/command/SimpleCommandTest.java b/testsrc/de/steamwar/command/SimpleCommandTest.java new file mode 100644 index 0000000..a039e75 --- /dev/null +++ b/testsrc/de/steamwar/command/SimpleCommandTest.java @@ -0,0 +1,32 @@ +package de.steamwar.command; + +import de.steamwar.command.dto.ExecutionIdentifier; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +public class SimpleCommandTest { + + @Test + public void testSimpleParsing() { + SimpleCommand cmd = new SimpleCommand(); + try { + cmd.execute("test", "", new String[0]); + } catch (CommandFrameworkException e) { + assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); + assertThat(e.getCause().getCause().getMessage(), is("RunSimple with noArgs")); + } + } + + @Test + public void testSimpleParsingNoResult() { + SimpleCommand cmd = new SimpleCommand(); + try { + cmd.execute("test", "", new String[]{"Hello"}); + } catch (CommandFrameworkException e) { + throw new AssertionError("No exception expected"); + } + } +} diff --git a/testsrc/de/steamwar/command/dto/ExecutionIdentifier.java b/testsrc/de/steamwar/command/dto/ExecutionIdentifier.java new file mode 100644 index 0000000..bde7122 --- /dev/null +++ b/testsrc/de/steamwar/command/dto/ExecutionIdentifier.java @@ -0,0 +1,7 @@ +package de.steamwar.command.dto; + +public class ExecutionIdentifier extends RuntimeException { + public ExecutionIdentifier(String message) { + super(message); + } +} diff --git a/testsrc/de/steamwar/command/dto/TestGuardChecker.java b/testsrc/de/steamwar/command/dto/TestGuardChecker.java new file mode 100644 index 0000000..ff38ebc --- /dev/null +++ b/testsrc/de/steamwar/command/dto/TestGuardChecker.java @@ -0,0 +1,6 @@ +package de.steamwar.command.dto; + +import de.steamwar.command.AbstractGuardChecker; + +public interface TestGuardChecker extends AbstractGuardChecker { +} diff --git a/testsrc/de/steamwar/command/dto/TestSWCommand.java b/testsrc/de/steamwar/command/dto/TestSWCommand.java new file mode 100644 index 0000000..9081000 --- /dev/null +++ b/testsrc/de/steamwar/command/dto/TestSWCommand.java @@ -0,0 +1,40 @@ +package de.steamwar.command.dto; + +import de.steamwar.command.AbstractSWCommand; +import de.steamwar.command.CommandFrameworkException; + +import java.util.function.Supplier; + +public class TestSWCommand extends AbstractSWCommand { + + protected TestSWCommand(String command) { + super(String.class, command); + } + + protected TestSWCommand(String command, String[] aliases) { + super(String.class, command, aliases); + } + + @Override + protected void createAndSafeCommand(String command, String[] aliases) { + + } + + @Override + public void unregister() { + + } + + @Override + public void register() { + + } + + @Override + protected void commandSystemError(String sender, CommandFrameworkException e) { + } + + @Override + protected void commandSystemWarning(Supplier message) { + } +} diff --git a/testsrc/de/steamwar/command/dto/TestTypeChecker.java b/testsrc/de/steamwar/command/dto/TestTypeChecker.java new file mode 100644 index 0000000..42a7933 --- /dev/null +++ b/testsrc/de/steamwar/command/dto/TestTypeChecker.java @@ -0,0 +1,6 @@ +package de.steamwar.command.dto; + +import de.steamwar.command.AbstractTypeMapper; + +public interface TestTypeChecker extends AbstractTypeMapper { +} -- 2.39.2 From 9022c62281dec823f48786b46fd15ba8007ddd1c Mon Sep 17 00:00:00 2001 From: yoyosource Date: Fri, 22 Apr 2022 12:19:43 +0200 Subject: [PATCH 4/8] Add AssertionUtils --- testsrc/de/steamwar/AssertionUtils.java | 26 +++++++++++++ .../steamwar/command/ArgumentCommandTest.java | 39 ++++++++++++------- .../steamwar/command/SimpleCommandTest.java | 19 ++++++--- 3 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 testsrc/de/steamwar/AssertionUtils.java diff --git a/testsrc/de/steamwar/AssertionUtils.java b/testsrc/de/steamwar/AssertionUtils.java new file mode 100644 index 0000000..58fe01c --- /dev/null +++ b/testsrc/de/steamwar/AssertionUtils.java @@ -0,0 +1,26 @@ +package de.steamwar; + +import de.steamwar.command.CommandFrameworkException; +import lombok.experimental.UtilityClass; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@UtilityClass +public class AssertionUtils { + + public static void assertCMDFramework(Exception e, Class clazz, String message) { + assertThat(e, is(instanceOf(CommandFrameworkException.class))); + assertThat(e.getCause().getCause(), is(instanceOf(clazz))); + assertThat(e.getCause().getCause().getMessage(), is(message)); + } + + public static void assertTabCompletes(List list, T... elements) { + assertThat(list.size(), is(elements.length)); + if (elements.length > 0) { + assertThat(list, containsInAnyOrder(elements)); + } + } +} diff --git a/testsrc/de/steamwar/command/ArgumentCommandTest.java b/testsrc/de/steamwar/command/ArgumentCommandTest.java index d7a24f1..7b74126 100644 --- a/testsrc/de/steamwar/command/ArgumentCommandTest.java +++ b/testsrc/de/steamwar/command/ArgumentCommandTest.java @@ -3,9 +3,10 @@ package de.steamwar.command; import de.steamwar.command.dto.ExecutionIdentifier; import org.junit.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; +import java.util.List; + +import static de.steamwar.AssertionUtils.assertCMDFramework; +import static de.steamwar.AssertionUtils.assertTabCompletes; public class ArgumentCommandTest { @@ -25,8 +26,7 @@ public class ArgumentCommandTest { try { cmd.execute("test", "", new String[]{"true", "false"}); } catch (Exception e) { - assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); - assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Boolean")); + assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Boolean"); } } @@ -36,8 +36,7 @@ public class ArgumentCommandTest { try { cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0"}); } catch (Exception e) { - assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); - assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Float")); + assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Float"); } } @@ -47,8 +46,7 @@ public class ArgumentCommandTest { try { cmd.execute("test", "", new String[]{"0.0", "0.0"}); } catch (Exception e) { - assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); - assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Double")); + assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Double"); } } @@ -58,8 +56,7 @@ public class ArgumentCommandTest { try { cmd.execute("test", "", new String[]{"0"}); } catch (Exception e) { - assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); - assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Integer")); + assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Integer"); } } @@ -69,8 +66,7 @@ public class ArgumentCommandTest { try { cmd.execute("test", "", new String[]{"0", "0"}); } catch (Exception e) { - assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); - assertThat(e.getCause().getCause().getMessage(), is("RunArgument with Long")); + assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Long"); } } @@ -80,8 +76,21 @@ public class ArgumentCommandTest { try { cmd.execute("test", "", new String[]{"Hello World"}); } catch (Exception e) { - assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); - assertThat(e.getCause().getCause().getMessage(), is("RunArgument with String")); + assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String"); } } + + @Test + public void testTabComplete() { + ArgumentCommand cmd = new ArgumentCommand(); + List strings = cmd.tabComplete("test", "", new String[]{""}); + assertTabCompletes(strings, "true", "false"); + } + + @Test + public void testPartialTabComplete() { + ArgumentCommand cmd = new ArgumentCommand(); + List strings = cmd.tabComplete("test", "", new String[]{"t"}); + assertTabCompletes(strings, "true", "t"); + } } diff --git a/testsrc/de/steamwar/command/SimpleCommandTest.java b/testsrc/de/steamwar/command/SimpleCommandTest.java index a039e75..4ba3ef1 100644 --- a/testsrc/de/steamwar/command/SimpleCommandTest.java +++ b/testsrc/de/steamwar/command/SimpleCommandTest.java @@ -3,9 +3,10 @@ package de.steamwar.command; import de.steamwar.command.dto.ExecutionIdentifier; import org.junit.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; +import java.util.List; + +import static de.steamwar.AssertionUtils.assertCMDFramework; +import static de.steamwar.AssertionUtils.assertTabCompletes; public class SimpleCommandTest { @@ -14,9 +15,8 @@ public class SimpleCommandTest { SimpleCommand cmd = new SimpleCommand(); try { cmd.execute("test", "", new String[0]); - } catch (CommandFrameworkException e) { - assertThat(e.getCause().getCause(), is(instanceOf(ExecutionIdentifier.class))); - assertThat(e.getCause().getCause().getMessage(), is("RunSimple with noArgs")); + } catch (Exception e) { + assertCMDFramework(e, ExecutionIdentifier.class, "RunSimple with noArgs"); } } @@ -29,4 +29,11 @@ public class SimpleCommandTest { throw new AssertionError("No exception expected"); } } + + @Test + public void testSimpleTabComplete() { + SimpleCommand cmd = new SimpleCommand(); + List strings = cmd.tabComplete("test", "", new String[]{""}); + assertTabCompletes(strings); + } } -- 2.39.2 From f3363ecedf485d9fcaac1da1f7c26203f22b0ba7 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Tue, 26 Apr 2022 09:04:36 +0200 Subject: [PATCH 5/8] Add more tests --- testsrc/de/steamwar/command/GuardCommand.java | 24 ++++++++++++++ .../de/steamwar/command/GuardCommandTest.java | 33 +++++++++++++++++++ .../steamwar/command/TypeMapperCommand.java | 21 ++++++++++++ .../command/TypeMapperCommandTest.java | 17 ++++++++++ ...stTypeChecker.java => TestTypeMapper.java} | 2 +- 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 testsrc/de/steamwar/command/GuardCommand.java create mode 100644 testsrc/de/steamwar/command/GuardCommandTest.java create mode 100644 testsrc/de/steamwar/command/TypeMapperCommand.java create mode 100644 testsrc/de/steamwar/command/TypeMapperCommandTest.java rename testsrc/de/steamwar/command/dto/{TestTypeChecker.java => TestTypeMapper.java} (52%) diff --git a/testsrc/de/steamwar/command/GuardCommand.java b/testsrc/de/steamwar/command/GuardCommand.java new file mode 100644 index 0000000..4285daf --- /dev/null +++ b/testsrc/de/steamwar/command/GuardCommand.java @@ -0,0 +1,24 @@ +package de.steamwar.command; + +import de.steamwar.command.dto.ExecutionIdentifier; +import de.steamwar.command.dto.TestGuardChecker; +import de.steamwar.command.dto.TestSWCommand; + +public class GuardCommand extends TestSWCommand { + + public GuardCommand() { + super("typemapper"); + } + + @Register + public void test(@Guard String sender) { + throw new ExecutionIdentifier("RunTypeMapper"); + } + + @ClassGuard(value = String.class, local = true) + public AbstractGuardChecker getGuardChecker() { + return (TestGuardChecker) (s, guardCheckType, previousArguments, s2) -> { + throw new ExecutionIdentifier("GuardChecker " + guardCheckType); + }; + } +} diff --git a/testsrc/de/steamwar/command/GuardCommandTest.java b/testsrc/de/steamwar/command/GuardCommandTest.java new file mode 100644 index 0000000..9937185 --- /dev/null +++ b/testsrc/de/steamwar/command/GuardCommandTest.java @@ -0,0 +1,33 @@ +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.instanceOf; +import static org.hamcrest.Matchers.is; + +public class GuardCommandTest { + + @Test + public void test() { + GuardCommand cmd = new GuardCommand(); + try { + cmd.execute("test", "", new String[0]); + } catch (Exception e) { + assertCMDFramework(e, ExecutionIdentifier.class, "RunTypeMapper"); + } + } + + @Test + public void testTabComplete() { + GuardCommand cmd = new GuardCommand(); + try { + cmd.tabComplete("test", "", new String[]{""}); + } catch (Exception e) { + assertThat(e, is(instanceOf(ExecutionIdentifier.class))); + assertThat(e.getMessage(), is("GuardChecker TAB_COMPLETE")); + } + } +} diff --git a/testsrc/de/steamwar/command/TypeMapperCommand.java b/testsrc/de/steamwar/command/TypeMapperCommand.java new file mode 100644 index 0000000..466d5ca --- /dev/null +++ b/testsrc/de/steamwar/command/TypeMapperCommand.java @@ -0,0 +1,21 @@ +package de.steamwar.command; + +import de.steamwar.command.dto.ExecutionIdentifier; +import de.steamwar.command.dto.TestSWCommand; + +public class TypeMapperCommand extends TestSWCommand { + + public TypeMapperCommand() { + super("typemapper"); + } + + @Register + public void test(String sender, String s) { + throw new ExecutionIdentifier("RunTypeMapper with CustomMapper"); + } + + @ClassMapper(value = String.class, local = true) + public AbstractTypeMapper getTypeMapper() { + return SWCommandUtils.createMapper("1", "2", "3", "4", "5"); + } +} diff --git a/testsrc/de/steamwar/command/TypeMapperCommandTest.java b/testsrc/de/steamwar/command/TypeMapperCommandTest.java new file mode 100644 index 0000000..c7a20c6 --- /dev/null +++ b/testsrc/de/steamwar/command/TypeMapperCommandTest.java @@ -0,0 +1,17 @@ +package de.steamwar.command; + +import org.junit.Test; + +import java.util.List; + +import static de.steamwar.AssertionUtils.assertTabCompletes; + +public class TypeMapperCommandTest { + + @Test + public void testTabComplete() { + TypeMapperCommand cmd = new TypeMapperCommand(); + List strings = cmd.tabComplete("test", "", new String[]{""}); + assertTabCompletes(strings, "1", "2", "3", "4", "5"); + } +} diff --git a/testsrc/de/steamwar/command/dto/TestTypeChecker.java b/testsrc/de/steamwar/command/dto/TestTypeMapper.java similarity index 52% rename from testsrc/de/steamwar/command/dto/TestTypeChecker.java rename to testsrc/de/steamwar/command/dto/TestTypeMapper.java index 42a7933..edac69d 100644 --- a/testsrc/de/steamwar/command/dto/TestTypeChecker.java +++ b/testsrc/de/steamwar/command/dto/TestTypeMapper.java @@ -2,5 +2,5 @@ package de.steamwar.command.dto; import de.steamwar.command.AbstractTypeMapper; -public interface TestTypeChecker extends AbstractTypeMapper { +public interface TestTypeMapper extends AbstractTypeMapper { } -- 2.39.2 From db254ed13b6aac9c055ff013ac796931a60577d4 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 27 Apr 2022 10:43:00 +0200 Subject: [PATCH 6/8] Update AbstractTypeMapper to allow Collection as return type --- src/de/steamwar/command/AbstractTypeMapper.java | 4 ++-- src/de/steamwar/command/CommandPart.java | 5 +++-- src/de/steamwar/command/SWCommandUtils.java | 16 +++++++--------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java index e5735fe..a9c326c 100644 --- a/src/de/steamwar/command/AbstractTypeMapper.java +++ b/src/de/steamwar/command/AbstractTypeMapper.java @@ -1,6 +1,6 @@ package de.steamwar.command; -import java.util.List; +import java.util.Collection; public interface AbstractTypeMapper { /** @@ -8,5 +8,5 @@ public interface AbstractTypeMapper { */ T map(K sender, String[] previousArguments, String s); - List tabCompletes(K sender, String[] previousArguments, String s); + Collection tabCompletes(K sender, String[] previousArguments, String s); } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 6a8ff4b..8a00c14 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -5,6 +5,7 @@ import lombok.Setter; import java.lang.reflect.Array; import java.util.Arrays; +import java.util.Collection; import java.util.List; class CommandPart { @@ -138,7 +139,7 @@ class CommandPart { return; } } - List strings = typeMapper.tabCompletes(sender, Arrays.copyOf(args, args.length - 1), args[args.length - 1]); + Collection strings = typeMapper.tabCompletes(sender, Arrays.copyOf(args, args.length - 1), args[args.length - 1]); if (strings != null) { current.addAll(strings); } @@ -157,7 +158,7 @@ class CommandPart { return; } - List strings = typeMapper.tabCompletes(sender, Arrays.copyOf(args, startIndex), args[startIndex]); + Collection strings = typeMapper.tabCompletes(sender, Arrays.copyOf(args, startIndex), 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 4a6088a..25bb3a3 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -9,7 +9,6 @@ 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 { @@ -198,20 +197,19 @@ public class SWCommandUtils { } public static AbstractTypeMapper> createEnumMapper(Class> enumClass) { - Enum[] enums = enumClass.getEnumConstants(); - List strings = Arrays.stream(enums).map(Enum::name).map(String::toLowerCase).collect(Collectors.toList()); + Map> enumMap = new HashMap<>(); + for (Enum e : enumClass.getEnumConstants()) { + enumMap.put(e.name(), e); + } return new AbstractTypeMapper>() { @Override public Enum map(Object commandSender, String[] previousArguments, String s) { - for (Enum e : enums) { - if (e.name().equalsIgnoreCase(s)) return e; - } - return null; + return enumMap.get(s); } @Override - public List tabCompletes(Object commandSender, String[] previousArguments, String s) { - return strings; + public Collection tabCompletes(Object commandSender, String[] previousArguments, String s) { + return enumMap.keySet(); } }; } -- 2.39.2 From 56d8065eeee9bb4368b8ab04e14f47334d7fa52e Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 27 Apr 2022 11:11:26 +0200 Subject: [PATCH 7/8] Fix CommandPart --- src/de/steamwar/command/CommandPart.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 8a00c14..da84aa8 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -134,7 +134,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(null, sender, args, i); + CheckArgumentResult validArgument = checkArgument(GuardCheckType.TAB_COMPLETE, sender, args, i); if (!validArgument.success) { return; } -- 2.39.2 From 1a2d9b9c4d56bb38169425677b6f57ec8738e725 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 1 May 2022 22:37:01 +0200 Subject: [PATCH 8/8] Add license header --- .../command/AbstractGuardChecker.java | 19 ++++++++++++++++++ .../steamwar/command/AbstractSWCommand.java | 19 ++++++++++++++++++ .../steamwar/command/AbstractTypeMapper.java | 19 ++++++++++++++++++ src/de/steamwar/command/CommandPart.java | 19 ++++++++++++++++++ src/de/steamwar/command/SWCommandUtils.java | 19 ++++++++++++++++++ src/de/steamwar/command/SubCommand.java | 20 ++++++++++++++++++- testsrc/de/steamwar/AssertionUtils.java | 19 ++++++++++++++++++ .../de/steamwar/command/ArgumentCommand.java | 19 ++++++++++++++++++ .../steamwar/command/ArgumentCommandTest.java | 19 ++++++++++++++++++ testsrc/de/steamwar/command/GuardCommand.java | 19 ++++++++++++++++++ .../de/steamwar/command/GuardCommandTest.java | 19 ++++++++++++++++++ .../de/steamwar/command/SimpleCommand.java | 19 ++++++++++++++++++ .../steamwar/command/SimpleCommandTest.java | 19 ++++++++++++++++++ .../steamwar/command/TypeMapperCommand.java | 19 ++++++++++++++++++ .../command/TypeMapperCommandTest.java | 19 ++++++++++++++++++ .../command/dto/ExecutionIdentifier.java | 19 ++++++++++++++++++ .../command/dto/TestGuardChecker.java | 19 ++++++++++++++++++ .../steamwar/command/dto/TestSWCommand.java | 19 ++++++++++++++++++ .../steamwar/command/dto/TestTypeMapper.java | 19 ++++++++++++++++++ 19 files changed, 361 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/command/AbstractGuardChecker.java b/src/de/steamwar/command/AbstractGuardChecker.java index 8d1a269..f5f2597 100644 --- a/src/de/steamwar/command/AbstractGuardChecker.java +++ b/src/de/steamwar/command/AbstractGuardChecker.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; @FunctionalInterface diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index da80f23..fd371df 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.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 java.lang.annotation.*; diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java index a9c326c..744b726 100644 --- a/src/de/steamwar/command/AbstractTypeMapper.java +++ b/src/de/steamwar/command/AbstractTypeMapper.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 java.util.Collection; diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index da84aa8..0688caf 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.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/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 25bb3a3..067bc35 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.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.Getter; diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 2f41791..603e6d3 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -1,10 +1,28 @@ +/* + * 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 java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; diff --git a/testsrc/de/steamwar/AssertionUtils.java b/testsrc/de/steamwar/AssertionUtils.java index 58fe01c..ed8c8ca 100644 --- a/testsrc/de/steamwar/AssertionUtils.java +++ b/testsrc/de/steamwar/AssertionUtils.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; import de.steamwar.command.CommandFrameworkException; diff --git a/testsrc/de/steamwar/command/ArgumentCommand.java b/testsrc/de/steamwar/command/ArgumentCommand.java index c630a93..b4ce500 100644 --- a/testsrc/de/steamwar/command/ArgumentCommand.java +++ b/testsrc/de/steamwar/command/ArgumentCommand.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.ExecutionIdentifier; diff --git a/testsrc/de/steamwar/command/ArgumentCommandTest.java b/testsrc/de/steamwar/command/ArgumentCommandTest.java index 7b74126..c546127 100644 --- a/testsrc/de/steamwar/command/ArgumentCommandTest.java +++ b/testsrc/de/steamwar/command/ArgumentCommandTest.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.ExecutionIdentifier; diff --git a/testsrc/de/steamwar/command/GuardCommand.java b/testsrc/de/steamwar/command/GuardCommand.java index 4285daf..7ee1854 100644 --- a/testsrc/de/steamwar/command/GuardCommand.java +++ b/testsrc/de/steamwar/command/GuardCommand.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.ExecutionIdentifier; diff --git a/testsrc/de/steamwar/command/GuardCommandTest.java b/testsrc/de/steamwar/command/GuardCommandTest.java index 9937185..233333c 100644 --- a/testsrc/de/steamwar/command/GuardCommandTest.java +++ b/testsrc/de/steamwar/command/GuardCommandTest.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.ExecutionIdentifier; diff --git a/testsrc/de/steamwar/command/SimpleCommand.java b/testsrc/de/steamwar/command/SimpleCommand.java index 88d0bd6..16d565f 100644 --- a/testsrc/de/steamwar/command/SimpleCommand.java +++ b/testsrc/de/steamwar/command/SimpleCommand.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.ExecutionIdentifier; diff --git a/testsrc/de/steamwar/command/SimpleCommandTest.java b/testsrc/de/steamwar/command/SimpleCommandTest.java index 4ba3ef1..dcb286c 100644 --- a/testsrc/de/steamwar/command/SimpleCommandTest.java +++ b/testsrc/de/steamwar/command/SimpleCommandTest.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.ExecutionIdentifier; diff --git a/testsrc/de/steamwar/command/TypeMapperCommand.java b/testsrc/de/steamwar/command/TypeMapperCommand.java index 466d5ca..a81500f 100644 --- a/testsrc/de/steamwar/command/TypeMapperCommand.java +++ b/testsrc/de/steamwar/command/TypeMapperCommand.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.ExecutionIdentifier; diff --git a/testsrc/de/steamwar/command/TypeMapperCommandTest.java b/testsrc/de/steamwar/command/TypeMapperCommandTest.java index c7a20c6..32b5b47 100644 --- a/testsrc/de/steamwar/command/TypeMapperCommandTest.java +++ b/testsrc/de/steamwar/command/TypeMapperCommandTest.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 org.junit.Test; diff --git a/testsrc/de/steamwar/command/dto/ExecutionIdentifier.java b/testsrc/de/steamwar/command/dto/ExecutionIdentifier.java index bde7122..d03b78f 100644 --- a/testsrc/de/steamwar/command/dto/ExecutionIdentifier.java +++ b/testsrc/de/steamwar/command/dto/ExecutionIdentifier.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.dto; public class ExecutionIdentifier extends RuntimeException { diff --git a/testsrc/de/steamwar/command/dto/TestGuardChecker.java b/testsrc/de/steamwar/command/dto/TestGuardChecker.java index ff38ebc..b85fa17 100644 --- a/testsrc/de/steamwar/command/dto/TestGuardChecker.java +++ b/testsrc/de/steamwar/command/dto/TestGuardChecker.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.dto; import de.steamwar.command.AbstractGuardChecker; diff --git a/testsrc/de/steamwar/command/dto/TestSWCommand.java b/testsrc/de/steamwar/command/dto/TestSWCommand.java index 9081000..5fd3b3f 100644 --- a/testsrc/de/steamwar/command/dto/TestSWCommand.java +++ b/testsrc/de/steamwar/command/dto/TestSWCommand.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.dto; import de.steamwar.command.AbstractSWCommand; diff --git a/testsrc/de/steamwar/command/dto/TestTypeMapper.java b/testsrc/de/steamwar/command/dto/TestTypeMapper.java index edac69d..96c4c6c 100644 --- a/testsrc/de/steamwar/command/dto/TestTypeMapper.java +++ b/testsrc/de/steamwar/command/dto/TestTypeMapper.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.dto; import de.steamwar.command.AbstractTypeMapper; -- 2.39.2