From 6ab1c8a2e0b114870ede4c7b5037399fbc719fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Mon, 12 Oct 2020 18:13:23 +0200 Subject: [PATCH] :sparkles: Merge pull request #43 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: Add a new caption system to allow for the configuration of… * :sparkles: Add caption support to all numerical types * Add more standard pasres to the registry * Add default messages for captions * :sparkles: Improve captions in core * Add captions for Bukkit * :sparkles: Add FactoryDelegatingCaptionRegistry.java --- .../annotations/AnnotationParser.java | 6 +- .../commandframework/annotations/Regex.java | 8 ++ .../commandframework/CommandManager.java | 40 +++++- .../preprocessor/RegexPreprocessor.java | 58 +++++++-- .../arguments/standard/BooleanArgument.java | 29 +++-- .../arguments/standard/ByteArgument.java | 32 +++-- .../arguments/standard/CharArgument.java | 23 ++-- .../arguments/standard/DoubleArgument.java | 36 ++++-- .../arguments/standard/EnumArgument.java | 23 ++-- .../arguments/standard/FloatArgument.java | 36 ++++-- .../arguments/standard/IntegerArgument.java | 36 ++++-- .../arguments/standard/LongArgument.java | 36 ++++-- .../arguments/standard/ShortArgument.java | 36 ++++-- .../arguments/standard/StringArgument.java | 29 +++-- .../arguments/standard/UUIDArgument.java | 31 +++-- .../commandframework/captions/Caption.java | 85 +++++++++++++ .../captions/CaptionRegistry.java | 44 +++++++ .../captions/CaptionVariable.java | 70 +++++++++++ .../CaptionVariableReplacementHandler.java | 42 +++++++ .../FactoryDelegatingCaptionRegistry.java | 48 +++++++ .../captions/SimpleCaptionRegistry.java | 118 ++++++++++++++++++ .../SimpleCaptionRegistryFactory.java | 44 +++++++ ...mpleCaptionVariableReplacementHandler.java | 45 +++++++ .../captions/StandardCaptionKeys.java | 86 +++++++++++++ .../captions/package-info.java | 29 +++++ .../context/CommandContext.java | 41 +++++- .../context/CommandContextFactory.java | 12 +- .../StandardCommandContextFactory.java | 18 ++- .../parsing/NumberParseException.java | 39 +++--- .../exceptions/parsing/ParserException.java | 76 +++++++++++ .../commandframework/CommandTreeTest.java | 61 ++++++--- .../bukkit/BukkitCaptionKeys.java | 78 ++++++++++++ .../bukkit/BukkitCaptionRegistry.java | 80 ++++++++++++ .../bukkit/BukkitCaptionRegistryFactory.java | 44 +++++++ .../bukkit/BukkitCommandManager.java | 2 + .../bukkit/CloudCommodoreManager.java | 5 +- .../bukkit/parsers/EnchantmentArgument.java | 26 ++-- .../bukkit/parsers/MaterialArgument.java | 26 ++-- .../bukkit/parsers/OfflinePlayerArgument.java | 26 ++-- .../bukkit/parsers/PlayerArgument.java | 26 ++-- .../bukkit/parsers/WorldArgument.java | 26 ++-- .../MultiplePlayerSelectorArgument.java | 2 +- .../SinglePlayerSelectorArgument.java | 2 +- .../paper/PaperBrigadierListener.java | 4 +- .../VelocityPluginRegistrationHandler.java | 4 +- .../examples/bukkit/ExamplePlugin.java | 14 ++- 46 files changed, 1482 insertions(+), 200 deletions(-) create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/Caption.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/CaptionRegistry.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/CaptionVariable.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/CaptionVariableReplacementHandler.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/FactoryDelegatingCaptionRegistry.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistry.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistryFactory.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionVariableReplacementHandler.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/StandardCaptionKeys.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/captions/package-info.java create mode 100644 cloud-core/src/main/java/cloud/commandframework/exceptions/parsing/ParserException.java create mode 100644 cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionKeys.java create mode 100644 cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionRegistry.java create mode 100644 cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionRegistryFactory.java diff --git a/cloud-annotations/src/main/java/cloud/commandframework/annotations/AnnotationParser.java b/cloud-annotations/src/main/java/cloud/commandframework/annotations/AnnotationParser.java index 9bec82b4..5c684f20 100644 --- a/cloud-annotations/src/main/java/cloud/commandframework/annotations/AnnotationParser.java +++ b/cloud-annotations/src/main/java/cloud/commandframework/annotations/AnnotationParser.java @@ -34,6 +34,7 @@ import cloud.commandframework.arguments.parser.ParserParameter; import cloud.commandframework.arguments.parser.ParserParameters; import cloud.commandframework.arguments.parser.StandardParameters; import cloud.commandframework.arguments.preprocessor.RegexPreprocessor; +import cloud.commandframework.captions.Caption; import cloud.commandframework.context.CommandContext; import cloud.commandframework.execution.CommandExecutionHandler; import cloud.commandframework.extra.confirmation.CommandConfirmationManager; @@ -97,7 +98,10 @@ public final class AnnotationParser { this.flagExtractor = new FlagExtractor(manager); this.registerAnnotationMapper(CommandDescription.class, d -> ParserParameters.single(StandardParameters.DESCRIPTION, d.value())); - this.registerPreprocessorMapper(Regex.class, annotation -> RegexPreprocessor.of(annotation.value())); + this.registerPreprocessorMapper(Regex.class, annotation -> RegexPreprocessor.of( + annotation.value(), + Caption.of(annotation.failureCaption()) + )); } /** diff --git a/cloud-annotations/src/main/java/cloud/commandframework/annotations/Regex.java b/cloud-annotations/src/main/java/cloud/commandframework/annotations/Regex.java index 45e31b35..83b7b836 100644 --- a/cloud-annotations/src/main/java/cloud/commandframework/annotations/Regex.java +++ b/cloud-annotations/src/main/java/cloud/commandframework/annotations/Regex.java @@ -46,4 +46,12 @@ public @interface Regex { */ @NonNull String value(); + /** + * Key for the caption used to generate the failure exception. + * Defaults to {@link cloud.commandframework.captions.StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_REGEX} + * + * @return Failure caption key + */ + @NonNull String failureCaption() default "argument.parse.failure.regex"; + } diff --git a/cloud-core/src/main/java/cloud/commandframework/CommandManager.java b/cloud-core/src/main/java/cloud/commandframework/CommandManager.java index aee8054e..f4e2a113 100644 --- a/cloud-core/src/main/java/cloud/commandframework/CommandManager.java +++ b/cloud-core/src/main/java/cloud/commandframework/CommandManager.java @@ -33,6 +33,8 @@ import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ParserParameter; import cloud.commandframework.arguments.parser.ParserRegistry; import cloud.commandframework.arguments.parser.StandardParserRegistry; +import cloud.commandframework.captions.CaptionRegistry; +import cloud.commandframework.captions.SimpleCaptionRegistryFactory; import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContextFactory; import cloud.commandframework.context.StandardCommandContextFactory; @@ -92,6 +94,7 @@ public abstract class CommandManager { private CommandSyntaxFormatter commandSyntaxFormatter = new StandardCommandSyntaxFormatter<>(); private CommandSuggestionProcessor commandSuggestionProcessor = new FilteringCommandSuggestionProcessor<>(); private CommandRegistrationHandler commandRegistrationHandler; + private CaptionRegistry captionRegistry; /** * Create a new command manager instance @@ -111,6 +114,7 @@ public abstract class CommandManager { }, new AcceptingCommandPreprocessor<>()); this.servicePipeline.registerServiceType(new TypeToken>() { }, new AcceptingCommandPostprocessor<>()); + this.captionRegistry = new SimpleCaptionRegistryFactory().create(); } /** @@ -124,7 +128,11 @@ public abstract class CommandManager { final @NonNull C commandSender, final @NonNull String input ) { - final CommandContext context = this.commandContextFactory.create(false, commandSender); + final CommandContext context = this.commandContextFactory.create( + false, + commandSender, + this.captionRegistry + ); final LinkedList inputQueue = new CommandInputTokenizer(input).tokenize(); try { if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) { @@ -153,7 +161,8 @@ public abstract class CommandManager { ) { final CommandContext context = this.commandContextFactory.create( true, - commandSender + commandSender, + this.captionRegistry ); return this.commandSuggestionEngine.getSuggestions(context, input); } @@ -240,6 +249,33 @@ public abstract class CommandManager { return false; } + /** + * Get the caption registry + * + * @return Caption registry + */ + public final @NonNull CaptionRegistry getCaptionRegistry() { + return this.captionRegistry; + } + + /** + * Replace the caption registry + * + * @param captionRegistry New caption registry + */ + public final void setCaptionRegistry(final @NonNull CaptionRegistry captionRegistry) { + this.captionRegistry = captionRegistry; + } + + /** + * Replace the default caption registry + * + * @param captionRegistry Caption registry to use + */ + public final void registerDefaultCaptions(final @NonNull CaptionRegistry captionRegistry) { + this.captionRegistry = captionRegistry; + } + /** * Check if the command sender has the required permission. If the permission node is * empty, this should return {@code true} diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/preprocessor/RegexPreprocessor.java b/cloud-core/src/main/java/cloud/commandframework/arguments/preprocessor/RegexPreprocessor.java index b64ee4f8..e68c0e0d 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/preprocessor/RegexPreprocessor.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/preprocessor/RegexPreprocessor.java @@ -24,6 +24,9 @@ package cloud.commandframework.arguments.preprocessor; import cloud.commandframework.arguments.parser.ArgumentParseResult; +import cloud.commandframework.captions.Caption; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.captions.StandardCaptionKeys; import cloud.commandframework.context.CommandContext; import org.checkerframework.checker.nullness.qual.NonNull; @@ -42,21 +45,42 @@ public final class RegexPreprocessor implements BiFunction<@NonNull CommandCo private final String rawPattern; private final Predicate<@NonNull String> predicate; + private final Caption failureCaption; - private RegexPreprocessor(final @NonNull String pattern) { + private RegexPreprocessor( + final @NonNull String pattern, + final @NonNull Caption failureCaption + ) { this.rawPattern = pattern; this.predicate = Pattern.compile(pattern).asPredicate(); + this.failureCaption = failureCaption; } /** - * Create a new preprocessor + * Create a new preprocessor using {@link cloud.commandframework.captions.StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_REGEX} + * as the failure caption * * @param pattern Regular expression * @param Command sender type * @return Preprocessor instance */ public static @NonNull RegexPreprocessor of(final @NonNull String pattern) { - return new RegexPreprocessor<>(pattern); + return of(pattern, StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_REGEX); + } + + /** + * Create a new preprocessor + * + * @param pattern Regular expression + * @param Command sender type + * @param failureCaption Caption sent when the input is invalid + * @return Preprocessor instance + */ + public static @NonNull RegexPreprocessor of( + final @NonNull String pattern, + final @NonNull Caption failureCaption + ) { + return new RegexPreprocessor<>(pattern, failureCaption); } @Override @@ -73,7 +97,9 @@ public final class RegexPreprocessor implements BiFunction<@NonNull CommandCo return ArgumentParseResult.failure( new RegexValidationException( this.rawPattern, - head + head, + this.failureCaption, + context ) ); } @@ -86,21 +112,33 @@ public final class RegexPreprocessor implements BiFunction<@NonNull CommandCo private final String pattern; private final String failedString; + private final Caption failureCaption; + private final CommandContext commandContext; private RegexValidationException( - @NonNull final String pattern, - @NonNull final String failedString + final @NonNull String pattern, + final @NonNull String failedString, + final @NonNull Caption failureCaption, + final @NonNull CommandContext commandContext ) { this.pattern = pattern; this.failedString = failedString; + this.failureCaption = failureCaption; + this.commandContext = commandContext; } @Override public String getMessage() { - return String.format( - "Input '%s' does not match the required pattern '%s'", - failedString, - pattern + return this.commandContext.formatMessage( + this.failureCaption, + CaptionVariable.of( + "input", + this.failedString + ), + CaptionVariable.of( + "pattern", + this.pattern + ) ); } diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/BooleanArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/BooleanArgument.java index e0ffdb9c..4b05935f 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/BooleanArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/BooleanArgument.java @@ -26,7 +26,10 @@ package cloud.commandframework.arguments.standard; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.captions.StandardCaptionKeys; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.ParserException; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -183,7 +186,7 @@ public final class BooleanArgument extends CommandArgument { return ArgumentParseResult.success(false); } - return ArgumentParseResult.failure(new BooleanParseException(input, false)); + return ArgumentParseResult.failure(new BooleanParseException(input, false, commandContext)); } final String uppercaseInput = input.toUpperCase(); @@ -196,7 +199,7 @@ public final class BooleanArgument extends CommandArgument { return ArgumentParseResult.success(false); } - return ArgumentParseResult.failure(new BooleanParseException(input, true)); + return ArgumentParseResult.failure(new BooleanParseException(input, true, commandContext)); } @Override @@ -222,7 +225,7 @@ public final class BooleanArgument extends CommandArgument { /** * Boolean parse exception */ - public static final class BooleanParseException extends IllegalArgumentException { + public static final class BooleanParseException extends ParserException { private final String input; private final boolean liberal; @@ -230,17 +233,26 @@ public final class BooleanArgument extends CommandArgument { /** * Construct a new boolean parse exception * - * @param input String input - * @param liberal Liberal value + * @param input Input + * @param liberal Whether or not the parser allows truthy and falsy values, or strictly true/false + * @param context Command context */ public BooleanParseException( final @NonNull String input, - final boolean liberal + final boolean liberal, + final @NonNull CommandContext context ) { + super( + BooleanParser.class, + context, + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_BOOLEAN, + CaptionVariable.of("input", input) + ); this.input = input; this.liberal = liberal; } + /** * Get the supplied input * @@ -259,11 +271,6 @@ public final class BooleanArgument extends CommandArgument { return liberal; } - @Override - public String getMessage() { - return String.format("Could not parse boolean from '%s'.", input); - } - } } diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/ByteArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/ByteArgument.java index 1104abbf..ab75c8aa 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/ByteArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/ByteArgument.java @@ -198,15 +198,19 @@ public final class ByteArgument extends CommandArgument { new ByteParseException( input, this.min, - this.max + this.max, + commandContext )); } inputQueue.remove(); return ArgumentParseResult.success(value); } catch (final Exception e) { return ArgumentParseResult.failure( - new ByteParseException(input, this.min, - this.max + new ByteParseException( + input, + this.min, + this.max, + commandContext )); } } @@ -253,12 +257,24 @@ public final class ByteArgument extends CommandArgument { /** * Construct a new byte parse exception * - * @param input String input - * @param min Minimum value - * @param max Maximum value + * @param input String input + * @param min Minimum value + * @param max Maximum value + * @param context Command context */ - public ByteParseException(final @NonNull String input, final byte min, final byte max) { - super(input, min, max); + public ByteParseException( + final @NonNull String input, + final byte min, + final byte max, + final @NonNull CommandContext context + ) { + super( + input, + min, + max, + ByteParser.class, + context + ); } @Override diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/CharArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/CharArgument.java index d2df467f..c82a5eb8 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/CharArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/CharArgument.java @@ -26,7 +26,10 @@ package cloud.commandframework.arguments.standard; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.captions.StandardCaptionKeys; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.ParserException; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -130,7 +133,7 @@ public final class CharArgument extends CommandArgument { } if (input.length() != 1) { - return ArgumentParseResult.failure(new CharParseException(input)); + return ArgumentParseResult.failure(new CharParseException(input, commandContext)); } return ArgumentParseResult.success(input.charAt(0)); @@ -147,16 +150,23 @@ public final class CharArgument extends CommandArgument { /** * Char parse exception */ - public static final class CharParseException extends IllegalArgumentException { + public static final class CharParseException extends ParserException { private final String input; /** * Construct a new Char parse exception * - * @param input String input + * @param input String input + * @param context Command context */ - public CharParseException(final @NonNull String input) { + public CharParseException(final @NonNull String input, final @NonNull CommandContext context) { + super( + CharacterParser.class, + context, + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_CHAR, + CaptionVariable.of("input", input) + ); this.input = input; } @@ -169,11 +179,6 @@ public final class CharArgument extends CommandArgument { return input; } - @Override - public String getMessage() { - return String.format("'%s' is not a valid character.", input); - } - } } diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/DoubleArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/DoubleArgument.java index 8c2f5e62..bb1c5dbb 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/DoubleArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/DoubleArgument.java @@ -194,12 +194,22 @@ public final class DoubleArgument extends CommandArgument { try { final double value = Double.parseDouble(input); if (value < this.min || value > this.max) { - return ArgumentParseResult.failure(new DoubleParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new DoubleParseException( + input, + this.min, + this.max, + commandContext + )); } inputQueue.remove(); return ArgumentParseResult.success(value); } catch (final Exception e) { - return ArgumentParseResult.failure(new DoubleParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new DoubleParseException( + input, + this.min, + this.max, + commandContext + )); } } @@ -234,12 +244,24 @@ public final class DoubleArgument extends CommandArgument { /** * Construct a new double parse exception * - * @param input String input - * @param min Minimum value - * @param max Maximum value + * @param input String input + * @param min Minimum value + * @param max Maximum value + * @param commandContext Command context */ - public DoubleParseException(final @NonNull String input, final double min, final double max) { - super(input, min, max); + public DoubleParseException( + final @NonNull String input, + final double min, + final double max, + final @NonNull CommandContext commandContext + ) { + super( + input, + min, + max, + DoubleParser.class, + commandContext + ); } @Override diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/EnumArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/EnumArgument.java index c974b9da..2c136a3e 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/EnumArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/EnumArgument.java @@ -26,7 +26,10 @@ package cloud.commandframework.arguments.standard; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.captions.StandardCaptionKeys; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.ParserException; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -174,7 +177,7 @@ public class EnumArgument> extends CommandArgument { } } - return ArgumentParseResult.failure(new EnumParseException(input, this.enumClass)); + return ArgumentParseResult.failure(new EnumParseException(input, this.enumClass, commandContext)); } @Override @@ -193,7 +196,7 @@ public class EnumArgument> extends CommandArgument { } - public static final class EnumParseException extends IllegalArgumentException { + public static final class EnumParseException extends ParserException { private final String input; private final Class> enumClass; @@ -203,11 +206,20 @@ public class EnumArgument> extends CommandArgument { * * @param input Input * @param enumClass Enum class + * @param context Command context */ public EnumParseException( final @NonNull String input, - final @NonNull Class> enumClass + final @NonNull Class> enumClass, + final @NonNull CommandContext context ) { + super( + EnumParser.class, + context, + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_ENUM, + CaptionVariable.of("input", input), + CaptionVariable.of("acceptableValues", join(enumClass)) + ); this.input = input; this.enumClass = enumClass; } @@ -238,11 +250,6 @@ public class EnumArgument> extends CommandArgument { return this.enumClass; } - @Override - public String getMessage() { - return String.format("'%s' is not one of the following: %s", this.input, join(enumClass)); - } - } } diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/FloatArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/FloatArgument.java index cc01ee82..da75face 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/FloatArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/FloatArgument.java @@ -194,12 +194,22 @@ public final class FloatArgument extends CommandArgument { try { final float value = Float.parseFloat(input); if (value < this.min || value > this.max) { - return ArgumentParseResult.failure(new FloatParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new FloatParseException( + input, + this.min, + this.max, + commandContext + )); } inputQueue.remove(); return ArgumentParseResult.success(value); } catch (final Exception e) { - return ArgumentParseResult.failure(new FloatParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new FloatParseException( + input, + this.min, + this.max, + commandContext + )); } } @@ -234,12 +244,24 @@ public final class FloatArgument extends CommandArgument { /** * Construct a new float parse exception * - * @param input String input - * @param min Minimum value - * @param max Maximum value + * @param input String input + * @param min Minimum value + * @param max Maximum value + * @param commandContext Command context */ - public FloatParseException(final @NonNull String input, final float min, final float max) { - super(input, min, max); + public FloatParseException( + final @NonNull String input, + final float min, + final float max, + final @NonNull CommandContext commandContext + ) { + super( + input, + min, + max, + FloatParser.class, + commandContext + ); } @Override diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/IntegerArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/IntegerArgument.java index 615383c5..dd8fcc3f 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/IntegerArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/IntegerArgument.java @@ -223,12 +223,22 @@ public final class IntegerArgument extends CommandArgument { try { final int value = Integer.parseInt(input); if (value < this.min || value > this.max) { - return ArgumentParseResult.failure(new IntegerParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new IntegerParseException( + input, + this.min, + this.max, + commandContext + )); } inputQueue.remove(); return ArgumentParseResult.success(value); } catch (final Exception e) { - return ArgumentParseResult.failure(new IntegerParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new IntegerParseException( + input, + this.min, + this.max, + commandContext + )); } } @@ -271,12 +281,24 @@ public final class IntegerArgument extends CommandArgument { /** * Construct a new integer parse exception * - * @param input String input - * @param min Minimum value - * @param max Maximum value + * @param input String input + * @param min Minimum value + * @param max Maximum value + * @param commandContext Command context */ - public IntegerParseException(final @NonNull String input, final int min, final int max) { - super(input, min, max); + public IntegerParseException( + final @NonNull String input, + final int min, + final int max, + final @NonNull CommandContext commandContext + ) { + super( + input, + min, + max, + IntegerParser.class, + commandContext + ); } @Override diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/LongArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/LongArgument.java index 30831588..04d44fb6 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/LongArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/LongArgument.java @@ -188,12 +188,22 @@ public final class LongArgument extends CommandArgument { try { final long value = Long.parseLong(input); if (value < this.min || value > this.max) { - return ArgumentParseResult.failure(new LongParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new LongParseException( + input, + this.min, + this.max, + commandContext + )); } inputQueue.remove(); return ArgumentParseResult.success(value); } catch (final Exception e) { - return ArgumentParseResult.failure(new LongParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new LongParseException( + input, + this.min, + this.max, + commandContext + )); } } @@ -218,12 +228,24 @@ public final class LongArgument extends CommandArgument { /** * Construct a new long parse exception * - * @param input String input - * @param min Minimum value - * @param max Maximum value + * @param input String input + * @param min Minimum value + * @param max Maximum value + * @param commandContext Command context */ - public LongParseException(final @NonNull String input, final long min, final long max) { - super(input, min, max); + public LongParseException( + final @NonNull String input, + final long min, + final long max, + final @NonNull CommandContext commandContext + ) { + super( + input, + min, + max, + LongParser.class, + commandContext + ); } @Override diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/ShortArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/ShortArgument.java index 238c7973..21e5cd2a 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/ShortArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/ShortArgument.java @@ -194,12 +194,22 @@ public final class ShortArgument extends CommandArgument { try { final short value = Short.parseShort(input); if (value < this.min || value > this.max) { - return ArgumentParseResult.failure(new ShortParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new ShortParseException( + input, + this.min, + this.max, + commandContext + )); } inputQueue.remove(); return ArgumentParseResult.success(value); } catch (final Exception e) { - return ArgumentParseResult.failure(new ShortParseException(input, this.min, this.max)); + return ArgumentParseResult.failure(new ShortParseException( + input, + this.min, + this.max, + commandContext + )); } } @@ -242,12 +252,24 @@ public final class ShortArgument extends CommandArgument { /** * Construct a new short parse exception * - * @param input String input - * @param min Minimum value - * @param max Maximum value + * @param input String input + * @param min Minimum value + * @param max Maximum value + * @param commandContext Command context */ - public ShortParseException(final @NonNull String input, final short min, final short max) { - super(input, min, max); + public ShortParseException( + final @NonNull String input, + final short min, + final short max, + final @NonNull CommandContext commandContext + ) { + super( + input, + min, + max, + ShortParser.class, + commandContext + ); } @Override diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArgument.java index 952b36eb..7b5f99f0 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArgument.java @@ -26,7 +26,10 @@ package cloud.commandframework.arguments.standard; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.captions.StandardCaptionKeys; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.ParserException; import org.checkerframework.checker.nullness.qual.NonNull; import java.util.Collections; @@ -342,7 +345,7 @@ public final class StringArgument extends CommandArgument { } if (this.stringMode == StringMode.QUOTED && (!started || !finished)) { - return ArgumentParseResult.failure(new StringParseException(sj.toString(), StringMode.GREEDY)); + return ArgumentParseResult.failure(new StringParseException(sj.toString(), StringMode.GREEDY, commandContext)); } return ArgumentParseResult.success(sj.toString()); @@ -373,7 +376,7 @@ public final class StringArgument extends CommandArgument { } - public static final class StringParseException extends IllegalArgumentException { + public static final class StringParseException extends ParserException { private final String input; private final StringMode stringMode; @@ -383,8 +386,20 @@ public final class StringArgument extends CommandArgument { * * @param input Input * @param stringMode String mode + * @param context Command context */ - public StringParseException(final @NonNull String input, final @NonNull StringMode stringMode) { + public StringParseException( + final @NonNull String input, + final @NonNull StringMode stringMode, + final @NonNull CommandContext context + ) { + super( + StringParser.class, + context, + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_STRING, + CaptionVariable.of("input", input), + CaptionVariable.of("stringMode", stringMode.name()) + ); this.input = input; this.stringMode = stringMode; } @@ -408,14 +423,6 @@ public final class StringArgument extends CommandArgument { return this.stringMode; } - @Override - public String getMessage() { - if (this.stringMode == StringMode.QUOTED) { - return "The string needs to be surrounded by quotation marks"; - } - return String.format("'%s' is not a valid string", this.input); - } - } } diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/UUIDArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/UUIDArgument.java index ccc975cb..cd66c230 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/UUIDArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/UUIDArgument.java @@ -26,7 +26,10 @@ package cloud.commandframework.arguments.standard; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.captions.StandardCaptionKeys; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.ParserException; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -133,7 +136,7 @@ public final class UUIDArgument extends CommandArgument { inputQueue.remove(); return ArgumentParseResult.success(uuid); } catch (IllegalArgumentException e) { - return ArgumentParseResult.failure(new UUIDParseException(input)); + return ArgumentParseResult.failure(new UUIDParseException(input, commandContext)); } } @@ -145,22 +148,36 @@ public final class UUIDArgument extends CommandArgument { } - public static final class UUIDParseException extends IllegalArgumentException { + public static final class UUIDParseException extends ParserException { private final String input; /** * Construct a new UUID parse exception * - * @param input String input + * @param input String input + * @param context Command context */ - public UUIDParseException(final @NonNull String input) { + public UUIDParseException( + final @NonNull String input, + final @NonNull CommandContext context + ) { + super( + UUIDParser.class, + context, + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_UUID, + CaptionVariable.of("input", input) + ); this.input = input; } - @Override - public String getMessage() { - return String.format("Could not parse UUID from '%s'.", input); + /** + * Get the supplied input + * + * @return String value + */ + public String getInput() { + return input; } } diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/Caption.java b/cloud-core/src/main/java/cloud/commandframework/captions/Caption.java new file mode 100644 index 00000000..3a859f2b --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/Caption.java @@ -0,0 +1,85 @@ +// +// MIT License +// +// Copyright (c) 2020 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.captions; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Objects; + +/** + * This is a reference to a caption and does not contain any message itself + */ +public final class Caption { + + private final String key; + + private Caption(final @NonNull String key) { + this.key = key; + } + + /** + * Create a new caption with a given key + * + * @param key Caption key + * @return Created caption + */ + public static @NonNull Caption of(final @NonNull String key) { + return new Caption(key); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Caption caption = (Caption) o; + return Objects.equals(key, caption.key); + } + + /** + * Get the caption key + * + * @return Caption key + */ + public @NonNull String getKey() { + return this.key; + } + + @Override + public int hashCode() { + return Objects.hash(key); + } + + @Override + public String toString() { + return String.format( + "Caption{key='%s'}", + this.key + ); + } + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/CaptionRegistry.java b/cloud-core/src/main/java/cloud/commandframework/captions/CaptionRegistry.java new file mode 100644 index 00000000..e45129d8 --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/CaptionRegistry.java @@ -0,0 +1,44 @@ +// +// MIT License +// +// Copyright (c) 2020 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.captions; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Registry that allows for messages to be configurable per-sender + * + * @param Command sender type + */ +public interface CaptionRegistry { + + /** + * Get a caption for a specific sender + * + * @param caption Caption key + * @param sender Sender + * @return Caption + */ + @NonNull String getCaption(@NonNull Caption caption, @NonNull C sender); + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/CaptionVariable.java b/cloud-core/src/main/java/cloud/commandframework/captions/CaptionVariable.java new file mode 100644 index 00000000..552ee728 --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/CaptionVariable.java @@ -0,0 +1,70 @@ +// +// MIT License +// +// Copyright (c) 2020 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.captions; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Key-value pair used to replace variables in captions + */ +public final class CaptionVariable { + + private final String key; + private final String value; + + private CaptionVariable(final @NonNull String key, final @NonNull String value) { + this.key = key; + this.value = value; + } + + /** + * Create a new caption variable instance + * + * @param key Key + * @param value Replacement + * @return Created instance + */ + public static @NonNull CaptionVariable of(final @NonNull String key, final @NonNull String value) { + return new CaptionVariable(key, value); + } + + /** + * Get the variable key + * + * @return Key + */ + public @NonNull String getKey() { + return this.key; + } + + /** + * Get the variable value + * + * @return Value + */ + public @NonNull String getValue() { + return this.value; + } + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/CaptionVariableReplacementHandler.java b/cloud-core/src/main/java/cloud/commandframework/captions/CaptionVariableReplacementHandler.java new file mode 100644 index 00000000..036a0e20 --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/CaptionVariableReplacementHandler.java @@ -0,0 +1,42 @@ +// +// MIT License +// +// Copyright (c) 2020 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.captions; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Utility that replaces variables in captions + */ +public interface CaptionVariableReplacementHandler { + + /** + * Replace the variables in a message and return the result + * + * @param string Message to replace variables in + * @param variables Variables + * @return Transformed message + */ + @NonNull String replaceVariables(@NonNull String string, @NonNull CaptionVariable... variables); + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/FactoryDelegatingCaptionRegistry.java b/cloud-core/src/main/java/cloud/commandframework/captions/FactoryDelegatingCaptionRegistry.java new file mode 100644 index 00000000..0f3cd3bc --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/FactoryDelegatingCaptionRegistry.java @@ -0,0 +1,48 @@ +// +// MIT License +// +// Copyright (c) 2020 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.captions; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.function.BiFunction; + +/** + * Caption registry that delegates to factory methods + * + * @param Command sender type + */ +public interface FactoryDelegatingCaptionRegistry extends CaptionRegistry { + + /** + * Register a message factory + * + * @param caption Caption key + * @param factory Message factory + */ + void registerMessageFactory( + @NonNull Caption caption, + @NonNull BiFunction factory + ); + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistry.java b/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistry.java new file mode 100644 index 00000000..50f049e5 --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistry.java @@ -0,0 +1,118 @@ +// +// MIT License +// +// Copyright (c) 2020 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.captions; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiFunction; + +/** + * Caption registry that uses bi-functions to produce messages + * + * @param Command sender type + */ +public class SimpleCaptionRegistry implements FactoryDelegatingCaptionRegistry { + + /** + * Default caption for {@link StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_BOOLEAN}. + */ + public static final String ARGUMENT_PARSE_FAILURE_BOOLEAN = "Could not parse boolean from '{input}'"; + /** + * Default caption for {@link StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_NUMBER} + */ + public static final String ARGUMENT_PARSE_FAILURE_NUMBER = "'{input}' is not a valid number in the range {min} to {max}"; + /** + * Default caption for {@link StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_CHAR} + */ + public static final String ARGUMENT_PARSE_FAILURE_CHAR = "'{input}' is not a valid character"; + /** + * Default caption for {@link StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_ENUM} + */ + public static final String ARGUMENT_PARSE_FAILURE_ENUM = "'{input}' is not one of the following: {acceptableValues}"; + /** + * Default caption for {@link StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_STRING} + */ + public static final String ARGUMENT_PARSE_FAILURE_STRING = "'{input}' is not a valid string of type {stringMode}"; + /** + * Default caption for {@link StandardCaptionKeys#ARGUMENT_PARSE_FAILURE_UUID} + */ + public static final String ARGUMENT_PARSE_FAILURE_UUID = "'{input}' is not a valid UUID"; + + private final Map> messageFactories = new HashMap<>(); + + protected SimpleCaptionRegistry() { + this.registerMessageFactory( + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_BOOLEAN, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_BOOLEAN + ); + this.registerMessageFactory( + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_NUMBER, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_NUMBER + ); + this.registerMessageFactory( + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_CHAR, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_CHAR + ); + this.registerMessageFactory( + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_ENUM, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_ENUM + ); + this.registerMessageFactory( + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_STRING, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_STRING + ); + this.registerMessageFactory( + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_UUID, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_UUID + ); + } + + @Override + public final @NonNull String getCaption( + @NonNull final Caption caption, + @NonNull final C sender + ) { + final BiFunction messageFactory = this.messageFactories.get(caption); + if (messageFactory == null) { + throw new IllegalArgumentException( + String.format( + "There is no caption stored with key '%s'", + caption + ) + ); + } + return messageFactory.apply(caption, sender); + } + + @Override + public final void registerMessageFactory( + final @NonNull Caption caption, + final @NonNull BiFunction messageFactory + ) { + this.messageFactories.put(caption, messageFactory); + } + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistryFactory.java b/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistryFactory.java new file mode 100644 index 00000000..ed66d891 --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionRegistryFactory.java @@ -0,0 +1,44 @@ +// +// MIT License +// +// Copyright (c) 2020 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.captions; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Factory creating {@link SimpleCaptionRegistry} instances + * + * @param Command sender type + */ +public final class SimpleCaptionRegistryFactory { + + /** + * Create a new simple caption registry instance + * + * @return Created instance + */ + public @NonNull SimpleCaptionRegistry create() { + return new SimpleCaptionRegistry<>(); + } + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionVariableReplacementHandler.java b/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionVariableReplacementHandler.java new file mode 100644 index 00000000..cdbde519 --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/SimpleCaptionVariableReplacementHandler.java @@ -0,0 +1,45 @@ +// +// MIT License +// +// Copyright (c) 2020 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.captions; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Simple implementation of {@link CaptionVariableReplacementHandler} + */ +public final class SimpleCaptionVariableReplacementHandler implements CaptionVariableReplacementHandler { + + @Override + public @NonNull String replaceVariables( + final @NonNull String string, + final @NonNull CaptionVariable... variables + ) { + String replacedString = string; + for (final CaptionVariable variable : variables) { + replacedString = replacedString.replace(String.format("{%s}", variable.getKey()), variable.getValue()); + } + return replacedString; + } + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/StandardCaptionKeys.java b/cloud-core/src/main/java/cloud/commandframework/captions/StandardCaptionKeys.java new file mode 100644 index 00000000..2b2dde96 --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/StandardCaptionKeys.java @@ -0,0 +1,86 @@ +// +// MIT License +// +// Copyright (c) 2020 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.captions; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; + +/** + * {@link Caption} instances for messages in cloud-core + */ +public final class StandardCaptionKeys { + + private static final Collection RECOGNIZED_CAPTIONS = new LinkedList<>(); + + /** + * Variables: {input} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_BOOLEAN = of("argument.parse.failure.boolean"); + /** + * Variables: {input}, {min}, {max} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_NUMBER = of("argument.parse.failure.number"); + /** + * Variables: {input} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_CHAR = of("argument.parse.failure.char"); + /** + * Variables: {input}, {stringMode} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_STRING = of("argument.parse.failure.number"); + /** + * Variables: {input} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_UUID = of("argument.parse.failure.number"); + /** + * Variables: {input}, {acceptableValues} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_ENUM = of("argument.parse.failure.enum"); + /** + * Variables: {input}, {pattern} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_REGEX = of("argument.parse.failure.regex"); + + private StandardCaptionKeys() { + } + + private static @NonNull Caption of(final @NonNull String key) { + final Caption caption = Caption.of(key); + RECOGNIZED_CAPTIONS.add(caption); + return caption; + } + + /** + * Get an immutable collection containing all standard caption keys + * + * @return Immutable collection of keys + */ + public static @NonNull Collection<@NonNull Caption> getStandardCaptionKeys() { + return Collections.unmodifiableCollection(RECOGNIZED_CAPTIONS); + } + +} diff --git a/cloud-core/src/main/java/cloud/commandframework/captions/package-info.java b/cloud-core/src/main/java/cloud/commandframework/captions/package-info.java new file mode 100644 index 00000000..fe3b2f4c --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/captions/package-info.java @@ -0,0 +1,29 @@ +// +// MIT License +// +// Copyright (c) 2020 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. +// + +/** + * Classes allowing for the configuration of messages. Mainly used in parser + * exception messages + */ +package cloud.commandframework.captions; diff --git a/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java b/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java index 098d8bc4..0ff9f25e 100644 --- a/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java +++ b/cloud-core/src/main/java/cloud/commandframework/context/CommandContext.java @@ -25,6 +25,11 @@ package cloud.commandframework.context; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.flags.FlagContext; +import cloud.commandframework.captions.Caption; +import cloud.commandframework.captions.CaptionRegistry; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.captions.CaptionVariableReplacementHandler; +import cloud.commandframework.captions.SimpleCaptionVariableReplacementHandler; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -41,33 +46,57 @@ import java.util.Optional; */ public final class CommandContext { + private final CaptionVariableReplacementHandler captionVariableReplacementHandler = + new SimpleCaptionVariableReplacementHandler(); private final Map, ArgumentTiming> argumentTimings = new HashMap<>(); private final FlagContext flagContext = FlagContext.create(); private final Map internalStorage = new HashMap<>(); private final C commandSender; private final boolean suggestions; + private final CaptionRegistry captionRegistry; /** * Create a new command context instance * - * @param commandSender Sender of the command + * @param commandSender Sender of the command + * @param captionRegistry Caption registry */ - public CommandContext(final @NonNull C commandSender) { - this(false, commandSender); + public CommandContext(final @NonNull C commandSender, final @NonNull CaptionRegistry captionRegistry) { + this(false, commandSender, captionRegistry); } /** * Create a new command context instance * - * @param suggestions Whether or not the context is created for command suggestions - * @param commandSender Sender of the command + * @param suggestions Whether or not the context is created for command suggestions + * @param commandSender Sender of the command + * @param captionRegistry Caption registry */ public CommandContext( final boolean suggestions, - final @NonNull C commandSender + final @NonNull C commandSender, + final @NonNull CaptionRegistry captionRegistry ) { this.commandSender = commandSender; this.suggestions = suggestions; + this.captionRegistry = captionRegistry; + } + + /** + * Format a caption + * + * @param caption Caption key + * @param variables Replacements + * @return Formatted message + */ + public @NonNull String formatMessage( + final @NonNull Caption caption, + final @NonNull CaptionVariable... variables + ) { + return this.captionVariableReplacementHandler.replaceVariables( + this.captionRegistry.getCaption(caption, this.commandSender), + variables + ); } /** diff --git a/cloud-core/src/main/java/cloud/commandframework/context/CommandContextFactory.java b/cloud-core/src/main/java/cloud/commandframework/context/CommandContextFactory.java index 0b9dadb5..48cc1170 100644 --- a/cloud-core/src/main/java/cloud/commandframework/context/CommandContextFactory.java +++ b/cloud-core/src/main/java/cloud/commandframework/context/CommandContextFactory.java @@ -23,6 +23,7 @@ // package cloud.commandframework.context; +import cloud.commandframework.captions.CaptionRegistry; import org.checkerframework.checker.nullness.qual.NonNull; /** @@ -35,10 +36,15 @@ public interface CommandContextFactory { /** * Create a new command context * - * @param suggestions Whether or not the sender is requesting suggestions - * @param sender Command sender + * @param suggestions Whether or not the sender is requesting suggestions + * @param sender Command sender + * @param captionRegistry Caption registry * @return Command context */ - @NonNull CommandContext create(boolean suggestions, @NonNull C sender); + @NonNull CommandContext create( + boolean suggestions, + @NonNull C sender, + @NonNull CaptionRegistry captionRegistry + ); } diff --git a/cloud-core/src/main/java/cloud/commandframework/context/StandardCommandContextFactory.java b/cloud-core/src/main/java/cloud/commandframework/context/StandardCommandContextFactory.java index 966e8a69..d030aaa0 100644 --- a/cloud-core/src/main/java/cloud/commandframework/context/StandardCommandContextFactory.java +++ b/cloud-core/src/main/java/cloud/commandframework/context/StandardCommandContextFactory.java @@ -23,6 +23,7 @@ // package cloud.commandframework.context; +import cloud.commandframework.captions.CaptionRegistry; import org.checkerframework.checker.nullness.qual.NonNull; public final class StandardCommandContextFactory implements CommandContextFactory { @@ -30,13 +31,22 @@ public final class StandardCommandContextFactory implements CommandContextFac /** * Construct a new command context * - * @param suggestions Whether or not the sender is requesting suggestions - * @param sender Command sender + * @param suggestions Whether or not the sender is requesting suggestions + * @param sender Command sender + * @param captionRegistry Caption registry * @return Created context */ @Override - public CommandContext create(final boolean suggestions, final @NonNull C sender) { - return new CommandContext<>(suggestions, sender); + public CommandContext create( + final boolean suggestions, + final @NonNull C sender, + final @NonNull CaptionRegistry captionRegistry + ) { + return new CommandContext<>( + suggestions, + sender, + captionRegistry + ); } } diff --git a/cloud-core/src/main/java/cloud/commandframework/exceptions/parsing/NumberParseException.java b/cloud-core/src/main/java/cloud/commandframework/exceptions/parsing/NumberParseException.java index 24412e2f..cf21b926 100644 --- a/cloud-core/src/main/java/cloud/commandframework/exceptions/parsing/NumberParseException.java +++ b/cloud-core/src/main/java/cloud/commandframework/exceptions/parsing/NumberParseException.java @@ -23,9 +23,12 @@ // package cloud.commandframework.exceptions.parsing; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.captions.StandardCaptionKeys; +import cloud.commandframework.context.CommandContext; import org.checkerframework.checker.nullness.qual.NonNull; -public abstract class NumberParseException extends IllegalArgumentException { +public abstract class NumberParseException extends ParserException { private final String input; private final Number min; @@ -34,34 +37,32 @@ public abstract class NumberParseException extends IllegalArgumentException { /** * Construct a new number parse exception * - * @param input Input - * @param min Maximum value - * @param max Minimum value + * @param input Input + * @param min Maximum value + * @param max Minimum value + * @param parserClass Parser class + * @param context Command context */ public NumberParseException( final @NonNull String input, final @NonNull Number min, - final @NonNull Number max + final @NonNull Number max, + final @NonNull Class parserClass, + final @NonNull CommandContext context ) { + super( + parserClass, + context, + StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_NUMBER, + CaptionVariable.of("input", input), + CaptionVariable.of("min", String.valueOf(min)), + CaptionVariable.of("max", String.valueOf(max)) + ); this.input = input; this.min = min; this.max = max; } - @Override - public final String getMessage() { - if (this.hasMin() && this.hasMax()) { - return "'" + this.input + "' is not a valid " + this.getNumberType() + " in the range [" - + this.min + ", " + this.max + "]"; - } else if (this.hasMin()) { - return "'" + this.input + "' is not a valid " + this.getNumberType() + " above " + this.min; - } else if (this.hasMax()) { - return "'" + this.input + "' is not a valid " + this.getNumberType() + " below " + this.max; - } else { - return String.format("'%s' is not a valid %s", this.input, this.getNumberType()); - } - } - /** * Get the number type * diff --git a/cloud-core/src/main/java/cloud/commandframework/exceptions/parsing/ParserException.java b/cloud-core/src/main/java/cloud/commandframework/exceptions/parsing/ParserException.java new file mode 100644 index 00000000..d6ec25f0 --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/exceptions/parsing/ParserException.java @@ -0,0 +1,76 @@ +// +// MIT License +// +// Copyright (c) 2020 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.exceptions.parsing; + +import cloud.commandframework.captions.Caption; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.context.CommandContext; +import org.checkerframework.checker.nullness.qual.NonNull; + +public class ParserException extends IllegalArgumentException { + + private final Class argumentParser; + private final CommandContext context; + private final Caption errorCaption; + private final CaptionVariable[] captionVariables; + + protected ParserException( + final Class argumentParser, + final @NonNull CommandContext context, + final @NonNull Caption errorCaption, + final @NonNull CaptionVariable... captionVariables + ) { + this.argumentParser = argumentParser; + this.context = context; + this.errorCaption = errorCaption; + this.captionVariables = captionVariables; + } + + @Override + public final String getMessage() { + return this.context.formatMessage( + this.errorCaption, + this.captionVariables + ); + } + + /** + * Get the argument parser + * + * @return Argument parser + */ + public final @NonNull Class getArgumentParserClass() { + return this.argumentParser; + } + + /** + * Get the command context + * + * @return Command context + */ + public final @NonNull CommandContext getContext() { + return this.context; + } + +} diff --git a/cloud-core/src/test/java/cloud/commandframework/CommandTreeTest.java b/cloud-core/src/test/java/cloud/commandframework/CommandTreeTest.java index bc5a29ad..6f3adc02 100644 --- a/cloud-core/src/test/java/cloud/commandframework/CommandTreeTest.java +++ b/cloud-core/src/test/java/cloud/commandframework/CommandTreeTest.java @@ -150,7 +150,9 @@ class CommandTreeTest { final Pair, Exception> command = manager.getCommandTree() .parse( new CommandContext<>( - new TestCommandSender()), + new TestCommandSender(), + manager.getCaptionRegistry() + ), new LinkedList<>( Arrays.asList( "test", @@ -161,31 +163,60 @@ class CommandTreeTest { Assertions.assertEquals(NoPermissionException.class, manager.getCommandTree() .parse( new CommandContext<>( - new TestCommandSender()), + new TestCommandSender(), + manager.getCaptionRegistry() + ), new LinkedList<>( Arrays.asList("test", "two")) ) .getSecond().getClass()); manager.getCommandTree() - .parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt"))) - .getFirst().getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender())); + .parse( + new CommandContext<>(new TestCommandSender(), manager.getCaptionRegistry()), + new LinkedList<>(Arrays.asList("test", "opt")) + ) + .getFirst().getCommandExecutionHandler().execute(new CommandContext<>( + new TestCommandSender(), + manager.getCaptionRegistry() + )); manager.getCommandTree() - .parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt", "12"))) - .getFirst().getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender())); + .parse( + new CommandContext<>(new TestCommandSender(), manager.getCaptionRegistry()), + new LinkedList<>(Arrays.asList("test", "opt", "12")) + ) + .getFirst().getCommandExecutionHandler().execute(new CommandContext<>( + new TestCommandSender(), + manager.getCaptionRegistry() + )); } @Test void testAlias() { manager.getCommandTree() - .parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("other", "öpt", "12"))) - .getFirst().getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender())); + .parse( + new CommandContext<>( + new TestCommandSender(), + manager.getCaptionRegistry() + ), + new LinkedList<>(Arrays.asList( + "other", + "öpt", + "12" + )) + ) + .getFirst().getCommandExecutionHandler().execute(new CommandContext<>( + new TestCommandSender(), + manager.getCaptionRegistry() + )); } @Test void getSuggestions() { Assertions.assertFalse( - manager.getCommandTree().getSuggestions(new CommandContext<>(new TestCommandSender()), new LinkedList<>( - Collections.singletonList("test "))).isEmpty()); + manager.getCommandTree().getSuggestions( + new CommandContext<>(new TestCommandSender(), manager.getCaptionRegistry()), + new LinkedList<>(Collections.singletonList("test ")) + ).isEmpty()); } @Test @@ -263,11 +294,11 @@ class CommandTreeTest { @Test void testPreprocessors() { - manager.executeCommand(new TestCommandSender(), "preprocess abc").join(); - Assertions.assertThrows( - CompletionException.class, - () -> manager.executeCommand(new TestCommandSender(), "preprocess ab").join() - ); + manager.executeCommand(new TestCommandSender(), "preprocess abc").join(); + Assertions.assertThrows( + CompletionException.class, + () -> manager.executeCommand(new TestCommandSender(), "preprocess ab").join() + ); } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionKeys.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionKeys.java new file mode 100644 index 00000000..6e0758d8 --- /dev/null +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionKeys.java @@ -0,0 +1,78 @@ +// +// MIT License +// +// Copyright (c) 2020 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.bukkit; + +import cloud.commandframework.captions.Caption; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; + +/** + * {@link Caption} instances for messages in cloud-bukkit + */ +public final class BukkitCaptionKeys { + private static final Collection RECOGNIZED_CAPTIONS = new LinkedList<>(); + + /** + * Variables: {input} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_ENCHANTMENT = of("argument.parse.failure.enchantment"); + /** + * Variables: {input} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_MATERIAL = of("argument.parse.failure.material"); + /** + * Variables: {input} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_OFFLINEPLAYER = of("argument.parse.failure.offlineplayer"); + /** + * Variables: {input} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_PLAYER = of("argument.parse.failure.player"); + /** + * Variables: {input} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_WORLD = of("argument.parse.failure.world"); + + private BukkitCaptionKeys() { + } + + private static @NonNull Caption of(final @NonNull String key) { + final Caption caption = Caption.of(key); + RECOGNIZED_CAPTIONS.add(caption); + return caption; + } + + /** + * Get an immutable collection containing all standard caption keys + * + * @return Immutable collection of keys + */ + public static @NonNull Collection<@NonNull Caption> getBukkitCaptionKeys() { + return Collections.unmodifiableCollection(RECOGNIZED_CAPTIONS); + } + +} diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionRegistry.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionRegistry.java new file mode 100644 index 00000000..60197826 --- /dev/null +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionRegistry.java @@ -0,0 +1,80 @@ +// +// MIT License +// +// Copyright (c) 2020 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.bukkit; + +import cloud.commandframework.captions.SimpleCaptionRegistry; + +/** + * Caption registry that uses bi-functions to produce messages + * + * @param Command sender type + */ +public class BukkitCaptionRegistry extends SimpleCaptionRegistry { + + /** + * Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_ENCHANTMENT} + */ + public static final String ARGUMENT_PARSE_FAILURE_ENCHANTMENT = "'{input}' is not a valid enchantment"; + /** + * Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_ENCHANTMENT} + */ + public static final String ARGUMENT_PARSE_FAILURE_MATERIAL = "'{input}' is not a valid material name"; + /** + * Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_ENCHANTMENT} + */ + public static final String ARGUMENT_PARSE_FAILURE_OFFLINEPLAYER = "No player found for input '{input}'"; + /** + * Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_ENCHANTMENT} + */ + public static final String ARGUMENT_PARSE_FAILURE_PLAYER = "No player found for input '{input}'"; + /** + * Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_ENCHANTMENT} + */ + public static final String ARGUMENT_PARSE_FAILURE_WORLD = "'{input}' is not a valid Minecraft world"; + + protected BukkitCaptionRegistry() { + super(); + this.registerMessageFactory( + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_ENCHANTMENT, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_ENCHANTMENT + ); + this.registerMessageFactory( + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_MATERIAL, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_MATERIAL + ); + this.registerMessageFactory( + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_OFFLINEPLAYER, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_OFFLINEPLAYER + ); + this.registerMessageFactory( + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_PLAYER + ); + this.registerMessageFactory( + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_WORLD, + (caption, sender) -> ARGUMENT_PARSE_FAILURE_WORLD + ); + } + +} diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionRegistryFactory.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionRegistryFactory.java new file mode 100644 index 00000000..d9e4eb1e --- /dev/null +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCaptionRegistryFactory.java @@ -0,0 +1,44 @@ +// +// MIT License +// +// Copyright (c) 2020 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.bukkit; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Factory creating {@link BukkitCaptionRegistry} instances + * + * @param Command sender type + */ +public final class BukkitCaptionRegistryFactory { + + /** + * Create a new bukkit caption registry instance + * + * @return Created instance + */ + public @NonNull BukkitCaptionRegistry create() { + return new BukkitCaptionRegistry<>(); + } + +} diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCommandManager.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCommandManager.java index f571fb2b..8e841858 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCommandManager.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitCommandManager.java @@ -162,6 +162,8 @@ public class BukkitCommandManager extends CommandManager { new CommandSuggestionsListener<>(this), this.owningPlugin ); + + this.registerDefaultCaptions(new BukkitCaptionRegistryFactory().create()); } /** diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/CloudCommodoreManager.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/CloudCommodoreManager.java index 4b3b89c4..692e2888 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/CloudCommodoreManager.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/CloudCommodoreManager.java @@ -54,7 +54,10 @@ class CloudCommodoreManager extends BukkitPluginRegistrationHandler { this.commandManager = commandManager; this.commodore = CommodoreProvider.getCommodore(commandManager.getOwningPlugin()); this.brigadierManager = new CloudBrigadierManager<>(commandManager, () -> - new CommandContext<>(commandManager.getCommandSenderMapper().apply(Bukkit.getConsoleSender()))); + new CommandContext<>( + commandManager.getCommandSenderMapper().apply(Bukkit.getConsoleSender()), + commandManager.getCaptionRegistry() + )); new BukkitBrigadierMapper<>(this.commandManager, this.brigadierManager); } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/EnchantmentArgument.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/EnchantmentArgument.java index 07da27a1..7a94a8f6 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/EnchantmentArgument.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/EnchantmentArgument.java @@ -26,7 +26,10 @@ package cloud.commandframework.bukkit.parsers; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.bukkit.BukkitCaptionKeys; +import cloud.commandframework.captions.CaptionVariable; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.ParserException; import org.bukkit.NamespacedKey; import org.bukkit.enchantments.Enchantment; import org.checkerframework.checker.nullness.qual.NonNull; @@ -143,7 +146,7 @@ public class EnchantmentArgument extends CommandArgument { final Enchantment enchantment = Enchantment.getByKey(key); if (enchantment == null) { - return ArgumentParseResult.failure(new EnchantmentParseException(input)); + return ArgumentParseResult.failure(new EnchantmentParseException(input, commandContext)); } inputQueue.remove(); return ArgumentParseResult.success(enchantment); @@ -168,16 +171,26 @@ public class EnchantmentArgument extends CommandArgument { } - public static final class EnchantmentParseException extends IllegalArgumentException { + public static final class EnchantmentParseException extends ParserException { private final String input; /** * Construct a new EnchantmentParseException * - * @param input Input + * @param input Input + * @param context Command context */ - public EnchantmentParseException(final @NonNull String input) { + public EnchantmentParseException( + final @NonNull String input, + final @NonNull CommandContext context + ) { + super( + EnchantmentParser.class, + context, + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_ENCHANTMENT, + CaptionVariable.of("input", input) + ); this.input = input; } @@ -190,11 +203,6 @@ public class EnchantmentArgument extends CommandArgument { return this.input; } - @Override - public String getMessage() { - return String.format("'%s' is not a valid enchantment", this.input); - } - } } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/MaterialArgument.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/MaterialArgument.java index 8c0ff20d..4e20b00a 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/MaterialArgument.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/MaterialArgument.java @@ -26,7 +26,10 @@ package cloud.commandframework.bukkit.parsers; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.bukkit.BukkitCaptionKeys; +import cloud.commandframework.captions.CaptionVariable; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.ParserException; import org.bukkit.Material; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -136,7 +139,7 @@ public class MaterialArgument extends CommandArgument { inputQueue.remove(); return ArgumentParseResult.success(material); } catch (final IllegalArgumentException exception) { - return ArgumentParseResult.failure(new MaterialParseException(input)); + return ArgumentParseResult.failure(new MaterialParseException(input, commandContext)); } } @@ -155,16 +158,26 @@ public class MaterialArgument extends CommandArgument { } - public static final class MaterialParseException extends IllegalArgumentException { + public static final class MaterialParseException extends ParserException { private final String input; /** * Construct a new MaterialParseException * - * @param input Input + * @param input Input + * @param context Command context */ - public MaterialParseException(final @NonNull String input) { + public MaterialParseException( + final @NonNull String input, + final @NonNull CommandContext context + ) { + super( + MaterialParser.class, + context, + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_MATERIAL, + CaptionVariable.of("input", input) + ); this.input = input; } @@ -177,11 +190,6 @@ public class MaterialArgument extends CommandArgument { return this.input; } - @Override - public String getMessage() { - return String.format("'%s' is not a valid material name", this.input); - } - } } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/OfflinePlayerArgument.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/OfflinePlayerArgument.java index 3b654788..a96b511d 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/OfflinePlayerArgument.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/OfflinePlayerArgument.java @@ -26,7 +26,10 @@ package cloud.commandframework.bukkit.parsers; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.bukkit.BukkitCaptionKeys; +import cloud.commandframework.captions.CaptionVariable; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.ParserException; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; @@ -146,7 +149,7 @@ public final class OfflinePlayerArgument extends CommandArgument extends CommandArgument context + ) { + super( + OfflinePlayerParser.class, + context, + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_OFFLINEPLAYER, + CaptionVariable.of("input", input) + ); this.input = input; } @@ -194,11 +207,6 @@ public final class OfflinePlayerArgument extends CommandArgument extends CommandArgument { Player player = Bukkit.getPlayer(input); if (player == null) { - return ArgumentParseResult.failure(new PlayerParseException(input)); + return ArgumentParseResult.failure(new PlayerParseException(input, commandContext)); } return ArgumentParseResult.success(player); @@ -166,16 +169,26 @@ public final class PlayerArgument extends CommandArgument { /** * Player parse exception */ - public static final class PlayerParseException extends IllegalArgumentException { + public static final class PlayerParseException extends ParserException { private final String input; /** * Construct a new Player parse exception * - * @param input String input + * @param input String input + * @param context Command context */ - public PlayerParseException(final @NonNull String input) { + public PlayerParseException( + final @NonNull String input, + final @NonNull CommandContext context + ) { + super( + PlayerParser.class, + context, + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER, + CaptionVariable.of("input", input) + ); this.input = input; } @@ -188,11 +201,6 @@ public final class PlayerArgument extends CommandArgument { return input; } - @Override - public String getMessage() { - return String.format("No player found for input '%s'.", input); - } - } } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/WorldArgument.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/WorldArgument.java index 3e76c332..ddf2c9cc 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/WorldArgument.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/WorldArgument.java @@ -26,7 +26,10 @@ package cloud.commandframework.bukkit.parsers; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.bukkit.BukkitCaptionKeys; +import cloud.commandframework.captions.CaptionVariable; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.ParserException; import org.bukkit.Bukkit; import org.bukkit.World; import org.checkerframework.checker.nullness.qual.NonNull; @@ -130,7 +133,7 @@ public class WorldArgument extends CommandArgument { final World world = Bukkit.getWorld(input); if (world == null) { - return ArgumentParseResult.failure(new WorldParseException(input)); + return ArgumentParseResult.failure(new WorldParseException(input, commandContext)); } inputQueue.remove(); @@ -145,16 +148,26 @@ public class WorldArgument extends CommandArgument { } - public static final class WorldParseException extends IllegalArgumentException { + public static final class WorldParseException extends ParserException { private final String input; /** * Construct a new WorldParseException * - * @param input Input + * @param input Input + * @param context Command context */ - public WorldParseException(final @NonNull String input) { + public WorldParseException( + final @NonNull String input, + final @NonNull CommandContext context + ) { + super( + WorldParser.class, + context, + BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_WORLD, + CaptionVariable.of("input", input) + ); this.input = input; } @@ -167,11 +180,6 @@ public class WorldArgument extends CommandArgument { return this.input; } - @Override - public String getMessage() { - return String.format("'%s' is not a valid Minecraft world", this.input); - } - } } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/selector/MultiplePlayerSelectorArgument.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/selector/MultiplePlayerSelectorArgument.java index 76d3d7d2..d0466c33 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/selector/MultiplePlayerSelectorArgument.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/parsers/selector/MultiplePlayerSelectorArgument.java @@ -146,7 +146,7 @@ public final class MultiplePlayerSelectorArgument extends CommandArgument extends CommandArgument implements Listener { this.paperCommandManager, () -> new CommandContext<>( this.paperCommandManager.getCommandSenderMapper() - .apply(Bukkit.getConsoleSender())) + .apply(Bukkit.getConsoleSender()), + this.paperCommandManager.getCaptionRegistry() + ) ); new BukkitBrigadierMapper<>(this.paperCommandManager, this.brigadierManager); } diff --git a/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityPluginRegistrationHandler.java b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityPluginRegistrationHandler.java index 3c567086..b04da822 100644 --- a/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityPluginRegistrationHandler.java +++ b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityPluginRegistrationHandler.java @@ -48,7 +48,9 @@ final class VelocityPluginRegistrationHandler implements CommandRegistrationH () -> new CommandContext<>( velocityCommandManager.getCommandSenderMapper() .apply(velocityCommandManager.getProxyServer() - .getConsoleCommandSource())) + .getConsoleCommandSource()), + velocityCommandManager.getCaptionRegistry() + ) ); } diff --git a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java index 03174054..0a61c52f 100644 --- a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java +++ b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java @@ -53,6 +53,8 @@ import cloud.commandframework.bukkit.parsers.EnchantmentArgument; import cloud.commandframework.bukkit.parsers.MaterialArgument; import cloud.commandframework.bukkit.parsers.WorldArgument; import cloud.commandframework.bukkit.parsers.selector.SingleEntitySelectorArgument; +import cloud.commandframework.captions.Caption; +import cloud.commandframework.captions.SimpleCaptionRegistry; import cloud.commandframework.context.CommandContext; import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator; import cloud.commandframework.execution.CommandExecutionCoordinator; @@ -409,6 +411,15 @@ public final class ExamplePlugin extends JavaPlugin { context.getSender().sendMessage("You wrote: " + StringUtils.join(args, " ")); }) ); + + /* Register a custom regex caption */ + final Caption moneyCaption = Caption.of("regex.money"); + if (manager.getCaptionRegistry() instanceof SimpleCaptionRegistry) { + ((SimpleCaptionRegistry) manager.getCaptionRegistry()).registerMessageFactory( + moneyCaption, + (sender, key) -> "'{input}' is not very cash money of you" + ); + } } @CommandMethod("example help [query]") @@ -466,7 +477,8 @@ public final class ExamplePlugin extends JavaPlugin { @CommandDescription("Command to test the preprocessing system") private void commandPay( final @NonNull CommandSender sender, - final @Argument("money") @Regex("(?=.*?\\d)^\\$?(([1-9]\\d{0,2}(,\\d{3})*)|\\d+)?(\\.\\d{1,2})?$") String money + final @Argument("money") @Regex(value = "(?=.*?\\d)^\\$?(([1-9]\\d{0,2}(,\\d{3})*)|\\d+)?(\\.\\d{1,2})?$", + failureCaption = "regex.money") String money ) { bukkitAudiences.sender(sender).sendMessage( Component.text().append(Component.text("You have been given ", NamedTextColor.AQUA))