Skip to content

Commit 62fa4a0

Browse files
committed
feature(annotations): add BuilderDecorator
Decorators are like builder modifiers, but they're applied to _all_ builders constructed by the annotation parser.
1 parent 409673f commit 62fa4a0

3 files changed

Lines changed: 191 additions & 0 deletions

File tree

cloud-annotations/src/main/java/cloud/commandframework/annotations/AnnotationParser.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public final class AnnotationParser<C> {
9898
private final Map<Class<? extends Annotation>, AnnotationMapper<?>> annotationMappers;
9999
private final Map<Class<? extends Annotation>, PreprocessorMapper<?, C>> preprocessorMappers;
100100
private final Map<Class<? extends Annotation>, BuilderModifier<?, C>> builderModifiers;
101+
private final List<BuilderDecorator<C>> builderDecorators;
101102
private final Map<Predicate<Method>, CommandMethodExecutionHandlerFactory<C>> commandMethodFactories;
102103
private final TypeToken<C> commandSenderType;
103104
private final MetaFactory metaFactory;
@@ -190,6 +191,7 @@ public AnnotationParser(
190191
this.commandExtractor = new CommandExtractorImpl(this);
191192
this.suggestionProviderFactory = SuggestionProviderFactory.defaultFactory();
192193
this.exceptionHandlerFactory = ExceptionHandlerFactory.defaultFactory();
194+
this.builderDecorators = new ArrayList<>();
193195
this.registerBuilderModifier(
194196
CommandDescription.class,
195197
(description, builder) -> builder.commandDescription(commandDescription(this.mapDescription(description.value())))
@@ -307,6 +309,20 @@ public <A extends Annotation> void registerBuilderModifier(
307309
this.builderModifiers.put(annotation, builderModifier);
308310
}
309311

312+
/**
313+
* Registers the given {@code decorator}.
314+
* <p>
315+
* The decorators are allowed to modify the command builders to set up default values.
316+
* All other steps of the command construction process take priority over the decorators.
317+
*
318+
* @param decorator the decorator
319+
* @since 2.0.0
320+
*/
321+
@API(status = API.Status.STABLE, since = "2.0.0")
322+
public void registerBuilderDecorator(final @NonNull BuilderDecorator<C> decorator) {
323+
this.builderDecorators.add(decorator);
324+
}
325+
310326
/**
311327
* Register an annotation mapper
312328
*
@@ -844,6 +860,10 @@ private <T> void parseParsers(final @NonNull T instance) {
844860
commandDescriptor.syntax().get(0).getMinor(),
845861
metaBuilder.build()
846862
);
863+
for (final BuilderDecorator<C> decorator : this.builderDecorators) {
864+
builder = decorator.decorate(builder);
865+
}
866+
847867
final Collection<ArgumentDescriptor> arguments = this.argumentExtractor.extractArguments(commandDescriptor.syntax(), method);
848868
final Collection<FlagDescriptor> flagDescriptors = this.flagExtractor.extractFlags(method);
849869
final Collection<CommandFlag<?>> flags = flagDescriptors.stream()
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2022 Alexander Söderberg & Contributors
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package cloud.commandframework.annotations;
25+
26+
import cloud.commandframework.Command;
27+
import cloud.commandframework.CommandDescription;
28+
import cloud.commandframework.permission.CommandPermission;
29+
import org.apiguardian.api.API;
30+
import org.checkerframework.checker.nullness.qual.NonNull;
31+
32+
/**
33+
* Decorators that get to modify a command builder that is used to construct the command for annotated command methods.
34+
*
35+
* @param <C> the command sender type
36+
* @since 2.0.0
37+
*/
38+
@API(status = API.Status.STABLE, since = "2.0.0")
39+
public interface BuilderDecorator<C> {
40+
41+
/**
42+
* Returns a {@link BuilderDecorator} that sets the default {@link Command.Builder#commandDescription() description}
43+
* of a command.
44+
*
45+
* @param <C> the command sender type
46+
* @param description the description
47+
* @return the decorator
48+
*/
49+
static <C> @NonNull BuilderDecorator<C> defaultDescription(final @NonNull CommandDescription description) {
50+
return builder -> builder.commandDescription(description);
51+
}
52+
53+
/**
54+
* Returns a {@link BuilderDecorator} that sets the default {@link Command.Builder#permission(CommandPermission) permission}
55+
* of a command.
56+
*
57+
* @param <C> the command sender type
58+
* @param permission the permission
59+
* @return the decorator
60+
*/
61+
static <C> @NonNull BuilderDecorator<C> defaultPermission(final @NonNull CommandPermission permission) {
62+
return builder -> builder.permission(permission);
63+
}
64+
65+
/**
66+
* Returns a {@link BuilderDecorator} applies the given {@code applicable} to the command builders.
67+
*
68+
* @param <C> the command sender type
69+
* @param applicable the applicable to apply
70+
* @return the decorator
71+
*/
72+
static <C> @NonNull BuilderDecorator<C> applicable(final Command.Builder.@NonNull Applicable<C> applicable) {
73+
return builder -> builder.apply(applicable);
74+
}
75+
76+
/**
77+
* Decorates the given {@code builder} and returns the result.
78+
*
79+
* @param builder the builder to decorate
80+
* @return the result
81+
*/
82+
Command.@NonNull Builder<C> decorate(Command.@NonNull Builder<C> builder);
83+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2022 Alexander Söderberg & Contributors
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package cloud.commandframework.annotations.feature;
25+
26+
import cloud.commandframework.CommandDescription;
27+
import cloud.commandframework.CommandManager;
28+
import cloud.commandframework.annotations.AnnotationParser;
29+
import cloud.commandframework.annotations.BuilderDecorator;
30+
import cloud.commandframework.annotations.CommandMethod;
31+
import cloud.commandframework.annotations.TestCommandManager;
32+
import cloud.commandframework.annotations.TestCommandSender;
33+
import cloud.commandframework.permission.Permission;
34+
import org.junit.jupiter.api.BeforeEach;
35+
import org.junit.jupiter.api.Test;
36+
37+
import static com.google.common.truth.Truth.assertThat;
38+
39+
class BuilderDecoratorTest {
40+
41+
private CommandManager<TestCommandSender> commandManager;
42+
private AnnotationParser<TestCommandSender> annotationParser;
43+
44+
@BeforeEach
45+
void setup() {
46+
this.commandManager = new TestCommandManager();
47+
this.annotationParser = new AnnotationParser<>(
48+
this.commandManager,
49+
TestCommandSender.class
50+
);
51+
}
52+
53+
@Test
54+
void testDefaultDescription() {
55+
// Arrange
56+
this.annotationParser.registerBuilderDecorator(
57+
BuilderDecorator.defaultDescription(CommandDescription.commandDescription("default description")));
58+
59+
// Act
60+
this.annotationParser.parse(new TestClass());
61+
62+
// Assert
63+
assertThat(this.commandManager.commandTree().getNamedNode("command").component().owningCommand().commandDescription())
64+
.isEqualTo(CommandDescription.commandDescription("default description"));
65+
}
66+
67+
@Test
68+
void testDefaultPermission() {
69+
// Arrange
70+
this.annotationParser.registerBuilderDecorator(
71+
BuilderDecorator.defaultPermission(Permission.of("default.permission")));
72+
73+
// Act
74+
this.annotationParser.parse(new TestClass());
75+
76+
// Assert
77+
assertThat(this.commandManager.commandTree().getNamedNode("command").component().owningCommand().commandPermission())
78+
.isEqualTo(Permission.of("default.permission"));
79+
}
80+
81+
82+
static class TestClass {
83+
84+
@CommandMethod("command")
85+
public void command() {
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)