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
|
|
@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Annotations: Annotation string processors ([#353](https://github.com/Incendo/cloud/pull/353))
|
||||
|
||||
### Fixed
|
||||
- Fix missing caption registration for the regex caption ([#351](https://github.com/Incendo/cloud/pull/351))
|
||||
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ dependencies {
|
|||
testImplementation(libs.jupiterEngine)
|
||||
testImplementation(libs.mockitoCore)
|
||||
testImplementation(libs.mockitoKotlin)
|
||||
testImplementation(libs.mockitoJupiter)
|
||||
testImplementation(libs.truth)
|
||||
testImplementation(libs.truthJava8)
|
||||
errorprone(libs.errorproneCore)
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ public final class AnnotationParser<C> {
|
|||
private final MetaFactory metaFactory;
|
||||
private final FlagExtractor flagExtractor;
|
||||
|
||||
private StringProcessor stringProcessor;
|
||||
|
||||
/**
|
||||
* Construct a new annotation parser
|
||||
*
|
||||
|
|
@ -118,12 +120,12 @@ public final class AnnotationParser<C> {
|
|||
this.preprocessorMappers = new HashMap<>();
|
||||
this.builderModifiers = new HashMap<>();
|
||||
this.commandMethodFactories = new HashMap<>();
|
||||
this.flagExtractor = new FlagExtractor(manager);
|
||||
this.flagExtractor = new FlagExtractor(manager, this);
|
||||
this.registerAnnotationMapper(CommandDescription.class, d ->
|
||||
ParserParameters.single(StandardParameters.DESCRIPTION, d.value()));
|
||||
ParserParameters.single(StandardParameters.DESCRIPTION, this.processString(d.value())));
|
||||
this.registerPreprocessorMapper(Regex.class, annotation -> RegexPreprocessor.of(
|
||||
annotation.value(),
|
||||
Caption.of(annotation.failureCaption())
|
||||
this.processString(annotation.value()),
|
||||
Caption.of(this.processString(annotation.failureCaption()))
|
||||
));
|
||||
this.getParameterInjectorRegistry().registerInjector(
|
||||
String[].class,
|
||||
|
|
@ -131,6 +133,7 @@ public final class AnnotationParser<C> {
|
|||
? null
|
||||
: context.getRawInput().toArray(new String[0])
|
||||
);
|
||||
this.stringProcessor = StringProcessor.noOp();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -273,6 +276,48 @@ public final class AnnotationParser<C> {
|
|||
return this.manager.parameterInjectorRegistry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string processor used by this parser.
|
||||
*
|
||||
* @return the string processor
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public @NonNull StringProcessor stringProcessor() {
|
||||
return this.stringProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the string processor of this parser.
|
||||
*
|
||||
* @param stringProcessor the new string processor
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public void stringProcessor(final @NonNull StringProcessor stringProcessor) {
|
||||
this.stringProcessor = stringProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the {@code input} string and returns the processed result.
|
||||
*
|
||||
* @param input the input string
|
||||
* @return the processed string
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public @NonNull String processString(final @NonNull String input) {
|
||||
return this.stringProcessor().processString(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the input {@code strings} and returns the processed result.
|
||||
*
|
||||
* @param strings the input strings
|
||||
* @return the processed strings
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public @NonNull String[] processStrings(final @NonNull String[] strings) {
|
||||
return Arrays.stream(strings).map(this::processString).toArray(String[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a class instance of {@link CommandMethod} annotations and attempt to
|
||||
* compile them into {@link Command} instances
|
||||
|
|
@ -336,7 +381,7 @@ public final class AnnotationParser<C> {
|
|||
}
|
||||
try {
|
||||
this.manager.getParserRegistry().registerSuggestionProvider(
|
||||
suggestions.value(),
|
||||
this.processString(suggestions.value()),
|
||||
new MethodSuggestionsProvider<>(instance, method)
|
||||
);
|
||||
} catch (final Exception e) {
|
||||
|
|
@ -367,15 +412,16 @@ public final class AnnotationParser<C> {
|
|||
));
|
||||
}
|
||||
try {
|
||||
final String suggestions = this.processString(parser.suggestions());
|
||||
final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider;
|
||||
if (parser.suggestions().isEmpty()) {
|
||||
if (suggestions.isEmpty()) {
|
||||
suggestionsProvider = (context, input) -> Collections.emptyList();
|
||||
} else {
|
||||
suggestionsProvider = this.manager.getParserRegistry().getSuggestionProvider(parser.suggestions())
|
||||
suggestionsProvider = this.manager.getParserRegistry().getSuggestionProvider(suggestions)
|
||||
.orElseThrow(() -> new NullPointerException(
|
||||
String.format(
|
||||
"Cannot find the suggestions provider with name '%s'",
|
||||
parser.suggestions()
|
||||
suggestions
|
||||
)
|
||||
));
|
||||
}
|
||||
|
|
@ -386,14 +432,15 @@ public final class AnnotationParser<C> {
|
|||
);
|
||||
final Function<ParserParameters, ArgumentParser<C, ?>> parserFunction =
|
||||
parameters -> methodArgumentParser;
|
||||
if (parser.name().isEmpty()) {
|
||||
final String name = this.processString(parser.name());
|
||||
if (name.isEmpty()) {
|
||||
this.manager.getParserRegistry().registerParserSupplier(
|
||||
TypeToken.get(method.getGenericReturnType()),
|
||||
parserFunction
|
||||
);
|
||||
} else {
|
||||
this.manager.getParserRegistry().registerNamedParserSupplier(
|
||||
parser.name(),
|
||||
name,
|
||||
parserFunction
|
||||
);
|
||||
}
|
||||
|
|
@ -410,12 +457,12 @@ public final class AnnotationParser<C> {
|
|||
) {
|
||||
final AnnotationAccessor classAnnotations = AnnotationAccessor.of(instance.getClass());
|
||||
final CommandMethod classCommandMethod = classAnnotations.annotation(CommandMethod.class);
|
||||
final String syntaxPrefix = classCommandMethod == null ? "" : (classCommandMethod.value() + " ");
|
||||
final String syntaxPrefix = classCommandMethod == null ? "" : (this.processString(classCommandMethod.value()) + " ");
|
||||
final Collection<Command<C>> commands = new ArrayList<>();
|
||||
for (final CommandMethodPair commandMethodPair : methodPairs) {
|
||||
final CommandMethod commandMethod = commandMethodPair.getCommandMethod();
|
||||
final Method method = commandMethodPair.getMethod();
|
||||
final String syntax = syntaxPrefix + commandMethod.value();
|
||||
final String syntax = syntaxPrefix + this.processString(commandMethod.value());
|
||||
final List<SyntaxFragment> tokens = this.syntaxParser.apply(syntax);
|
||||
/* Determine command name */
|
||||
final String commandToken = syntax.split(" ")[0].split("\\|")[0];
|
||||
|
|
@ -438,9 +485,10 @@ public final class AnnotationParser<C> {
|
|||
final Map<CommandArgument<C, ?>, String> argumentDescriptions = new HashMap<>();
|
||||
/* Go through all annotated parameters and build up the argument tree */
|
||||
for (final ArgumentParameterPair argumentPair : arguments) {
|
||||
final String argumentName = this.processString(argumentPair.argumentName());
|
||||
final CommandArgument<C, ?> argument = this.buildArgument(
|
||||
method,
|
||||
this.findSyntaxFragment(tokens, argumentPair.argumentName()),
|
||||
this.findSyntaxFragment(tokens, argumentName),
|
||||
argumentPair
|
||||
);
|
||||
commandArguments.put(argument.getName(), argument);
|
||||
|
|
@ -482,7 +530,7 @@ public final class AnnotationParser<C> {
|
|||
|
||||
final CommandPermission commandPermission = getMethodOrClassAnnotation(method, CommandPermission.class);
|
||||
if (commandPermission != null) {
|
||||
builder = builder.permission(commandPermission.value());
|
||||
builder = builder.permission(this.processString(commandPermission.value()));
|
||||
}
|
||||
|
||||
if (commandMethod.requiredSender() != Object.class) {
|
||||
|
|
@ -496,7 +544,7 @@ public final class AnnotationParser<C> {
|
|||
instance,
|
||||
commandArguments,
|
||||
method,
|
||||
this.getParameterInjectorRegistry()
|
||||
this /* annotationParser */
|
||||
);
|
||||
|
||||
/* Create the command execution handler */
|
||||
|
|
@ -542,7 +590,7 @@ public final class AnnotationParser<C> {
|
|||
/* Check if we need to construct a proxy */
|
||||
if (method.isAnnotationPresent(ProxiedBy.class)) {
|
||||
final ProxiedBy proxyAnnotation = method.getAnnotation(ProxiedBy.class);
|
||||
final String proxy = proxyAnnotation.value();
|
||||
final String proxy = this.processString(proxyAnnotation.value());
|
||||
if (proxy.contains(" ")) {
|
||||
throw new IllegalArgumentException("@ProxiedBy proxies may only contain single literals");
|
||||
}
|
||||
|
|
@ -605,16 +653,17 @@ public final class AnnotationParser<C> {
|
|||
)));
|
||||
}
|
||||
/* Check whether or not the corresponding method parameter actually exists */
|
||||
final String argumentName = this.processString(argumentPair.argumentName());
|
||||
if (syntaxFragment == null || syntaxFragment.getArgumentMode() == ArgumentMode.LITERAL) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Invalid command argument '%s' in method '%s': "
|
||||
+ "Missing syntax mapping", argumentPair.argumentName(), method.getName()));
|
||||
+ "Missing syntax mapping", argumentName, method.getName()));
|
||||
}
|
||||
final Argument argument = argumentPair.getArgument();
|
||||
/* Create the argument builder */
|
||||
@SuppressWarnings("rawtypes") final CommandArgument.Builder argumentBuilder = CommandArgument.ofType(
|
||||
parameter.getType(),
|
||||
argumentPair.argumentName()
|
||||
argumentName
|
||||
);
|
||||
/* Set the argument requirement status */
|
||||
if (syntaxFragment.getArgumentMode() == ArgumentMode.OPTIONAL) {
|
||||
|
|
|
|||
|
|
@ -45,9 +45,14 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
final class FlagExtractor implements Function<@NonNull Method, Collection<@NonNull CommandFlag<?>>> {
|
||||
|
||||
private final CommandManager<?> commandManager;
|
||||
private final AnnotationParser<?> annotationParser;
|
||||
|
||||
FlagExtractor(final @NonNull CommandManager<?> commandManager) {
|
||||
FlagExtractor(
|
||||
final @NonNull CommandManager<?> commandManager,
|
||||
final @NonNull AnnotationParser<?> annotationParser
|
||||
) {
|
||||
this.commandManager = commandManager;
|
||||
this.annotationParser = annotationParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -59,11 +64,12 @@ final class FlagExtractor implements Function<@NonNull Method, Collection<@NonNu
|
|||
continue;
|
||||
}
|
||||
final Flag flag = parameter.getAnnotation(Flag.class);
|
||||
final String flagName = this.annotationParser.processString(flag.value());
|
||||
final CommandFlag.Builder<Void> builder = this.commandManager
|
||||
.flagBuilder(flag.value())
|
||||
.withDescription(ArgumentDescription.of(flag.description()))
|
||||
.withAliases(flag.aliases())
|
||||
.withPermission(Permission.of(flag.permission()));
|
||||
.flagBuilder(this.annotationParser.processString(flagName))
|
||||
.withDescription(ArgumentDescription.of(this.annotationParser.processString(flag.description())))
|
||||
.withAliases(this.annotationParser.processStrings(flag.aliases()))
|
||||
.withPermission(Permission.of(this.annotationParser.processString(flag.permission())));
|
||||
if (parameter.getType().equals(boolean.class)) {
|
||||
flags.add(builder.build());
|
||||
} else {
|
||||
|
|
@ -71,28 +77,30 @@ final class FlagExtractor implements Function<@NonNull Method, Collection<@NonNu
|
|||
final Collection<Annotation> annotations = Arrays.asList(parameter.getAnnotations());
|
||||
final ParserRegistry<?> registry = this.commandManager.getParserRegistry();
|
||||
final ArgumentParser<?, ?> parser;
|
||||
if (flag.parserName().isEmpty()) {
|
||||
final String parserName = this.annotationParser.processString(flag.parserName());
|
||||
if (parserName.isEmpty()) {
|
||||
parser = registry.createParser(token, registry.parseAnnotations(token, annotations))
|
||||
.orElse(null);
|
||||
} else {
|
||||
parser = registry.createParser(flag.parserName(), registry.parseAnnotations(token, annotations))
|
||||
parser = registry.createParser(parserName, registry.parseAnnotations(token, annotations))
|
||||
.orElse(null);
|
||||
}
|
||||
if (parser == null) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Cannot find parser for type '%s' for flag '%s' in method '%s'",
|
||||
parameter.getType().getCanonicalName(), flag.value(), method.getName()
|
||||
parameter.getType().getCanonicalName(), flagName, method.getName()
|
||||
));
|
||||
}
|
||||
final BiFunction<?, @NonNull String, @NonNull List<String>> suggestionProvider;
|
||||
if (!flag.suggestions().isEmpty()) {
|
||||
suggestionProvider = registry.getSuggestionProvider(flag.suggestions()).orElse(null);
|
||||
final String suggestions = this.annotationParser.processString(flag.suggestions());
|
||||
if (!suggestions.isEmpty()) {
|
||||
suggestionProvider = registry.getSuggestionProvider(suggestions).orElse(null);
|
||||
} else {
|
||||
suggestionProvider = null;
|
||||
}
|
||||
final CommandArgument.Builder argumentBuilder0 = CommandArgument.ofType(
|
||||
parameter.getType(),
|
||||
flag.value()
|
||||
flagName
|
||||
);
|
||||
final CommandArgument.Builder argumentBuilder = argumentBuilder0.asRequired()
|
||||
.manager(this.commandManager)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public class MethodCommandExecutionHandler<C> implements CommandExecutionHandler
|
|||
private final Parameter[] parameters;
|
||||
private final MethodHandle methodHandle;
|
||||
private final AnnotationAccessor annotationAccessor;
|
||||
private final AnnotationParser<C> annotationParser;
|
||||
|
||||
/**
|
||||
* Constructs a new method command execution handler
|
||||
|
|
@ -65,6 +66,7 @@ public class MethodCommandExecutionHandler<C> implements CommandExecutionHandler
|
|||
this.methodHandle = MethodHandles.lookup().unreflect(context.method).bindTo(context.instance);
|
||||
this.parameters = context.method.getParameters();
|
||||
this.annotationAccessor = AnnotationAccessor.of(context.method);
|
||||
this.annotationParser = context.annotationParser();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -123,10 +125,11 @@ public class MethodCommandExecutionHandler<C> implements CommandExecutionHandler
|
|||
}
|
||||
} else if (parameter.isAnnotationPresent(Flag.class)) {
|
||||
final Flag flag = parameter.getAnnotation(Flag.class);
|
||||
final String flagName = this.annotationParser.processString(flag.value());
|
||||
if (parameter.getType() == boolean.class) {
|
||||
arguments.add(flagContext.isPresent(flag.value()));
|
||||
arguments.add(flagContext.isPresent(flagName));
|
||||
} else {
|
||||
arguments.add(flagContext.getValue(flag.value(), null));
|
||||
arguments.add(flagContext.getValue(flagName, null));
|
||||
}
|
||||
} else {
|
||||
if (parameter.getType().isAssignableFrom(commandContext.getSender().getClass())) {
|
||||
|
|
@ -205,18 +208,20 @@ public class MethodCommandExecutionHandler<C> implements CommandExecutionHandler
|
|||
private final Map<String, CommandArgument<C, ?>> commandArguments;
|
||||
private final Method method;
|
||||
private final ParameterInjectorRegistry<C> injectorRegistry;
|
||||
private final AnnotationParser<C> annotationParser;
|
||||
|
||||
CommandMethodContext(
|
||||
final @NonNull Object instance,
|
||||
final @NonNull Map<@NonNull String, @NonNull CommandArgument<@NonNull C, @NonNull ?>> commandArguments,
|
||||
final @NonNull Method method,
|
||||
final @NonNull ParameterInjectorRegistry<C> injectorRegistry
|
||||
final @NonNull AnnotationParser<C> annotationParser
|
||||
) {
|
||||
this.instance = instance;
|
||||
this.commandArguments = commandArguments;
|
||||
this.method = method;
|
||||
this.method.setAccessible(true);
|
||||
this.injectorRegistry = injectorRegistry;
|
||||
this.injectorRegistry = annotationParser.getParameterInjectorRegistry();
|
||||
this.annotationParser = annotationParser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -259,6 +264,16 @@ public class MethodCommandExecutionHandler<C> implements CommandExecutionHandler
|
|||
return this.injectorRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* The annotation parser
|
||||
*
|
||||
* @return Annotation parser
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public @NonNull AnnotationParser<C> annotationParser() {
|
||||
return this.annotationParser;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// 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 java.util.function.Function;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* {@link StringProcessor} that replaces matches of a given {@link Pattern}.
|
||||
*
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public class PatternReplacingStringProcessor implements StringProcessor {
|
||||
|
||||
private final Pattern pattern;
|
||||
private final Function<MatchResult, String> replacementProvider;
|
||||
|
||||
/**
|
||||
* Creates a new property replacing string processor.
|
||||
*
|
||||
* @param pattern the pattern to search for
|
||||
* @param replacementProvider function generating the replacement strings
|
||||
*/
|
||||
public PatternReplacingStringProcessor(
|
||||
final @NonNull Pattern pattern,
|
||||
final @NonNull Function<@NonNull MatchResult, @Nullable String> replacementProvider
|
||||
) {
|
||||
this.pattern = pattern;
|
||||
this.replacementProvider = replacementProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NonNull String processString(
|
||||
@NonNull final String input
|
||||
) {
|
||||
final Matcher matcher = this.pattern.matcher(input);
|
||||
|
||||
// Kind of copied from the JDK 9+ implementation of "Matcher#replaceFirst"
|
||||
final StringBuffer stringBuffer = new StringBuffer();
|
||||
while (matcher.find()) {
|
||||
final String replacement = this.replacementProvider.apply(matcher);
|
||||
matcher.appendReplacement(stringBuffer, replacement == null ? "$0" : replacement);
|
||||
}
|
||||
matcher.appendTail(stringBuffer);
|
||||
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// 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 java.util.function.Function;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Pattern;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* {@link PropertyReplacementProvider} that replaces all sub-strings with the format
|
||||
* {@code ${some.property}} with a function-generated string.
|
||||
*
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public class PropertyReplacingStringProcessor extends PatternReplacingStringProcessor {
|
||||
|
||||
public static final Pattern PROPERTY_REGEX = Pattern.compile("\\$\\{(\\S+)}");
|
||||
|
||||
/**
|
||||
* Creates a new property replacing string processor.
|
||||
*
|
||||
* @param replacementProvider function generating the replacement strings
|
||||
*/
|
||||
public PropertyReplacingStringProcessor(
|
||||
final @NonNull Function<@NonNull String, @Nullable String> replacementProvider
|
||||
) {
|
||||
super(PROPERTY_REGEX, new PropertyReplacementProvider(replacementProvider));
|
||||
}
|
||||
|
||||
|
||||
private static final class PropertyReplacementProvider implements Function<@NonNull MatchResult, @Nullable String> {
|
||||
|
||||
private final Function<String, String> replacementProvider;
|
||||
|
||||
private PropertyReplacementProvider(
|
||||
final @NonNull Function<String, String> replacementProvider
|
||||
) {
|
||||
this.replacementProvider = replacementProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String apply(final @NonNull MatchResult matchResult) {
|
||||
return this.replacementProvider.apply(matchResult.group(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// 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 org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Processor that intercepts all cloud annotation strings.
|
||||
*
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface StringProcessor {
|
||||
|
||||
/**
|
||||
* Returns a string processor that simply returns the input string.
|
||||
*
|
||||
* @return no-op string processor
|
||||
*/
|
||||
static @NonNull StringProcessor noOp() {
|
||||
return new NoOpStringProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the {@code input} string and returns the processed result.
|
||||
* <p>
|
||||
* This should always return a non-{@code null} result. If the input string
|
||||
* isn't applicable to the processor implementation, the original string should
|
||||
* be returned.
|
||||
*
|
||||
* @param input the input string
|
||||
* @return the processed string
|
||||
*/
|
||||
@NonNull String processString(@NonNull String input);
|
||||
|
||||
|
||||
final class NoOpStringProcessor implements StringProcessor {
|
||||
|
||||
@Override
|
||||
public @NonNull String processString(final @NonNull String input) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -112,6 +112,7 @@ public final class RegexPreprocessor<C> implements BiFunction<@NonNull CommandCo
|
|||
/**
|
||||
* Exception thrown when input fails regex matching in {@link RegexPreprocessor}
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static final class RegexValidationException extends IllegalArgumentException {
|
||||
|
||||
private static final long serialVersionUID = 747826566058072233L;
|
||||
|
|
|
|||
|
|
@ -290,6 +290,7 @@ public final class ByteArgument<C> extends CommandArgument<C, Byte> {
|
|||
/**
|
||||
* Byte parse exception
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static final class ByteParseException extends NumberParseException {
|
||||
|
||||
private static final long serialVersionUID = -4724241304872989208L;
|
||||
|
|
|
|||
|
|
@ -279,6 +279,7 @@ public final class DoubleArgument<C> extends CommandArgument<C, Double> {
|
|||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final class DoubleParseException extends NumberParseException {
|
||||
|
||||
private static final long serialVersionUID = 1764554911581976586L;
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ public final class FloatArgument<C> extends CommandArgument<C, Float> {
|
|||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final class FloatParseException extends NumberParseException {
|
||||
|
||||
private static final long serialVersionUID = -1162983846751812292L;
|
||||
|
|
|
|||
|
|
@ -337,6 +337,7 @@ public final class IntegerArgument<C> extends CommandArgument<C, Integer> {
|
|||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final class IntegerParseException extends NumberParseException {
|
||||
|
||||
private static final long serialVersionUID = -6933923056628373853L;
|
||||
|
|
|
|||
|
|
@ -282,6 +282,7 @@ public final class LongArgument<C> extends CommandArgument<C, Long> {
|
|||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final class LongParseException extends NumberParseException {
|
||||
|
||||
private static final long serialVersionUID = 4366856282301198232L;
|
||||
|
|
|
|||
|
|
@ -279,6 +279,7 @@ public final class ShortArgument<C> extends CommandArgument<C, Short> {
|
|||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final class ShortParseException extends NumberParseException {
|
||||
|
||||
private static final long serialVersionUID = -478674263339091032L;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
* is being inserted into a {@link CommandTree} and an ambiguity
|
||||
* is detected.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@SuppressWarnings({"unused", "serial"})
|
||||
public final class AmbiguousNodeException extends IllegalStateException {
|
||||
|
||||
private static final long serialVersionUID = -200207173805584709L;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CommandExecutionException extends IllegalArgumentException {
|
||||
|
||||
private static final long serialVersionUID = -4785446899438294661L;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
/**
|
||||
* Exception thrown when parsing user input into a command
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@SuppressWarnings({"unused", "serial"})
|
||||
public class CommandParseException extends IllegalArgumentException {
|
||||
|
||||
private static final long serialVersionUID = -2415981126382517435L;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
/**
|
||||
* Exception thrown when an invalid command sender tries to execute a command
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public final class InvalidCommandSenderException extends CommandParseException {
|
||||
|
||||
private static final long serialVersionUID = 7372142477529875598L;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
* Thrown when a {@link CommandArgument}
|
||||
* that is registered as a leaf node, does not contain an owning {@link Command}
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@SuppressWarnings({"unused", "serial"})
|
||||
public final class NoCommandInLeafException extends IllegalStateException {
|
||||
|
||||
private static final long serialVersionUID = 3373529875213310821L;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
* Exception thrown when a command sender misses a permission required
|
||||
* to execute a {@link Command}
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@SuppressWarnings({"unused", "serial"})
|
||||
public class NoPermissionException extends CommandParseException {
|
||||
|
||||
private static final long serialVersionUID = 7103413337750692843L;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import cloud.commandframework.context.CommandContext;
|
|||
import java.util.Arrays;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ParserException extends IllegalArgumentException {
|
||||
|
||||
private static final long serialVersionUID = -4409795575435072170L;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ versions:
|
|||
jupiterEngine : 5.8.2
|
||||
mockitoCore : 4.1.0
|
||||
mockitoKotlin : 4.0.0
|
||||
mockitoJupiter: 4.5.1
|
||||
truth : 1.1.3
|
||||
|
||||
# build-logic
|
||||
|
|
@ -206,6 +207,10 @@ dependencies:
|
|||
group: org.mockito.kotlin
|
||||
name: mockito-kotlin
|
||||
version: { ref: mockitoKotlin }
|
||||
mockitoJupiter:
|
||||
group: org.mockito
|
||||
name: mockito-junit-jupiter
|
||||
version: { ref: mockitoJupiter }
|
||||
truth:
|
||||
group: com.google.truth
|
||||
name: truth
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue