diff --git a/src/main/java/com/mojang/brigadier/StringReader.java b/src/main/java/com/mojang/brigadier/StringReader.java new file mode 100644 index 0000000..e85e617 --- /dev/null +++ b/src/main/java/com/mojang/brigadier/StringReader.java @@ -0,0 +1,175 @@ +package com.mojang.brigadier; + +import com.mojang.brigadier.exceptions.CommandException; +import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; + +public class StringReader { + private static final char SYNTAX_ESCAPE = '\\'; + private static final char SYNTAX_QUOTE = '"'; + + public static final SimpleCommandExceptionType ERROR_EXPECTED_START_OF_QUOTE = new SimpleCommandExceptionType("parsing.quote.expected.start", "Expected quote to start a string"); + public static final SimpleCommandExceptionType ERROR_EXPECTED_END_OF_QUOTE = new SimpleCommandExceptionType("parsing.quote.expected.end", "Unclosed quoted string"); + public static final ParameterizedCommandExceptionType ERROR_INVALID_ESCAPE = new ParameterizedCommandExceptionType("parsing.quote.escape", "Invalid escape sequence '\\${character}' in quoted string)", "character"); + public static final ParameterizedCommandExceptionType ERROR_INVALID_BOOL = new ParameterizedCommandExceptionType("parsing.bool.invalid", "Invalid bool, expected true or false but found '${value}'", "value"); + public static final ParameterizedCommandExceptionType ERROR_INVALID_INT = new ParameterizedCommandExceptionType("parsing.int.invalid", "Invalid integer '${value}'", "value"); + public static final SimpleCommandExceptionType ERROR_EXPECTED_INT = new SimpleCommandExceptionType("parsing.int.expected", "Expected integer"); + public static final ParameterizedCommandExceptionType ERROR_INVALID_DOUBLE = new ParameterizedCommandExceptionType("parsing.double.invalid", "Invalid double '${value}'", "value"); + public static final SimpleCommandExceptionType ERROR_EXPECTED_DOUBLE = new SimpleCommandExceptionType("parsing.double.expected", "Expected double"); + + private final String string; + private int cursor; + + public StringReader(String string) { + this.string = string; + } + + public String getString() { + return string; + } + + public void setCursor(int cursor) { + this.cursor = cursor; + } + + public int getRemainingLength() { + return string.length() - cursor; + } + + public int getTotalLength() { + return string.length(); + } + + public int getCursor() { + return cursor; + } + + public String getRead() { + return string.substring(0, cursor); + } + + public String getRemaining() { + return string.substring(cursor); + } + + public boolean canRead(final int length) { + return cursor + length <= string.length(); + } + + public boolean canRead() { + return canRead(1); + } + + public char peek() { + return string.charAt(cursor); + } + + public char read() { + return string.charAt(cursor++); + } + + public void skip() { + cursor++; + } + + private static boolean isAllowedNumber(final char c) { + return c >= '0' && c <= '9' || c == '.' || c == '-'; + } + + public int readInt() throws CommandException { + final int start = cursor; + while (canRead() && isAllowedNumber(peek())) { + skip(); + } + final String number = string.substring(start, cursor); + if (number.isEmpty()) { + throw ERROR_EXPECTED_INT.create(); + } + try { + return Integer.parseInt(number); + } catch (final NumberFormatException ex) { + throw ERROR_INVALID_INT.create(number); + } + } + + public double readDouble() throws CommandException { + final int start = cursor; + while (canRead() && isAllowedNumber(peek())) { + skip(); + } + final String number = string.substring(start, cursor); + if (number.isEmpty()) { + throw ERROR_EXPECTED_DOUBLE.create(); + } + try { + return Double.parseDouble(number); + } catch (final NumberFormatException ex) { + throw ERROR_INVALID_DOUBLE.create(number); + } + } + + private static boolean isAllowedInUnquotedString(final char c) { + return c >= '0' && c <= '9' + || c >= 'A' && c <= 'Z' + || c >= 'a' && c <= 'z' + || c == '_' || c == '-' + || c == '.' || c == '+'; + } + + public String readUnquotedString() throws CommandException { + final int start = cursor; + while (canRead() && isAllowedInUnquotedString(peek())) { + skip(); + } + return string.substring(start, cursor); + } + + public String readQuotedString() throws CommandException { + if (!canRead()) { + return ""; + } else if (peek() != SYNTAX_QUOTE) { + throw ERROR_EXPECTED_START_OF_QUOTE.create(); + } + skip(); + final StringBuilder result = new StringBuilder(); + boolean escaped = false; + while (canRead()) { + final char c = read(); + if (escaped) { + if (c == SYNTAX_QUOTE || c == SYNTAX_ESCAPE) { + result.append(c); + escaped = false; + } else { + throw ERROR_INVALID_ESCAPE.create(String.valueOf(c)); + } + } else if (c == SYNTAX_ESCAPE) { + escaped = true; + } else if (c == SYNTAX_QUOTE) { + return result.toString(); + } else { + result.append(c); + } + } + + throw ERROR_EXPECTED_END_OF_QUOTE.create(); + } + + public String readString() throws CommandException { + if (canRead() && peek() == SYNTAX_QUOTE) { + return readQuotedString(); + } else { + return readUnquotedString(); + } + } + + public boolean readBoolean() throws CommandException { + String value = readString(); + if (value.equals("true")) { + return true; + } else if (value.equals("false")) { + return false; + } else { + throw ERROR_INVALID_BOOL.create(value); + } + } +} diff --git a/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java index 7ce8650..0b05767 100644 --- a/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java +++ b/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java @@ -1,5 +1,6 @@ package com.mojang.brigadier.arguments; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.ParsedArgument; import com.mojang.brigadier.exceptions.CommandException; @@ -7,7 +8,13 @@ import com.mojang.brigadier.exceptions.CommandException; import java.util.Set; public interface ArgumentType { - ParsedArgument parse(String command, CommandContextBuilder contextBuilder) throws CommandException; + 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/arguments/BoolArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/BoolArgumentType.java index 7109e31..2cb0fa9 100644 --- a/src/main/java/com/mojang/brigadier/arguments/BoolArgumentType.java +++ b/src/main/java/com/mojang/brigadier/arguments/BoolArgumentType.java @@ -1,6 +1,7 @@ package com.mojang.brigadier.arguments; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.ParsedArgument; @@ -22,20 +23,8 @@ public class BoolArgumentType implements ArgumentType { } @Override - public ParsedArgument parse(String command, CommandContextBuilder contextBuilder) throws CommandException { - int end = command.indexOf(CommandDispatcher.ARGUMENT_SEPARATOR); - String raw = command; - if (end > -1) { - raw = command.substring(0, end); - } - - if (raw.equals("true")) { - return new ParsedArgument<>(raw, true); - } else if (raw.equals("false")) { - return new ParsedArgument<>(raw, false); - } else { - throw ERROR_INVALID.create(); - } + public Boolean parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandException { + return reader.readBoolean(); } @Override diff --git a/src/main/java/com/mojang/brigadier/arguments/CommandArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/CommandArgumentType.java deleted file mode 100644 index afbd6f8..0000000 --- a/src/main/java/com/mojang/brigadier/arguments/CommandArgumentType.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.mojang.brigadier.arguments; - -import com.mojang.brigadier.ParseResults; -import com.mojang.brigadier.context.CommandContextBuilder; -import com.mojang.brigadier.context.ParsedArgument; -import com.mojang.brigadier.exceptions.CommandException; - -import java.util.Arrays; -import java.util.Set; - -public class CommandArgumentType implements ArgumentType> { - public static CommandArgumentType command() { - return new CommandArgumentType(); - } - - @Override - public ParsedArgument> parse(String command, CommandContextBuilder contextBuilder) throws CommandException { - final ParseResults parse = contextBuilder.getDispatcher().parse(command, contextBuilder.getSource()); - - //noinspection unchecked - return new ParsedArgument<>(command, (ParseResults) parse); - } - - @Override - public void listSuggestions(String command, Set output, CommandContextBuilder contextBuilder) { - final String[] suggestions = contextBuilder.getDispatcher().getCompletionSuggestions(command, contextBuilder.getSource()); - output.addAll(Arrays.asList(suggestions)); - } -} diff --git a/src/main/java/com/mojang/brigadier/arguments/IntegerArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/IntegerArgumentType.java index b2bd188..fc013cc 100644 --- a/src/main/java/com/mojang/brigadier/arguments/IntegerArgumentType.java +++ b/src/main/java/com/mojang/brigadier/arguments/IntegerArgumentType.java @@ -1,6 +1,7 @@ package com.mojang.brigadier.arguments; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.ParsedArgument; @@ -10,7 +11,6 @@ import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType; import java.util.Objects; public class IntegerArgumentType implements ArgumentType { - public static final ParameterizedCommandExceptionType ERROR_NOT_A_NUMBER = new ParameterizedCommandExceptionType("argument.integer.invalid", "Expected an integer, found '${found}'", "found"); public static final ParameterizedCommandExceptionType ERROR_WRONG_SUFFIX = new ParameterizedCommandExceptionType("argument.integer.wrongsuffix", "Expected suffix '${suffix}'", "suffix"); public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument.integer.low", "Integer must not be less than ${minimum}, found ${found}", "found", "minimum"); public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument.integer.big", "Integer must not be more than ${maximum}, found ${found}", "found", "maximum"); @@ -46,38 +46,22 @@ public class IntegerArgumentType implements ArgumentType { } @Override - public ParsedArgument parse(String command, CommandContextBuilder contextBuilder) throws CommandException { - int end = command.indexOf(CommandDispatcher.ARGUMENT_SEPARATOR); - String raw = command; - if (end > -1) { - raw = command.substring(0, end); - } - - if (raw.length() < suffix.length()) { - throw ERROR_WRONG_SUFFIX.create(this.suffix); - } - - String number = raw.substring(0, raw.length() - suffix.length()); - String suffix = raw.substring(number.length()); - - if (!suffix.equals(this.suffix)) { - throw ERROR_WRONG_SUFFIX.create(this.suffix); - } - - try { - int value = Integer.parseInt(number); - - if (value < minimum) { - throw ERROR_TOO_SMALL.create(value, minimum); + public Integer parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandException { + int result = reader.readInt(); + for (int i = 0; i < suffix.length(); i++) { + if (reader.canRead() && reader.peek() == suffix.charAt(i)) { + reader.skip(); + } else { + throw ERROR_WRONG_SUFFIX.create(suffix); } - if (value > maximum) { - throw ERROR_TOO_BIG.create(value, maximum); - } - - return new ParsedArgument<>(raw, value); - } catch (NumberFormatException ignored) { - throw ERROR_NOT_A_NUMBER.create(number); } + if (result < minimum) { + throw ERROR_TOO_SMALL.create(result, minimum); + } + if (result > maximum) { + throw ERROR_TOO_BIG.create(result, maximum); + } + return result; } @Override diff --git a/src/main/java/com/mojang/brigadier/arguments/StringArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/StringArgumentType.java index 9df3d4f..25fd7b4 100644 --- a/src/main/java/com/mojang/brigadier/arguments/StringArgumentType.java +++ b/src/main/java/com/mojang/brigadier/arguments/StringArgumentType.java @@ -1,6 +1,7 @@ package com.mojang.brigadier.arguments; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.ParsedArgument; @@ -9,11 +10,6 @@ import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; public class StringArgumentType implements ArgumentType { - public static final ParameterizedCommandExceptionType ERROR_INVALID_ESCAPE = new ParameterizedCommandExceptionType("argument.string.escape.invalid", "Unknown or invalid escape sequence: ${input}", "input"); - public static final SimpleCommandExceptionType ERROR_UNEXPECTED_ESCAPE = new SimpleCommandExceptionType("argument.string.escape.unexpected", "Unexpected escape sequence, please quote the whole argument"); - public static final SimpleCommandExceptionType ERROR_UNEXPECTED_START_OF_QUOTE = new SimpleCommandExceptionType("argument.string.quote.unexpected_start", "Unexpected start-of-quote character (\"), please quote the whole argument"); - public static final SimpleCommandExceptionType ERROR_UNEXPECTED_END_OF_QUOTE = new SimpleCommandExceptionType("argument.string.quote.unexpected_end", "Unexpected end-of-quote character (\"), it must be at the end or followed by a space (' ') for the next argument"); - public static final SimpleCommandExceptionType ERROR_EXPECTED_END_OF_QUOTE = new SimpleCommandExceptionType("argument.string.quote.expected_end", "Expected end-of-quote character (\") but found no more input"); private final StringType type; private StringArgumentType(StringType type) { @@ -37,59 +33,15 @@ public class StringArgumentType implements ArgumentType { } @Override - public ParsedArgument parse(String command, CommandContextBuilder contextBuilder) throws CommandException { + public String parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandException { if (type == StringType.GREEDY_PHRASE) { - return new ParsedArgument<>(command, command); + String text = reader.getRemaining(); + reader.setCursor(reader.getTotalLength()); + return text; } else if (type == StringType.SINGLE_WORD) { - int index = command.indexOf(CommandDispatcher.ARGUMENT_SEPARATOR); - if (index > 0) { - final String word = command.substring(0, index); - return new ParsedArgument<>(word, word); - } else { - return new ParsedArgument<>(command, command); - } + return reader.readUnquotedString(); } else { - StringBuilder result = new StringBuilder(); - int i = 0; - boolean escaped = false; - boolean quoted = false; - while (i < command.length()) { - char c = command.charAt(i); - if (escaped) { - if (c == '"' || c == '\\') { - result.append(c); - } else { - throw ERROR_INVALID_ESCAPE.create("\\" + c); - } - escaped = false; - } else if (c == '\\') { - if (quoted) { - escaped = true; - } else { - throw ERROR_UNEXPECTED_ESCAPE.create(); - } - } else if (c == '"') { - if (i == 0) { - quoted = true; - } else if (!quoted) { - throw ERROR_UNEXPECTED_START_OF_QUOTE.create(); - } else if (i == command.length() - 1 || command.charAt(i + 1) == CommandDispatcher.ARGUMENT_SEPARATOR_CHAR) { - i++; - break; - } else { - throw ERROR_UNEXPECTED_END_OF_QUOTE.create(); - } - } else if (!quoted && c == CommandDispatcher.ARGUMENT_SEPARATOR_CHAR) { - break; - } else if (quoted && i == command.length() - 1) { - throw ERROR_EXPECTED_END_OF_QUOTE.create(); - } else { - result.append(c); - } - - i++; - } - return new ParsedArgument<>(command.substring(0, i), result.toString()); + return reader.readString(); } } diff --git a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java index f1defe2..64702a6 100644 --- a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java +++ b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java @@ -168,8 +168,8 @@ public class CommandDispatcherTest { subject.execute("foo bar", source); fail(); } catch (CommandException ex) { - assertThat(ex.getType(), is(IntegerArgumentType.ERROR_NOT_A_NUMBER)); - assertThat(ex.getData(), is(ImmutableMap.of("found", "bar"))); + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT)); + assertThat(ex.getData(), is(Collections.emptyMap())); } } } \ No newline at end of file diff --git a/src/test/java/com/mojang/brigadier/StringReaderTest.java b/src/test/java/com/mojang/brigadier/StringReaderTest.java new file mode 100644 index 0000000..f2daede --- /dev/null +++ b/src/test/java/com/mojang/brigadier/StringReaderTest.java @@ -0,0 +1,325 @@ +package com.mojang.brigadier; + +import com.google.common.collect.ImmutableMap; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.exceptions.CommandException; +import org.hamcrest.CoreMatchers; +import org.junit.Test; + +import java.util.Collections; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class StringReaderTest { + @Test + public void canRead() throws Exception { + StringReader reader = new StringReader("abc"); + assertThat(reader.canRead(), is(true)); + reader.skip(); // 'a' + assertThat(reader.canRead(), is(true)); + reader.skip(); // 'b' + assertThat(reader.canRead(), is(true)); + reader.skip(); // 'c' + assertThat(reader.canRead(), is(false)); + } + + @Test + public void getRemainingLength() throws Exception { + StringReader reader = new StringReader("abc"); + assertThat(reader.getRemainingLength(), is(3)); + reader.setCursor(1); + assertThat(reader.getRemainingLength(), is(2)); + reader.setCursor(2); + assertThat(reader.getRemainingLength(), is(1)); + reader.setCursor(3); + assertThat(reader.getRemainingLength(), is(0)); + } + + @Test + public void canRead_length() throws Exception { + StringReader reader = new StringReader("abc"); + assertThat(reader.canRead(1), is(true)); + assertThat(reader.canRead(2), is(true)); + assertThat(reader.canRead(3), is(true)); + assertThat(reader.canRead(4), is(false)); + assertThat(reader.canRead(5), is(false)); + } + + @Test + public void peek() throws Exception { + StringReader reader = new StringReader("abc"); + assertThat(reader.peek(), is('a')); + assertThat(reader.getCursor(), is(0)); + reader.setCursor(2); + assertThat(reader.peek(), is('c')); + assertThat(reader.getCursor(), is(2)); + } + + @Test + public void read() throws Exception { + StringReader reader = new StringReader("abc"); + assertThat(reader.read(), is('a')); + assertThat(reader.read(), is('b')); + assertThat(reader.read(), is('c')); + assertThat(reader.getCursor(), is(3)); + } + + @Test + public void skip() throws Exception { + StringReader reader = new StringReader("abc"); + reader.skip(); + assertThat(reader.getCursor(), is(1)); + } + + @Test + public void getRemaining() throws Exception { + StringReader reader = new StringReader("Hello!"); + assertThat(reader.getRemaining(), equalTo("Hello!")); + reader.setCursor(3); + assertThat(reader.getRemaining(), equalTo("lo!")); + reader.setCursor(6); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void getRead() throws Exception { + StringReader reader = new StringReader("Hello!"); + assertThat(reader.getRead(), equalTo("")); + reader.setCursor(3); + assertThat(reader.getRead(), equalTo("Hel")); + reader.setCursor(6); + assertThat(reader.getRead(), equalTo("Hello!")); + } + + @Test + public void readUnquotedString() throws Exception { + final StringReader reader = new StringReader("hello world"); + assertThat(reader.readUnquotedString(), equalTo("hello")); + assertThat(reader.getRead(), equalTo("hello")); + assertThat(reader.getRemaining(), equalTo(" world")); + } + + @Test + public void readUnquotedString_empty() throws Exception { + final StringReader reader = new StringReader(""); + assertThat(reader.readUnquotedString(), equalTo("")); + assertThat(reader.getRead(), equalTo("")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readUnquotedString_empty_withRemaining() throws Exception { + final StringReader reader = new StringReader(" hello world"); + assertThat(reader.readUnquotedString(), equalTo("")); + assertThat(reader.getRead(), equalTo("")); + assertThat(reader.getRemaining(), equalTo(" hello world")); + } + + @Test + public void readQuotedString() throws Exception { + final StringReader reader = new StringReader("\"hello world\""); + assertThat(reader.readQuotedString(), equalTo("hello world")); + assertThat(reader.getRead(), equalTo("\"hello world\"")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readQuotedString_empty() throws Exception { + final StringReader reader = new StringReader(""); + assertThat(reader.readQuotedString(), equalTo("")); + assertThat(reader.getRead(), equalTo("")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readQuotedString_emptyQuoted() throws Exception { + final StringReader reader = new StringReader("\"\""); + assertThat(reader.readQuotedString(), equalTo("")); + assertThat(reader.getRead(), equalTo("\"\"")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readQuotedString_emptyQuoted_withRemaining() throws Exception { + final StringReader reader = new StringReader("\"\" hello world"); + assertThat(reader.readQuotedString(), equalTo("")); + assertThat(reader.getRead(), equalTo("\"\"")); + assertThat(reader.getRemaining(), equalTo(" hello world")); + } + + @Test + public void readQuotedString_withEscapedQuote() throws Exception { + final StringReader reader = new StringReader("\"hello \\\"world\\\"\""); + assertThat(reader.readQuotedString(), equalTo("hello \"world\"")); + assertThat(reader.getRead(), equalTo("\"hello \\\"world\\\"\"")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readQuotedString_withEscapedEscapes() throws Exception { + final StringReader reader = new StringReader("\"\\\\o/\""); + assertThat(reader.readQuotedString(), equalTo("\\o/")); + assertThat(reader.getRead(), equalTo("\"\\\\o/\"")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readQuotedString_withRemaining() throws Exception { + final StringReader reader = new StringReader("\"hello world\" foo bar"); + assertThat(reader.readQuotedString(), equalTo("hello world")); + assertThat(reader.getRead(), equalTo("\"hello world\"")); + assertThat(reader.getRemaining(), equalTo(" foo bar")); + } + + @Test + public void readQuotedString_withImmediateRemaining() throws Exception { + final StringReader reader = new StringReader("\"hello world\"foo bar"); + assertThat(reader.readQuotedString(), equalTo("hello world")); + assertThat(reader.getRead(), equalTo("\"hello world\"")); + assertThat(reader.getRemaining(), equalTo("foo bar")); + } + + @Test + public void readQuotedString_noOpen() throws Exception { + try { + new StringReader("hello world\"").readQuotedString(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_START_OF_QUOTE)); + assertThat(ex.getData(), equalTo(Collections.emptyMap())); + } + } + + @Test + public void readQuotedString_noClose() throws Exception { + try { + new StringReader("\"hello world").readQuotedString(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_END_OF_QUOTE)); + assertThat(ex.getData(), equalTo(Collections.emptyMap())); + } + } + + @Test + public void readQuotedString_invalidEscape() throws Exception { + try { + new StringReader("\"hello\\nworld\"").readQuotedString(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(StringReader.ERROR_INVALID_ESCAPE)); + assertThat(ex.getData(), equalTo(ImmutableMap.of("character", "n"))); + } + } + + @Test + public void readInt() throws Exception { + final StringReader reader = new StringReader("1234567890"); + assertThat(reader.readInt(), is(1234567890)); + assertThat(reader.getRead(), equalTo("1234567890")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readInt_negative() throws Exception { + final StringReader reader = new StringReader("-1234567890"); + assertThat(reader.readInt(), is(-1234567890)); + assertThat(reader.getRead(), equalTo("-1234567890")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readInt_invalid() throws Exception { + try { + new StringReader("12.34").readInt(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(StringReader.ERROR_INVALID_INT)); + assertThat(ex.getData(), equalTo(ImmutableMap.of("value", "12.34"))); + } + } + + @Test + public void readInt_none() throws Exception { + try { + new StringReader("").readInt(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT)); + assertThat(ex.getData(), equalTo(Collections.emptyMap())); + } + } + + @Test + public void readInt_withRemaining() throws Exception { + final StringReader reader = new StringReader("1234567890 foo bar"); + assertThat(reader.readInt(), is(1234567890)); + assertThat(reader.getRead(), equalTo("1234567890")); + assertThat(reader.getRemaining(), equalTo(" foo bar")); + } + + @Test + public void readInt_withRemainingImmediate() throws Exception { + final StringReader reader = new StringReader("1234567890foo bar"); + assertThat(reader.readInt(), is(1234567890)); + assertThat(reader.getRead(), equalTo("1234567890")); + assertThat(reader.getRemaining(), equalTo("foo bar")); + } + + @Test + public void readDouble() throws Exception { + final StringReader reader = new StringReader("123"); + assertThat(reader.readDouble(), is(123.0)); + assertThat(reader.getRead(), equalTo("123")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readDouble_withDecimal() throws Exception { + final StringReader reader = new StringReader("12.34"); + assertThat(reader.readDouble(), is(12.34)); + assertThat(reader.getRead(), equalTo("12.34")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readDouble_negative() throws Exception { + final StringReader reader = new StringReader("-123"); + assertThat(reader.readDouble(), is(-123.0)); + assertThat(reader.getRead(), equalTo("-123")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readDouble_invalid() throws Exception { + try { + new StringReader("12.34.56").readDouble(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(StringReader.ERROR_INVALID_DOUBLE)); + assertThat(ex.getData(), equalTo(ImmutableMap.of("value", "12.34.56"))); + } + } + + @Test + public void readDouble_none() throws Exception { + try { + new StringReader("").readDouble(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_DOUBLE)); + assertThat(ex.getData(), equalTo(Collections.emptyMap())); + } + } + + @Test + public void readDouble_withRemaining() throws Exception { + final StringReader reader = new StringReader("12.34 foo bar"); + assertThat(reader.readDouble(), is(12.34)); + assertThat(reader.getRead(), equalTo("12.34")); + assertThat(reader.getRemaining(), equalTo(" foo bar")); + } + + @Test + public void readDouble_withRemainingImmediate() throws Exception { + final StringReader reader = new StringReader("12.34foo bar"); + assertThat(reader.readDouble(), is(12.34)); + assertThat(reader.getRead(), equalTo("12.34")); + assertThat(reader.getRemaining(), equalTo("foo bar")); + } +} \ No newline at end of file diff --git a/src/test/java/com/mojang/brigadier/arguments/BoolArgumentTypeTest.java b/src/test/java/com/mojang/brigadier/arguments/BoolArgumentTypeTest.java index c2bbff8..de29be9 100644 --- a/src/test/java/com/mojang/brigadier/arguments/BoolArgumentTypeTest.java +++ b/src/test/java/com/mojang/brigadier/arguments/BoolArgumentTypeTest.java @@ -1,5 +1,6 @@ package com.mojang.brigadier.arguments; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.ParsedArgument; import com.mojang.brigadier.exceptions.CommandException; @@ -7,6 +8,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import java.util.Collections; @@ -16,13 +18,14 @@ import static com.mojang.brigadier.arguments.BoolArgumentType.bool; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class BoolArgumentTypeTest { private BoolArgumentType type; @Mock - private Object source; - @Mock private CommandContextBuilder context; @Before @@ -31,54 +34,11 @@ public class BoolArgumentTypeTest { } @Test - public void parse_true() throws Exception { - ParsedArgument parse = type.parse("true", context); - assertThat(parse.getResult(), is(true)); - assertThat(parse.getRaw(), equalTo("true")); - } - - @Test - public void parse_false() throws Exception { - ParsedArgument parse = type.parse("false", context); - assertThat(parse.getResult(), is(false)); - assertThat(parse.getRaw(), equalTo("false")); - } - - @Test - public void parse_trailing() throws Exception { - ParsedArgument parse = type.parse("false hello world", context); - assertThat(parse.getResult(), is(false)); - assertThat(parse.getRaw(), equalTo("false")); - } - - @Test - public void parse_invalid() throws Exception { - try { - type.parse("tuesday", context); - } catch (CommandException ex) { - assertThat(ex.getType(), is(ERROR_INVALID)); - assertThat(ex.getData(), equalTo(Collections.emptyMap())); - } - } - - @Test - public void parse_empty() throws Exception { - try { - type.parse("", context); - } catch (CommandException ex) { - assertThat(ex.getType(), is(ERROR_INVALID)); - assertThat(ex.getData(), equalTo(Collections.emptyMap())); - } - } - - @Test - public void parse_empty_remaining() throws Exception { - try { - type.parse(" true", context); - } catch (CommandException ex) { - assertThat(ex.getType(), is(ERROR_INVALID)); - assertThat(ex.getData(), equalTo(Collections.emptyMap())); - } + public void parse() throws Exception { + StringReader reader = mock(StringReader.class); + when(reader.readBoolean()).thenReturn(true); + assertThat(type.parse(reader, context), is(true)); + verify(reader).readBoolean(); } @Test diff --git a/src/test/java/com/mojang/brigadier/arguments/CommandArgumentTypeTest.java b/src/test/java/com/mojang/brigadier/arguments/CommandArgumentTypeTest.java deleted file mode 100644 index cf8adb4..0000000 --- a/src/test/java/com/mojang/brigadier/arguments/CommandArgumentTypeTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.mojang.brigadier.arguments; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.ParseResults; -import com.mojang.brigadier.context.CommandContextBuilder; -import com.mojang.brigadier.context.ParsedArgument; -import com.mojang.brigadier.exceptions.CommandException; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -import java.util.Set; - -import static com.mojang.brigadier.arguments.CommandArgumentType.command; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class CommandArgumentTypeTest { - @Mock - private Object source; - @Mock - private CommandDispatcher dispatcher; - - @SuppressWarnings("unchecked") - @Test - public void testParse() throws Exception { - final ParseResults command = mock(ParseResults.class); - when(dispatcher.parse("hello world", source)).thenReturn(command); - final ParsedArgument> argument = command().parse("hello world", new CommandContextBuilder<>(dispatcher, source)); - assertThat(argument.getRaw(), equalTo("hello world")); - assertThat(argument.getResult(), is(command)); - } - - @SuppressWarnings("unchecked") - @Test - public void testParse_fail() throws Exception { - final CommandException thrown = mock(CommandException.class); - when(dispatcher.parse("hello world", source)).thenThrow(thrown); - try { - command().parse("hello world", new CommandContextBuilder<>(dispatcher, source)); - fail(); - } catch (CommandException exception) { - assertThat(exception, is(thrown)); - } - } - - @Test - public void listSuggestions() throws Exception { - Set output = Sets.newHashSet(); - when(dispatcher.getCompletionSuggestions("foo bar baz", source)).thenReturn(new String[] {"a", "b"}); - command().listSuggestions("foo bar baz", output, new CommandContextBuilder<>(dispatcher, source)); - verify(dispatcher).getCompletionSuggestions("foo bar baz", source); - assertThat(output, equalTo(ImmutableSet.of("a", "b"))); - } -} \ No newline at end of file diff --git a/src/test/java/com/mojang/brigadier/arguments/IntegerArgumentTypeTest.java b/src/test/java/com/mojang/brigadier/arguments/IntegerArgumentTypeTest.java index b953a3f..9f506a6 100644 --- a/src/test/java/com/mojang/brigadier/arguments/IntegerArgumentTypeTest.java +++ b/src/test/java/com/mojang/brigadier/arguments/IntegerArgumentTypeTest.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap; 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.CommandContext; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.ParsedArgument; @@ -15,6 +16,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; +import java.util.Collections; import java.util.Set; import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; @@ -40,6 +42,7 @@ public class IntegerArgumentTypeTest { @Test public void testParse() throws Exception { + ParsedArgument result = type.parse("50", new CommandContextBuilder<>(dispatcher, source)); assertThat(result.getRaw(), is("50")); @@ -78,13 +81,8 @@ public class IntegerArgumentTypeTest { @Test public void testParse_unexpectedSuffix() throws Exception { - try { - type.parse("50L", new CommandContextBuilder<>(dispatcher, source)); - fail(); - } catch (CommandException ex) { - assertThat(ex.getType(), is(IntegerArgumentType.ERROR_NOT_A_NUMBER)); - assertThat(ex.getData(), is(ImmutableMap.of("found", "50L"))); - } + type.parse("50L", new CommandContextBuilder<>(dispatcher, source)); + // This has to pass, it's the responsibility of a node to decide "this isn't right, it's followed by text!" } @Test @@ -93,8 +91,8 @@ public class IntegerArgumentTypeTest { type.parse("fifty", new CommandContextBuilder<>(dispatcher, source)); fail(); } catch (CommandException ex) { - assertThat(ex.getType(), is(IntegerArgumentType.ERROR_NOT_A_NUMBER)); - assertThat(ex.getData(), is(ImmutableMap.of("found", "fifty"))); + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT)); + assertThat(ex.getData(), is(Collections.emptyMap())); } } @@ -104,8 +102,8 @@ public class IntegerArgumentTypeTest { type.parse("", new CommandContextBuilder<>(dispatcher, source)); fail(); } catch (CommandException ex) { - assertThat(ex.getType(), is(IntegerArgumentType.ERROR_NOT_A_NUMBER)); - assertThat(ex.getData(), is(ImmutableMap.of("found", ""))); + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT)); + assertThat(ex.getData(), is(Collections.emptyMap())); } } @@ -115,8 +113,8 @@ public class IntegerArgumentTypeTest { integer(0, 100, "L").parse("", new CommandContextBuilder<>(dispatcher, source)); fail(); } catch (CommandException ex) { - assertThat(ex.getType(), is(IntegerArgumentType.ERROR_WRONG_SUFFIX)); - assertThat(ex.getData(), is(ImmutableMap.of("suffix", "L"))); + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT)); + assertThat(ex.getData(), is(Collections.emptyMap())); } } @@ -126,8 +124,8 @@ public class IntegerArgumentTypeTest { integer(0, 100, "L").parse("L", new CommandContextBuilder<>(dispatcher, source)); fail(); } catch (CommandException ex) { - assertThat(ex.getType(), is(IntegerArgumentType.ERROR_NOT_A_NUMBER)); - assertThat(ex.getData(), is(ImmutableMap.of("found", ""))); + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT)); + assertThat(ex.getData(), is(Collections.emptyMap())); } } diff --git a/src/test/java/com/mojang/brigadier/arguments/StringArgumentTypeTest.java b/src/test/java/com/mojang/brigadier/arguments/StringArgumentTypeTest.java index b1572d0..7796e28 100644 --- a/src/test/java/com/mojang/brigadier/arguments/StringArgumentTypeTest.java +++ b/src/test/java/com/mojang/brigadier/arguments/StringArgumentTypeTest.java @@ -4,21 +4,14 @@ import com.google.common.collect.Sets; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.ParsedArgument; -import com.mojang.brigadier.exceptions.CommandException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; -import java.util.Collections; import java.util.Set; -import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_EXPECTED_END_OF_QUOTE; -import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_INVALID_ESCAPE; -import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_UNEXPECTED_END_OF_QUOTE; -import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_UNEXPECTED_ESCAPE; -import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_UNEXPECTED_START_OF_QUOTE; import static com.mojang.brigadier.arguments.StringArgumentType.escapeIfRequired; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; import static com.mojang.brigadier.arguments.StringArgumentType.string; @@ -28,7 +21,6 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; @RunWith(MockitoJUnitRunner.class) public class StringArgumentTypeTest { @@ -97,8 +89,8 @@ public class StringArgumentTypeTest { type = word(); ParsedArgument result = type.parse("\"hello \\\" world\"", new CommandContextBuilder<>(dispatcher, source)); - assertThat(result.getRaw(), is("\"hello")); - assertThat(result.getResult(), is("\"hello")); + assertThat(result.getRaw(), is("")); + assertThat(result.getResult(), is("")); } @Test @@ -128,18 +120,6 @@ public class StringArgumentTypeTest { assertThat(result.getResult(), is("hello")); } - @Test - public void testParseInvalidQuote_earlyUnquote() throws Exception { - try { - type = string(); - type.parse("\"hello \"world", new CommandContextBuilder<>(dispatcher, source)); - fail(); - } catch (CommandException e) { - assertThat(e.getType(), is(ERROR_UNEXPECTED_END_OF_QUOTE)); - assertThat(e.getData(), is(equalTo(Collections.emptyMap()))); - } - } - @Test public void testParseQuote_earlyUnquoteWithRemaining() throws Exception { type = string(); @@ -149,18 +129,6 @@ public class StringArgumentTypeTest { assertThat(result.getResult(), is("hello")); } - @Test - public void testParseInvalidQuote_lateQuote() throws Exception { - try { - type = string(); - type.parse("hello\" world\"", new CommandContextBuilder<>(dispatcher, source)); - fail(); - } catch (CommandException e) { - assertThat(e.getType(), is(ERROR_UNEXPECTED_START_OF_QUOTE)); - assertThat(e.getData(), is(equalTo(Collections.emptyMap()))); - } - } - @Test public void testParseQuote_lateQuoteWithRemaining() throws Exception { type = string(); @@ -170,30 +138,6 @@ public class StringArgumentTypeTest { assertThat(result.getResult(), is("hello")); } - @Test - public void testParseInvalidQuote_middleQuote() throws Exception { - try { - type = string(); - type.parse("hel\"lo", new CommandContextBuilder<>(dispatcher, source)); - fail(); - } catch (CommandException e) { - assertThat(e.getType(), is(ERROR_UNEXPECTED_START_OF_QUOTE)); - assertThat(e.getData(), is(equalTo(Collections.emptyMap()))); - } - } - - @Test - public void testParseInvalidQuote_noUnquote() throws Exception { - try { - type = string(); - type.parse("\"hello world", new CommandContextBuilder<>(dispatcher, source)); - fail(); - } catch (CommandException e) { - assertThat(e.getType(), is(ERROR_EXPECTED_END_OF_QUOTE)); - assertThat(e.getData(), is(equalTo(Collections.emptyMap()))); - } - } - @Test public void testParseEmpty() throws Exception { type = string(); @@ -203,42 +147,6 @@ public class StringArgumentTypeTest { assertThat(result.getResult(), is("")); } - @Test - public void testParseInvalidEscape_onlyEscape() throws Exception { - try { - type = string(); - type.parse("\\", new CommandContextBuilder<>(dispatcher, source)); - fail(); - } catch (CommandException e) { - assertThat(e.getType(), is(ERROR_UNEXPECTED_ESCAPE)); - assertThat(e.getData(), is(equalTo(Collections.emptyMap()))); - } - } - - @Test - public void testParseInvalidEscape_unknownSequence() throws Exception { - try { - type = string(); - type.parse("\"\\n\"", new CommandContextBuilder<>(dispatcher, source)); - fail(); - } catch (CommandException e) { - assertThat(e.getType(), is(ERROR_INVALID_ESCAPE)); - assertThat(e.getData(), is(equalTo(Collections.singletonMap("input", "\\n")))); - } - } - - @Test - public void testParseInvalidEscape_notQuoted() throws Exception { - try { - type = string(); - type.parse("hel\\\\o", new CommandContextBuilder<>(dispatcher, source)); - fail(); - } catch (CommandException e) { - assertThat(e.getType(), is(ERROR_UNEXPECTED_ESCAPE)); - assertThat(e.getData(), is(equalTo(Collections.emptyMap()))); - } - } - @Test public void testSuggestions() throws Exception { type = string(); diff --git a/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java b/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java index 0509b7f..c8d075b 100644 --- a/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java +++ b/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java @@ -57,17 +57,6 @@ public class ArgumentCommandNodeTest extends AbstractCommandNodeTest { assertThat(contextBuilder.getArguments().get("foo").getResult(), is(123)); } - @Test - public void testParseInvalid() throws Exception { - try { - node.parse("foo", contextBuilder); - fail(); - } catch (CommandException ex) { - assertThat(ex.getType(), is(IntegerArgumentType.ERROR_NOT_A_NUMBER)); - assertThat(ex.getData(), is(ImmutableMap.of("found", "foo"))); - } - } - @Test public void testUsage() throws Exception { assertThat(node.getUsageText(), is(""));