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.ParserParameters;
import cloud.commandframework.arguments.parser.StandardParameters; import cloud.commandframework.arguments.parser.StandardParameters;
import cloud.commandframework.arguments.preprocessor.RegexPreprocessor; import cloud.commandframework.arguments.preprocessor.RegexPreprocessor;
import cloud.commandframework.captions.Caption;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.execution.CommandExecutionHandler; import cloud.commandframework.execution.CommandExecutionHandler;
import cloud.commandframework.extra.confirmation.CommandConfirmationManager; import cloud.commandframework.extra.confirmation.CommandConfirmationManager;
@ -97,7 +98,10 @@ public final class AnnotationParser<C> {
this.flagExtractor = new FlagExtractor(manager); this.flagExtractor = new FlagExtractor(manager);
this.registerAnnotationMapper(CommandDescription.class, d -> this.registerAnnotationMapper(CommandDescription.class, d ->
ParserParameters.single(StandardParameters.DESCRIPTION, d.value())); 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(); @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.ParserParameter;
import cloud.commandframework.arguments.parser.ParserRegistry; import cloud.commandframework.arguments.parser.ParserRegistry;
import cloud.commandframework.arguments.parser.StandardParserRegistry; 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.CommandContext;
import cloud.commandframework.context.CommandContextFactory; import cloud.commandframework.context.CommandContextFactory;
import cloud.commandframework.context.StandardCommandContextFactory; import cloud.commandframework.context.StandardCommandContextFactory;
@ -92,6 +94,7 @@ public abstract class CommandManager<C> {
private CommandSyntaxFormatter<C> commandSyntaxFormatter = new StandardCommandSyntaxFormatter<>(); private CommandSyntaxFormatter<C> commandSyntaxFormatter = new StandardCommandSyntaxFormatter<>();
private CommandSuggestionProcessor<C> commandSuggestionProcessor = new FilteringCommandSuggestionProcessor<>(); private CommandSuggestionProcessor<C> commandSuggestionProcessor = new FilteringCommandSuggestionProcessor<>();
private CommandRegistrationHandler commandRegistrationHandler; private CommandRegistrationHandler commandRegistrationHandler;
private CaptionRegistry<C> captionRegistry;
/** /**
* Create a new command manager instance * Create a new command manager instance
@ -111,6 +114,7 @@ public abstract class CommandManager<C> {
}, new AcceptingCommandPreprocessor<>()); }, new AcceptingCommandPreprocessor<>());
this.servicePipeline.registerServiceType(new TypeToken<CommandPostprocessor<C>>() { this.servicePipeline.registerServiceType(new TypeToken<CommandPostprocessor<C>>() {
}, new AcceptingCommandPostprocessor<>()); }, new AcceptingCommandPostprocessor<>());
this.captionRegistry = new SimpleCaptionRegistryFactory<C>().create();
} }
/** /**
@ -124,7 +128,11 @@ public abstract class CommandManager<C> {
final @NonNull C commandSender, final @NonNull C commandSender,
final @NonNull String input 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(); final LinkedList<String> inputQueue = new CommandInputTokenizer(input).tokenize();
try { try {
if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) { if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) {
@ -153,7 +161,8 @@ public abstract class CommandManager<C> {
) { ) {
final CommandContext<C> context = this.commandContextFactory.create( final CommandContext<C> context = this.commandContextFactory.create(
true, true,
commandSender commandSender,
this.captionRegistry
); );
return this.commandSuggestionEngine.getSuggestions(context, input); return this.commandSuggestionEngine.getSuggestions(context, input);
} }
@ -240,6 +249,33 @@ public abstract class CommandManager<C> {
return false; 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 * Check if the command sender has the required permission. If the permission node is
* empty, this should return {@code true} * empty, this should return {@code true}

View file

@ -24,6 +24,9 @@
package cloud.commandframework.arguments.preprocessor; package cloud.commandframework.arguments.preprocessor;
import cloud.commandframework.arguments.parser.ArgumentParseResult; 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 cloud.commandframework.context.CommandContext;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -42,10 +45,27 @@ public final class RegexPreprocessor<C> implements BiFunction<@NonNull CommandCo
private final String rawPattern; private final String rawPattern;
private final Predicate<@NonNull String> predicate; 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.rawPattern = pattern;
this.predicate = Pattern.compile(pattern).asPredicate(); this.predicate = Pattern.compile(pattern).asPredicate();
this.failureCaption = failureCaption;
}
/**
* 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 of(pattern, StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_REGEX);
} }
/** /**
@ -53,10 +73,14 @@ public final class RegexPreprocessor<C> implements BiFunction<@NonNull CommandCo
* *
* @param pattern Regular expression * @param pattern Regular expression
* @param <C> Command sender type * @param <C> Command sender type
* @param failureCaption Caption sent when the input is invalid
* @return Preprocessor instance * @return Preprocessor instance
*/ */
public static <C> @NonNull RegexPreprocessor<C> of(final @NonNull String pattern) { public static <C> @NonNull RegexPreprocessor<C> of(
return new RegexPreprocessor<>(pattern); final @NonNull String pattern,
final @NonNull Caption failureCaption
) {
return new RegexPreprocessor<>(pattern, failureCaption);
} }
@Override @Override
@ -73,7 +97,9 @@ public final class RegexPreprocessor<C> implements BiFunction<@NonNull CommandCo
return ArgumentParseResult.failure( return ArgumentParseResult.failure(
new RegexValidationException( new RegexValidationException(
this.rawPattern, 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 pattern;
private final String failedString; private final String failedString;
private final Caption failureCaption;
private final CommandContext<?> commandContext;
private RegexValidationException( private RegexValidationException(
@NonNull final String pattern, final @NonNull String pattern,
@NonNull final String failedString final @NonNull String failedString,
final @NonNull Caption failureCaption,
final @NonNull CommandContext<?> commandContext
) { ) {
this.pattern = pattern; this.pattern = pattern;
this.failedString = failedString; this.failedString = failedString;
this.failureCaption = failureCaption;
this.commandContext = commandContext;
} }
@Override @Override
public String getMessage() { public String getMessage() {
return String.format( return this.commandContext.formatMessage(
"Input '%s' does not match the required pattern '%s'", this.failureCaption,
failedString, CaptionVariable.of(
pattern "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.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.captions.StandardCaptionKeys;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; 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.success(false);
} }
return ArgumentParseResult.failure(new BooleanParseException(input, false)); return ArgumentParseResult.failure(new BooleanParseException(input, false, commandContext));
} }
final String uppercaseInput = input.toUpperCase(); final String uppercaseInput = input.toUpperCase();
@ -196,7 +199,7 @@ public final class BooleanArgument<C> extends CommandArgument<C, Boolean> {
return ArgumentParseResult.success(false); return ArgumentParseResult.success(false);
} }
return ArgumentParseResult.failure(new BooleanParseException(input, true)); return ArgumentParseResult.failure(new BooleanParseException(input, true, commandContext));
} }
@Override @Override
@ -222,7 +225,7 @@ public final class BooleanArgument<C> extends CommandArgument<C, Boolean> {
/** /**
* Boolean parse exception * Boolean parse exception
*/ */
public static final class BooleanParseException extends IllegalArgumentException { public static final class BooleanParseException extends ParserException {
private final String input; private final String input;
private final boolean liberal; private final boolean liberal;
@ -230,17 +233,26 @@ public final class BooleanArgument<C> extends CommandArgument<C, Boolean> {
/** /**
* Construct a new boolean parse exception * Construct a new boolean parse exception
* *
* @param input String input * @param input Input
* @param liberal Liberal value * @param liberal Whether or not the parser allows truthy and falsy values, or strictly true/false
* @param context Command context
*/ */
public BooleanParseException( public BooleanParseException(
final @NonNull String input, 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.input = input;
this.liberal = liberal; this.liberal = liberal;
} }
/** /**
* Get the supplied input * Get the supplied input
* *
@ -259,11 +271,6 @@ public final class BooleanArgument<C> extends CommandArgument<C, Boolean> {
return liberal; 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( new ByteParseException(
input, input,
this.min, this.min,
this.max this.max,
commandContext
)); ));
} }
inputQueue.remove(); inputQueue.remove();
return ArgumentParseResult.success(value); return ArgumentParseResult.success(value);
} catch (final Exception e) { } catch (final Exception e) {
return ArgumentParseResult.failure( return ArgumentParseResult.failure(
new ByteParseException(input, this.min, new ByteParseException(
this.max input,
this.min,
this.max,
commandContext
)); ));
} }
} }
@ -256,9 +260,21 @@ public final class ByteArgument<C> extends CommandArgument<C, Byte> {
* @param input String input * @param input String input
* @param min Minimum value * @param min Minimum value
* @param max Maximum value * @param max Maximum value
* @param context Command context
*/ */
public ByteParseException(final @NonNull String input, final byte min, final byte max) { public ByteParseException(
super(input, min, max); final @NonNull String input,
final byte min,
final byte max,
final @NonNull CommandContext<?> context
) {
super(
input,
min,
max,
ByteParser.class,
context
);
} }
@Override @Override

View file

@ -26,7 +26,10 @@ package cloud.commandframework.arguments.standard;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.captions.StandardCaptionKeys;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -130,7 +133,7 @@ public final class CharArgument<C> extends CommandArgument<C, Character> {
} }
if (input.length() != 1) { if (input.length() != 1) {
return ArgumentParseResult.failure(new CharParseException(input)); return ArgumentParseResult.failure(new CharParseException(input, commandContext));
} }
return ArgumentParseResult.success(input.charAt(0)); return ArgumentParseResult.success(input.charAt(0));
@ -147,7 +150,7 @@ public final class CharArgument<C> extends CommandArgument<C, Character> {
/** /**
* Char parse exception * Char parse exception
*/ */
public static final class CharParseException extends IllegalArgumentException { public static final class CharParseException extends ParserException {
private final String input; private final String input;
@ -155,8 +158,15 @@ public final class CharArgument<C> extends CommandArgument<C, Character> {
* Construct a new Char parse exception * 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; this.input = input;
} }
@ -169,11 +179,6 @@ public final class CharArgument<C> extends CommandArgument<C, Character> {
return input; 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 { try {
final double value = Double.parseDouble(input); final double value = Double.parseDouble(input);
if (value < this.min || value > this.max) { 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(); inputQueue.remove();
return ArgumentParseResult.success(value); return ArgumentParseResult.success(value);
} catch (final Exception e) { } 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
));
} }
} }
@ -237,9 +247,21 @@ public final class DoubleArgument<C> extends CommandArgument<C, Double> {
* @param input String input * @param input String input
* @param min Minimum value * @param min Minimum value
* @param max Maximum value * @param max Maximum value
* @param commandContext Command context
*/ */
public DoubleParseException(final @NonNull String input, final double min, final double max) { public DoubleParseException(
super(input, min, max); final @NonNull String input,
final double min,
final double max,
final @NonNull CommandContext<?> commandContext
) {
super(
input,
min,
max,
DoubleParser.class,
commandContext
);
} }
@Override @Override

View file

@ -26,7 +26,10 @@ package cloud.commandframework.arguments.standard;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.captions.StandardCaptionKeys;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; 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 @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 String input;
private final Class<? extends Enum<?>> enumClass; 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 input Input
* @param enumClass Enum class * @param enumClass Enum class
* @param context Command context
*/ */
public EnumParseException( public EnumParseException(
final @NonNull String input, 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.input = input;
this.enumClass = enumClass; this.enumClass = enumClass;
} }
@ -238,11 +250,6 @@ public class EnumArgument<C, E extends Enum<E>> extends CommandArgument<C, E> {
return this.enumClass; 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 { try {
final float value = Float.parseFloat(input); final float value = Float.parseFloat(input);
if (value < this.min || value > this.max) { 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(); inputQueue.remove();
return ArgumentParseResult.success(value); return ArgumentParseResult.success(value);
} catch (final Exception e) { } 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
));
} }
} }
@ -237,9 +247,21 @@ public final class FloatArgument<C> extends CommandArgument<C, Float> {
* @param input String input * @param input String input
* @param min Minimum value * @param min Minimum value
* @param max Maximum value * @param max Maximum value
* @param commandContext Command context
*/ */
public FloatParseException(final @NonNull String input, final float min, final float max) { public FloatParseException(
super(input, min, max); final @NonNull String input,
final float min,
final float max,
final @NonNull CommandContext<?> commandContext
) {
super(
input,
min,
max,
FloatParser.class,
commandContext
);
} }
@Override @Override

View file

@ -223,12 +223,22 @@ public final class IntegerArgument<C> extends CommandArgument<C, Integer> {
try { try {
final int value = Integer.parseInt(input); final int value = Integer.parseInt(input);
if (value < this.min || value > this.max) { 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(); inputQueue.remove();
return ArgumentParseResult.success(value); return ArgumentParseResult.success(value);
} catch (final Exception e) { } 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
));
} }
} }
@ -274,9 +284,21 @@ public final class IntegerArgument<C> extends CommandArgument<C, Integer> {
* @param input String input * @param input String input
* @param min Minimum value * @param min Minimum value
* @param max Maximum value * @param max Maximum value
* @param commandContext Command context
*/ */
public IntegerParseException(final @NonNull String input, final int min, final int max) { public IntegerParseException(
super(input, min, max); final @NonNull String input,
final int min,
final int max,
final @NonNull CommandContext<?> commandContext
) {
super(
input,
min,
max,
IntegerParser.class,
commandContext
);
} }
@Override @Override

View file

@ -188,12 +188,22 @@ public final class LongArgument<C> extends CommandArgument<C, Long> {
try { try {
final long value = Long.parseLong(input); final long value = Long.parseLong(input);
if (value < this.min || value > this.max) { 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(); inputQueue.remove();
return ArgumentParseResult.success(value); return ArgumentParseResult.success(value);
} catch (final Exception e) { } 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
));
} }
} }
@ -221,9 +231,21 @@ public final class LongArgument<C> extends CommandArgument<C, Long> {
* @param input String input * @param input String input
* @param min Minimum value * @param min Minimum value
* @param max Maximum value * @param max Maximum value
* @param commandContext Command context
*/ */
public LongParseException(final @NonNull String input, final long min, final long max) { public LongParseException(
super(input, min, max); final @NonNull String input,
final long min,
final long max,
final @NonNull CommandContext<?> commandContext
) {
super(
input,
min,
max,
LongParser.class,
commandContext
);
} }
@Override @Override

View file

@ -194,12 +194,22 @@ public final class ShortArgument<C> extends CommandArgument<C, Short> {
try { try {
final short value = Short.parseShort(input); final short value = Short.parseShort(input);
if (value < this.min || value > this.max) { 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(); inputQueue.remove();
return ArgumentParseResult.success(value); return ArgumentParseResult.success(value);
} catch (final Exception e) { } 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
));
} }
} }
@ -245,9 +255,21 @@ public final class ShortArgument<C> extends CommandArgument<C, Short> {
* @param input String input * @param input String input
* @param min Minimum value * @param min Minimum value
* @param max Maximum value * @param max Maximum value
* @param commandContext Command context
*/ */
public ShortParseException(final @NonNull String input, final short min, final short max) { public ShortParseException(
super(input, min, max); final @NonNull String input,
final short min,
final short max,
final @NonNull CommandContext<?> commandContext
) {
super(
input,
min,
max,
ShortParser.class,
commandContext
);
} }
@Override @Override

View file

@ -26,7 +26,10 @@ package cloud.commandframework.arguments.standard;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.captions.StandardCaptionKeys;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collections; import java.util.Collections;
@ -342,7 +345,7 @@ public final class StringArgument<C> extends CommandArgument<C, String> {
} }
if (this.stringMode == StringMode.QUOTED && (!started || !finished)) { 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()); 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 String input;
private final StringMode stringMode; private final StringMode stringMode;
@ -383,8 +386,20 @@ public final class StringArgument<C> extends CommandArgument<C, String> {
* *
* @param input Input * @param input Input
* @param stringMode String mode * @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.input = input;
this.stringMode = stringMode; this.stringMode = stringMode;
} }
@ -408,14 +423,6 @@ public final class StringArgument<C> extends CommandArgument<C, String> {
return this.stringMode; 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.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.captions.StandardCaptionKeys;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -133,7 +136,7 @@ public final class UUIDArgument<C> extends CommandArgument<C, UUID> {
inputQueue.remove(); inputQueue.remove();
return ArgumentParseResult.success(uuid); return ArgumentParseResult.success(uuid);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return ArgumentParseResult.failure(new UUIDParseException(input)); return ArgumentParseResult.failure(new UUIDParseException(input, commandContext));
} }
} }
@ -145,7 +148,7 @@ 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; private final String input;
@ -153,14 +156,28 @@ public final class UUIDArgument<C> extends CommandArgument<C, UUID> {
* Construct a new UUID parse exception * 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; this.input = input;
} }
@Override /**
public String getMessage() { * Get the supplied input
return String.format("Could not parse UUID from '%s'.", 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.CommandArgument;
import cloud.commandframework.arguments.flags.FlagContext; 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.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -41,19 +46,23 @@ import java.util.Optional;
*/ */
public final class CommandContext<C> { public final class CommandContext<C> {
private final CaptionVariableReplacementHandler captionVariableReplacementHandler =
new SimpleCaptionVariableReplacementHandler();
private final Map<CommandArgument<C, ?>, ArgumentTiming> argumentTimings = new HashMap<>(); private final Map<CommandArgument<C, ?>, ArgumentTiming> argumentTimings = new HashMap<>();
private final FlagContext flagContext = FlagContext.create(); private final FlagContext flagContext = FlagContext.create();
private final Map<String, Object> internalStorage = new HashMap<>(); private final Map<String, Object> internalStorage = new HashMap<>();
private final C commandSender; private final C commandSender;
private final boolean suggestions; private final boolean suggestions;
private final CaptionRegistry<C> captionRegistry;
/** /**
* Create a new command context instance * 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) { public CommandContext(final @NonNull C commandSender, final @NonNull CaptionRegistry<C> captionRegistry) {
this(false, commandSender); this(false, commandSender, captionRegistry);
} }
/** /**
@ -61,13 +70,33 @@ public final class CommandContext<C> {
* *
* @param suggestions Whether or not the context is created for command suggestions * @param suggestions Whether or not the context is created for command suggestions
* @param commandSender Sender of the command * @param commandSender Sender of the command
* @param captionRegistry Caption registry
*/ */
public CommandContext( public CommandContext(
final boolean suggestions, final boolean suggestions,
final @NonNull C commandSender final @NonNull C commandSender,
final @NonNull CaptionRegistry<C> captionRegistry
) { ) {
this.commandSender = commandSender; this.commandSender = commandSender;
this.suggestions = suggestions; 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; package cloud.commandframework.context;
import cloud.commandframework.captions.CaptionRegistry;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
@ -37,8 +38,13 @@ public interface CommandContextFactory<C> {
* *
* @param suggestions Whether or not the sender is requesting suggestions * @param suggestions Whether or not the sender is requesting suggestions
* @param sender Command sender * @param sender Command sender
* @param captionRegistry Caption registry
* @return Command context * @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; package cloud.commandframework.context;
import cloud.commandframework.captions.CaptionRegistry;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
public final class StandardCommandContextFactory<C> implements CommandContextFactory<C> { public final class StandardCommandContextFactory<C> implements CommandContextFactory<C> {
@ -32,11 +33,20 @@ public final class StandardCommandContextFactory<C> implements CommandContextFac
* *
* @param suggestions Whether or not the sender is requesting suggestions * @param suggestions Whether or not the sender is requesting suggestions
* @param sender Command sender * @param sender Command sender
* @param captionRegistry Caption registry
* @return Created context * @return Created context
*/ */
@Override @Override
public CommandContext<C> create(final boolean suggestions, final @NonNull C sender) { public CommandContext<C> create(
return new CommandContext<>(suggestions, sender); 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; 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; 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 String input;
private final Number min; private final Number min;
@ -37,31 +40,29 @@ public abstract class NumberParseException extends IllegalArgumentException {
* @param input Input * @param input Input
* @param min Maximum value * @param min Maximum value
* @param max Minimum value * @param max Minimum value
* @param parserClass Parser class
* @param context Command context
*/ */
public NumberParseException( public NumberParseException(
final @NonNull String input, final @NonNull String input,
final @NonNull Number min, 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.input = input;
this.min = min; this.min = min;
this.max = max; 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 * 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() final Pair<Command<TestCommandSender>, Exception> command = manager.getCommandTree()
.parse( .parse(
new CommandContext<>( new CommandContext<>(
new TestCommandSender()), new TestCommandSender(),
manager.getCaptionRegistry()
),
new LinkedList<>( new LinkedList<>(
Arrays.asList( Arrays.asList(
"test", "test",
@ -161,31 +163,60 @@ class CommandTreeTest {
Assertions.assertEquals(NoPermissionException.class, manager.getCommandTree() Assertions.assertEquals(NoPermissionException.class, manager.getCommandTree()
.parse( .parse(
new CommandContext<>( new CommandContext<>(
new TestCommandSender()), new TestCommandSender(),
manager.getCaptionRegistry()
),
new LinkedList<>( new LinkedList<>(
Arrays.asList("test", "two")) Arrays.asList("test", "two"))
) )
.getSecond().getClass()); .getSecond().getClass());
manager.getCommandTree() manager.getCommandTree()
.parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt"))) .parse(
.getFirst().getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender())); new CommandContext<>(new TestCommandSender(), manager.getCaptionRegistry()),
new LinkedList<>(Arrays.asList("test", "opt"))
)
.getFirst().getCommandExecutionHandler().execute(new CommandContext<>(
new TestCommandSender(),
manager.getCaptionRegistry()
));
manager.getCommandTree() manager.getCommandTree()
.parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt", "12"))) .parse(
.getFirst().getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender())); new CommandContext<>(new TestCommandSender(), manager.getCaptionRegistry()),
new LinkedList<>(Arrays.asList("test", "opt", "12"))
)
.getFirst().getCommandExecutionHandler().execute(new CommandContext<>(
new TestCommandSender(),
manager.getCaptionRegistry()
));
} }
@Test @Test
void testAlias() { void testAlias() {
manager.getCommandTree() manager.getCommandTree()
.parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("other", "öpt", "12"))) .parse(
.getFirst().getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender())); new CommandContext<>(
new TestCommandSender(),
manager.getCaptionRegistry()
),
new LinkedList<>(Arrays.asList(
"other",
"öpt",
"12"
))
)
.getFirst().getCommandExecutionHandler().execute(new CommandContext<>(
new TestCommandSender(),
manager.getCaptionRegistry()
));
} }
@Test @Test
void getSuggestions() { void getSuggestions() {
Assertions.assertFalse( Assertions.assertFalse(
manager.getCommandTree().getSuggestions(new CommandContext<>(new TestCommandSender()), new LinkedList<>( manager.getCommandTree().getSuggestions(
Collections.singletonList("test "))).isEmpty()); new CommandContext<>(new TestCommandSender(), manager.getCaptionRegistry()),
new LinkedList<>(Collections.singletonList("test "))
).isEmpty());
} }
@Test @Test

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), new CommandSuggestionsListener<>(this),
this.owningPlugin this.owningPlugin
); );
this.registerDefaultCaptions(new BukkitCaptionRegistryFactory<C>().create());
} }
/** /**

View file

@ -54,7 +54,10 @@ class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
this.commandManager = commandManager; this.commandManager = commandManager;
this.commodore = CommodoreProvider.getCommodore(commandManager.getOwningPlugin()); this.commodore = CommodoreProvider.getCommodore(commandManager.getOwningPlugin());
this.brigadierManager = new CloudBrigadierManager<>(commandManager, () -> 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); 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.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.bukkit.BukkitCaptionKeys;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.checkerframework.checker.nullness.qual.NonNull; 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); final Enchantment enchantment = Enchantment.getByKey(key);
if (enchantment == null) { if (enchantment == null) {
return ArgumentParseResult.failure(new EnchantmentParseException(input)); return ArgumentParseResult.failure(new EnchantmentParseException(input, commandContext));
} }
inputQueue.remove(); inputQueue.remove();
return ArgumentParseResult.success(enchantment); return ArgumentParseResult.success(enchantment);
@ -168,7 +171,7 @@ 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; private final String input;
@ -176,8 +179,18 @@ public class EnchantmentArgument<C> extends CommandArgument<C, Enchantment> {
* Construct a new EnchantmentParseException * 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; this.input = input;
} }
@ -190,11 +203,6 @@ public class EnchantmentArgument<C> extends CommandArgument<C, Enchantment> {
return this.input; 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.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.bukkit.BukkitCaptionKeys;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.bukkit.Material; import org.bukkit.Material;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -136,7 +139,7 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
inputQueue.remove(); inputQueue.remove();
return ArgumentParseResult.success(material); return ArgumentParseResult.success(material);
} catch (final IllegalArgumentException exception) { } catch (final IllegalArgumentException exception) {
return ArgumentParseResult.failure(new MaterialParseException(input)); return ArgumentParseResult.failure(new MaterialParseException(input, commandContext));
} }
} }
@ -155,7 +158,7 @@ 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; private final String input;
@ -163,8 +166,18 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
* Construct a new MaterialParseException * 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; this.input = input;
} }
@ -177,11 +190,6 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
return this.input; 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.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.bukkit.BukkitCaptionKeys;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -146,7 +149,7 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
OfflinePlayer player = Bukkit.getOfflinePlayer(input); OfflinePlayer player = Bukkit.getOfflinePlayer(input);
if (player == null || (!player.hasPlayedBefore() && !player.isOnline())) { if (player == null || (!player.hasPlayedBefore() && !player.isOnline())) {
return ArgumentParseResult.failure(new OfflinePlayerParseException(input)); return ArgumentParseResult.failure(new OfflinePlayerParseException(input, commandContext));
} }
return ArgumentParseResult.success(player); return ArgumentParseResult.success(player);
@ -172,7 +175,7 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
/** /**
* OfflinePlayer parse exception * OfflinePlayer parse exception
*/ */
public static final class OfflinePlayerParseException extends IllegalArgumentException { public static final class OfflinePlayerParseException extends ParserException {
private final String input; private final String input;
@ -180,8 +183,18 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
* Construct a new OfflinePlayer parse exception * 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; this.input = input;
} }
@ -194,11 +207,6 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
return input; 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.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.bukkit.BukkitCaptionKeys;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.NonNull; 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); Player player = Bukkit.getPlayer(input);
if (player == null) { if (player == null) {
return ArgumentParseResult.failure(new PlayerParseException(input)); return ArgumentParseResult.failure(new PlayerParseException(input, commandContext));
} }
return ArgumentParseResult.success(player); return ArgumentParseResult.success(player);
@ -166,7 +169,7 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
/** /**
* Player parse exception * Player parse exception
*/ */
public static final class PlayerParseException extends IllegalArgumentException { public static final class PlayerParseException extends ParserException {
private final String input; private final String input;
@ -174,8 +177,18 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
* Construct a new Player parse exception * 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; this.input = input;
} }
@ -188,11 +201,6 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
return input; 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.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult; import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.bukkit.BukkitCaptionKeys;
import cloud.commandframework.captions.CaptionVariable;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.parsing.ParserException;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.checkerframework.checker.nullness.qual.NonNull; 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); final World world = Bukkit.getWorld(input);
if (world == null) { if (world == null) {
return ArgumentParseResult.failure(new WorldParseException(input)); return ArgumentParseResult.failure(new WorldParseException(input, commandContext));
} }
inputQueue.remove(); inputQueue.remove();
@ -145,7 +148,7 @@ 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; private final String input;
@ -153,8 +156,18 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
* Construct a new WorldParseException * 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; this.input = input;
} }
@ -167,11 +180,6 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
return this.input; 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); Player player = Bukkit.getPlayer(input);
if (player == null) { 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))); 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); Player player = Bukkit.getPlayer(input);
if (player == null) { 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))); return ArgumentParseResult.success(new SinglePlayerSelector(input, ImmutableList.of(player)));
} }

View file

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

View file

@ -48,7 +48,9 @@ final class VelocityPluginRegistrationHandler<C> implements CommandRegistrationH
() -> new CommandContext<>( () -> new CommandContext<>(
velocityCommandManager.getCommandSenderMapper() velocityCommandManager.getCommandSenderMapper()
.apply(velocityCommandManager.getProxyServer() .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.MaterialArgument;
import cloud.commandframework.bukkit.parsers.WorldArgument; import cloud.commandframework.bukkit.parsers.WorldArgument;
import cloud.commandframework.bukkit.parsers.selector.SingleEntitySelectorArgument; 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.context.CommandContext;
import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator; import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator;
import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.execution.CommandExecutionCoordinator;
@ -409,6 +411,15 @@ public final class ExamplePlugin extends JavaPlugin {
context.getSender().sendMessage("You wrote: " + StringUtils.join(args, " ")); 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]") @CommandMethod("example help [query]")
@ -466,7 +477,8 @@ public final class ExamplePlugin extends JavaPlugin {
@CommandDescription("Command to test the preprocessing system") @CommandDescription("Command to test the preprocessing system")
private void commandPay( private void commandPay(
final @NonNull CommandSender sender, 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( bukkitAudiences.sender(sender).sendMessage(
Component.text().append(Component.text("You have been given ", NamedTextColor.AQUA)) Component.text().append(Component.text("You have been given ", NamedTextColor.AQUA))