Store component type in the component. Add mappings to native (NMS) Brigadier types. Shorten builder names. Make the Bukkit command manager take in a generic command sender type.

This commit is contained in:
Alexander Söderberg 2020-09-15 13:36:13 +02:00
parent b8db1d3cb7
commit d144c3ea8c
No known key found for this signature in database
GPG key ID: C0207FF7EA146678
29 changed files with 524 additions and 158 deletions

View file

@ -244,7 +244,7 @@ public class Command<C extends CommandSender, M extends CommandMeta> {
* @return New builder instance with the command component inserted into the component list
*/
@Nonnull
public <T> Builder<C, M> withComponent(@Nonnull final CommandComponent<C, T> component) {
public <T> Builder<C, M> component(@Nonnull final CommandComponent<C, T> component) {
final List<CommandComponent<C, ?>> commandComponents = new LinkedList<>(this.commandComponents);
commandComponents.add(component);
return new Builder<>(this.commandMeta, this.senderType, commandComponents, this.commandExecutionHandler,
@ -261,12 +261,12 @@ public class Command<C extends CommandSender, M extends CommandMeta> {
* @return New builder instance with the command component inserted into the component list
*/
@Nonnull
public <T> Builder<C, M> withComponent(@Nonnull final Class<T> clazz,
@Nonnull final String name,
@Nonnull final Consumer<CommandComponent.Builder<C, T>> builderConsumer) {
public <T> Builder<C, M> component(@Nonnull final Class<T> clazz,
@Nonnull final String name,
@Nonnull final Consumer<CommandComponent.Builder<C, T>> builderConsumer) {
final CommandComponent.Builder<C, T> builder = CommandComponent.ofType(clazz, name);
builderConsumer.accept(builder);
return this.withComponent(builder.build());
return this.component(builder.build());
}
/**
@ -276,7 +276,7 @@ public class Command<C extends CommandSender, M extends CommandMeta> {
* @return New builder instance using the command execution handler
*/
@Nonnull
public Builder<C, M> withHandler(@Nonnull final CommandExecutionHandler<C> commandExecutionHandler) {
public Builder<C, M> handler(@Nonnull final CommandExecutionHandler<C> commandExecutionHandler) {
return new Builder<>(this.commandMeta, this.senderType, this.commandComponents, commandExecutionHandler,
this.commandPermission);
}

View file

@ -159,7 +159,7 @@ public abstract class CommandManager<C extends CommandSender, M extends CommandM
* @param command Command to register
* @return The command manager instance
*/
public CommandManager<C, M> registerCommand(@Nonnull final Command<C, M> command) {
public CommandManager<C, M> command(@Nonnull final Command<C, M> command) {
this.commandTree.insertCommand(command);
return this;
}

View file

@ -107,12 +107,12 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
private Optional<Command<C, M>> parseCommand(@Nonnull final CommandContext<C> commandContext,
@Nonnull final Queue<String> commandQueue,
@Nonnull final Node<CommandComponent<C, ?>> root) {
String permission = this.isPermitted(commandContext.getCommandSender(), root);
String permission = this.isPermitted(commandContext.getSender(), root);
if (permission != null) {
throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
throw new NoPermissionException(permission, commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
}
final Optional<Command<C, M>> parsedChild = this.attemptParseUnambiguousChild(commandContext, root, commandQueue);
@ -133,10 +133,10 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
.apply(root.getValue()
.getOwningCommand()
.getComponents()),
commandContext.getCommandSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
}
} else {
/* Too many arguments. We have a unique path, so we can send the entire context */
@ -145,10 +145,10 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
Objects.requireNonNull(root.getValue())
.getOwningCommand())
.getComponents()),
commandContext.getCommandSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
}
} else {
final Iterator<Node<CommandComponent<C, ?>>> childIterator = root.getChildren().iterator();
@ -165,7 +165,7 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
}
}
/* We could not find a match */
throw new NoSuchCommandException(commandContext.getCommandSender(),
throw new NoSuchCommandException(commandContext.getSender(),
getChain(root).stream().map(Node::getValue).collect(Collectors.toList()),
stringOrEmpty(commandQueue.peek()));
}
@ -180,12 +180,12 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) {
// The value has to be a variable
final Node<CommandComponent<C, ?>> child = children.get(0);
permission = this.isPermitted(commandContext.getCommandSender(), child);
permission = this.isPermitted(commandContext.getSender(), child);
if (permission != null) {
throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(child)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
throw new NoPermissionException(permission, commandContext.getSender(), this.getChain(child)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
}
if (child.getValue() != null) {
if (commandQueue.isEmpty()) {
@ -199,12 +199,12 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
.apply(Objects.requireNonNull(
child.getValue().getOwningCommand())
.getComponents()),
commandContext.getCommandSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
} else {
throw new NoSuchCommandException(commandContext.getCommandSender(),
throw new NoSuchCommandException(commandContext.getSender(),
this.getChain(root)
.stream()
.map(Node::getValue)
@ -224,17 +224,17 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
.apply(Objects.requireNonNull(child.getValue()
.getOwningCommand())
.getComponents()),
commandContext.getCommandSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(
Collectors.toList()));
}
} else {
return this.parseCommand(commandContext, commandQueue, child);
}
} else if (result.getFailure().isPresent()) {
throw new ComponentParseException(result.getFailure().get(), commandContext.getCommandSender(),
throw new ComponentParseException(result.getFailure().get(), commandContext.getSender(),
this.getChain(child)
.stream()
.map(Node::getValue)
@ -264,7 +264,7 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
@Nonnull final Node<CommandComponent<C, ?>> root) {
/* If the sender isn't allowed to access the root node, no suggestions are needed */
if (this.isPermitted(commandContext.getCommandSender(), root) != null) {
if (this.isPermitted(commandContext.getSender(), root) != null) {
return Collections.emptyList();
}
final List<Node<CommandComponent<C, ?>>> children = root.getChildren();
@ -307,7 +307,7 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
}
final List<String> suggestions = new LinkedList<>();
for (final Node<CommandComponent<C, ?>> component : root.getChildren()) {
if (component.getValue() == null || this.isPermitted(commandContext.getCommandSender(), component) != null) {
if (component.getValue() == null || this.isPermitted(commandContext.getSender(), component) != null) {
continue;
}
suggestions.addAll(

View file

@ -69,6 +69,10 @@ public class CommandComponent<C extends CommandSender, T> implements Comparable<
* Default value, will be empty if none was supplied
*/
private final String defaultValue;
/**
* The type that is produces by the component's parser
*/
private final Class<T> valueType;
private Command<C, ?> owningCommand;
@ -79,9 +83,13 @@ public class CommandComponent<C extends CommandSender, T> implements Comparable<
* @param name The component name
* @param parser The component parser
* @param defaultValue Default value used when no value is provided by the command sender
* @param valueType Type produced by the parser
*/
public CommandComponent(final boolean required, @Nonnull final String name,
@Nonnull final ComponentParser<C, T> parser, @Nonnull final String defaultValue) {
public CommandComponent(final boolean required,
@Nonnull final String name,
@Nonnull final ComponentParser<C, T> parser,
@Nonnull final String defaultValue,
@Nonnull final Class<T> valueType) {
this.required = required;
this.name = Objects.requireNonNull(name, "Name may not be null");
if (!NAME_PATTERN.asPredicate().test(name)) {
@ -89,6 +97,7 @@ public class CommandComponent<C extends CommandSender, T> implements Comparable<
}
this.parser = Objects.requireNonNull(parser, "Parser may not be null");
this.defaultValue = defaultValue;
this.valueType = valueType;
}
/**
@ -97,10 +106,13 @@ public class CommandComponent<C extends CommandSender, T> implements Comparable<
* @param required Whether or not the component is required
* @param name The component name
* @param parser The component parser
* @param valueType Type produced by the parser
*/
public CommandComponent(final boolean required, @Nonnull final String name,
@Nonnull final ComponentParser<C, T> parser) {
this(required, name, parser, "");
public CommandComponent(final boolean required,
@Nonnull final String name,
@Nonnull final ComponentParser<C, T> parser,
@Nonnull final Class<T> valueType) {
this(required, name, parser, "", valueType);
}
/**
@ -115,7 +127,7 @@ public class CommandComponent<C extends CommandSender, T> implements Comparable<
@Nonnull
public static <C extends CommandSender, T> CommandComponent.Builder<C, T> ofType(@Nonnull final Class<T> clazz,
@Nonnull final String name) {
return new Builder<>(name);
return new Builder<>(clazz, name);
}
/**
@ -230,6 +242,15 @@ public class CommandComponent<C extends CommandSender, T> implements Comparable<
&& !this.getDefaultValue().isEmpty();
}
/**
* Get the type of this component's value
*
* @return Value type
*/
@Nonnull
public Class<T> getValueType() {
return this.valueType;
}
/**
* Mutable builder for {@link CommandComponent} instances
@ -239,13 +260,17 @@ public class CommandComponent<C extends CommandSender, T> implements Comparable<
*/
public static class Builder<C extends CommandSender, T> {
private final Class<T> valueType;
private final String name;
private boolean required = true;
private ComponentParser<C, T> parser = (c, i) -> ComponentParseResult.failure(
new UnsupportedOperationException("No parser was specified"));
private String defaultValue = "";
protected Builder(@Nonnull final String name) {
protected Builder(@Nonnull final Class<T> valueType,
@Nonnull final String name) {
this.valueType = valueType;
this.name = name;
}
@ -315,7 +340,7 @@ public class CommandComponent<C extends CommandSender, T> implements Comparable<
*/
@Nonnull
public CommandComponent<C, T> build() {
return new CommandComponent<>(this.required, this.name, this.parser, this.defaultValue);
return new CommandComponent<>(this.required, this.name, this.parser, this.defaultValue, this.valueType);
}
@Nonnull

View file

@ -44,7 +44,7 @@ import java.util.Set;
public final class StaticComponent<C extends CommandSender> extends CommandComponent<C, String> {
private StaticComponent(final boolean required, @Nonnull final String name, @Nonnull final String... aliases) {
super(required, name, new StaticComponentParser<>(name, aliases));
super(required, name, new StaticComponentParser<>(name, aliases), String.class);
}
/**

View file

@ -40,7 +40,7 @@ public final class BooleanComponent<C extends CommandSender> extends CommandComp
private BooleanComponent(final boolean required, @Nonnull final String name,
final boolean liberal, @Nonnull final String defaultValue) {
super(required, name, new BooleanParser<>(liberal), defaultValue);
super(required, name, new BooleanParser<>(liberal), defaultValue, Boolean.class);
this.liberal = liberal;
}
@ -100,7 +100,7 @@ public final class BooleanComponent<C extends CommandSender> extends CommandComp
private boolean liberal = false;
protected Builder(@Nonnull final String name) {
super(name);
super(Boolean.class, name);
}
/**

View file

@ -41,7 +41,7 @@ public final class ByteComponent<C extends CommandSender> extends CommandCompone
private ByteComponent(final boolean required, @Nonnull final String name, final byte min,
final byte max, final String defaultValue) {
super(required, name, new ByteParser<>(min, max), defaultValue);
super(required, name, new ByteParser<>(min, max), defaultValue, Byte.class);
this.min = min;
this.max = max;
}
@ -102,7 +102,7 @@ public final class ByteComponent<C extends CommandSender> extends CommandCompone
private byte max = Byte.MAX_VALUE;
protected Builder(@Nonnull final String name) {
super(name);
super(Byte.class, name);
}
/**

View file

@ -37,7 +37,7 @@ public final class CharComponent<C extends CommandSender> extends CommandCompone
private CharComponent(final boolean required, @Nonnull final String name,
@Nonnull final String defaultValue) {
super(required, name, new CharacterParser<>(), defaultValue);
super(required, name, new CharacterParser<>(), defaultValue, Character.class);
}
/**
@ -94,7 +94,7 @@ public final class CharComponent<C extends CommandSender> extends CommandCompone
public static final class Builder<C extends CommandSender> extends CommandComponent.Builder<C, Character> {
protected Builder(@Nonnull final String name) {
super(name);
super(Character.class, name);
}
/**

View file

@ -44,7 +44,7 @@ public final class DoubleComponent<C extends CommandSender> extends CommandCompo
final double min,
final double max,
final String defaultValue) {
super(required, name, new DoubleParser<>(min, max), defaultValue);
super(required, name, new DoubleParser<>(min, max), defaultValue, Double.class);
this.min = min;
this.max = max;
}
@ -106,7 +106,7 @@ public final class DoubleComponent<C extends CommandSender> extends CommandCompo
private double max = Double.MAX_VALUE;
protected Builder(@Nonnull final String name) {
super(name);
super(Double.class, name);
}
/**

View file

@ -48,7 +48,7 @@ public class EnumComponent<C extends CommandSender, E extends Enum<E>> extends C
final boolean required,
@Nonnull final String name,
@Nonnull final String defaultValue) {
super(required, name, new EnumParser<>(enumClass), defaultValue);
super(required, name, new EnumParser<>(enumClass), defaultValue, enumClass);
}
/**
@ -118,7 +118,7 @@ public class EnumComponent<C extends CommandSender, E extends Enum<E>> extends C
private final Class<E> enumClass;
protected Builder(@Nonnull final String name, @Nonnull final Class<E> enumClass) {
super(name);
super(enumClass, name);
this.enumClass = enumClass;
}

View file

@ -44,7 +44,7 @@ public final class FloatComponent<C extends CommandSender> extends CommandCompon
final float min,
final float max,
final String defaultValue) {
super(required, name, new FloatParser<>(min, max), defaultValue);
super(required, name, new FloatParser<>(min, max), defaultValue, Float.class);
this.min = min;
this.max = max;
}
@ -106,7 +106,7 @@ public final class FloatComponent<C extends CommandSender> extends CommandCompon
private float max = Float.MAX_VALUE;
protected Builder(@Nonnull final String name) {
super(name);
super(Float.class, name);
}
/**

View file

@ -44,7 +44,7 @@ public final class IntegerComponent<C extends CommandSender> extends CommandComp
final int min,
final int max,
final String defaultValue) {
super(required, name, new IntegerParser<>(min, max), defaultValue);
super(required, name, new IntegerParser<>(min, max), defaultValue, Integer.class);
this.min = min;
this.max = max;
}
@ -106,7 +106,7 @@ public final class IntegerComponent<C extends CommandSender> extends CommandComp
private int max = Integer.MAX_VALUE;
protected Builder(@Nonnull final String name) {
super(name);
super(Integer.class, name);
}
/**

View file

@ -44,7 +44,7 @@ public final class LongComponent<C extends CommandSender> extends CommandCompone
final long min,
final long max,
final String defaultValue) {
super(required, name, new LongParser<>(min, max), defaultValue);
super(required, name, new LongParser<>(min, max), defaultValue, Long.class);
this.min = min;
this.max = max;
}
@ -106,7 +106,7 @@ public final class LongComponent<C extends CommandSender> extends CommandCompone
private long max = Long.MAX_VALUE;
protected Builder(@Nonnull final String name) {
super(name);
super(Long.class, name);
}
/**

View file

@ -44,7 +44,7 @@ public final class ShortComponent<C extends CommandSender> extends CommandCompon
final short min,
final short max,
final String defaultValue) {
super(required, name, new ShortParser<>(min, max), defaultValue);
super(required, name, new ShortParser<>(min, max), defaultValue, Short.class);
this.min = min;
this.max = max;
}
@ -106,7 +106,7 @@ public final class ShortComponent<C extends CommandSender> extends CommandCompon
private short max = Short.MAX_VALUE;
protected Builder(@Nonnull final String name) {
super(name);
super(Short.class, name);
}
/**

View file

@ -46,7 +46,7 @@ public final class StringComponent<C extends CommandSender> extends CommandCompo
@Nonnull final StringMode stringMode,
@Nonnull final String defaultValue,
@Nonnull final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider) {
super(required, name, new StringParser<>(stringMode, suggestionsProvider), defaultValue);
super(required, name, new StringParser<>(stringMode, suggestionsProvider), defaultValue, String.class);
this.stringMode = stringMode;
}
@ -124,7 +124,7 @@ public final class StringComponent<C extends CommandSender> extends CommandCompo
private BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider = (v1, v2) -> Collections.emptyList();
protected Builder(@Nonnull final String name) {
super(name);
super(String.class, name);
}
/**

View file

@ -55,7 +55,7 @@ public final class CommandContext<C extends CommandSender> {
* @return Command sender
*/
@Nonnull
public C getCommandSender() {
public C getSender() {
return this.commandSender;
}
@ -77,6 +77,7 @@ public final class CommandContext<C extends CommandSender> {
* @param <T> Value type
* @return Value
*/
@Nonnull
public <T> Optional<T> get(@Nonnull final String key) {
final Object value = this.internalStorage.get(key);
if (value != null) {
@ -87,4 +88,22 @@ public final class CommandContext<C extends CommandSender> {
}
}
/**
* Get a required argument from the context
*
* @param key Argument key
* @param <T> Argument type
* @return Argument
* @throws NullPointerException If no such argument is stored
*/
@Nonnull
@SuppressWarnings("unchecked")
public <T> T getRequired(@Nonnull final String key) {
final Object value = this.internalStorage.get(key);
if (value == null) {
throw new NullPointerException("No such object stored in the context: " + key);
}
return (T) value;
}
}

View file

@ -42,9 +42,9 @@ public class CommandPreProcessorTest {
@BeforeAll
static void newTree() {
manager = new TestCommandManager();
manager.registerCommand(manager.commandBuilder("test", SimpleCommandMeta.empty())
.withComponent(EnumComponent.required(SampleEnum.class, "enum"))
.withHandler(
manager.command(manager.commandBuilder("test", SimpleCommandMeta.empty())
.component(EnumComponent.required(SampleEnum.class, "enum"))
.handler(
commandContext -> System.out.printf("enum = %s | integer = %d\n",
commandContext.<SampleEnum>get(
"enum").orElse(

View file

@ -38,8 +38,8 @@ class CommandTest {
@Test
void ensureOrdering() {
Assertions.assertThrows(IllegalArgumentException.class, () ->
Command.newBuilder("test", SimpleCommandMeta.empty()).withComponent(StaticComponent.optional("something"))
.withComponent(StaticComponent.required("somethingelse")).build());
Command.newBuilder("test", SimpleCommandMeta.empty()).component(StaticComponent.optional("something"))
.component(StaticComponent.required("somethingelse")).build());
}
}

View file

@ -46,14 +46,14 @@ class CommandTreeTest {
@BeforeAll
static void newTree() {
commandManager = new TestCommandManager();
commandManager.registerCommand(commandManager.commandBuilder("test", SimpleCommandMeta.empty())
.withComponent(StaticComponent.required("one")).build())
.registerCommand(commandManager.commandBuilder("test", SimpleCommandMeta.empty())
.withComponent(StaticComponent.required("two")).withPermission("no").build())
.registerCommand(commandManager.commandBuilder("test", Collections.singleton("other"),
commandManager.command(commandManager.commandBuilder("test", SimpleCommandMeta.empty())
.component(StaticComponent.required("one")).build())
.command(commandManager.commandBuilder("test", SimpleCommandMeta.empty())
.component(StaticComponent.required("two")).withPermission("no").build())
.command(commandManager.commandBuilder("test", Collections.singleton("other"),
SimpleCommandMeta.empty())
.withComponent(StaticComponent.required("opt", "öpt"))
.withComponent(IntegerComponent
.component(StaticComponent.required("opt", "öpt"))
.component(IntegerComponent
.optional("num", EXPECTED_INPUT_NUMBER))
.build());
}