feat: annotation string processors (#353)
adds a system for processing strings found in annotations before they're used by AnnotationParser implements #347 Also, because we're using "-Werror", the code won't actually build (and thus tests won't work) using JDK18. To remedy this, a bunch of @SuppressWarnings("serial")s has been added to the exceptions. We don't serialize exceptions, and they're in fact non-serializable because of their members, so this is the appropriate solution (well, the better solution would be to make them serializable, but that's outside the scope of this PR).
This commit is contained in:
parent
ed7b7569a8
commit
d681ba5840
28 changed files with 715 additions and 38 deletions
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// 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;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class NoOpStringProcessorTest {
|
||||
|
||||
@Test
|
||||
void ProcessString_AnyInput_ReturnsOriginalInput() {
|
||||
// Will force the input string to be scrambled 10 times.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Arrange
|
||||
final StringProcessor stringProcessor = StringProcessor.noOp();
|
||||
final String input = ThreadLocalRandom.current().ints().mapToObj(Integer::toString).limit(32).collect(Collectors.joining());
|
||||
|
||||
// Act
|
||||
final String output = stringProcessor.processString(input);
|
||||
|
||||
// Assert
|
||||
assertThat(input).isEqualTo(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// 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;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.notNull;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Pattern;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PatternReplacingStringProcessorTest {
|
||||
|
||||
private static final Pattern TEST_PATTERN = Pattern.compile("\\[(\\S+)]");
|
||||
|
||||
private PatternReplacingStringProcessor patternReplacingStringProcessor;
|
||||
|
||||
@Mock
|
||||
private Function<MatchResult, String> replacementProvider;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.patternReplacingStringProcessor = new PatternReplacingStringProcessor(
|
||||
TEST_PATTERN,
|
||||
this.replacementProvider
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ProcessString_MatchingInput_ReplacesGroups() {
|
||||
// Arrange
|
||||
when(this.replacementProvider.apply(any())).thenAnswer(iom -> iom.getArgument(0, MatchResult.class).group(1));
|
||||
|
||||
final String input = "[hello] [world]!";
|
||||
|
||||
// Act
|
||||
final String output = this.patternReplacingStringProcessor.processString(input);
|
||||
|
||||
// Act
|
||||
assertThat(output).isEqualTo("hello world!");
|
||||
|
||||
verify(this.replacementProvider, times(2)).apply(notNull());
|
||||
verifyNoMoreInteractions(this.replacementProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ProcessString_NullReplacement_InputPreserved() {
|
||||
// Arrange
|
||||
final String input = "[input] ...";
|
||||
|
||||
// Act
|
||||
final String output = this.patternReplacingStringProcessor.processString(input);
|
||||
|
||||
// Act
|
||||
assertThat(output).isEqualTo(input);
|
||||
|
||||
verify(this.replacementProvider).apply(notNull());
|
||||
verifyNoMoreInteractions(this.replacementProvider);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// 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;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.notNull;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.function.Function;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PropertyReplacingStringProcessorTest {
|
||||
|
||||
private PropertyReplacingStringProcessor propertyReplacingStringProcessor;
|
||||
|
||||
@Mock
|
||||
private Function<String, String> propertyProvider;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.propertyReplacingStringProcessor = new PropertyReplacingStringProcessor(this.propertyProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ProcessString_KnownProperty_ReplacesWithValue() {
|
||||
// Arrange
|
||||
when(this.propertyProvider.apply(anyString())).thenAnswer(iom -> "transformed: " + iom.getArgument(0, String.class));
|
||||
|
||||
final String input = "${hello.world}";
|
||||
|
||||
// Act
|
||||
final String output = this.propertyReplacingStringProcessor.processString(input);
|
||||
|
||||
// Assert
|
||||
assertThat(output).isEqualTo("transformed: hello.world");
|
||||
|
||||
verify(this.propertyProvider).apply("hello.world");
|
||||
verifyNoMoreInteractions(this.propertyProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ProcessString_MultipleProperties_ReplacesAll() {
|
||||
// Arrange
|
||||
when(this.propertyProvider.apply(anyString())).thenAnswer(iom -> iom.getArgument(0, String.class));
|
||||
|
||||
final String input = "${cats} are cute, and so are ${dogs}!";
|
||||
|
||||
// Act
|
||||
final String output = this.propertyReplacingStringProcessor.processString(input);
|
||||
|
||||
// Assert
|
||||
assertThat(output).isEqualTo("cats are cute, and so are dogs!");
|
||||
|
||||
verify(this.propertyProvider).apply("cats");
|
||||
verify(this.propertyProvider).apply("dogs");
|
||||
verifyNoMoreInteractions(this.propertyProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ProcessString_NullProperty_InputPreserved() {
|
||||
// Arrange
|
||||
final String input = "${input} ...";
|
||||
|
||||
// Act
|
||||
final String output = this.propertyReplacingStringProcessor.processString(input);
|
||||
|
||||
// Act
|
||||
assertThat(output).isEqualTo(input);
|
||||
|
||||
verify(this.propertyProvider).apply(notNull());
|
||||
verifyNoMoreInteractions(this.propertyProvider);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ import cloud.commandframework.meta.SimpleCommandMeta;
|
|||
|
||||
public class TestCommandManager extends CommandManager<TestCommandSender> {
|
||||
|
||||
protected TestCommandManager() {
|
||||
public TestCommandManager() {
|
||||
super(CommandExecutionCoordinator.simpleCoordinator(), CommandRegistrationHandler.nullCommandRegistrationHandler());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// 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 static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
|
||||
import cloud.commandframework.Command;
|
||||
import cloud.commandframework.annotations.AnnotationParser;
|
||||
import cloud.commandframework.annotations.Argument;
|
||||
import cloud.commandframework.annotations.CommandDescription;
|
||||
import cloud.commandframework.annotations.CommandMethod;
|
||||
import cloud.commandframework.annotations.CommandPermission;
|
||||
import cloud.commandframework.annotations.Flag;
|
||||
import cloud.commandframework.annotations.PropertyReplacingStringProcessor;
|
||||
import cloud.commandframework.annotations.TestCommandManager;
|
||||
import cloud.commandframework.annotations.TestCommandSender;
|
||||
import cloud.commandframework.arguments.CommandArgument;
|
||||
import cloud.commandframework.arguments.compound.FlagArgument;
|
||||
import cloud.commandframework.arguments.flags.CommandFlag;
|
||||
import cloud.commandframework.arguments.parser.StandardParameters;
|
||||
import cloud.commandframework.meta.CommandMeta;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class StringProcessingTest {
|
||||
|
||||
private AnnotationParser<TestCommandSender> annotationParser;
|
||||
private TestCommandManager commandManager;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.commandManager = new TestCommandManager();
|
||||
this.annotationParser = new AnnotationParser<>(
|
||||
this.commandManager,
|
||||
TestCommandSender.class,
|
||||
p -> CommandMeta.simple()
|
||||
.with(CommandMeta.DESCRIPTION, p.get(StandardParameters.DESCRIPTION, "No description"))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Tests @CommandMethod, @CommandPermission, @CommandDescription, @Argument & @Flag")
|
||||
@SuppressWarnings("unchecked")
|
||||
void testStringProcessing() {
|
||||
// Arrange
|
||||
final String testProperty = ThreadLocalRandom.current()
|
||||
.ints()
|
||||
.mapToObj(Integer::toString)
|
||||
.limit(32)
|
||||
.collect(Collectors.joining());
|
||||
final String testFlagName = ThreadLocalRandom.current()
|
||||
.ints()
|
||||
.mapToObj(Integer::toString)
|
||||
.limit(32)
|
||||
.collect(Collectors.joining());
|
||||
this.annotationParser.stringProcessor(
|
||||
new PropertyReplacingStringProcessor(
|
||||
s -> ImmutableMap.of(
|
||||
"property.test", testProperty,
|
||||
"property.arg", "argument",
|
||||
"property.flag", testFlagName
|
||||
).get(s)
|
||||
)
|
||||
);
|
||||
final TestClassA testClassA = new TestClassA();
|
||||
|
||||
// Act
|
||||
this.annotationParser.parse(testClassA);
|
||||
|
||||
// Assert
|
||||
final List<Command<TestCommandSender>> commands = new ArrayList<>(this.commandManager.getCommands());
|
||||
assertThat(commands).hasSize(1);
|
||||
|
||||
final Command<TestCommandSender> command = commands.get(0);
|
||||
assertThat(command.toString()).isEqualTo(String.format("%s argument flags", testProperty));
|
||||
assertThat(command.getCommandPermission().toString()).isEqualTo(testProperty);
|
||||
assertThat(command.getCommandMeta().get(CommandMeta.DESCRIPTION)).hasValue(testProperty);
|
||||
|
||||
final List<CommandArgument<TestCommandSender, ?>> arguments = command.getArguments();
|
||||
assertThat(arguments).hasSize(3);
|
||||
|
||||
final FlagArgument<TestCommandSender> flagArgument = (FlagArgument<TestCommandSender>) arguments.get(2);
|
||||
assertThat(flagArgument).isNotNull();
|
||||
|
||||
final List<CommandFlag<?>> flags = new ArrayList<>(flagArgument.getFlags());
|
||||
assertThat(flags).hasSize(1);
|
||||
assertThat(flags.get(0).getName()).isEqualTo(testFlagName);
|
||||
}
|
||||
|
||||
|
||||
private static class TestClassA {
|
||||
|
||||
@CommandDescription("${property.test}")
|
||||
@CommandPermission("${property.test}")
|
||||
@CommandMethod("${property.test} <argument>")
|
||||
public void commandA(
|
||||
final TestCommandSender sender,
|
||||
@Argument("${property.arg}") final String arg,
|
||||
@Flag("${property.flag}") final String flag
|
||||
) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue