From 5181559f460b8fe66bfd67de3572b80380825399 Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Thu, 27 Jul 2017 09:58:14 +0200 Subject: [PATCH] Made all exceptions throw with context --- .../mojang/brigadier/CommandDispatcher.java | 14 ++--- .../brigadier/ImmutableStringReader.java | 23 +++++++++ .../com/mojang/brigadier/ParseResults.java | 12 ++--- .../com/mojang/brigadier/StringReader.java | 40 +++++++++++---- .../arguments/IntegerArgumentType.java | 10 ++-- .../exceptions/CommandException.java | 51 +++++++++++++++++-- .../exceptions/CommandExceptionType.java | 4 +- .../ParameterizedCommandExceptionType.java | 20 +++++--- .../SimpleCommandExceptionType.java | 10 ++-- .../brigadier/tree/LiteralCommandNode.java | 4 +- .../brigadier/CommandDispatcherTest.java | 41 ++++++++++++++- .../mojang/brigadier/StringReaderTest.java | 42 +++++++++++++++ .../arguments/IntegerArgumentTypeTest.java | 8 ++- ...ParameterizedCommandExceptionTypeTest.java | 20 ++++---- .../SimpleCommandExceptionTypeTest.java | 36 +++++++++++-- .../tree/LiteralCommandNodeTest.java | 1 + 16 files changed, 275 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/mojang/brigadier/ImmutableStringReader.java diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java index 071aa18..08a70d8 100644 --- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java +++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java @@ -22,7 +22,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_UNKNOWN_ARGUMENT = new SimpleCommandExceptionType("command.unknown.argument", "Incorrect argument for command"); 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 = " "; @@ -52,19 +52,19 @@ public class CommandDispatcher { } public int execute(final ParseResults parse) throws CommandException { - if (parse.getRemaining().length() > 0) { + if (parse.getReader().canRead()) { if (parse.getExceptions().size() == 1) { throw parse.getExceptions().values().iterator().next(); } else if (parse.getContext().getInput().isEmpty()) { - throw ERROR_UNKNOWN_COMMAND.create(); + throw ERROR_UNKNOWN_COMMAND.createWithContext(parse.getReader()); } else { - throw ERROR_UNKNOWN_ARGUMENT.create(parse.getRemaining()); + throw ERROR_UNKNOWN_ARGUMENT.createWithContext(parse.getReader()); } } final CommandContext context = parse.getContext().build(); final Command command = context.getCommand(); if (command == null) { - throw ERROR_UNKNOWN_COMMAND.create(); + throw ERROR_UNKNOWN_COMMAND.createWithContext(parse.getReader()); } return command.run(context); } @@ -95,7 +95,7 @@ public class CommandDispatcher { context.withCommand(child.getCommand()); if (reader.canRead()) { if (reader.peek() != ARGUMENT_SEPARATOR_CHAR) { - throw ERROR_EXPECTED_ARGUMENT_SEPARATOR.create(); + throw ERROR_EXPECTED_ARGUMENT_SEPARATOR.createWithContext(reader); } reader.skip(); return parseNodes(child, reader, context); @@ -104,7 +104,7 @@ public class CommandDispatcher { } } - return new ParseResults<>(contextBuilder, reader.getRemaining(), errors); + return new ParseResults<>(contextBuilder, reader, errors); } public String[] getAllUsage(final CommandNode node, final S source) { diff --git a/src/main/java/com/mojang/brigadier/ImmutableStringReader.java b/src/main/java/com/mojang/brigadier/ImmutableStringReader.java new file mode 100644 index 0000000..d8f8c50 --- /dev/null +++ b/src/main/java/com/mojang/brigadier/ImmutableStringReader.java @@ -0,0 +1,23 @@ +package com.mojang.brigadier; + +public interface ImmutableStringReader { + String getString(); + + int getRemainingLength(); + + int getTotalLength(); + + int getCursor(); + + String getRead(); + + String getRemaining(); + + boolean canRead(int length); + + boolean canRead(); + + char peek(); + + char peek(int offset); +} diff --git a/src/main/java/com/mojang/brigadier/ParseResults.java b/src/main/java/com/mojang/brigadier/ParseResults.java index b761676..208fe8e 100644 --- a/src/main/java/com/mojang/brigadier/ParseResults.java +++ b/src/main/java/com/mojang/brigadier/ParseResults.java @@ -9,25 +9,25 @@ import java.util.Map; public class ParseResults { private final CommandContextBuilder context; - private final String remaining; private final Map, CommandException> exceptions; + private final ImmutableStringReader reader; - public ParseResults(final CommandContextBuilder context, final String remaining, final Map, CommandException> exceptions) { + public ParseResults(final CommandContextBuilder context, final ImmutableStringReader reader, final Map, CommandException> exceptions) { this.context = context; - this.remaining = remaining; + this.reader = reader; this.exceptions = exceptions; } public ParseResults(final CommandContextBuilder context) { - this(context, "", Collections.emptyMap()); + this(context, new StringReader(""), Collections.emptyMap()); } public CommandContextBuilder getContext() { return context; } - public String getRemaining() { - return remaining; + public ImmutableStringReader getReader() { + return reader; } public Map, CommandException> getExceptions() { diff --git a/src/main/java/com/mojang/brigadier/StringReader.java b/src/main/java/com/mojang/brigadier/StringReader.java index e902b9d..661622f 100644 --- a/src/main/java/com/mojang/brigadier/StringReader.java +++ b/src/main/java/com/mojang/brigadier/StringReader.java @@ -4,7 +4,7 @@ import com.mojang.brigadier.exceptions.CommandException; import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -public class StringReader { +public class StringReader implements ImmutableStringReader { private static final char SYNTAX_ESCAPE = '\\'; private static final char SYNTAX_QUOTE = '"'; @@ -16,6 +16,7 @@ public class StringReader { 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"); + public static final SimpleCommandExceptionType ERROR_EXPECTED_BOOL = new SimpleCommandExceptionType("parsing.bool.expected", "Expected bool"); public static final ParameterizedCommandExceptionType ERROR_EXPECTED_SYMBOL = new ParameterizedCommandExceptionType("parsing.expected", "Expected '${symbol}'", "symbol"); private final String string; @@ -25,6 +26,7 @@ public class StringReader { this.string = string; } + @Override public String getString() { return string; } @@ -33,38 +35,47 @@ public class StringReader { this.cursor = cursor; } + @Override public int getRemainingLength() { return string.length() - cursor; } + @Override public int getTotalLength() { return string.length(); } + @Override public int getCursor() { return cursor; } + @Override public String getRead() { return string.substring(0, cursor); } + @Override public String getRemaining() { return string.substring(cursor); } + @Override public boolean canRead(final int length) { return cursor + length <= string.length(); } + @Override public boolean canRead() { return canRead(1); } + @Override public char peek() { return string.charAt(cursor); } + @Override public char peek(final int offset) { return string.charAt(cursor + offset); } @@ -94,12 +105,13 @@ public class StringReader { } final String number = string.substring(start, cursor); if (number.isEmpty()) { - throw ERROR_EXPECTED_INT.create(); + throw ERROR_EXPECTED_INT.createWithContext(this); } try { return Integer.parseInt(number); } catch (final NumberFormatException ex) { - throw ERROR_INVALID_INT.create(number); + cursor = start; + throw ERROR_INVALID_INT.createWithContext(this, number); } } @@ -110,12 +122,13 @@ public class StringReader { } final String number = string.substring(start, cursor); if (number.isEmpty()) { - throw ERROR_EXPECTED_DOUBLE.create(); + throw ERROR_EXPECTED_DOUBLE.createWithContext(this); } try { return Double.parseDouble(number); } catch (final NumberFormatException ex) { - throw ERROR_INVALID_DOUBLE.create(number); + cursor = start; + throw ERROR_INVALID_DOUBLE.createWithContext(this, number); } } @@ -139,7 +152,7 @@ public class StringReader { if (!canRead()) { return ""; } else if (peek() != SYNTAX_QUOTE) { - throw ERROR_EXPECTED_START_OF_QUOTE.create(); + throw ERROR_EXPECTED_START_OF_QUOTE.createWithContext(this); } skip(); final StringBuilder result = new StringBuilder(); @@ -151,7 +164,8 @@ public class StringReader { result.append(c); escaped = false; } else { - throw ERROR_INVALID_ESCAPE.create(String.valueOf(c)); + setCursor(getCursor() - 1); + throw ERROR_INVALID_ESCAPE.createWithContext(this, String.valueOf(c)); } } else if (c == SYNTAX_ESCAPE) { escaped = true; @@ -162,7 +176,7 @@ public class StringReader { } } - throw ERROR_EXPECTED_END_OF_QUOTE.create(); + throw ERROR_EXPECTED_END_OF_QUOTE.createWithContext(this); } public String readString() throws CommandException { @@ -174,19 +188,25 @@ public class StringReader { } public boolean readBoolean() throws CommandException { + final int start = cursor; final String value = readString(); + if (value.isEmpty()) { + throw ERROR_EXPECTED_BOOL.createWithContext(this); + } + if (value.equals("true")) { return true; } else if (value.equals("false")) { return false; } else { - throw ERROR_INVALID_BOOL.create(value); + cursor = start; + throw ERROR_INVALID_BOOL.createWithContext(this, value); } } public void expect(final char c) throws CommandException { if (!canRead() || peek() != c) { - throw ERROR_EXPECTED_SYMBOL.create(String.valueOf(c)); + throw ERROR_EXPECTED_SYMBOL.createWithContext(this, String.valueOf(c)); } skip(); } diff --git a/src/main/java/com/mojang/brigadier/arguments/IntegerArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/IntegerArgumentType.java index 5df435b..2bdf398 100644 --- a/src/main/java/com/mojang/brigadier/arguments/IntegerArgumentType.java +++ b/src/main/java/com/mojang/brigadier/arguments/IntegerArgumentType.java @@ -45,19 +45,23 @@ public class IntegerArgumentType implements ArgumentType { @Override public Integer parse(final StringReader reader, final CommandContextBuilder contextBuilder) throws CommandException { + final int start = reader.getCursor(); final 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); + reader.setCursor(start); + throw ERROR_WRONG_SUFFIX.createWithContext(reader, suffix); } } if (result < minimum) { - throw ERROR_TOO_SMALL.create(result, minimum); + reader.setCursor(start); + throw ERROR_TOO_SMALL.createWithContext(reader, result, minimum); } if (result > maximum) { - throw ERROR_TOO_BIG.create(result, maximum); + reader.setCursor(start); + throw ERROR_TOO_BIG.createWithContext(reader, result, maximum); } return result; } diff --git a/src/main/java/com/mojang/brigadier/exceptions/CommandException.java b/src/main/java/com/mojang/brigadier/exceptions/CommandException.java index 5a02421..c893a34 100644 --- a/src/main/java/com/mojang/brigadier/exceptions/CommandException.java +++ b/src/main/java/com/mojang/brigadier/exceptions/CommandException.java @@ -3,27 +3,70 @@ package com.mojang.brigadier.exceptions; import java.util.Map; public class CommandException extends Exception { + public static final int CONTEXT_AMOUNT = 10; public static boolean ENABLE_COMMAND_STACK_TRACES = true; private final CommandExceptionType type; - private final Map data; + private final Map data; + private final String input; + private final int cursor; - public CommandException(final CommandExceptionType type, final Map data) { + public CommandException(final CommandExceptionType type, final Map data) { super(type.getTypeName(), null, ENABLE_COMMAND_STACK_TRACES, ENABLE_COMMAND_STACK_TRACES); this.type = type; this.data = data; + this.input = null; + this.cursor = -1; + } + + public CommandException(final CommandExceptionType type, final Map data, final String input, final int cursor) { + super(type.getTypeName(), null, ENABLE_COMMAND_STACK_TRACES, ENABLE_COMMAND_STACK_TRACES); + this.type = type; + this.data = data; + this.input = input; + this.cursor = cursor; } @Override public String getMessage() { - return type.getErrorMessage(this); + String message = type.getErrorMessage(data); + final String context = getContext(); + if (context != null) { + message += " at position " + cursor + ": " + context; + } + return message; + } + + public String getContext() { + if (input == null || cursor < 0) { + return null; + } + final StringBuilder builder = new StringBuilder(); + final int cursor = Math.min(input.length(), this.cursor); + + if (cursor > CONTEXT_AMOUNT) { + builder.append("..."); + } + + builder.append(input.substring(Math.max(0, cursor - CONTEXT_AMOUNT), cursor)); + builder.append("<--[HERE]"); + + return builder.toString(); } public CommandExceptionType getType() { return type; } - public Map getData() { + public Map getData() { return data; } + + public String getInput() { + return input; + } + + public int getCursor() { + return cursor; + } } diff --git a/src/main/java/com/mojang/brigadier/exceptions/CommandExceptionType.java b/src/main/java/com/mojang/brigadier/exceptions/CommandExceptionType.java index 37ed893..41e36fc 100644 --- a/src/main/java/com/mojang/brigadier/exceptions/CommandExceptionType.java +++ b/src/main/java/com/mojang/brigadier/exceptions/CommandExceptionType.java @@ -1,7 +1,9 @@ package com.mojang.brigadier.exceptions; +import java.util.Map; + public interface CommandExceptionType { String getTypeName(); - String getErrorMessage(CommandException exception); + String getErrorMessage(Map data); } diff --git a/src/main/java/com/mojang/brigadier/exceptions/ParameterizedCommandExceptionType.java b/src/main/java/com/mojang/brigadier/exceptions/ParameterizedCommandExceptionType.java index e05fa87..dfeb17b 100644 --- a/src/main/java/com/mojang/brigadier/exceptions/ParameterizedCommandExceptionType.java +++ b/src/main/java/com/mojang/brigadier/exceptions/ParameterizedCommandExceptionType.java @@ -2,7 +2,10 @@ package com.mojang.brigadier.exceptions; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; +import com.mojang.brigadier.ImmutableStringReader; +import com.mojang.brigadier.StringReader; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -26,28 +29,31 @@ public class ParameterizedCommandExceptionType implements CommandExceptionType { } @Override - public String getErrorMessage(final CommandException exception) { + public String getErrorMessage(final Map data) { final Matcher matcher = PATTERN.matcher(message); final StringBuffer result = new StringBuffer(); while (matcher.find()) { - matcher.appendReplacement(result, Matcher.quoteReplacement(exception.getData().get(matcher.group(1)).toString())); + matcher.appendReplacement(result, Matcher.quoteReplacement(data.get(matcher.group(1)))); } matcher.appendTail(result); return result.toString(); } - public CommandException create(final Object... values) { + public CommandException createWithContext(final ImmutableStringReader reader, final Object... values) { + return new CommandException(this, createMap(values), reader.getString(), reader.getCursor()); + } + + public Map createMap(final Object... values) { if (values.length != keys.length) { throw new IllegalArgumentException("Invalid values! (Expected: " + JOINER.join(keys) + ")"); } - ImmutableMap.Builder builder = ImmutableMap.builder(); + ImmutableMap.Builder builder = ImmutableMap.builder(); for (int i = 0; i < keys.length; i++) { - builder = builder.put(keys[i], values[i]); + builder = builder.put(keys[i], String.valueOf(values[i])); } - - return new CommandException(this, builder.build()); + return builder.build(); } @Override diff --git a/src/main/java/com/mojang/brigadier/exceptions/SimpleCommandExceptionType.java b/src/main/java/com/mojang/brigadier/exceptions/SimpleCommandExceptionType.java index 1670f5a..2e9d41c 100644 --- a/src/main/java/com/mojang/brigadier/exceptions/SimpleCommandExceptionType.java +++ b/src/main/java/com/mojang/brigadier/exceptions/SimpleCommandExceptionType.java @@ -1,6 +1,10 @@ package com.mojang.brigadier.exceptions; import com.google.common.collect.ImmutableMap; +import com.mojang.brigadier.ImmutableStringReader; +import com.mojang.brigadier.StringReader; + +import java.util.Map; public class SimpleCommandExceptionType implements CommandExceptionType { private final String name; @@ -17,12 +21,12 @@ public class SimpleCommandExceptionType implements CommandExceptionType { } @Override - public String getErrorMessage(final CommandException exception) { + public String getErrorMessage(final Map data) { return message; } - public CommandException create() { - return new CommandException(this, ImmutableMap.of()); + public CommandException createWithContext(final ImmutableStringReader reader) { + return new CommandException(this, ImmutableMap.of(), reader.getString(), reader.getCursor()); } @Override diff --git a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java index 3a00b71..625aa99 100644 --- a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java @@ -31,11 +31,13 @@ public class LiteralCommandNode extends CommandNode { @Override public void parse(final StringReader reader, final CommandContextBuilder contextBuilder) throws CommandException { + final int start = reader.getCursor(); 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); + reader.setCursor(start); + throw ERROR_INCORRECT_LITERAL.createWithContext(reader, literal); } } diff --git a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java index f20a978..693fdc1 100644 --- a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java +++ b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java @@ -68,6 +68,7 @@ public class CommandDispatcherTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND)); assertThat(ex.getData(), is(Collections.emptyMap())); + assertThat(ex.getCursor(), is(0)); } } @@ -81,6 +82,7 @@ public class CommandDispatcherTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND)); assertThat(ex.getData(), is(Collections.emptyMap())); + assertThat(ex.getCursor(), is(0)); } } @@ -94,6 +96,7 @@ public class CommandDispatcherTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND)); assertThat(ex.getData(), is(Collections.emptyMap())); + assertThat(ex.getCursor(), is(0)); } } @@ -106,7 +109,8 @@ public class CommandDispatcherTest { fail(); } catch (final CommandException ex) { assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_ARGUMENT)); - assertThat(ex.getData(), is(Collections.singletonMap("argument", "bar"))); + assertThat(ex.getData(), is(Collections.emptyMap())); + assertThat(ex.getCursor(), is(4)); } } @@ -120,6 +124,7 @@ public class CommandDispatcherTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(LiteralCommandNode.ERROR_INCORRECT_LITERAL)); assertThat(ex.getData(), is(Collections.singletonMap("expected", "bar"))); + assertThat(ex.getCursor(), is(4)); } } @@ -136,7 +141,8 @@ public class CommandDispatcherTest { fail(); } catch (final CommandException ex) { assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_ARGUMENT)); - assertThat(ex.getData(), is(Collections.singletonMap("argument", "unknown"))); + assertThat(ex.getData(), is(Collections.emptyMap())); + assertThat(ex.getCursor(), is(4)); } } @@ -158,6 +164,36 @@ public class CommandDispatcherTest { verify(subCommand).run(any(CommandContext.class)); } + @Test + public void testExecuteOrphanedSubcommand() throws Exception { + subject.register(literal("foo").then( + argument("bar", integer()) + ).executes(command)); + + try { + subject.execute("foo 5", source); + fail(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND)); + assertThat(ex.getData(), is(Collections.emptyMap())); + assertThat(ex.getCursor(), is(0)); + } + } + + @Test + public void parse_noSpaceSeparator() throws Exception { + subject.register(literal("foo").then(argument("bar", integer()).executes(command))); + + try { + subject.execute("foo5", source); + fail(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(CommandDispatcher.ERROR_EXPECTED_ARGUMENT_SEPARATOR)); + assertThat(ex.getData(), is(Collections.emptyMap())); + assertThat(ex.getCursor(), is(3)); + } + } + @Test public void testExecuteInvalidSubcommand() throws Exception { subject.register(literal("foo").then( @@ -170,6 +206,7 @@ public class CommandDispatcherTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT)); assertThat(ex.getData(), is(Collections.emptyMap())); + assertThat(ex.getCursor(), is(4)); } } } \ 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 index 0001301..4f400fc 100644 --- a/src/test/java/com/mojang/brigadier/StringReaderTest.java +++ b/src/test/java/com/mojang/brigadier/StringReaderTest.java @@ -219,6 +219,7 @@ public class StringReaderTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_START_OF_QUOTE)); assertThat(ex.getData(), equalTo(Collections.emptyMap())); + assertThat(ex.getCursor(), is(0)); } } @@ -229,6 +230,7 @@ public class StringReaderTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_END_OF_QUOTE)); assertThat(ex.getData(), equalTo(Collections.emptyMap())); + assertThat(ex.getCursor(), is(12)); } } @@ -239,6 +241,7 @@ public class StringReaderTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_INVALID_ESCAPE)); assertThat(ex.getData(), equalTo(ImmutableMap.of("character", "n"))); + assertThat(ex.getCursor(), is(7)); } } @@ -265,6 +268,7 @@ public class StringReaderTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_INVALID_INT)); assertThat(ex.getData(), equalTo(ImmutableMap.of("value", "12.34"))); + assertThat(ex.getCursor(), is(0)); } } @@ -275,6 +279,7 @@ public class StringReaderTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_INT)); assertThat(ex.getData(), equalTo(Collections.emptyMap())); + assertThat(ex.getCursor(), is(0)); } } @@ -325,6 +330,7 @@ public class StringReaderTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_INVALID_DOUBLE)); assertThat(ex.getData(), equalTo(ImmutableMap.of("value", "12.34.56"))); + assertThat(ex.getCursor(), is(0)); } } @@ -335,6 +341,7 @@ public class StringReaderTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_DOUBLE)); assertThat(ex.getData(), equalTo(Collections.emptyMap())); + assertThat(ex.getCursor(), is(0)); } } @@ -370,6 +377,7 @@ public class StringReaderTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_SYMBOL)); assertThat(ex.getData(), equalTo(ImmutableMap.of("symbol", "a"))); + assertThat(ex.getCursor(), is(0)); } } @@ -382,6 +390,40 @@ public class StringReaderTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_SYMBOL)); assertThat(ex.getData(), equalTo(ImmutableMap.of("symbol", "a"))); + assertThat(ex.getCursor(), is(0)); + } + } + + @Test + public void readBoolean_correct() throws Exception { + final StringReader reader = new StringReader("true"); + assertThat(reader.readBoolean(), is(true)); + assertThat(reader.getRead(), equalTo("true")); + } + + @Test + public void readBoolean_incorrect() throws Exception { + final StringReader reader = new StringReader("tuesday"); + try { + reader.readBoolean(); + fail(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(StringReader.ERROR_INVALID_BOOL)); + assertThat(ex.getData(), equalTo(ImmutableMap.of("value", "tuesday"))); + assertThat(ex.getCursor(), is(0)); + } + } + + @Test + public void readBoolean_none() throws Exception { + final StringReader reader = new StringReader(""); + try { + reader.readBoolean(); + fail(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(StringReader.ERROR_EXPECTED_BOOL)); + assertThat(ex.getData(), equalTo(Collections.emptyMap())); + assertThat(ex.getCursor(), is(0)); } } } \ 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 84781b3..e35477b 100644 --- a/src/test/java/com/mojang/brigadier/arguments/IntegerArgumentTypeTest.java +++ b/src/test/java/com/mojang/brigadier/arguments/IntegerArgumentTypeTest.java @@ -57,6 +57,7 @@ public class IntegerArgumentTypeTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(IntegerArgumentType.ERROR_WRONG_SUFFIX)); assertThat(ex.getData(), equalTo(ImmutableMap.of("suffix", "L"))); + assertThat(ex.getCursor(), is(0)); } } @@ -69,6 +70,7 @@ public class IntegerArgumentTypeTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(IntegerArgumentType.ERROR_WRONG_SUFFIX)); assertThat(ex.getData(), equalTo(ImmutableMap.of("suffix", "L"))); + assertThat(ex.getCursor(), is(0)); } } @@ -80,7 +82,8 @@ public class IntegerArgumentTypeTest { fail(); } catch (final CommandException ex) { assertThat(ex.getType(), is(IntegerArgumentType.ERROR_TOO_SMALL)); - assertThat(ex.getData(), equalTo(ImmutableMap.of("found", -5, "minimum", 0))); + assertThat(ex.getData(), equalTo(ImmutableMap.of("found", "-5", "minimum", "0"))); + assertThat(ex.getCursor(), is(0)); } } @@ -92,7 +95,8 @@ public class IntegerArgumentTypeTest { fail(); } catch (final CommandException ex) { assertThat(ex.getType(), is(IntegerArgumentType.ERROR_TOO_BIG)); - assertThat(ex.getData(), equalTo(ImmutableMap.of("found", 5, "maximum", 0))); + assertThat(ex.getData(), equalTo(ImmutableMap.of("found", "5", "maximum", "0"))); + assertThat(ex.getCursor(), is(0)); } } diff --git a/src/test/java/com/mojang/brigadier/exceptions/ParameterizedCommandExceptionTypeTest.java b/src/test/java/com/mojang/brigadier/exceptions/ParameterizedCommandExceptionTypeTest.java index a268b50..174a27c 100644 --- a/src/test/java/com/mojang/brigadier/exceptions/ParameterizedCommandExceptionTypeTest.java +++ b/src/test/java/com/mojang/brigadier/exceptions/ParameterizedCommandExceptionTypeTest.java @@ -2,10 +2,12 @@ package com.mojang.brigadier.exceptions; import com.google.common.collect.ImmutableMap; import com.google.common.testing.EqualsTester; +import com.mojang.brigadier.StringReader; import org.junit.Before; import org.junit.Test; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; @SuppressWarnings("ThrowableResultOfMethodCallIgnored") @@ -18,21 +20,19 @@ public class ParameterizedCommandExceptionTypeTest { } @Test(expected = IllegalArgumentException.class) - public void testCreateTooFewArguments() throws Exception { - type.create(); - } - - @Test(expected = IllegalArgumentException.class) - public void testCreateTooManyArguments() throws Exception { - type.create("World", "Universe"); + public void createMap_TooManyArguments() throws Exception { + type.createMap("World", "Universe"); } @Test - public void testCreate() throws Exception { - final CommandException exception = type.create("World"); + public void createWithContext() throws Exception { + final StringReader reader = new StringReader("Foo bar"); + reader.setCursor(5); + final CommandException exception = type.createWithContext(reader, "World"); assertThat(exception.getType(), is(type)); assertThat(exception.getData(), is(ImmutableMap.of("name", "World"))); - assertThat(exception.getMessage(), is("Hello, World!")); + assertThat(exception.getInput(), is("Foo bar")); + assertThat(exception.getCursor(), is(5)); } @Test diff --git a/src/test/java/com/mojang/brigadier/exceptions/SimpleCommandExceptionTypeTest.java b/src/test/java/com/mojang/brigadier/exceptions/SimpleCommandExceptionTypeTest.java index 7e94269..90ac92e 100644 --- a/src/test/java/com/mojang/brigadier/exceptions/SimpleCommandExceptionTypeTest.java +++ b/src/test/java/com/mojang/brigadier/exceptions/SimpleCommandExceptionTypeTest.java @@ -1,21 +1,29 @@ package com.mojang.brigadier.exceptions; import com.google.common.testing.EqualsTester; +import com.mojang.brigadier.StringReader; import org.junit.Test; -import static org.hamcrest.Matchers.empty; +import java.util.Collections; + +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public class SimpleCommandExceptionTypeTest { @Test - public void testCreate() throws Exception { + public void createWithContext() throws Exception { final SimpleCommandExceptionType type = new SimpleCommandExceptionType("foo", "bar"); - final CommandException exception = type.create(); + final StringReader reader = new StringReader("Foo bar"); + reader.setCursor(5); + final CommandException exception = type.createWithContext(reader); assertThat(exception.getType(), is(type)); - assertThat(exception.getMessage(), is("bar")); - assertThat(exception.getData().values(), empty()); + assertThat(exception.getData(), is(Collections.emptyMap())); + assertThat(exception.getInput(), is("Foo bar")); + assertThat(exception.getCursor(), is(5)); } @Test @@ -25,4 +33,22 @@ public class SimpleCommandExceptionTypeTest { .addEqualityGroup(new SimpleCommandExceptionType("bar", "Hello, world!"), new SimpleCommandExceptionType("bar", "Hello, universe!")) .testEquals(); } + + @Test + public void getContext_none() throws Exception { + final CommandException exception = new CommandException(mock(CommandExceptionType.class), Collections.emptyMap()); + assertThat(exception.getContext(), is(nullValue())); + } + + @Test + public void getContext_short() throws Exception { + final CommandException exception = new CommandException(mock(CommandExceptionType.class), Collections.emptyMap(), "Hello world!", 5); + assertThat(exception.getContext(), equalTo("Hello<--[HERE]")); + } + + @Test + public void getContext_long() throws Exception { + final CommandException exception = new CommandException(mock(CommandExceptionType.class), Collections.emptyMap(), "Hello world! This has an error in it. Oh dear!", 20); + assertThat(exception.getContext(), equalTo("...d! This ha<--[HERE]")); + } } \ No newline at end of file diff --git a/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java b/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java index 6adb879..bbe903c 100644 --- a/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java +++ b/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java @@ -69,6 +69,7 @@ public class LiteralCommandNodeTest extends AbstractCommandNodeTest { } catch (final CommandException ex) { assertThat(ex.getType(), is(LiteralCommandNode.ERROR_INCORRECT_LITERAL)); assertThat(ex.getData(), is(ImmutableMap.of("expected", "foo"))); + assertThat(ex.getCursor(), is(0)); } }