Made all exceptions throw with context
This commit is contained in:
@@ -22,7 +22,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class CommandDispatcher<S> {
|
||||
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<S> {
|
||||
}
|
||||
|
||||
public int execute(final ParseResults<S> 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<S> context = parse.getContext().build();
|
||||
final Command<S> 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<S> {
|
||||
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<S> {
|
||||
}
|
||||
}
|
||||
|
||||
return new ParseResults<>(contextBuilder, reader.getRemaining(), errors);
|
||||
return new ParseResults<>(contextBuilder, reader, errors);
|
||||
}
|
||||
|
||||
public String[] getAllUsage(final CommandNode<S> node, final S source) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -9,25 +9,25 @@ import java.util.Map;
|
||||
|
||||
public class ParseResults<S> {
|
||||
private final CommandContextBuilder<S> context;
|
||||
private final String remaining;
|
||||
private final Map<CommandNode<S>, CommandException> exceptions;
|
||||
private final ImmutableStringReader reader;
|
||||
|
||||
public ParseResults(final CommandContextBuilder<S> context, final String remaining, final Map<CommandNode<S>, CommandException> exceptions) {
|
||||
public ParseResults(final CommandContextBuilder<S> context, final ImmutableStringReader reader, final Map<CommandNode<S>, CommandException> exceptions) {
|
||||
this.context = context;
|
||||
this.remaining = remaining;
|
||||
this.reader = reader;
|
||||
this.exceptions = exceptions;
|
||||
}
|
||||
|
||||
public ParseResults(final CommandContextBuilder<S> context) {
|
||||
this(context, "", Collections.emptyMap());
|
||||
this(context, new StringReader(""), Collections.emptyMap());
|
||||
}
|
||||
|
||||
public CommandContextBuilder<S> getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public String getRemaining() {
|
||||
return remaining;
|
||||
public ImmutableStringReader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
public Map<CommandNode<S>, CommandException> getExceptions() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -45,19 +45,23 @@ public class IntegerArgumentType implements ArgumentType<Integer> {
|
||||
|
||||
@Override
|
||||
public <S> Integer parse(final StringReader reader, final CommandContextBuilder<S> 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;
|
||||
}
|
||||
|
||||
@@ -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<String, Object> data;
|
||||
private final Map<String, String> data;
|
||||
private final String input;
|
||||
private final int cursor;
|
||||
|
||||
public CommandException(final CommandExceptionType type, final Map<String, Object> data) {
|
||||
public CommandException(final CommandExceptionType type, final Map<String, String> 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<String, String> 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<String, Object> getData() {
|
||||
public Map<String, String> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
public int getCursor() {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, String> data);
|
||||
}
|
||||
|
||||
+13
-7
@@ -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<String, String> 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<String, String> createMap(final Object... values) {
|
||||
if (values.length != keys.length) {
|
||||
throw new IllegalArgumentException("Invalid values! (Expected: " + JOINER.join(keys) + ")");
|
||||
}
|
||||
|
||||
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<String, String> 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
|
||||
|
||||
@@ -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<String, String> 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
|
||||
|
||||
@@ -31,11 +31,13 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
|
||||
|
||||
@Override
|
||||
public void parse(final StringReader reader, final CommandContextBuilder<S> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user