feat(annotations): use TypeToken in AnnotationParser

This commit is contained in:
Citymonstret 2022-06-13 18:04:51 +02:00 committed by Jason
parent 296539d56c
commit d3864414aa
2 changed files with 138 additions and 3 deletions

View file

@ -48,6 +48,7 @@ import cloud.commandframework.execution.CommandExecutionHandler;
import cloud.commandframework.extra.confirmation.CommandConfirmationManager; import cloud.commandframework.extra.confirmation.CommandConfirmationManager;
import cloud.commandframework.meta.CommandMeta; import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta; import cloud.commandframework.meta.SimpleCommandMeta;
import io.leangen.geantyref.GenericTypeReflector;
import io.leangen.geantyref.TypeToken; import io.leangen.geantyref.TypeToken;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStream; import java.io.InputStream;
@ -73,6 +74,7 @@ import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -100,7 +102,7 @@ public final class AnnotationParser<C> {
builderModifiers; builderModifiers;
private final Map<Predicate<Method>, Function<MethodCommandExecutionHandler.CommandMethodContext<C>, private final Map<Predicate<Method>, Function<MethodCommandExecutionHandler.CommandMethodContext<C>,
MethodCommandExecutionHandler<C>>> commandMethodFactories; MethodCommandExecutionHandler<C>>> commandMethodFactories;
private final Class<C> commandSenderClass; private final TypeToken<C> commandSenderType;
private final MetaFactory metaFactory; private final MetaFactory metaFactory;
private final FlagExtractor flagExtractor; private final FlagExtractor flagExtractor;
@ -121,7 +123,27 @@ public final class AnnotationParser<C> {
final @NonNull Class<C> commandSenderClass, final @NonNull Class<C> commandSenderClass,
final @NonNull Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper 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<C> manager,
final @NonNull TypeToken<C> commandSenderType,
final @NonNull Function<@NonNull ParserParameters, @NonNull CommandMeta> metaMapper
) {
this.commandSenderType = commandSenderType;
this.manager = manager; this.manager = manager;
this.metaFactory = new MetaFactory(this, metaMapper); this.metaFactory = new MetaFactory(this, metaMapper);
this.annotationMappers = new HashMap<>(); this.annotationMappers = new HashMap<>();
@ -581,7 +603,7 @@ public final class AnnotationParser<C> {
if (parameter.isAnnotationPresent(Argument.class)) { if (parameter.isAnnotationPresent(Argument.class)) {
continue; continue;
} }
if (this.commandSenderClass.isAssignableFrom(parameter.getType())) { if (GenericTypeReflector.isSuperType(this.commandSenderType.getType(), parameter.getType())) {
senderType = (Class<? extends C>) parameter.getType(); senderType = (Class<? extends C>) parameter.getType();
break; break;
} }

View file

@ -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<SuperSender<?>> commandManager;
@BeforeEach
void setup() {
this.commandManager = new CommandManager<SuperSender<?>>(
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<SuperSender<?>> annotationParser = new AnnotationParser<>(
this.commandManager,
new TypeToken<SuperSender<?>>() {
},
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<A> {
}
public static class StringSender implements SuperSender<String> {
}
public static class IntegerSender implements SuperSender<Integer> {
}
}