Exposed parse results a little easier. Make no distinction between "no permission" and "no command".

This commit is contained in:
Nathan Adams 2017-06-28 16:34:52 +02:00
parent 523e9d0236
commit 60c5770f5e
5 changed files with 101 additions and 22 deletions

View file

@ -2,6 +2,7 @@ package com.mojang.brigadier;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
@ -14,6 +15,7 @@ import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -21,7 +23,6 @@ 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_IMPERMISSIBLE = new SimpleCommandExceptionType("command.impermissible", "You are not allowed to use this command");
public static final String ARGUMENT_SEPARATOR = " ";
private static final String USAGE_OPTIONAL_OPEN = "[";
@ -43,7 +44,17 @@ public class CommandDispatcher<S> {
}
public int execute(String input, S source) throws CommandException {
CommandContext<S> context = parse(input, source).build();
final ParseResults<S> parse = parse(input, source);
if (parse.getRemaining().length() > 0) {
if (parse.getExceptions().size() == 1) {
throw parse.getExceptions().values().iterator().next();
} else if (parse.getContext().getInput().isEmpty()) {
throw ERROR_UNKNOWN_COMMAND.create();
} else {
throw ERROR_UNKNOWN_ARGUMENT.create(parse.getRemaining());
}
}
CommandContext<S> context = parse.getContext().build();
Command<S> command = context.getCommand();
if (command == null) {
throw ERROR_UNKNOWN_COMMAND.create();
@ -51,17 +62,16 @@ public class CommandDispatcher<S> {
return command.run(context);
}
public CommandContextBuilder<S> parse(String command, S source) throws CommandException {
public ParseResults<S> parse(String command, S source) throws CommandException {
return parseNodes(root, command, new CommandContextBuilder<>(source));
}
private CommandContextBuilder<S> parseNodes(CommandNode<S> node, String command, CommandContextBuilder<S> contextBuilder) throws CommandException {
CommandException exception = null;
private ParseResults<S> parseNodes(CommandNode<S> node, String command, CommandContextBuilder<S> contextBuilder) throws CommandException {
final S source = contextBuilder.getSource();
Map<CommandNode<S>, CommandException> errors = Maps.newHashMap();
for (CommandNode<S> child : node.getChildren()) {
if (!child.canUse(source)) {
exception = ERROR_IMPERMISSIBLE.create();
continue;
}
CommandContextBuilder<S> context = contextBuilder.copy();
@ -69,7 +79,7 @@ public class CommandDispatcher<S> {
try {
remaining = child.parse(command, context);
} catch (CommandException ex) {
exception = ex;
errors.put(child, ex);
continue;
}
@ -77,27 +87,22 @@ public class CommandDispatcher<S> {
context.withCommand(child.getCommand());
}
if (remaining.isEmpty()) {
return context;
return new ParseResults<>(context);
} else {
return parseNodes(child, remaining.substring(1), context);
}
}
if (command.length() > 0) {
if (node == root && (exception == null || exception.getType() != ERROR_IMPERMISSIBLE)) {
throw ERROR_UNKNOWN_COMMAND.create();
}
if (exception != null && node.getChildren().size() == 1) {
throw exception;
}
throw ERROR_UNKNOWN_ARGUMENT.create(command);
}
return contextBuilder;
return new ParseResults<>(contextBuilder, command, errors);
}
public String getUsage(String command, S source) throws CommandException {
CommandContext<S> context = parseNodes(root, command, new CommandContextBuilder<>(source)).build();
final ParseResults<S> parse = parseNodes(root, command, new CommandContextBuilder<>(source));
if (parse.getContext().getNodes().isEmpty()) {
throw ERROR_UNKNOWN_COMMAND.create();
}
CommandContext<S> context = parse.getContext().build();
CommandNode<S> base = Iterables.getLast(context.getNodes().keySet());
List<CommandNode<S>> children = base.getChildren().stream().filter(hasCommand).collect(Collectors.toList());
boolean optional = base.getCommand() != null;

View file

@ -0,0 +1,36 @@
package com.mojang.brigadier;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandException;
import com.mojang.brigadier.tree.CommandNode;
import java.util.Collections;
import java.util.Map;
public class ParseResults<S> {
private final CommandContextBuilder<S> context;
private final String remaining;
private final Map<CommandNode<S>, CommandException> exceptions;
public ParseResults(CommandContextBuilder<S> context, String remaining, Map<CommandNode<S>, CommandException> exceptions) {
this.context = context;
this.remaining = remaining;
this.exceptions = exceptions;
}
public ParseResults(CommandContextBuilder<S> context) {
this(context, "", Collections.emptyMap());
}
public CommandContextBuilder<S> getContext() {
return context;
}
public String getRemaining() {
return remaining;
}
public Map<CommandNode<S>, CommandException> getExceptions() {
return exceptions;
}
}

View file

@ -63,6 +63,10 @@ public class CommandContextBuilder<S> {
return input.toString();
}
public Map<CommandNode<S>, String> getNodes() {
return nodes;
}
public CommandContext<S> build() {
return new CommandContext<>(source, arguments, command, nodes, input.toString());
}

View file

@ -79,7 +79,7 @@ public class CommandDispatcherTest {
subject.execute("foo", source);
fail();
} catch (CommandException ex) {
assertThat(ex.getType(), is(CommandDispatcher.ERROR_IMPERMISSIBLE));
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND));
assertThat(ex.getData(), is(Collections.<String, Object>emptyMap()));
}
}

View file

@ -42,6 +42,40 @@ public class CommandDispatcherUsagesTest {
}
}
@Test
public void testNoCommand() throws Exception {
try {
subject.getUsage("", source);
fail();
} catch (CommandException ex) {
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND));
assertThat(ex.getData(), is(Collections.<String, Object>emptyMap()));
}
}
@Test
public void testUnknownSubcommand() throws Exception {
subject.register(
literal("base").then(
literal("foo").executes(command)
).then(
literal("bar").then(
literal("baz").executes(command)
).then(
literal("qux").then(
literal("not_runnable")
)
).then(
literal("quux").then(
literal("corge").executes(command)
)
).executes(command)
).executes(command)
);
assertThat(subject.getUsage("base unknown", source), hasToString("base [foo|bar]"));
}
@Test
public void testInaccessibleCommand() throws Exception {
subject.register(literal("foo").requires(s -> false));
@ -50,7 +84,7 @@ public class CommandDispatcherUsagesTest {
subject.getUsage("foo", source);
fail();
} catch (CommandException ex) {
assertThat(ex.getType(), is(CommandDispatcher.ERROR_IMPERMISSIBLE));
assertThat(ex.getType(), is(CommandDispatcher.ERROR_UNKNOWN_COMMAND));
assertThat(ex.getData(), is(Collections.<String, Object>emptyMap()));
}
}