Merge pull request #43

*  Add a new caption system to allow for the configuration of…

*  Add caption support to all numerical types

* Add more standard pasres to the registry

* Add default messages for captions

*  Improve captions in core

* Add captions for Bukkit

*  Add FactoryDelegatingCaptionRegistry.java
This commit is contained in:
Alexander Söderberg 2020-10-12 18:13:23 +02:00 committed by GitHub
parent 378d57964f
commit 6ab1c8a2e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 1482 additions and 200 deletions

View file

@ -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<C> {
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())
));
}
/**

View file

@ -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";
}

View file

@ -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<C> {
private CommandSyntaxFormatter<C> commandSyntaxFormatter = new StandardCommandSyntaxFormatter<>();
private CommandSuggestionProcessor<C> commandSuggestionProcessor = new FilteringCommandSuggestionProcessor<>();
private CommandRegistrationHandler commandRegistrationHandler;
private CaptionRegistry<C> captionRegistry;
/**
* Create a new command manager instance
@ -111,6 +114,7 @@ public abstract class CommandManager<C> {
}, new AcceptingCommandPreprocessor<>());
this.servicePipeline.registerServiceType(new TypeToken<CommandPostprocessor<C>>() {
}, new AcceptingCommandPostprocessor<>());
this.captionRegistry = new SimpleCaptionRegistryFactory<C>().create();
}
/**
@ -124,7 +128,11 @@ public abstract class CommandManager<C> {
final @NonNull C commandSender,
final @NonNull String input
) {
final CommandContext<C> context = this.commandContextFactory.create(false, commandSender);
final CommandContext<C> context = this.commandContextFactory.create(
false,
commandSender,
this.captionRegistry
);
final LinkedList<String> inputQueue = new CommandInputTokenizer(input).tokenize();
try {
if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) {
@ -153,7 +161,8 @@ public abstract class CommandManager<C> {
) {
final CommandContext<C> context = this.commandContextFactory.create(
true,
commandSender
commandSender,
this.captionRegistry
);
return this.commandSuggestionEngine.getSuggestions(context, input);
}
@ -240,6 +249,33 @@ public abstract class CommandManager<C> {
return false;
}
/**
* Get the caption registry
*
* @return Caption registry
*/
public final @NonNull CaptionRegistry<C> getCaptionRegistry() {
return this.captionRegistry;
}
/**
* Replace the caption registry
*
* @param captionRegistry New caption registry
*/
public final void setCaptionRegistry(final @NonNull CaptionRegistry<C> captionRegistry) {
this.captionRegistry = captionRegistry;
}
/**
* Replace the default caption registry
*
* @param captionRegistry Caption registry to use
*/
public final void registerDefaultCaptions(final @NonNull CaptionRegistry<C> captionRegistry) {
this.captionRegistry = captionRegistry;
}
/**
* Check if the command sender has the required permission. If the permission node is
* empty, this should return {@code true}

View file

@ -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<C> 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 <C> Command sender type
* @return Preprocessor instance
*/
public static <C> @NonNull RegexPreprocessor<C> 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 <C> Command sender type
* @param failureCaption Caption sent when the input is invalid
* @return Preprocessor instance
*/
public static <C> @NonNull RegexPreprocessor<C> of(
final @NonNull String pattern,
final @NonNull Caption failureCaption
) {
return new RegexPreprocessor<>(pattern, failureCaption);
}
@Override
@ -73,7 +97,9 @@ public final class RegexPreprocessor<C> implements BiFunction<@NonNull CommandCo
return ArgumentParseResult.failure(
new RegexValidationException(
this.rawPattern,
head
head,
this.failureCaption,
context
)
);
}
@ -86,21 +112,33 @@ public final class RegexPreprocessor<C> 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
)
);
}

View file

@ -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<C> extends CommandArgument<C, Boolean> {
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<C> extends CommandArgument<C, Boolean> {
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<C> extends CommandArgument<C, Boolean> {
/**
* 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<C> extends CommandArgument<C, Boolean> {
/**
* 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<C> extends CommandArgument<C, Boolean> {
return liberal;
}
@Override
public String getMessage() {
return String.format("Could not parse boolean from '%s'.", input);
}
}
}

View file

@ -198,15 +198,19 @@ public final class ByteArgument<C> extends CommandArgument<C, Byte> {
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<C> extends CommandArgument<C, Byte> {
/**
* 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

View file

@ -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<C> extends CommandArgument<C, Character> {
}
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<C> extends CommandArgument<C, Character> {
/**
* 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<C> extends CommandArgument<C, Character> {
return input;
}
@Override
public String getMessage() {
return String.format("'%s' is not a valid character.", input);
}
}
}

View file

@ -194,12 +194,22 @@ public final class DoubleArgument<C> extends CommandArgument<C, Double> {
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<C> extends CommandArgument<C, Double> {
/**
* 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

View file

@ -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<C, E extends Enum<E>> extends CommandArgument<C, E> {
}
}
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<C, E extends Enum<E>> extends CommandArgument<C, E> {
}
public static final class EnumParseException extends IllegalArgumentException {
public static final class EnumParseException extends ParserException {
private final String input;
private final Class<? extends Enum<?>> enumClass;
@ -203,11 +206,20 @@ public class EnumArgument<C, E extends Enum<E>> extends CommandArgument<C, E> {
*
* @param input Input
* @param enumClass Enum class
* @param context Command context
*/
public EnumParseException(
final @NonNull String input,
final @NonNull Class<? extends Enum<?>> enumClass
final @NonNull Class<? extends Enum<?>> 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<C, E extends Enum<E>> extends CommandArgument<C, E> {
return this.enumClass;
}
@Override
public String getMessage() {
return String.format("'%s' is not one of the following: %s", this.input, join(enumClass));
}
}
}

View file

@ -194,12 +194,22 @@ public final class FloatArgument<C> extends CommandArgument<C, Float> {
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<C> extends CommandArgument<C, Float> {
/**
* 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

View file

@ -223,12 +223,22 @@ public final class IntegerArgument<C> extends CommandArgument<C, Integer> {
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<C> extends CommandArgument<C, Integer> {
/**
* 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

View file

@ -188,12 +188,22 @@ public final class LongArgument<C> extends CommandArgument<C, Long> {
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<C> extends CommandArgument<C, Long> {
/**
* 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

View file

@ -194,12 +194,22 @@ public final class ShortArgument<C> extends CommandArgument<C, Short> {
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<C> extends CommandArgument<C, Short> {
/**
* 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

View file

@ -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<C> extends CommandArgument<C, String> {
}
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<C> extends CommandArgument<C, String> {
}
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<C> extends CommandArgument<C, String> {
*
* @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<C> extends CommandArgument<C, String> {
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);
}
}
}

View file

@ -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<C> extends CommandArgument<C, UUID> {
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<C> extends CommandArgument<C, UUID> {
}
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;
}
}

View file

@ -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
);
}
}

View file

@ -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 <C> Command sender type
*/
public interface CaptionRegistry<C> {
/**
* 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);
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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 <C> Command sender type
*/
public interface FactoryDelegatingCaptionRegistry<C> extends CaptionRegistry<C> {
/**
* Register a message factory
*
* @param caption Caption key
* @param factory Message factory
*/
void registerMessageFactory(
@NonNull Caption caption,
@NonNull BiFunction<Caption, C, String> factory
);
}

View file

@ -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 <C> Command sender type
*/
public class SimpleCaptionRegistry<C> implements FactoryDelegatingCaptionRegistry<C> {
/**
* 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<Caption, BiFunction<Caption, C, String>> 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<Caption, C, String> 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<Caption, C, String> messageFactory
) {
this.messageFactories.put(caption, messageFactory);
}
}

View file

@ -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 <C> Command sender type
*/
public final class SimpleCaptionRegistryFactory<C> {
/**
* Create a new simple caption registry instance
*
* @return Created instance
*/
public @NonNull SimpleCaptionRegistry<C> create() {
return new SimpleCaptionRegistry<>();
}
}

View file

@ -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;
}
}

View file

@ -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<Caption> 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);
}
}

View file

@ -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;

View file

@ -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<C> {
private final CaptionVariableReplacementHandler captionVariableReplacementHandler =
new SimpleCaptionVariableReplacementHandler();
private final Map<CommandArgument<C, ?>, ArgumentTiming> argumentTimings = new HashMap<>();
private final FlagContext flagContext = FlagContext.create();
private final Map<String, Object> internalStorage = new HashMap<>();
private final C commandSender;
private final boolean suggestions;
private final CaptionRegistry<C> 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<C> 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<C> 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
);
}
/**

View file

@ -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<C> {
/**
* 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<C> create(boolean suggestions, @NonNull C sender);
@NonNull CommandContext<C> create(
boolean suggestions,
@NonNull C sender,
@NonNull CaptionRegistry<C> captionRegistry
);
}

View file

@ -23,6 +23,7 @@
//
package cloud.commandframework.context;
import cloud.commandframework.captions.CaptionRegistry;
import org.checkerframework.checker.nullness.qual.NonNull;
public final class StandardCommandContextFactory<C> implements CommandContextFactory<C> {
@ -30,13 +31,22 @@ public final class StandardCommandContextFactory<C> 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<C> create(final boolean suggestions, final @NonNull C sender) {
return new CommandContext<>(suggestions, sender);
public CommandContext<C> create(
final boolean suggestions,
final @NonNull C sender,
final @NonNull CaptionRegistry<C> captionRegistry
) {
return new CommandContext<>(
suggestions,
sender,
captionRegistry
);
}
}

View file

@ -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
*

View file

@ -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;
}
}

View file

@ -150,7 +150,9 @@ class CommandTreeTest {
final Pair<Command<TestCommandSender>, 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()
);
}

View file

@ -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<Caption> 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);
}
}

View file

@ -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 <C> Command sender type
*/
public class BukkitCaptionRegistry<C> extends SimpleCaptionRegistry<C> {
/**
* 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
);
}
}

View file

@ -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 <C> Command sender type
*/
public final class BukkitCaptionRegistryFactory<C> {
/**
* Create a new bukkit caption registry instance
*
* @return Created instance
*/
public @NonNull BukkitCaptionRegistry<C> create() {
return new BukkitCaptionRegistry<>();
}
}

View file

@ -162,6 +162,8 @@ public class BukkitCommandManager<C> extends CommandManager<C> {
new CommandSuggestionsListener<>(this),
this.owningPlugin
);
this.registerDefaultCaptions(new BukkitCaptionRegistryFactory<C>().create());
}
/**

View file

@ -54,7 +54,10 @@ class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
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);
}

View file

@ -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<C> extends CommandArgument<C, Enchantment> {
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<C> extends CommandArgument<C, Enchantment> {
}
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<C> extends CommandArgument<C, Enchantment> {
return this.input;
}
@Override
public String getMessage() {
return String.format("'%s' is not a valid enchantment", this.input);
}
}
}

View file

@ -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<C> extends CommandArgument<C, Material> {
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<C> extends CommandArgument<C, Material> {
}
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<C> extends CommandArgument<C, Material> {
return this.input;
}
@Override
public String getMessage() {
return String.format("'%s' is not a valid material name", this.input);
}
}
}

View file

@ -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<C> extends CommandArgument<C, OfflinePl
OfflinePlayer player = Bukkit.getOfflinePlayer(input);
if (player == null || (!player.hasPlayedBefore() && !player.isOnline())) {
return ArgumentParseResult.failure(new OfflinePlayerParseException(input));
return ArgumentParseResult.failure(new OfflinePlayerParseException(input, commandContext));
}
return ArgumentParseResult.success(player);
@ -172,16 +175,26 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
/**
* OfflinePlayer parse exception
*/
public static final class OfflinePlayerParseException extends IllegalArgumentException {
public static final class OfflinePlayerParseException extends ParserException {
private final String input;
/**
* Construct a new OfflinePlayer parse exception
*
* @param input String input
* @param input String input
* @param context Command context
*/
public OfflinePlayerParseException(final @NonNull String input) {
public OfflinePlayerParseException(
final @NonNull String input,
final @NonNull CommandContext<?> 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<C> extends CommandArgument<C, OfflinePl
return input;
}
@Override
public String getMessage() {
return String.format("No player found for input '%s'.", input);
}
}
}

View file

@ -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.entity.Player;
import org.checkerframework.checker.nullness.qual.NonNull;
@ -140,7 +143,7 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
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<C> extends CommandArgument<C, Player> {
/**
* 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<C> extends CommandArgument<C, Player> {
return input;
}
@Override
public String getMessage() {
return String.format("No player found for input '%s'.", input);
}
}
}

View file

@ -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<C> extends CommandArgument<C, World> {
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<C> extends CommandArgument<C, World> {
}
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<C> extends CommandArgument<C, World> {
return this.input;
}
@Override
public String getMessage() {
return String.format("'%s' is not a valid Minecraft world", this.input);
}
}
}

View file

@ -146,7 +146,7 @@ public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C,
Player player = Bukkit.getPlayer(input);
if (player == null) {
return ArgumentParseResult.failure(new PlayerArgument.PlayerParseException(input));
return ArgumentParseResult.failure(new PlayerArgument.PlayerParseException(input, commandContext));
}
return ArgumentParseResult.success(new MultiplePlayerSelector(input, ImmutableList.of(player)));
}

View file

@ -144,7 +144,7 @@ public final class SinglePlayerSelectorArgument<C> extends CommandArgument<C, Si
Player player = Bukkit.getPlayer(input);
if (player == null) {
return ArgumentParseResult.failure(new PlayerArgument.PlayerParseException(input));
return ArgumentParseResult.failure(new PlayerArgument.PlayerParseException(input, commandContext));
}
return ArgumentParseResult.success(new SinglePlayerSelector(input, ImmutableList.of(player)));
}

View file

@ -51,7 +51,9 @@ class PaperBrigadierListener<C> implements Listener {
this.paperCommandManager,
() -> new CommandContext<>(
this.paperCommandManager.getCommandSenderMapper()
.apply(Bukkit.getConsoleSender()))
.apply(Bukkit.getConsoleSender()),
this.paperCommandManager.getCaptionRegistry()
)
);
new BukkitBrigadierMapper<>(this.paperCommandManager, this.brigadierManager);
}

View file

@ -48,7 +48,9 @@ final class VelocityPluginRegistrationHandler<C> implements CommandRegistrationH
() -> new CommandContext<>(
velocityCommandManager.getCommandSenderMapper()
.apply(velocityCommandManager.getProxyServer()
.getConsoleCommandSource()))
.getConsoleCommandSource()),
velocityCommandManager.getCaptionRegistry()
)
);
}

View file

@ -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<CommandSender>) 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))