Changed how parsing is done, performed by dispatcher and can now look as deep as needed

This commit is contained in:
Nathan Adams 2014-09-24 17:12:06 +02:00
parent 8da6087618
commit d678a5cb21
11 changed files with 171 additions and 103 deletions

View file

@ -1,32 +1,51 @@
package net.minecraft.commands; package net.minecraft.commands;
import com.google.common.collect.Maps;
import net.minecraft.commands.builder.LiteralArgumentBuilder; import net.minecraft.commands.builder.LiteralArgumentBuilder;
import net.minecraft.commands.context.CommandContext; 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.CommandException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.UnknownCommandException; import net.minecraft.commands.exceptions.UnknownCommandException;
import net.minecraft.commands.tree.LiteralCommandNode; import net.minecraft.commands.tree.CommandNode;
import net.minecraft.commands.tree.RootCommandNode;
import java.util.HashMap;
import java.util.Map;
public class CommandDispatcher { public class CommandDispatcher {
private final Map<String, LiteralCommandNode> commands = Maps.newHashMap(); private final RootCommandNode root = new RootCommandNode();
public void register(LiteralArgumentBuilder command) { public void register(LiteralArgumentBuilder command) {
if (commands.containsKey(command.getLiteral())) { root.addChild(command.build());
throw new IllegalArgumentException("New command " + command.getLiteral() + " conflicts with existing command " + command.getLiteral());
}
commands.put(command.getLiteral(), command.build());
} }
public void execute(String command) throws CommandException { public void execute(String command) throws CommandException {
LiteralCommandNode node = commands.get(command); CommandContextBuilder contextBuilder = new CommandContextBuilder();
if (node == null) { 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(); throw new UnknownCommandException();
} }
node.getCommand().run(new CommandContext(new HashMap<String, ParsedArgument<?>>())); CommandContext context = contextBuilder.build();
context.getCommand().run(context);
} }
} }

View file

@ -1,14 +1,21 @@
package net.minecraft.commands.context; package net.minecraft.commands.context;
import com.google.common.primitives.Primitives; import com.google.common.primitives.Primitives;
import net.minecraft.commands.Command;
import java.util.Map; import java.util.Map;
public class CommandContext { public class CommandContext {
private final Map<String, ParsedArgument<?>> arguments; private final Map<String, ParsedArgument<?>> arguments;
private final Command command;
public CommandContext(Map<String, ParsedArgument<?>> arguments) { public CommandContext(Map<String, ParsedArgument<?>> arguments, Command command) {
this.arguments = arguments; this.arguments = arguments;
this.command = command;
}
public Command getCommand() {
return command;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View file

@ -1,11 +1,13 @@
package net.minecraft.commands.context; package net.minecraft.commands.context;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import net.minecraft.commands.Command;
import java.util.Map; import java.util.Map;
public class CommandContextBuilder { public class CommandContextBuilder {
private final Map<String, ParsedArgument<?>> arguments = Maps.newHashMap(); private final Map<String, ParsedArgument<?>> arguments = Maps.newHashMap();
private Command command;
public CommandContextBuilder() { public CommandContextBuilder() {
} }
@ -15,7 +17,16 @@ public class CommandContextBuilder {
return this; return this;
} }
public Map<String, ParsedArgument<?>> getArguments() {
return arguments;
}
public CommandContextBuilder withCommand(Command command) {
this.command = command;
return this;
}
public CommandContext build() { public CommandContext build() {
return new CommandContext(arguments); return new CommandContext(arguments, command);
} }
} }

View file

@ -2,6 +2,7 @@ package net.minecraft.commands.tree;
import net.minecraft.commands.Command; import net.minecraft.commands.Command;
import net.minecraft.commands.arguments.CommandArgumentType; import net.minecraft.commands.arguments.CommandArgumentType;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.context.ParsedArgument; import net.minecraft.commands.context.ParsedArgument;
import net.minecraft.commands.exceptions.ArgumentValidationException; import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
@ -25,23 +26,16 @@ public class ArgumentCommandNode<T> extends CommandNode {
} }
@Override @Override
public CommandNode parse(String command) throws IllegalArgumentSyntaxException, ArgumentValidationException { public String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException {
ParsedArgument<T> parsed = type.parse(command); ParsedArgument<T> parsed = type.parse(command);
int start = parsed.getRaw().length() + 1; int start = parsed.getRaw().length();
if (start < command.length()) { contextBuilder.withArgument(name, parsed);
String result = command.substring(start);
for (CommandNode node : getChildren()) { if (command.length() > start) {
try { return command.substring(start + 1);
return node.parse(result);
} catch (IllegalArgumentSyntaxException ignored) {
}
}
throw new IllegalArgumentSyntaxException();
} else { } else {
return this; return "";
} }
} }
} }

View file

@ -2,6 +2,7 @@ package net.minecraft.commands.tree;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import net.minecraft.commands.Command; import net.minecraft.commands.Command;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.ArgumentValidationException; import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
@ -27,5 +28,5 @@ public abstract class CommandNode {
children.add(node); children.add(node);
} }
public abstract CommandNode parse(String command) throws IllegalArgumentSyntaxException, ArgumentValidationException; public abstract String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException;
} }

View file

@ -1,6 +1,7 @@
package net.minecraft.commands.tree; package net.minecraft.commands.tree;
import net.minecraft.commands.Command; import net.minecraft.commands.Command;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.ArgumentValidationException; import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
@ -17,26 +18,16 @@ public class LiteralCommandNode extends CommandNode {
} }
@Override @Override
public CommandNode parse(String command) throws IllegalArgumentSyntaxException, ArgumentValidationException { public String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException {
if (command.startsWith(literal)) { 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 {
throw new IllegalArgumentSyntaxException(); throw new IllegalArgumentSyntaxException();
} }
int start = literal.length();
if (command.length() > start) {
return command.substring(start + 1);
} else {
return "";
}
} }
} }

View file

@ -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;
}
}

View file

@ -8,8 +8,11 @@ import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; 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.LiteralArgumentBuilder.literal;
import static net.minecraft.commands.builder.RequiredArgumentBuilder.argument;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@ -22,12 +25,6 @@ public class CommandDispatcherTest {
subject = new CommandDispatcher(); 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 @Test
public void testCreateAndExecuteCommand() throws Exception { public void testCreateAndExecuteCommand() throws Exception {
subject.register(literal("foo").executes(command)); subject.register(literal("foo").executes(command));
@ -40,4 +37,35 @@ public class CommandDispatcherTest {
public void testExecuteUnknownCommand() throws Exception { public void testExecuteUnknownCommand() throws Exception {
subject.execute("foo"); 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");
}
} }

View file

@ -1,5 +1,6 @@
package net.minecraft.commands.tree; package net.minecraft.commands.tree;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -11,40 +12,32 @@ import static org.junit.Assert.assertThat;
public class ArgumentCommandNodeTest { public class ArgumentCommandNodeTest {
ArgumentCommandNode node; ArgumentCommandNode node;
CommandContextBuilder contextBuilder;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
node = argument("foo", integer()).build(); node = argument("foo", integer()).build();
contextBuilder = new CommandContextBuilder();
} }
@Test @Test
public void testParse() throws Exception { 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) @Test(expected = IllegalArgumentSyntaxException.class)
public void testParseInvalid() throws Exception { public void testParseInvalid() throws Exception {
node.parse("bar"); node.parse("foo", contextBuilder);
}
@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");
} }
} }

View file

@ -1,51 +1,36 @@
package net.minecraft.commands.tree; package net.minecraft.commands.tree;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException; import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; 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.LiteralArgumentBuilder.literal;
import static net.minecraft.commands.builder.RequiredArgumentBuilder.argument;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
public class LiteralCommandNodeTest { public class LiteralCommandNodeTest {
LiteralCommandNode node; LiteralCommandNode node;
CommandContextBuilder contextBuilder;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
node = literal("foo").build(); node = literal("foo").build();
contextBuilder = new CommandContextBuilder();
} }
@Test @Test
public void testParse() throws Exception { 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) @Test(expected = IllegalArgumentSyntaxException.class)
public void testParseInvalid() throws Exception { public void testParseInvalid() throws Exception {
node.parse("bar"); node.parse("bar", contextBuilder);
}
@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");
} }
} }

View file

@ -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"));
}
}