diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/Command.java b/cloud-core/src/main/java/com/intellectualsites/commands/Command.java index 09e0ae40..456968a9 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/Command.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/Command.java @@ -135,7 +135,8 @@ public class Command { public static Builder newBuilder(@Nonnull final String commandName, @Nonnull final M commandMeta, @Nonnull final String... aliases) { - return new Builder<>(commandMeta, null, Collections.singletonList(StaticComponent.required(commandName, aliases)), + return new Builder<>(null, commandMeta, null, + Collections.singletonList(StaticComponent.required(commandName, aliases)), new CommandExecutionHandler.NullCommandExecutionHandler<>(), ""); } @@ -223,12 +224,15 @@ public class Command { @Nonnull private final CommandExecutionHandler commandExecutionHandler; @Nullable private final Class senderType; @Nonnull private final String commandPermission; + @Nullable private final CommandManager commandManager; - private Builder(@Nonnull final M commandMeta, + private Builder(@Nullable final CommandManager commandManager, + @Nonnull final M commandMeta, @Nullable final Class senderType, @Nonnull final List> commandComponents, @Nonnull final CommandExecutionHandler commandExecutionHandler, @Nonnull final String commandPermission) { + this.commandManager = commandManager; this.senderType = senderType; this.commandComponents = Objects.requireNonNull(commandComponents, "Components may not be null"); this.commandExecutionHandler = Objects.requireNonNull(commandExecutionHandler, "Execution handler may not be null"); @@ -236,6 +240,20 @@ public class Command { this.commandMeta = Objects.requireNonNull(commandMeta, "Meta may not be null"); } + /** + * Supply a command manager instance to the builder. This will be used when attempting to + * retrieve command component parsers, in the case that they're needed. This + * is optional + * + * @param commandManager Command manager + * @return New builder instance using the provided command manager + */ + @Nonnull + public Builder manager(@Nullable final CommandManager commandManager) { + return new Builder<>(commandManager, this.commandMeta, this.senderType, this.commandComponents, + this.commandExecutionHandler, this.commandPermission); + } + /** * Add a new command component to the command * @@ -247,8 +265,8 @@ public class Command { public Builder component(@Nonnull final CommandComponent component) { final List> commandComponents = new LinkedList<>(this.commandComponents); commandComponents.add(component); - return new Builder<>(this.commandMeta, this.senderType, commandComponents, this.commandExecutionHandler, - this.commandPermission); + return new Builder<>(this.commandManager, this.commandMeta, this.senderType, commandComponents, + this.commandExecutionHandler, this.commandPermission); } /** @@ -265,6 +283,9 @@ public class Command { @Nonnull final String name, @Nonnull final Consumer> builderConsumer) { final CommandComponent.Builder builder = CommandComponent.ofType(clazz, name); + if (this.commandManager != null) { + builder.manager(this.commandManager); + } builderConsumer.accept(builder); return this.component(builder.build()); } @@ -277,8 +298,8 @@ public class Command { */ @Nonnull public Builder handler(@Nonnull final CommandExecutionHandler commandExecutionHandler) { - return new Builder<>(this.commandMeta, this.senderType, this.commandComponents, commandExecutionHandler, - this.commandPermission); + return new Builder<>(this.commandManager, this.commandMeta, this.senderType, this.commandComponents, + commandExecutionHandler, this.commandPermission); } /** @@ -289,8 +310,8 @@ public class Command { */ @Nonnull public Builder withSenderType(@Nonnull final Class senderType) { - return new Builder<>(this.commandMeta, senderType, this.commandComponents, this.commandExecutionHandler, - this.commandPermission); + return new Builder<>(this.commandManager, this.commandMeta, senderType, this.commandComponents, + this.commandExecutionHandler, this.commandPermission); } /** @@ -301,8 +322,8 @@ public class Command { */ @Nonnull public Builder withPermission(@Nonnull final String permission) { - return new Builder<>(this.commandMeta, this.senderType, this.commandComponents, this.commandExecutionHandler, - permission); + return new Builder<>(this.commandManager, this.commandMeta, this.senderType, this.commandComponents, + this.commandExecutionHandler, permission); } /** @@ -312,8 +333,8 @@ public class Command { */ @Nonnull public Command build() { - return new Command<>(Collections.unmodifiableList(this.commandComponents), this.commandExecutionHandler, - this.senderType, this.commandPermission, this.commandMeta); + return new Command<>(Collections.unmodifiableList(this.commandComponents), + this.commandExecutionHandler, this.senderType, this.commandPermission, this.commandMeta); } } diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java b/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java index 6a26f3e4..a684096e 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java @@ -24,6 +24,7 @@ package com.intellectualsites.commands; import com.google.common.reflect.TypeToken; +import com.intellectualsites.commands.components.CommandComponent; import com.intellectualsites.commands.components.CommandSyntaxFormatter; import com.intellectualsites.commands.components.StandardCommandSyntaxFormatter; import com.intellectualsites.commands.components.parser.ParserRegistry; @@ -231,7 +232,20 @@ public abstract class CommandManager commandBuilder(@Nonnull final String name) { - return Command.newBuilder(name, this.createDefaultCommandMeta()); + return Command.newBuilder(name, this.createDefaultCommandMeta()).manager(this); + } + + /** + * Create a new command component builder + * + * @param type Component type + * @param name Component name + * @param Generic component name + * @return Component builder + */ + @Nonnull + public CommandComponent.Builder componentBuilder(@Nonnull final Class type, @Nonnull final String name) { + return CommandComponent.ofType(type, name).manager(this); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/CommandComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/CommandComponent.java index 16c75301..00567ee5 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/CommandComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/CommandComponent.java @@ -23,9 +23,12 @@ // package com.intellectualsites.commands.components; +import com.google.common.reflect.TypeToken; import com.intellectualsites.commands.Command; +import com.intellectualsites.commands.CommandManager; import com.intellectualsites.commands.components.parser.ComponentParseResult; import com.intellectualsites.commands.components.parser.ComponentParser; +import com.intellectualsites.commands.components.parser.ParserParameters; import com.intellectualsites.commands.sender.CommandSender; import javax.annotation.Nonnull; @@ -263,9 +266,9 @@ public class CommandComponent implements Comparable< private final Class valueType; private final String name; + private CommandManager manager; private boolean required = true; - private ComponentParser parser = (c, i) -> ComponentParseResult.failure( - new UnsupportedOperationException("No parser was specified")); + private ComponentParser parser; private String defaultValue = ""; protected Builder(@Nonnull final Class valueType, @@ -274,6 +277,19 @@ public class CommandComponent implements Comparable< this.name = name; } + /** + * Set the command manager. Will be used to create a default parser + * if none was provided + * + * @param manager Command manager + * @return Builder instance + */ + @Nonnull + public Builder manager(@Nonnull final CommandManager manager) { + this.manager = manager; + return this; + } + /** * Indicates that the component is required. * All components prior to any other required @@ -340,6 +356,14 @@ public class CommandComponent implements Comparable< */ @Nonnull public CommandComponent build() { + if (this.parser == null && this.manager != null) { + this.parser = this.manager.getParserRegistry().createParser(TypeToken.of(valueType), ParserParameters.empty()) + .orElse(null); + } + if (this.parser == null) { + this.parser = (c, i) -> ComponentParseResult + .failure(new UnsupportedOperationException("No parser was specified")); + } return new CommandComponent<>(this.required, this.name, this.parser, this.defaultValue, this.valueType); } diff --git a/cloud-core/src/test/java/com/intellectualsites/commands/CommandTreeTest.java b/cloud-core/src/test/java/com/intellectualsites/commands/CommandTreeTest.java index e02b05cc..e51b8a3a 100644 --- a/cloud-core/src/test/java/com/intellectualsites/commands/CommandTreeTest.java +++ b/cloud-core/src/test/java/com/intellectualsites/commands/CommandTreeTest.java @@ -42,66 +42,81 @@ import java.util.concurrent.CompletionException; class CommandTreeTest { private static final int EXPECTED_INPUT_NUMBER = 15; - private static CommandManager commandManager; + private static CommandManager manager; @BeforeAll static void newTree() { - commandManager = new TestCommandManager(); - 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()) - .component(StaticComponent.required("opt", "öpt")) - .component(IntegerComponent + manager = new TestCommandManager(); + manager.command(manager.commandBuilder("test", SimpleCommandMeta.empty()) + .component(StaticComponent.required("one")).build()) + .command(manager.commandBuilder("test", SimpleCommandMeta.empty()) + .component(StaticComponent.required("two")).withPermission("no").build()) + .command(manager.commandBuilder("test", Collections.singleton("other"), + SimpleCommandMeta.empty()) + .component(StaticComponent.required("opt", "öpt")) + .component(IntegerComponent .optional("num", EXPECTED_INPUT_NUMBER)) - .build()) - .command(commandManager.commandBuilder("req").withSenderType(SpecificCommandSender.class).build()); + .build()) + .command(manager.commandBuilder("req").withSenderType(SpecificCommandSender.class).build()); } @Test void parse() { - final Optional> command = commandManager.getCommandTree() - .parse(new CommandContext<>( + final Optional> command = manager.getCommandTree() + .parse(new CommandContext<>( new TestCommandSender()), new LinkedList<>( Arrays.asList("test", "one"))); Assertions.assertTrue(command.isPresent()); - Assertions.assertThrows(NoPermissionException.class, () -> commandManager.getCommandTree() - .parse(new CommandContext<>( + Assertions.assertThrows(NoPermissionException.class, () -> manager.getCommandTree() + .parse(new CommandContext<>( new TestCommandSender()), new LinkedList<>( Arrays.asList("test", "two")))); - commandManager.getCommandTree() - .parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt"))) - .ifPresent(c -> c.getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender()))); - commandManager.getCommandTree() - .parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt", "12"))) - .ifPresent(c -> c.getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender()))); + manager.getCommandTree() + .parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt"))) + .ifPresent(c -> c.getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender()))); + manager.getCommandTree() + .parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("test", "opt", "12"))) + .ifPresent(c -> c.getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender()))); } @Test void testAlias() { - commandManager.getCommandTree() - .parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("other", "öpt", "12"))) - .ifPresent(c -> c.getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender()))); + manager.getCommandTree() + .parse(new CommandContext<>(new TestCommandSender()), new LinkedList<>(Arrays.asList("other", "öpt", "12"))) + .ifPresent(c -> c.getCommandExecutionHandler().execute(new CommandContext<>(new TestCommandSender()))); } @Test void getSuggestions() { Assertions.assertFalse( - commandManager.getCommandTree().getSuggestions(new CommandContext<>(new TestCommandSender()), new LinkedList<>( + manager.getCommandTree().getSuggestions(new CommandContext<>(new TestCommandSender()), new LinkedList<>( Collections.singletonList("test "))).isEmpty()); } @Test void testRequiredSender() { Assertions.assertThrows(CompletionException.class, () -> - commandManager.executeCommand(new TestCommandSender(), "req").join()); + manager.executeCommand(new TestCommandSender(), "req").join()); } + @Test + void testDefaultParser() { + manager.command( + manager.commandBuilder("default") + .component(manager.componentBuilder(Integer.class, "int").build()) + .handler(context -> { + final int number = context.getRequired("int"); + System.out.printf("Supplied number is: %d\n", number); + }) + .build() + ); + manager.executeCommand(new TestCommandSender(), "default 5").join(); + } + + public static final class SpecificCommandSender extends TestCommandSender { }