From d3864414aa068d43ae21211a77351a274d4ec3a5 Mon Sep 17 00:00:00 2001 From: Citymonstret Date: Mon, 13 Jun 2022 18:04:51 +0200 Subject: [PATCH] feat(annotations): use TypeToken in AnnotationParser --- .../annotations/AnnotationParser.java | 28 ++++- .../feature/RequiredSenderDeductionTest.java | 113 ++++++++++++++++++ 2 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 cloud-annotations/src/test/java/cloud/commandframework/annotations/feature/RequiredSenderDeductionTest.java diff --git a/cloud-annotations/src/main/java/cloud/commandframework/annotations/AnnotationParser.java b/cloud-annotations/src/main/java/cloud/commandframework/annotations/AnnotationParser.java index 510a88c9..40349382 100644 --- a/cloud-annotations/src/main/java/cloud/commandframework/annotations/AnnotationParser.java +++ b/cloud-annotations/src/main/java/cloud/commandframework/annotations/AnnotationParser.java @@ -48,6 +48,7 @@ import cloud.commandframework.execution.CommandExecutionHandler; import cloud.commandframework.extra.confirmation.CommandConfirmationManager; import cloud.commandframework.meta.CommandMeta; import cloud.commandframework.meta.SimpleCommandMeta; +import io.leangen.geantyref.GenericTypeReflector; import io.leangen.geantyref.TypeToken; import java.io.BufferedReader; import java.io.InputStream; @@ -73,6 +74,7 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; +import org.apiguardian.api.API; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -100,7 +102,7 @@ public final class AnnotationParser { builderModifiers; private final Map, Function, MethodCommandExecutionHandler>> commandMethodFactories; - private final Class commandSenderClass; + private final TypeToken commandSenderType; private final MetaFactory metaFactory; private final FlagExtractor flagExtractor; @@ -121,7 +123,27 @@ public final class AnnotationParser { final @NonNull Class commandSenderClass, final @NonNull Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper ) { - this.commandSenderClass = commandSenderClass; + this(manager, TypeToken.get(commandSenderClass), metaMapper); + } + + /** + * Construct a new annotation parser + * + * @param manager Command manager instance + * @param commandSenderType Command sender type + * @param metaMapper Function that is used to create {@link CommandMeta} instances from annotations on the + * command methods. These annotations will be mapped to + * {@link ParserParameter}. Mappers for the + * parser parameters can be registered using {@link #registerAnnotationMapper(Class, Function)} + * @since 1.7.0 + */ + @API(status = API.Status.STABLE, since = "1.7.0") + public AnnotationParser( + final @NonNull CommandManager manager, + final @NonNull TypeToken commandSenderType, + final @NonNull Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper + ) { + this.commandSenderType = commandSenderType; this.manager = manager; this.metaFactory = new MetaFactory(this, metaMapper); this.annotationMappers = new HashMap<>(); @@ -581,7 +603,7 @@ public final class AnnotationParser { if (parameter.isAnnotationPresent(Argument.class)) { continue; } - if (this.commandSenderClass.isAssignableFrom(parameter.getType())) { + if (GenericTypeReflector.isSuperType(this.commandSenderType.getType(), parameter.getType())) { senderType = (Class) parameter.getType(); break; } diff --git a/cloud-annotations/src/test/java/cloud/commandframework/annotations/feature/RequiredSenderDeductionTest.java b/cloud-annotations/src/test/java/cloud/commandframework/annotations/feature/RequiredSenderDeductionTest.java new file mode 100644 index 00000000..9ef92fa1 --- /dev/null +++ b/cloud-annotations/src/test/java/cloud/commandframework/annotations/feature/RequiredSenderDeductionTest.java @@ -0,0 +1,113 @@ +// +// MIT License +// +// Copyright (c) 2021 Alexander Söderberg & Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package cloud.commandframework.annotations.feature; + +import cloud.commandframework.CommandManager; +import cloud.commandframework.annotations.AnnotationParser; +import cloud.commandframework.annotations.CommandMethod; +import cloud.commandframework.execution.CommandExecutionCoordinator; +import cloud.commandframework.internal.CommandRegistrationHandler; +import cloud.commandframework.meta.CommandMeta; +import cloud.commandframework.meta.SimpleCommandMeta; +import io.leangen.geantyref.TypeToken; +import java.util.concurrent.CompletionException; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +class RequiredSenderDeductionTest { + + private CommandManager> commandManager; + + @BeforeEach + void setup() { + this.commandManager = new CommandManager>( + CommandExecutionCoordinator.simpleCoordinator(), + CommandRegistrationHandler.nullCommandRegistrationHandler() + ) { + @Override + public boolean hasPermission( + final @NonNull SuperSender sender, + final @NonNull String permission + ) { + return true; + } + + @Override + public @NonNull CommandMeta createDefaultCommandMeta() { + return SimpleCommandMeta.empty(); + } + }; + AnnotationParser> annotationParser = new AnnotationParser<>( + this.commandManager, + new TypeToken>() { + }, + parameters -> SimpleCommandMeta.empty() + ); + annotationParser.parse(new TestClassA()); + } + + @Test + void testCorrectSender() { + // Arrange + final SuperSender sender = new StringSender(); + + // Act + this.commandManager.executeCommand(sender, "teststring").join(); + } + + @Test + void testIncorrectSender() { + // Arrange + final SuperSender sender = new IntegerSender(); + + // Act + assertThrows( + CompletionException.class, + () -> this.commandManager.executeCommand(sender, "teststring").join() + ); + } + + + private static class TestClassA { + + @CommandMethod("teststring") + public void command(final StringSender sender) { + } + } + + + public interface SuperSender { + } + + + public static class StringSender implements SuperSender { + } + + + public static class IntegerSender implements SuperSender { + } +}