-
-
Notifications
You must be signed in to change notification settings - Fork 41
Value resolvers
Value resolvers are resolvers for command parameters that fetch their value from the command's input arguments. That is, for example:
- numbers
- enums
- players by name
- discord channels by name
These types retrieve their values by reading input from the arguments, then extracting the value, checking its validity and finally returning.
All value resolvers must inherit from ValueResolver, and should be registered with CommandHandler#registerValueResolver...
methods.
All value resolvers get access to a mutable argument stack (list of strings), where each value resolver must consume at least one value from the argument stack.
The argument stack contains useful methods for dealing with the appropriate value. Most value resolvers should either use ArgumentStack#pop()
, ArgumentStack#removeFirst()
or ArgumentStack#popForParameter(CommandParameter)
. All these methods achieve similar functionality, which is returning the value on the top of the stack and removing it.
We can write a value resolver for parsing integer parameters.
commandHandler.registerValueResolver(int.class, (arguments, actor, parameter, command) -> {
String value = arguments.pop();
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new CommandErrorException("Expected a number, but found '" + value + "'.");
}
});
((by default primitives and few other types have built-in value resolvers, and do not need to be registered explicitly))
We can then instantly use int
in our commands without having to parse it and check its validity.
@Command("repeat")
public void repeat(CommandActor actor, int times, String value) {
for (int i = 0; i < times; i++)
actor.reply("#" + i + ": " + value);
}
In certain cases it may be desired to provide custom value resolvers for certain parameters that have an annotation, or extend a certain interface or superclass. In such cases it is possible to use ValueResolverFactory to handle this.
For example, we can write a value resolver factory that caches all constants of enums and returns a value resolver for them. We also respect enums that would like their values to be fetched case-sensitive by checking against a custom @CaseSensitive
annotation.
public enum EnumResolverFactory implements ValueResolverFactory {
INSTANCE;
@Override public @Nullable ValueResolver<?> create(@NotNull CommandParameter parameter) {
Class<?> type = parameter.getType();
if (!type.isEnum()) return null;
Class<? extends Enum> enumType = type.asSubclass(Enum.class);
Map<String, Enum<?>> values = new HashMap<>();
boolean caseSensitive = parameter.hasAnnotation(CaseSensitive.class);
for (Enum<?> enumConstant : enumType.getEnumConstants()) {
if (caseSensitive)
values.put(enumConstant.name(), enumConstant);
else
values.put(enumConstant.name().toLowerCase(), enumConstant);
}
return (ValueResolver<Enum<?>>) (arguments, actor, parameter1, command) -> {
String value = arguments.pop();
Enum<?> v = values.get(caseSensitive ? value : value.toLowerCase());
if (v == null)
throw new EnumNotFoundException(parameter, value);
return v;
};
}
}
👋 If you're having trouble, need support, or just feel like chatting, feel free to hop by our Discord server!