From 3bb1a888fb494963e034b25aaee39c12cdda2796 Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Tue, 25 Jul 2017 16:00:38 +0200 Subject: [PATCH] Made node parsing also use readers --- .../mojang/brigadier/CommandDispatcher.java | 44 ++++++++++++------- .../brigadier/arguments/ArgumentType.java | 7 --- .../brigadier/tree/ArgumentCommandNode.java | 14 +++--- .../mojang/brigadier/tree/CommandNode.java | 3 +- .../brigadier/tree/LiteralCommandNode.java | 15 ++++--- .../brigadier/tree/RootCommandNode.java | 4 +- .../brigadier/context/CommandContextTest.java | 11 ++--- .../tree/ArgumentCommandNodeTest.java | 12 ++--- .../tree/LiteralCommandNodeTest.java | 23 +++++----- .../brigadier/tree/RootCommandNodeTest.java | 5 ++- 10 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java index 6e22994..3d11635 100644 --- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java +++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java @@ -23,6 +23,7 @@ import java.util.stream.Collectors; public class CommandDispatcher { public static final SimpleCommandExceptionType ERROR_UNKNOWN_COMMAND = new SimpleCommandExceptionType("command.unknown.command", "Unknown command"); public static final ParameterizedCommandExceptionType ERROR_UNKNOWN_ARGUMENT = new ParameterizedCommandExceptionType("command.unknown.argument", "Incorrect argument for command, couldn't parse: ${argument}", "argument"); + public static final SimpleCommandExceptionType ERROR_EXPECTED_ARGUMENT_SEPARATOR = new SimpleCommandExceptionType("command.expected.separator", "Expected whitespace to end one argument, but found trailing data"); public static final String ARGUMENT_SEPARATOR = " "; public static final char ARGUMENT_SEPARATOR_CHAR = ' '; @@ -69,10 +70,11 @@ public class CommandDispatcher { } public ParseResults parse(String command, S source) throws CommandException { - return parseNodes(root, command, new CommandContextBuilder<>(this, source)); + StringReader reader = new StringReader(command); + return parseNodes(root, reader, new CommandContextBuilder<>(this, source)); } - private ParseResults parseNodes(CommandNode node, String command, CommandContextBuilder contextBuilder) throws CommandException { + private ParseResults parseNodes(CommandNode node, StringReader reader, CommandContextBuilder contextBuilder) throws CommandException { final S source = contextBuilder.getSource(); Map, CommandException> errors = Maps.newHashMap(); @@ -81,23 +83,28 @@ public class CommandDispatcher { continue; } CommandContextBuilder context = contextBuilder.copy(); - String remaining; + int cursor = reader.getCursor(); try { - remaining = child.parse(command, context); + child.parse(reader, context); } catch (CommandException ex) { errors.put(child, ex); + reader.setCursor(cursor); continue; } context.withCommand(child.getCommand()); - if (remaining.isEmpty()) { - return new ParseResults<>(context); + if (reader.canRead()) { + if (reader.peek() != ARGUMENT_SEPARATOR_CHAR) { + throw ERROR_EXPECTED_ARGUMENT_SEPARATOR.create(); + } + reader.skip(); + return parseNodes(child, reader, context); } else { - return parseNodes(child, remaining.substring(1), context); + return new ParseResults<>(context); } } - return new ParseResults<>(contextBuilder, command, errors); + return new ParseResults<>(contextBuilder, reader.getRemaining(), errors); } public String[] getAllUsage(CommandNode node, S source) { @@ -184,22 +191,28 @@ public class CommandDispatcher { return self; } - private Set findSuggestions(CommandNode node, String command, CommandContextBuilder contextBuilder, Set result) { + private Set findSuggestions(CommandNode node, StringReader reader, CommandContextBuilder contextBuilder, Set result) { final S source = contextBuilder.getSource(); for (CommandNode child : node.getChildren()) { if (!child.canUse(source)) { continue; } CommandContextBuilder context = contextBuilder.copy(); + int cursor = reader.getCursor(); try { - String remaining = child.parse(command, context); - if (remaining.isEmpty()) { - child.listSuggestions(command, result, context); + child.parse(reader, context); + if (reader.canRead()) { + if (reader.peek() == ARGUMENT_SEPARATOR_CHAR) { + reader.skip(); + return findSuggestions(child, reader, context, result); + } } else { - return findSuggestions(child, remaining.substring(1), context, result); + reader.setCursor(cursor); + child.listSuggestions(reader.getRemaining(), result, context); } } catch (CommandException e) { - child.listSuggestions(command, result, context); + reader.setCursor(cursor); + child.listSuggestions(reader.getRemaining(), result, context); } } @@ -207,7 +220,8 @@ public class CommandDispatcher { } public String[] getCompletionSuggestions(String command, S source) { - final Set nodes = findSuggestions(root, command, new CommandContextBuilder<>(this, source), Sets.newLinkedHashSet()); + StringReader reader = new StringReader(command); + final Set nodes = findSuggestions(root, reader, new CommandContextBuilder<>(this, source), Sets.newLinkedHashSet()); return nodes.toArray(new String[nodes.size()]); } diff --git a/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java index a733d88..c9fe5f3 100644 --- a/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java +++ b/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java @@ -8,13 +8,6 @@ import com.mojang.brigadier.exceptions.CommandException; import java.util.Set; public interface ArgumentType { - @Deprecated - default ParsedArgument parse(String command, CommandContextBuilder contextBuilder) throws CommandException { - StringReader reader = new StringReader(command); - T result = parse(reader, contextBuilder); - return new ParsedArgument<>(reader.getRead(), result); - } - T parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandException; default void listSuggestions(String command, Set output, CommandContextBuilder contextBuilder) {} diff --git a/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java b/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java index d584c27..45e470c 100644 --- a/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java @@ -1,6 +1,7 @@ package com.mojang.brigadier.tree; import com.mojang.brigadier.Command; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContextBuilder; @@ -50,18 +51,13 @@ public class ArgumentCommandNode extends CommandNode { } @Override - public String parse(String command, CommandContextBuilder contextBuilder) throws CommandException { - ParsedArgument parsed = type.parse(command, contextBuilder); - int start = parsed.getRaw().length(); + public void parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandException { + int start = reader.getCursor(); + T result = type.parse(reader, contextBuilder); + ParsedArgument parsed = new ParsedArgument<>(reader.getString().substring(start, reader.getCursor()), result); contextBuilder.withArgument(name, parsed); contextBuilder.withNode(this, parsed.getRaw()); - - if (command.length() > start) { - return command.substring(start); - } else { - return ""; - } } @Override diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java index 16c5a27..266837d 100644 --- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java @@ -3,6 +3,7 @@ package com.mojang.brigadier.tree; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Maps; import com.mojang.brigadier.Command; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.exceptions.CommandException; @@ -79,7 +80,7 @@ public abstract class CommandNode implements Comparable> { public abstract String getUsageText(); - public abstract String parse(String command, CommandContextBuilder contextBuilder) throws CommandException; + public abstract void parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandException; public abstract void listSuggestions(String command, Set output, CommandContextBuilder contextBuilder); diff --git a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java index 85fec97..c615a2e 100644 --- a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java @@ -2,6 +2,7 @@ package com.mojang.brigadier.tree; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.exceptions.CommandException; @@ -30,16 +31,16 @@ public class LiteralCommandNode extends CommandNode { } @Override - public String parse(String command, CommandContextBuilder contextBuilder) throws CommandException { - String expected = literal + (command.length() > literal.length() ? CommandDispatcher.ARGUMENT_SEPARATOR : ""); - - if (!command.startsWith(expected)) { - throw ERROR_INCORRECT_LITERAL.create(literal); + public void parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandException { + for (int i = 0; i < literal.length(); i++) { + if (reader.canRead() && reader.peek() == literal.charAt(i)) { + reader.skip(); + } else { + throw ERROR_INCORRECT_LITERAL.create(literal); + } } contextBuilder.withNode(this, literal); - int start = literal.length(); - return command.substring(start); } @Override diff --git a/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java b/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java index 98620d0..af56fab 100644 --- a/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java @@ -1,5 +1,6 @@ package com.mojang.brigadier.tree; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.exceptions.CommandException; @@ -22,8 +23,7 @@ public class RootCommandNode extends CommandNode { } @Override - public String parse(String command, CommandContextBuilder contextBuilder) throws CommandException { - return command; + public void parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandException { } @Override diff --git a/src/test/java/com/mojang/brigadier/context/CommandContextTest.java b/src/test/java/com/mojang/brigadier/context/CommandContextTest.java index cda7d2b..3cb289b 100644 --- a/src/test/java/com/mojang/brigadier/context/CommandContextTest.java +++ b/src/test/java/com/mojang/brigadier/context/CommandContextTest.java @@ -10,17 +10,12 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import java.util.function.Function; - import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class CommandContextTest { @@ -42,13 +37,13 @@ public class CommandContextTest { @Test(expected = IllegalArgumentException.class) public void testGetArgument_wrongType() throws Exception { - CommandContext context = builder.withArgument("foo", integer().parse("123", new CommandContextBuilder<>(dispatcher, source))).build(); + CommandContext context = builder.withArgument("foo", new ParsedArgument<>("123", 123)).build(); context.getArgument("foo", String.class); } @Test public void testGetArgument() throws Exception { - CommandContext context = builder.withArgument("foo", integer().parse("123", new CommandContextBuilder<>(dispatcher, source))).build(); + CommandContext context = builder.withArgument("foo", new ParsedArgument<>("123", 123)).build(); assertThat(context.getArgument("foo", int.class), is(123)); } @@ -70,7 +65,7 @@ public class CommandContextTest { .addEqualityGroup(new CommandContextBuilder<>(dispatcher, otherSource).build(), new CommandContextBuilder<>(dispatcher, otherSource).build()) .addEqualityGroup(new CommandContextBuilder<>(dispatcher, source).withCommand(command).build(), new CommandContextBuilder<>(dispatcher, source).withCommand(command).build()) .addEqualityGroup(new CommandContextBuilder<>(dispatcher, source).withCommand(otherCommand).build(), new CommandContextBuilder<>(dispatcher, source).withCommand(otherCommand).build()) - .addEqualityGroup(new CommandContextBuilder<>(dispatcher, source).withArgument("foo", integer().parse("123", new CommandContextBuilder<>(dispatcher, source))).build(), new CommandContextBuilder<>(dispatcher, source).withArgument("foo", integer().parse("123", new CommandContextBuilder<>(dispatcher, source))).build()) + .addEqualityGroup(new CommandContextBuilder<>(dispatcher, source).withArgument("foo", new ParsedArgument<>("123", 123)).build(), new CommandContextBuilder<>(dispatcher, source).withArgument("foo", new ParsedArgument<>("123", 123)).build()) .addEqualityGroup(new CommandContextBuilder<>(dispatcher, source).withNode(node, "foo").withNode(otherNode, "bar").build(), new CommandContextBuilder<>(dispatcher, source).withNode(node, "foo").withNode(otherNode, "bar").build()) .addEqualityGroup(new CommandContextBuilder<>(dispatcher, source).withNode(otherNode, "bar").withNode(node, "foo").build(), new CommandContextBuilder<>(dispatcher, source).withNode(otherNode, "bar").withNode(node, "foo").build()) .testEquals(); diff --git a/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java b/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java index c8d075b..5728c90 100644 --- a/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java +++ b/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java @@ -5,6 +5,7 @@ import com.google.common.collect.Sets; import com.google.common.testing.EqualsTester; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.builder.RequiredArgumentBuilder; @@ -43,15 +44,8 @@ public class ArgumentCommandNodeTest extends AbstractCommandNodeTest { @Test public void testParse() throws Exception { - assertThat(node.parse("123 456", contextBuilder), is(" 456")); - - assertThat(contextBuilder.getArguments().containsKey("foo"), is(true)); - assertThat(contextBuilder.getArguments().get("foo").getResult(), is(123)); - } - - @Test - public void testParseExact() throws Exception { - assertThat(node.parse("123", contextBuilder), is("")); + StringReader reader = new StringReader("123 456"); + node.parse(reader, contextBuilder); assertThat(contextBuilder.getArguments().containsKey("foo"), is(true)); assertThat(contextBuilder.getArguments().get("foo").getResult(), is(123)); diff --git a/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java b/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java index b2d8505..762666c 100644 --- a/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java +++ b/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java @@ -5,6 +5,7 @@ import com.google.common.collect.Sets; import com.google.common.testing.EqualsTester; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.exceptions.CommandException; @@ -39,29 +40,31 @@ public class LiteralCommandNodeTest extends AbstractCommandNodeTest { @Test public void testParse() throws Exception { - assertThat(node.parse("foo bar", contextBuilder), is(" bar")); + StringReader reader = new StringReader("foo bar"); + node.parse(reader, contextBuilder); + assertThat(reader.getRemaining(), equalTo(" bar")); } @Test public void testParseExact() throws Exception { - assertThat(node.parse("foo", contextBuilder), is("")); + StringReader reader = new StringReader("foo"); + node.parse(reader, contextBuilder); + assertThat(reader.getRemaining(), equalTo("")); } @Test public void testParseSimilar() throws Exception { - try { - node.parse("foobar", contextBuilder); - fail(); - } catch (CommandException ex) { - assertThat(ex.getType(), is(LiteralCommandNode.ERROR_INCORRECT_LITERAL)); - assertThat(ex.getData(), is(ImmutableMap.of("expected", "foo"))); - } + StringReader reader = new StringReader("foobar"); + node.parse(reader, contextBuilder); + assertThat(reader.getRemaining(), equalTo("bar")); + // This should succeed, because it's the responsibility of the dispatcher to realize there's trailing text } @Test public void testParseInvalid() throws Exception { + StringReader reader = new StringReader("bar"); try { - node.parse("bar", contextBuilder); + node.parse(reader, contextBuilder); fail(); } catch (CommandException ex) { assertThat(ex.getType(), is(LiteralCommandNode.ERROR_INCORRECT_LITERAL)); diff --git a/src/test/java/com/mojang/brigadier/tree/RootCommandNodeTest.java b/src/test/java/com/mojang/brigadier/tree/RootCommandNodeTest.java index 685dc36..babd73c 100644 --- a/src/test/java/com/mojang/brigadier/tree/RootCommandNodeTest.java +++ b/src/test/java/com/mojang/brigadier/tree/RootCommandNodeTest.java @@ -3,6 +3,7 @@ package com.mojang.brigadier.tree; import com.google.common.collect.Sets; import com.google.common.testing.EqualsTester; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContextBuilder; import org.junit.Before; import org.junit.Test; @@ -30,7 +31,9 @@ public class RootCommandNodeTest extends AbstractCommandNodeTest { @Test public void testParse() throws Exception { - assertThat(node.parse("foo bar baz", new CommandContextBuilder<>(new CommandDispatcher<>(), new Object())), is("foo bar baz")); + StringReader reader = new StringReader("hello world"); + node.parse(reader, new CommandContextBuilder<>(new CommandDispatcher<>(), new Object())); + assertThat(reader.getCursor(), is(0)); } @Test(expected = UnsupportedOperationException.class)