diff --git a/src/main/java/net/minecraft/commands/CommandDispatcher.java b/src/main/java/net/minecraft/commands/CommandDispatcher.java index 72e29de..29a8251 100644 --- a/src/main/java/net/minecraft/commands/CommandDispatcher.java +++ b/src/main/java/net/minecraft/commands/CommandDispatcher.java @@ -1,32 +1,51 @@ package net.minecraft.commands; -import com.google.common.collect.Maps; import net.minecraft.commands.builder.LiteralArgumentBuilder; import net.minecraft.commands.context.CommandContext; -import net.minecraft.commands.context.ParsedArgument; +import net.minecraft.commands.context.CommandContextBuilder; import net.minecraft.commands.exceptions.CommandException; +import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; import net.minecraft.commands.exceptions.UnknownCommandException; -import net.minecraft.commands.tree.LiteralCommandNode; - -import java.util.HashMap; -import java.util.Map; +import net.minecraft.commands.tree.CommandNode; +import net.minecraft.commands.tree.RootCommandNode; public class CommandDispatcher { - private final Map commands = Maps.newHashMap(); + private final RootCommandNode root = new RootCommandNode(); public void register(LiteralArgumentBuilder command) { - if (commands.containsKey(command.getLiteral())) { - throw new IllegalArgumentException("New command " + command.getLiteral() + " conflicts with existing command " + command.getLiteral()); - } - commands.put(command.getLiteral(), command.build()); + root.addChild(command.build()); } public void execute(String command) throws CommandException { - LiteralCommandNode node = commands.get(command); - if (node == null) { + CommandContextBuilder contextBuilder = new CommandContextBuilder(); + CommandNode node = root; + + while (command.length() > 0 && !node.getChildren().isEmpty()) { + IllegalArgumentSyntaxException exception = null; + + for (CommandNode child : node.getChildren()) { + try { + command = child.parse(command, contextBuilder); + if (child.getCommand() != null) { + contextBuilder.withCommand(child.getCommand()); + } + node = child; + break; + } catch (IllegalArgumentSyntaxException ex) { + exception = ex; + } + } + + if (exception != null) { + break; + } + } + + if (command.length() > 0) { throw new UnknownCommandException(); } - node.getCommand().run(new CommandContext(new HashMap>())); + CommandContext context = contextBuilder.build(); + context.getCommand().run(context); } } diff --git a/src/main/java/net/minecraft/commands/context/CommandContext.java b/src/main/java/net/minecraft/commands/context/CommandContext.java index efda5f2..2796264 100644 --- a/src/main/java/net/minecraft/commands/context/CommandContext.java +++ b/src/main/java/net/minecraft/commands/context/CommandContext.java @@ -1,14 +1,21 @@ package net.minecraft.commands.context; import com.google.common.primitives.Primitives; +import net.minecraft.commands.Command; import java.util.Map; public class CommandContext { private final Map> arguments; + private final Command command; - public CommandContext(Map> arguments) { + public CommandContext(Map> arguments, Command command) { this.arguments = arguments; + this.command = command; + } + + public Command getCommand() { + return command; } @SuppressWarnings("unchecked") diff --git a/src/main/java/net/minecraft/commands/context/CommandContextBuilder.java b/src/main/java/net/minecraft/commands/context/CommandContextBuilder.java index 2051ae2..b4604cc 100644 --- a/src/main/java/net/minecraft/commands/context/CommandContextBuilder.java +++ b/src/main/java/net/minecraft/commands/context/CommandContextBuilder.java @@ -1,11 +1,13 @@ package net.minecraft.commands.context; import com.google.common.collect.Maps; +import net.minecraft.commands.Command; import java.util.Map; public class CommandContextBuilder { private final Map> arguments = Maps.newHashMap(); + private Command command; public CommandContextBuilder() { } @@ -15,7 +17,16 @@ public class CommandContextBuilder { return this; } + public Map> getArguments() { + return arguments; + } + + public CommandContextBuilder withCommand(Command command) { + this.command = command; + return this; + } + public CommandContext build() { - return new CommandContext(arguments); + return new CommandContext(arguments, command); } } diff --git a/src/main/java/net/minecraft/commands/tree/ArgumentCommandNode.java b/src/main/java/net/minecraft/commands/tree/ArgumentCommandNode.java index 0d22f32..4f0b7ab 100644 --- a/src/main/java/net/minecraft/commands/tree/ArgumentCommandNode.java +++ b/src/main/java/net/minecraft/commands/tree/ArgumentCommandNode.java @@ -2,6 +2,7 @@ package net.minecraft.commands.tree; import net.minecraft.commands.Command; import net.minecraft.commands.arguments.CommandArgumentType; +import net.minecraft.commands.context.CommandContextBuilder; import net.minecraft.commands.context.ParsedArgument; import net.minecraft.commands.exceptions.ArgumentValidationException; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; @@ -25,23 +26,16 @@ public class ArgumentCommandNode extends CommandNode { } @Override - public CommandNode parse(String command) throws IllegalArgumentSyntaxException, ArgumentValidationException { + public String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException { ParsedArgument parsed = type.parse(command); - int start = parsed.getRaw().length() + 1; + int start = parsed.getRaw().length(); - if (start < command.length()) { - String result = command.substring(start); + contextBuilder.withArgument(name, parsed); - for (CommandNode node : getChildren()) { - try { - return node.parse(result); - } catch (IllegalArgumentSyntaxException ignored) { - } - } - - throw new IllegalArgumentSyntaxException(); + if (command.length() > start) { + return command.substring(start + 1); } else { - return this; + return ""; } } } diff --git a/src/main/java/net/minecraft/commands/tree/CommandNode.java b/src/main/java/net/minecraft/commands/tree/CommandNode.java index 987d18b..4ff6fb5 100644 --- a/src/main/java/net/minecraft/commands/tree/CommandNode.java +++ b/src/main/java/net/minecraft/commands/tree/CommandNode.java @@ -2,6 +2,7 @@ package net.minecraft.commands.tree; import com.google.common.collect.Lists; import net.minecraft.commands.Command; +import net.minecraft.commands.context.CommandContextBuilder; import net.minecraft.commands.exceptions.ArgumentValidationException; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; @@ -27,5 +28,5 @@ public abstract class CommandNode { children.add(node); } - public abstract CommandNode parse(String command) throws IllegalArgumentSyntaxException, ArgumentValidationException; + public abstract String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException; } diff --git a/src/main/java/net/minecraft/commands/tree/LiteralCommandNode.java b/src/main/java/net/minecraft/commands/tree/LiteralCommandNode.java index bf5a840..24f8606 100644 --- a/src/main/java/net/minecraft/commands/tree/LiteralCommandNode.java +++ b/src/main/java/net/minecraft/commands/tree/LiteralCommandNode.java @@ -1,6 +1,7 @@ package net.minecraft.commands.tree; import net.minecraft.commands.Command; +import net.minecraft.commands.context.CommandContextBuilder; import net.minecraft.commands.exceptions.ArgumentValidationException; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; @@ -17,26 +18,16 @@ public class LiteralCommandNode extends CommandNode { } @Override - public CommandNode parse(String command) throws IllegalArgumentSyntaxException, ArgumentValidationException { - if (command.startsWith(literal)) { - int start = literal.length() + 1; - - if (start < command.length()) { - String result = command.substring(start); - - for (CommandNode node : getChildren()) { - try { - return node.parse(result); - } catch (IllegalArgumentSyntaxException ignored) { - } - } - - throw new IllegalArgumentSyntaxException(); - } else { - return this; - } - } else { + public String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException { + if (!command.startsWith(literal)) { throw new IllegalArgumentSyntaxException(); } + + int start = literal.length(); + if (command.length() > start) { + return command.substring(start + 1); + } else { + return ""; + } } } diff --git a/src/main/java/net/minecraft/commands/tree/RootCommandNode.java b/src/main/java/net/minecraft/commands/tree/RootCommandNode.java new file mode 100644 index 0000000..c70f084 --- /dev/null +++ b/src/main/java/net/minecraft/commands/tree/RootCommandNode.java @@ -0,0 +1,16 @@ +package net.minecraft.commands.tree; + +import net.minecraft.commands.context.CommandContextBuilder; +import net.minecraft.commands.exceptions.ArgumentValidationException; +import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; + +public class RootCommandNode extends CommandNode { + public RootCommandNode() { + super(null); + } + + @Override + public String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException { + return command; + } +} diff --git a/src/main/test/net/minecraft/commands/CommandDispatcherTest.java b/src/main/test/net/minecraft/commands/CommandDispatcherTest.java index 108ede9..33002cf 100644 --- a/src/main/test/net/minecraft/commands/CommandDispatcherTest.java +++ b/src/main/test/net/minecraft/commands/CommandDispatcherTest.java @@ -8,8 +8,11 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import static net.minecraft.commands.arguments.IntegerArgumentType.integer; import static net.minecraft.commands.builder.LiteralArgumentBuilder.literal; +import static net.minecraft.commands.builder.RequiredArgumentBuilder.argument; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) @@ -22,12 +25,6 @@ public class CommandDispatcherTest { subject = new CommandDispatcher(); } - @Test(expected = IllegalArgumentException.class) - public void testDuplicateCommand() throws Exception { - subject.register(literal("foo").executes(command)); - subject.register(literal("foo").executes(command)); - } - @Test public void testCreateAndExecuteCommand() throws Exception { subject.register(literal("foo").executes(command)); @@ -40,4 +37,35 @@ public class CommandDispatcherTest { public void testExecuteUnknownCommand() throws Exception { subject.execute("foo"); } + + @Test(expected = UnknownCommandException.class) + public void testExecuteUnknownSubcommand() throws Exception { + subject.register(literal("foo").executes(command)); + subject.execute("foo bar"); + } + + @Test + public void testExecuteSubcommand() throws Exception { + Command subCommand = mock(Command.class); + + subject.register(literal("foo").then( + literal("a") + ).then( + literal("b").executes(subCommand) + ).then( + literal("c") + ).executes(command)); + + subject.execute("foo b"); + verify(subCommand).run(any(CommandContext.class)); + } + + @Test(expected = UnknownCommandException.class) + public void testExecuteInvalidSubcommand() throws Exception { + subject.register(literal("foo").then( + argument("bar", integer()) + ).executes(command)); + + subject.execute("foo bar"); + } } \ No newline at end of file diff --git a/src/main/test/net/minecraft/commands/tree/ArgumentCommandNodeTest.java b/src/main/test/net/minecraft/commands/tree/ArgumentCommandNodeTest.java index 2f79cf6..eba45d4 100644 --- a/src/main/test/net/minecraft/commands/tree/ArgumentCommandNodeTest.java +++ b/src/main/test/net/minecraft/commands/tree/ArgumentCommandNodeTest.java @@ -1,5 +1,6 @@ package net.minecraft.commands.tree; +import net.minecraft.commands.context.CommandContextBuilder; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; import org.junit.Before; import org.junit.Test; @@ -11,40 +12,32 @@ import static org.junit.Assert.assertThat; public class ArgumentCommandNodeTest { ArgumentCommandNode node; + CommandContextBuilder contextBuilder; @Before public void setUp() throws Exception { node = argument("foo", integer()).build(); + contextBuilder = new CommandContextBuilder(); } @Test public void testParse() throws Exception { - assertThat((ArgumentCommandNode) node.parse("123"), is(node)); + assertThat(node.parse("123 456", contextBuilder), is("456")); + + assertThat(contextBuilder.getArguments().containsKey("foo"), is(true)); + assertThat(contextBuilder.getArguments().get("foo").getResult(), is((Object) 123)); + } + + @Test + public void testParseExact() throws Exception { + assertThat(node.parse("123", contextBuilder), is("")); + + assertThat(contextBuilder.getArguments().containsKey("foo"), is(true)); + assertThat(contextBuilder.getArguments().get("foo").getResult(), is((Object) 123)); } @Test(expected = IllegalArgumentSyntaxException.class) public void testParseInvalid() throws Exception { - node.parse("bar"); - } - - @Test - public void testParseChild() throws Exception { - CommandNode child = argument("bar", integer()).build(); - - node.addChild(child); - - assertThat(node.parse("123 123"), is(child)); - } - - @Test(expected = IllegalArgumentSyntaxException.class) - public void testParseInvalidChild() throws Exception { - node.addChild(argument("bar", integer()).build()); - - node.parse("123 bar"); - } - - @Test(expected = IllegalArgumentSyntaxException.class) - public void testParseNoChildren() throws Exception { - node.parse("123 123"); + node.parse("foo", contextBuilder); } } \ No newline at end of file diff --git a/src/main/test/net/minecraft/commands/tree/LiteralCommandNodeTest.java b/src/main/test/net/minecraft/commands/tree/LiteralCommandNodeTest.java index f49f714..89b0bc4 100644 --- a/src/main/test/net/minecraft/commands/tree/LiteralCommandNodeTest.java +++ b/src/main/test/net/minecraft/commands/tree/LiteralCommandNodeTest.java @@ -1,51 +1,36 @@ package net.minecraft.commands.tree; +import net.minecraft.commands.context.CommandContextBuilder; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; import org.junit.Before; import org.junit.Test; -import static net.minecraft.commands.arguments.IntegerArgumentType.integer; import static net.minecraft.commands.builder.LiteralArgumentBuilder.literal; -import static net.minecraft.commands.builder.RequiredArgumentBuilder.argument; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; public class LiteralCommandNodeTest { LiteralCommandNode node; + CommandContextBuilder contextBuilder; @Before public void setUp() throws Exception { node = literal("foo").build(); + contextBuilder = new CommandContextBuilder(); } @Test public void testParse() throws Exception { - assertThat((LiteralCommandNode) node.parse("foo"), is(node)); + assertThat(node.parse("foo bar", contextBuilder), is("bar")); + } + + @Test + public void testParseExact() throws Exception { + assertThat(node.parse("foo", contextBuilder), is("")); } @Test(expected = IllegalArgumentSyntaxException.class) public void testParseInvalid() throws Exception { - node.parse("bar"); - } - - @Test - public void testParseChild() throws Exception { - CommandNode child = argument("bar", integer()).build(); - - node.addChild(child); - - assertThat(node.parse("foo 123"), is(child)); - } - - @Test(expected = IllegalArgumentSyntaxException.class) - public void testParseInvalidChild() throws Exception { - node.addChild(argument("bar", integer()).build()); - - node.parse("foo bar"); - } - - @Test(expected = IllegalArgumentSyntaxException.class) - public void testParseNoChildren() throws Exception { - node.parse("foo 123"); + node.parse("bar", contextBuilder); } } \ No newline at end of file diff --git a/src/main/test/net/minecraft/commands/tree/RootCommandNodeTest.java b/src/main/test/net/minecraft/commands/tree/RootCommandNodeTest.java new file mode 100644 index 0000000..87d9610 --- /dev/null +++ b/src/main/test/net/minecraft/commands/tree/RootCommandNodeTest.java @@ -0,0 +1,23 @@ +package net.minecraft.commands.tree; + +import net.minecraft.commands.context.CommandContextBuilder; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class RootCommandNodeTest { + RootCommandNode node; + + @Before + public void setUp() throws Exception { + node = new RootCommandNode(); + } + + @Test + public void testParse() throws Exception { + assertThat(node.parse("foo bar baz", new CommandContextBuilder()), is("foo bar baz")); + + } +} \ No newline at end of file