✨ Allow interception of command builders based on annotations in AnnotationParser
Fixes #179
This commit is contained in:
parent
c684c6607f
commit
9ed40a698a
3 changed files with 66 additions and 5 deletions
|
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Added `@Suggestions` annotated methods
|
- Added `@Suggestions` annotated methods
|
||||||
- Added `@Parser` annotated methods
|
- Added `@Parser` annotated methods
|
||||||
- Type safe meta system
|
- Type safe meta system
|
||||||
|
- Allow interception of command builders based on annotations in AnnotationParser
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Moved the parser injector registry into CommandManager and added injection to CommandContext
|
- Moved the parser injector registry into CommandManager and added injection to CommandContext
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ public final class AnnotationParser<C> {
|
||||||
private final Map<Class<? extends Annotation>, Function<? extends Annotation, ParserParameters>> annotationMappers;
|
private final Map<Class<? extends Annotation>, Function<? extends Annotation, ParserParameters>> annotationMappers;
|
||||||
private final Map<Class<? extends Annotation>, Function<? extends Annotation, BiFunction<@NonNull CommandContext<C>,
|
private final Map<Class<? extends Annotation>, Function<? extends Annotation, BiFunction<@NonNull CommandContext<C>,
|
||||||
@NonNull Queue<@NonNull String>, @NonNull ArgumentParseResult<Boolean>>>> preprocessorMappers;
|
@NonNull Queue<@NonNull String>, @NonNull ArgumentParseResult<Boolean>>>> preprocessorMappers;
|
||||||
|
private final Map<Class<? extends Annotation>, BiFunction<? extends Annotation, Command.Builder<C>, Command.Builder<C>>>
|
||||||
|
builderModifiers;
|
||||||
private final Class<C> commandSenderClass;
|
private final Class<C> commandSenderClass;
|
||||||
private final MetaFactory metaFactory;
|
private final MetaFactory metaFactory;
|
||||||
private final FlagExtractor flagExtractor;
|
private final FlagExtractor flagExtractor;
|
||||||
|
|
@ -107,6 +109,7 @@ public final class AnnotationParser<C> {
|
||||||
this.metaFactory = new MetaFactory(this, metaMapper);
|
this.metaFactory = new MetaFactory(this, metaMapper);
|
||||||
this.annotationMappers = new HashMap<>();
|
this.annotationMappers = new HashMap<>();
|
||||||
this.preprocessorMappers = new HashMap<>();
|
this.preprocessorMappers = new HashMap<>();
|
||||||
|
this.builderModifiers = new HashMap<>();
|
||||||
this.flagExtractor = new FlagExtractor(manager);
|
this.flagExtractor = new FlagExtractor(manager);
|
||||||
this.registerAnnotationMapper(CommandDescription.class, d ->
|
this.registerAnnotationMapper(CommandDescription.class, d ->
|
||||||
ParserParameters.single(StandardParameters.DESCRIPTION, d.value()));
|
ParserParameters.single(StandardParameters.DESCRIPTION, d.value()));
|
||||||
|
|
@ -177,6 +180,25 @@ public final class AnnotationParser<C> {
|
||||||
return getMethodOrClassAnnotation(method, clazz) != null;
|
return getMethodOrClassAnnotation(method, clazz) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a builder modifier for a specific annotation. The builder modifiers are
|
||||||
|
* allowed to act on a {@link Command.Builder} after all arguments have been added
|
||||||
|
* to the builder. This allows for modifications of the builder instance before
|
||||||
|
* the command is registered to the command manager.
|
||||||
|
*
|
||||||
|
* @param annotation Annotation (class) that the builder modifier reacts to
|
||||||
|
* @param builderModifier Modifier that acts on the given annotation and the incoming builder. Command builders
|
||||||
|
* are immutable, so the modifier should return the instance of the command builder that is
|
||||||
|
* returned as a result of any operation on the builder
|
||||||
|
* @param <A> Annotation type
|
||||||
|
*/
|
||||||
|
public <A extends Annotation> void registerBuilderModifier(
|
||||||
|
final @NonNull Class<A> annotation,
|
||||||
|
final @NonNull BiFunction<A, Command.Builder<C>, Command.Builder<C>> builderModifier
|
||||||
|
) {
|
||||||
|
this.builderModifiers.put(annotation, builderModifier);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an annotation mapper
|
* Register an annotation mapper
|
||||||
*
|
*
|
||||||
|
|
@ -275,9 +297,9 @@ public final class AnnotationParser<C> {
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
}
|
}
|
||||||
if (method.getParameterCount() != 2
|
if (method.getParameterCount() != 2
|
||||||
|| !method.getReturnType().equals(List.class)
|
|| !method.getReturnType().equals(List.class)
|
||||||
|| !method.getParameters()[0].getType().equals(CommandContext.class)
|
|| !method.getParameters()[0].getType().equals(CommandContext.class)
|
||||||
|| !method.getParameters()[1].getType().equals(String.class)
|
|| !method.getParameters()[1].getType().equals(String.class)
|
||||||
) {
|
) {
|
||||||
throw new IllegalArgumentException(String.format(
|
throw new IllegalArgumentException(String.format(
|
||||||
"@Suggestions annotated method '%s' in class '%s' does not have the correct signature",
|
"@Suggestions annotated method '%s' in class '%s' does not have the correct signature",
|
||||||
|
|
@ -457,6 +479,14 @@ public final class AnnotationParser<C> {
|
||||||
for (final CommandFlag<?> flag : flags) {
|
for (final CommandFlag<?> flag : flags) {
|
||||||
builder = builder.flag(flag);
|
builder = builder.flag(flag);
|
||||||
}
|
}
|
||||||
|
for (final Annotation annotation : method.getDeclaredAnnotations()) {
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
|
final BiFunction builderModifier = this.builderModifiers.get(annotation.annotationType());
|
||||||
|
if (builderModifier == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
builder = (Command.Builder<C>) builderModifier.apply(annotation, builder);
|
||||||
|
}
|
||||||
/* Construct and register the command */
|
/* Construct and register the command */
|
||||||
final Command<C> builtCommand = builder.build();
|
final Command<C> builtCommand = builder.build();
|
||||||
commands.add(builtCommand);
|
commands.add(builtCommand);
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import cloud.commandframework.annotations.specifier.Range;
|
||||||
import cloud.commandframework.annotations.suggestions.Suggestions;
|
import cloud.commandframework.annotations.suggestions.Suggestions;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
import cloud.commandframework.arguments.parser.ArgumentParser;
|
||||||
import cloud.commandframework.arguments.parser.ParserParameters;
|
import cloud.commandframework.arguments.parser.ParserParameters;
|
||||||
|
import cloud.commandframework.arguments.standard.IntegerArgument;
|
||||||
import cloud.commandframework.arguments.standard.StringArgument;
|
import cloud.commandframework.arguments.standard.StringArgument;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
import cloud.commandframework.meta.SimpleCommandMeta;
|
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||||
|
|
@ -78,7 +79,11 @@ class AnnotationParserTest {
|
||||||
InjectableValue.class,
|
InjectableValue.class,
|
||||||
(context, annotations) -> new InjectableValue("Hello World!")
|
(context, annotations) -> new InjectableValue("Hello World!")
|
||||||
);
|
);
|
||||||
|
/* Register a builder modifier */
|
||||||
|
annotationParser.registerBuilderModifier(
|
||||||
|
IntegerArgumentInjector.class,
|
||||||
|
(injector, builder) -> builder.argument(IntegerArgument.of(injector.value()))
|
||||||
|
);
|
||||||
/* Parse the class. Required for both testMethodConstruction() and testNamedSuggestionProvider() */
|
/* Parse the class. Required for both testMethodConstruction() and testNamedSuggestionProvider() */
|
||||||
commands = annotationParser.parse(this);
|
commands = annotationParser.parse(this);
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +140,7 @@ class AnnotationParserTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testParameterInjection() {
|
void testParameterInjection() {
|
||||||
manager.executeCommand(new TestCommandSender(), "inject").join();
|
manager.executeCommand(new TestCommandSender(), "injected 10").join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -167,6 +172,11 @@ class AnnotationParserTest {
|
||||||
).contains("Stella"));
|
).contains("Stella"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInjectedCommand() {
|
||||||
|
manager.executeCommand(new TestCommandSender(), "injected 10").join();
|
||||||
|
}
|
||||||
|
|
||||||
@Suggestions("cows")
|
@Suggestions("cows")
|
||||||
public List<String> cowSuggestions(final CommandContext<TestCommandSender> context, final String input) {
|
public List<String> cowSuggestions(final CommandContext<TestCommandSender> context, final String input) {
|
||||||
return Arrays.asList("Stella", "Bella", "Agda");
|
return Arrays.asList("Stella", "Bella", "Agda");
|
||||||
|
|
@ -177,6 +187,12 @@ class AnnotationParserTest {
|
||||||
return new CustomType("yay");
|
return new CustomType("yay");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IntegerArgumentInjector
|
||||||
|
@CommandMethod("injected")
|
||||||
|
public void injectedCommand(final CommandContext<TestCommandSender> context) {
|
||||||
|
System.out.printf("Got an integer: %d\n", context.<Integer>get("number"));
|
||||||
|
}
|
||||||
|
|
||||||
@ProxiedBy("proxycommand")
|
@ProxiedBy("proxycommand")
|
||||||
@CommandMethod("test|t literal <int> [string]")
|
@CommandMethod("test|t literal <int> [string]")
|
||||||
public void testCommand(
|
public void testCommand(
|
||||||
|
|
@ -278,4 +294,18 @@ class AnnotationParserTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
private @interface IntegerArgumentInjector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the integer argument to insert
|
||||||
|
*
|
||||||
|
* @return Integer argument name
|
||||||
|
*/
|
||||||
|
String value() default "number";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue