Start of new exception system

This commit is contained in:
Nathan Adams 2014-10-02 12:56:51 +02:00
parent 29f99e8710
commit 8c5338a6bd
21 changed files with 274 additions and 60 deletions

View file

@ -27,6 +27,7 @@ repositories {
dependencies {
compile 'com.google.code.findbugs:jsr305:2.0.1'
compile 'com.google.guava:guava:17.0'
compile 'org.apache.commons:commons-lang3:3.3.2'
testCompile 'junit:junit-dep:4.10'
testCompile 'org.hamcrest:hamcrest-library:1.2.1'
testCompile 'org.mockito:mockito-core:1.8.5'

View file

@ -3,14 +3,13 @@ package net.minecraft.commands;
import net.minecraft.commands.builder.LiteralArgumentBuilder;
import net.minecraft.commands.context.CommandContext;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.CommandException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.UnknownCommandException;
import net.minecraft.commands.exceptions.SimpleCommandExceptionType;
import net.minecraft.commands.tree.CommandNode;
import net.minecraft.commands.tree.RootCommandNode;
public class CommandDispatcher<T> {
public static final SimpleCommandExceptionType ERROR_UNKNOWN_COMMAND = new SimpleCommandExceptionType("unknown_command", "Unknown command");
public static final String ARGUMENT_SEPARATOR = " ";
private final RootCommandNode root = new RootCommandNode();
@ -24,8 +23,8 @@ public class CommandDispatcher<T> {
context.getCommand().run(context);
}
protected CommandContext<T> parseNodes(CommandNode node, String command, CommandContextBuilder<T> contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException, UnknownCommandException {
IllegalArgumentSyntaxException exception = null;
protected CommandContext<T> parseNodes(CommandNode node, String command, CommandContextBuilder<T> contextBuilder) throws CommandException {
CommandException exception = null;
for (CommandNode child : node.getChildren()) {
try {
@ -35,7 +34,7 @@ public class CommandDispatcher<T> {
context.withCommand(child.getCommand());
}
return parseNodes(child, remaining, context);
} catch (IllegalArgumentSyntaxException ex) {
} catch (CommandException ex) {
exception = ex;
}
}
@ -44,7 +43,7 @@ public class CommandDispatcher<T> {
throw exception;
}
if (command.length() > 0) {
throw new UnknownCommandException();
throw ERROR_UNKNOWN_COMMAND.create();
}
return contextBuilder.build();

View file

@ -1,9 +1,8 @@
package net.minecraft.commands.arguments;
import net.minecraft.commands.context.ParsedArgument;
import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.CommandException;
public interface CommandArgumentType<T> {
ParsedArgument<T> parse(String command) throws IllegalArgumentSyntaxException, ArgumentValidationException;
ParsedArgument<T> parse(String command) throws CommandException;
}

View file

@ -4,10 +4,14 @@ import com.google.common.base.Splitter;
import net.minecraft.commands.CommandDispatcher;
import net.minecraft.commands.context.CommandContext;
import net.minecraft.commands.context.ParsedArgument;
import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.CommandException;
import net.minecraft.commands.exceptions.ParameterizedCommandExceptionType;
public class IntegerArgumentType implements CommandArgumentType<Integer> {
public static final ParameterizedCommandExceptionType ERROR_NOT_A_NUMBER = new ParameterizedCommandExceptionType("argument-integer-invalid", "Expected an integer, found '${found}'", "found");
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");
private static final Splitter SPLITTER = Splitter.on(CommandDispatcher.ARGUMENT_SEPARATOR).limit(2);
private final int minimum;
@ -35,22 +39,22 @@ public class IntegerArgumentType implements CommandArgumentType<Integer> {
}
@Override
public ParsedArgument<Integer> parse(String command) throws IllegalArgumentSyntaxException, ArgumentValidationException {
public ParsedArgument<Integer> parse(String command) throws CommandException {
String raw = SPLITTER.split(command).iterator().next();
try {
int value = Integer.parseInt(raw);
if (value < minimum) {
throw new ArgumentValidationException();
throw ERROR_TOO_SMALL.create(value, minimum);
}
if (value > maximum) {
throw new ArgumentValidationException();
throw ERROR_TOO_BIG.create(value, maximum);
}
return new ParsedArgument<Integer>(raw, value);
} catch (NumberFormatException ignored) {
throw new IllegalArgumentSyntaxException();
throw ERROR_NOT_A_NUMBER.create(raw);
}
}

View file

@ -1,4 +0,0 @@
package net.minecraft.commands.exceptions;
public class ArgumentValidationException extends CommandException {
}

View file

@ -1,4 +1,26 @@
package net.minecraft.commands.exceptions;
import java.util.Map;
public class CommandException extends Exception {
private final CommandExceptionType type;
private final Map<String, Object> data;
public CommandException(CommandExceptionType type, Map<String, Object> data) {
this.type = type;
this.data = data;
}
@Override
public String getMessage() {
return type.getErrorMessage(this);
}
public CommandExceptionType getType() {
return type;
}
public Map<String, Object> getData() {
return data;
}
}

View file

@ -0,0 +1,6 @@
package net.minecraft.commands.exceptions;
public interface CommandExceptionType {
String getTypeName();
String getErrorMessage(CommandException exception);
}

View file

@ -1,4 +0,0 @@
package net.minecraft.commands.exceptions;
public class IllegalArgumentSyntaxException extends CommandException {
}

View file

@ -0,0 +1,58 @@
package net.minecraft.commands.exceptions;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.text.StrSubstitutor;
public class ParameterizedCommandExceptionType implements CommandExceptionType {
private static final Joiner JOINER = Joiner.on(", ");
private final String name;
private final String message;
private final String[] keys;
public ParameterizedCommandExceptionType(String name, String message, String... keys) {
this.name = name;
this.message = message;
this.keys = keys;
}
@Override
public String getTypeName() {
return name;
}
@Override
public String getErrorMessage(CommandException exception) {
return new StrSubstitutor(exception.getData()).replace(message);
}
public CommandException create(Object... values) {
if (values.length != keys.length) {
throw new IllegalArgumentException("Invalid values! (Expected: " + JOINER.join(keys) + ")");
}
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
for (int i = 0; i < keys.length; i++) {
builder = builder.put(keys[i], values[i]);
}
return new CommandException(this, builder.build());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CommandExceptionType)) return false;
CommandExceptionType that = (CommandExceptionType) o;
return getTypeName().equals(that.getTypeName());
}
@Override
public int hashCode() {
return getTypeName().hashCode();
}
}

View file

@ -0,0 +1,42 @@
package net.minecraft.commands.exceptions;
import com.google.common.collect.ImmutableMap;
public class SimpleCommandExceptionType implements CommandExceptionType {
private final String name;
private final String message;
public SimpleCommandExceptionType(String name, String message) {
this.name = name;
this.message = message;
}
@Override
public String getTypeName() {
return name;
}
@Override
public String getErrorMessage(CommandException exception) {
return message;
}
public CommandException create() {
return new CommandException(this, ImmutableMap.<String, Object>of());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CommandExceptionType)) return false;
CommandExceptionType that = (CommandExceptionType) o;
return getTypeName().equals(that.getTypeName());
}
@Override
public int hashCode() {
return getTypeName().hashCode();
}
}

View file

@ -1,4 +0,0 @@
package net.minecraft.commands.exceptions;
public class UnknownCommandException extends CommandException {
}

View file

@ -4,8 +4,7 @@ import net.minecraft.commands.Command;
import net.minecraft.commands.arguments.CommandArgumentType;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.context.ParsedArgument;
import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.CommandException;
public class ArgumentCommandNode<T> extends CommandNode {
private final String name;
@ -31,7 +30,7 @@ public class ArgumentCommandNode<T> extends CommandNode {
}
@Override
public String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException {
public String parse(String command, CommandContextBuilder<?> contextBuilder) throws CommandException {
ParsedArgument<T> parsed = type.parse(command);
int start = parsed.getRaw().length();

View file

@ -3,8 +3,7 @@ package net.minecraft.commands.tree;
import com.google.common.collect.Maps;
import net.minecraft.commands.Command;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.CommandException;
import java.util.Collection;
import java.util.Map;
@ -60,5 +59,5 @@ public abstract class CommandNode {
protected abstract Object getMergeKey();
public abstract String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException;
public abstract String parse(String command, CommandContextBuilder<?> contextBuilder) throws CommandException;
}

View file

@ -3,10 +3,12 @@ package net.minecraft.commands.tree;
import net.minecraft.commands.Command;
import net.minecraft.commands.CommandDispatcher;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.CommandException;
import net.minecraft.commands.exceptions.ParameterizedCommandExceptionType;
public class LiteralCommandNode extends CommandNode {
public static final ParameterizedCommandExceptionType ERROR_INCORRECT_LITERAL = new ParameterizedCommandExceptionType("incorrect_literal", "Expected literal ${expected}", "expected");
private final String literal;
public LiteralCommandNode(String literal, Command command) {
@ -24,11 +26,11 @@ public class LiteralCommandNode extends CommandNode {
}
@Override
public String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException {
public String parse(String command, CommandContextBuilder<?> contextBuilder) throws CommandException {
String expected = literal + (command.length() > literal.length() ? CommandDispatcher.ARGUMENT_SEPARATOR : "");
if (!command.startsWith(expected)) {
throw new IllegalArgumentSyntaxException();
throw ERROR_INCORRECT_LITERAL.create(expected);
}
int start = expected.length();

View file

@ -1,8 +1,7 @@
package net.minecraft.commands.tree;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.CommandException;
public class RootCommandNode extends CommandNode {
public RootCommandNode() {
@ -15,7 +14,7 @@ public class RootCommandNode extends CommandNode {
}
@Override
public String parse(String command, CommandContextBuilder contextBuilder) throws IllegalArgumentSyntaxException, ArgumentValidationException {
public String parse(String command, CommandContextBuilder<?> contextBuilder) throws CommandException {
return command;
}

View file

@ -1,8 +1,7 @@
package net.minecraft.commands;
import net.minecraft.commands.context.CommandContext;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.UnknownCommandException;
import net.minecraft.commands.exceptions.CommandException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -78,12 +77,12 @@ public class CommandDispatcherTest {
verify(three).run(any(CommandContext.class));
}
@Test(expected = UnknownCommandException.class)
@Test(expected = CommandException.class)
public void testExecuteUnknownCommand() throws Exception {
subject.execute("foo", source);
}
@Test(expected = UnknownCommandException.class)
@Test(expected = CommandException.class)
public void testExecuteUnknownSubcommand() throws Exception {
subject.register(literal("foo").executes(command));
subject.execute("foo bar", source);
@ -105,7 +104,7 @@ public class CommandDispatcherTest {
verify(subCommand).run(any(CommandContext.class));
}
@Test(expected = IllegalArgumentSyntaxException.class)
@Test(expected = CommandException.class)
public void testExecuteInvalidSubcommand() throws Exception {
subject.register(literal("foo").then(
argument("bar", integer())

View file

@ -1,18 +1,22 @@
package net.minecraft.commands.arguments;
import com.google.common.collect.ImmutableMap;
import com.google.common.testing.EqualsTester;
import net.minecraft.commands.context.CommandContext;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.context.ParsedArgument;
import net.minecraft.commands.exceptions.ArgumentValidationException;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import com.google.common.testing.EqualsTester;
import net.minecraft.commands.exceptions.CommandException;
import net.minecraft.commands.exceptions.CommandExceptionType;
import org.junit.Before;
import org.junit.Test;
import java.util.Map;
import static net.minecraft.commands.arguments.IntegerArgumentType.integer;
import static org.hamcrest.Matchers.hasToString;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
public class IntegerArgumentTypeTest {
IntegerArgumentType type;
@ -30,14 +34,26 @@ public class IntegerArgumentTypeTest {
assertThat(result.getResult(), is(50));
}
@Test(expected = IllegalArgumentSyntaxException.class)
@Test
public void testParseInvalid() throws Exception {
type.parse("fifty");
try {
type.parse("fifty");
fail();
} catch (CommandException ex) {
assertThat(ex.getType(), is((CommandExceptionType) IntegerArgumentType.ERROR_NOT_A_NUMBER));
assertThat(ex.getData(), is((Map<String, Object>) ImmutableMap.<String, Object>of("found", "fifty")));
}
}
@Test(expected = ArgumentValidationException.class)
@Test
public void testParseTooLow() throws Exception {
type.parse("-101");
try {
type.parse("-101");
fail();
} catch (CommandException ex) {
assertThat(ex.getType(), is((CommandExceptionType) IntegerArgumentType.ERROR_TOO_SMALL));
assertThat(ex.getData(), is((Map<String, Object>) ImmutableMap.<String, Object>of("found", -101, "minimum", -100)));
}
}
@Test
@ -48,9 +64,15 @@ public class IntegerArgumentTypeTest {
assertThat(result.getResult(), is(-100));
}
@Test(expected = ArgumentValidationException.class)
@Test
public void testParseTooHigh() throws Exception {
type.parse("101");
try {
type.parse("101");
fail();
} catch (CommandException ex) {
assertThat(ex.getType(), is((CommandExceptionType) IntegerArgumentType.ERROR_TOO_BIG));
assertThat(ex.getData(), is((Map<String, Object>) ImmutableMap.<String, Object>of("found", 101, "maximum", 100)));
}
}
@Test

View file

@ -0,0 +1,47 @@
package net.minecraft.commands.exceptions;
import com.google.common.collect.ImmutableMap;
import com.google.common.testing.EqualsTester;
import org.junit.Before;
import org.junit.Test;
import java.util.Map;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
public class ParameterizedCommandExceptionTypeTest {
ParameterizedCommandExceptionType type;
@Before
public void setUp() throws Exception {
type = new ParameterizedCommandExceptionType("foo", "Hello, ${name}!", "name");
}
@Test(expected = IllegalArgumentException.class)
public void testCreateTooFewArguments() throws Exception {
type.create();
}
@Test(expected = IllegalArgumentException.class)
public void testCreateTooManyArguments() throws Exception {
type.create("World", "Universe");
}
@Test
public void testCreate() throws Exception {
CommandException exception = type.create("World");
assertThat(exception.getType(), is((CommandExceptionType) type));
assertThat(exception.getData(), is((Map<String, Object>) ImmutableMap.<String, Object>of("name", "World")));
assertThat(exception.getMessage(), is("Hello, World!"));
}
@Test
public void testEquals() throws Exception {
new EqualsTester()
.addEqualityGroup(new ParameterizedCommandExceptionType("foo", "Hello, world!"), new ParameterizedCommandExceptionType("foo", "Hello, universe!"), new ParameterizedCommandExceptionType("foo", "Hello, world!", "bar"))
.addEqualityGroup(new ParameterizedCommandExceptionType("bar", "Hello, world!"), new ParameterizedCommandExceptionType("bar", "Hello, universe!"), new ParameterizedCommandExceptionType("bar", "Hello, world!", "bar"))
.testEquals();
}
}

View file

@ -0,0 +1,28 @@
package net.minecraft.commands.exceptions;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
public class SimpleCommandExceptionTypeTest {
@Test
public void testCreate() throws Exception {
SimpleCommandExceptionType type = new SimpleCommandExceptionType("foo", "bar");
CommandException exception = type.create();
assertThat(exception.getType(), is((CommandExceptionType) type));
assertThat(exception.getMessage(), is("bar"));
assertThat(exception.getData().values(), empty());
}
@Test
public void testEquals() throws Exception {
new EqualsTester()
.addEqualityGroup(new SimpleCommandExceptionType("foo", "Hello, world!"), new SimpleCommandExceptionType("foo", "Hello, universe!"))
.addEqualityGroup(new SimpleCommandExceptionType("bar", "Hello, world!"), new SimpleCommandExceptionType("bar", "Hello, universe!"))
.testEquals();
}
}

View file

@ -3,7 +3,7 @@ package net.minecraft.commands.tree;
import com.google.common.testing.EqualsTester;
import net.minecraft.commands.Command;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.CommandException;
import org.junit.Before;
import org.junit.Test;
@ -44,7 +44,7 @@ public class ArgumentCommandNodeTest extends AbstractCommandNodeTest {
assertThat(contextBuilder.getArguments().get("foo").getResult(), is((Object) 123));
}
@Test(expected = IllegalArgumentSyntaxException.class)
@Test(expected = CommandException.class)
public void testParseInvalid() throws Exception {
node.parse("foo", contextBuilder);
}

View file

@ -3,7 +3,7 @@ package net.minecraft.commands.tree;
import com.google.common.testing.EqualsTester;
import net.minecraft.commands.Command;
import net.minecraft.commands.context.CommandContextBuilder;
import net.minecraft.commands.exceptions.IllegalArgumentSyntaxException;
import net.minecraft.commands.exceptions.CommandException;
import org.junit.Before;
import org.junit.Test;
@ -37,12 +37,12 @@ public class LiteralCommandNodeTest extends AbstractCommandNodeTest {
assertThat(node.parse("foo", contextBuilder), is(""));
}
@Test(expected = IllegalArgumentSyntaxException.class)
@Test(expected = CommandException.class)
public void testParseSimilar() throws Exception {
node.parse("foobar", contextBuilder);
}
@Test(expected = IllegalArgumentSyntaxException.class)
@Test(expected = CommandException.class)
public void testParseInvalid() throws Exception {
node.parse("bar", contextBuilder);
}