Added simple usage generation
This commit is contained in:
parent
b3703db535
commit
91559191b6
12 changed files with 340 additions and 5 deletions
|
@ -1,14 +1,31 @@
|
|||
package com.mojang.brigadier;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import com.mojang.brigadier.tree.RootCommandNode;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandDispatcher<T> {
|
||||
private static final Predicate<CommandNode> HAS_COMMAND = new Predicate<CommandNode>() {
|
||||
@Override
|
||||
public boolean apply(@Nullable CommandNode input) {
|
||||
return input != null && (input.getCommand() != null || Iterables.any(input.getChildren(), HAS_COMMAND));
|
||||
}
|
||||
};
|
||||
|
||||
public static final SimpleCommandExceptionType ERROR_UNKNOWN_COMMAND = new SimpleCommandExceptionType("unknown_command", "Unknown command");
|
||||
public static final String ARGUMENT_SEPARATOR = " ";
|
||||
|
||||
|
@ -39,13 +56,57 @@ public class CommandDispatcher<T> {
|
|||
}
|
||||
}
|
||||
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
if (command.length() > 0) {
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
throw ERROR_UNKNOWN_COMMAND.create();
|
||||
}
|
||||
|
||||
return contextBuilder.build();
|
||||
}
|
||||
|
||||
public String getUsage(String command, T source) throws CommandException {
|
||||
CommandContext<T> context = parseNodes(root, command, new CommandContextBuilder<T>(source));
|
||||
CommandNode base = Iterables.getLast(context.getNodes().keySet());
|
||||
List<CommandNode> children = Lists.newArrayList(Iterables.filter(base.getChildren(), HAS_COMMAND));
|
||||
boolean optional = base.getCommand() != null;
|
||||
|
||||
if (children.isEmpty()) {
|
||||
return context.getInput();
|
||||
}
|
||||
|
||||
Collections.sort(children, new Comparator<CommandNode>() {
|
||||
@Override
|
||||
public int compare(CommandNode o1, CommandNode o2) {
|
||||
return ComparisonChain.start()
|
||||
.compareTrueFirst(o1 instanceof LiteralCommandNode, o2 instanceof LiteralCommandNode)
|
||||
.result();
|
||||
}
|
||||
});
|
||||
|
||||
StringBuilder result = new StringBuilder(context.getInput());
|
||||
result.append(ARGUMENT_SEPARATOR);
|
||||
if (optional) {
|
||||
result.append("[");
|
||||
} else if (children.size() > 1) {
|
||||
result.append("(");
|
||||
}
|
||||
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
result.append(children.get(i).getUsageText());
|
||||
|
||||
if (i < children.size() - 1) {
|
||||
result.append("|");
|
||||
}
|
||||
}
|
||||
|
||||
if (optional) {
|
||||
result.append("]");
|
||||
} else if (children.size() > 1) {
|
||||
result.append(")");
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,27 @@
|
|||
package com.mojang.brigadier.context;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class CommandContext<T> {
|
||||
private final Joiner JOINER = Joiner.on(CommandDispatcher.ARGUMENT_SEPARATOR);
|
||||
|
||||
private final T source;
|
||||
private final Map<String, ParsedArgument<?>> arguments;
|
||||
private final Command command;
|
||||
private final Map<CommandNode, String> nodes;
|
||||
|
||||
public CommandContext(T source, Map<String, ParsedArgument<?>> arguments, Command command) {
|
||||
public CommandContext(T source, Map<String, ParsedArgument<?>> arguments, Command command, Map<CommandNode, String> nodes) {
|
||||
this.source = source;
|
||||
this.arguments = arguments;
|
||||
this.command = command;
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
public Command getCommand() {
|
||||
|
@ -47,6 +55,7 @@ public class CommandContext<T> {
|
|||
CommandContext that = (CommandContext) o;
|
||||
|
||||
if (!arguments.equals(that.arguments)) return false;
|
||||
if (!Iterables.elementsEqual(nodes.entrySet(), that.nodes.entrySet())) return false;
|
||||
if (command != null ? !command.equals(that.command) : that.command != null) return false;
|
||||
if (!source.equals(that.source)) return false;
|
||||
|
||||
|
@ -58,6 +67,15 @@ public class CommandContext<T> {
|
|||
int result = source.hashCode();
|
||||
result = 31 * result + arguments.hashCode();
|
||||
result = 31 * result + (command != null ? command.hashCode() : 0);
|
||||
result = 31 * result + nodes.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getInput() {
|
||||
return JOINER.join(nodes.values());
|
||||
}
|
||||
|
||||
public Map<CommandNode, String> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,13 @@ package com.mojang.brigadier.context;
|
|||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class CommandContextBuilder<T> {
|
||||
private final Map<String, ParsedArgument<?>> arguments = Maps.newHashMap();
|
||||
private final Map<CommandNode, String> nodes = Maps.newLinkedHashMap();
|
||||
private final T source;
|
||||
private Command command;
|
||||
|
||||
|
@ -28,14 +30,20 @@ public class CommandContextBuilder<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public CommandContextBuilder<T> withNode(CommandNode node, String raw) {
|
||||
this.nodes.put(node, raw);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandContextBuilder<T> copy() {
|
||||
CommandContextBuilder<T> copy = new CommandContextBuilder<T>(source);
|
||||
copy.command = this.command;
|
||||
copy.arguments.putAll(this.arguments);
|
||||
copy.nodes.putAll(this.nodes);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public CommandContext<T> build() {
|
||||
return new CommandContext<T>(source, arguments, command);
|
||||
return new CommandContext<T>(source, arguments, command, nodes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,12 +29,18 @@ public class ArgumentCommandNode<T> extends CommandNode {
|
|||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageText() {
|
||||
return "<" + name + ">";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(String command, CommandContextBuilder<?> contextBuilder) throws CommandException {
|
||||
ParsedArgument<T> parsed = type.parse(command);
|
||||
int start = parsed.getRaw().length();
|
||||
|
||||
contextBuilder.withArgument(name, parsed);
|
||||
contextBuilder.withNode(this, parsed.getRaw());
|
||||
|
||||
if (command.length() > start) {
|
||||
return command.substring(start + 1);
|
||||
|
|
|
@ -59,5 +59,7 @@ public abstract class CommandNode {
|
|||
|
||||
protected abstract Object getMergeKey();
|
||||
|
||||
public abstract String getUsageText();
|
||||
|
||||
public abstract String parse(String command, CommandContextBuilder<?> contextBuilder) throws CommandException;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ public class LiteralCommandNode extends CommandNode {
|
|||
throw ERROR_INCORRECT_LITERAL.create(literal);
|
||||
}
|
||||
|
||||
contextBuilder.withNode(this, literal);
|
||||
int start = expected.length();
|
||||
return command.substring(start);
|
||||
}
|
||||
|
@ -48,6 +49,11 @@ public class LiteralCommandNode extends CommandNode {
|
|||
return super.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageText() {
|
||||
return literal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = literal.hashCode();
|
||||
|
|
|
@ -13,6 +13,11 @@ public class RootCommandNode extends CommandNode {
|
|||
throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(String command, CommandContextBuilder<?> contextBuilder) throws CommandException {
|
||||
return command;
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
package com.mojang.brigadier;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandException;
|
||||
import com.mojang.brigadier.exceptions.CommandExceptionType;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
|
||||
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
|
||||
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 CommandDispatcherUsagesTest {
|
||||
CommandDispatcher<Object> subject;
|
||||
@Mock Object source;
|
||||
@Mock Command command;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
subject = new CommandDispatcher<Object>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnknownCommand() throws Exception {
|
||||
try {
|
||||
subject.getUsage("foo", source);
|
||||
fail();
|
||||
} catch (CommandException ex) {
|
||||
assertThat(ex.getType(), is((CommandExceptionType) CommandDispatcher.ERROR_UNKNOWN_COMMAND));
|
||||
assertThat(ex.getData(), is(Collections.<String, Object>emptyMap()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubcommandUsage() 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 bar", source), hasToString("base bar [baz|quux]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalSingleLiteral() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
literal("foo").executes(command)
|
||||
).executes(command)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base [foo]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoArguments() throws Exception {
|
||||
subject.register(
|
||||
literal("base").executes(command)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiredSingleLiteral() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
literal("foo").executes(command)
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalTwoLiterals() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
literal("foo").executes(command)
|
||||
).then(
|
||||
literal("bar").executes(command)
|
||||
).executes(command)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base [foo|bar]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiredTwoLiterals() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
literal("foo").executes(command)
|
||||
).then(
|
||||
literal("bar").executes(command)
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base (foo|bar)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalOneArgument() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
argument("foo", integer()).executes(command)
|
||||
).executes(command)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base [<foo>]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiredOneArgument() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
argument("foo", integer()).executes(command)
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base <foo>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalTwoArguments() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
argument("foo", integer()).executes(command)
|
||||
).then(
|
||||
argument("bar", integer()).executes(command)
|
||||
).executes(command)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base [<foo>|<bar>]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiredTwoArguments() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
argument("foo", integer()).executes(command)
|
||||
).then(
|
||||
argument("bar", integer()).executes(command)
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base (<foo>|<bar>)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalLiteralOrArgument() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
literal("foo").executes(command)
|
||||
).then(
|
||||
argument("bar", integer()).executes(command)
|
||||
).then(
|
||||
literal("baz").executes(command)
|
||||
).executes(command)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base [foo|baz|<bar>]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiredLiteralOrArgument() throws Exception {
|
||||
subject.register(
|
||||
literal("base").then(
|
||||
literal("foo").executes(command)
|
||||
).then(
|
||||
argument("bar", integer()).executes(command)
|
||||
).then(
|
||||
literal("baz").executes(command)
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(subject.getUsage("base", source), hasToString("base (foo|baz|<bar>)"));
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.mojang.brigadier.context;
|
|||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -9,6 +10,8 @@ import org.mockito.Mock;
|
|||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
|
||||
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
@ -50,12 +53,23 @@ public class CommandContextTest {
|
|||
Object otherSource = new Object();
|
||||
Command command = mock(Command.class);
|
||||
Command otherCommand = mock(Command.class);
|
||||
CommandNode node = mock(CommandNode.class);
|
||||
CommandNode otherNode = mock(CommandNode.class);
|
||||
new EqualsTester()
|
||||
.addEqualityGroup(new CommandContextBuilder<Object>(source).build(), new CommandContextBuilder<Object>(source).build())
|
||||
.addEqualityGroup(new CommandContextBuilder<Object>(otherSource).build(), new CommandContextBuilder<Object>(otherSource).build())
|
||||
.addEqualityGroup(new CommandContextBuilder<Object>(source).withCommand(command).build(), new CommandContextBuilder<Object>(source).withCommand(command).build())
|
||||
.addEqualityGroup(new CommandContextBuilder<Object>(source).withCommand(otherCommand).build(), new CommandContextBuilder<Object>(source).withCommand(otherCommand).build())
|
||||
.addEqualityGroup(new CommandContextBuilder<Object>(source).withArgument("foo", integer().parse("123")).build(), new CommandContextBuilder<Object>(source).withArgument("foo", integer().parse("123")).build())
|
||||
.addEqualityGroup(new CommandContextBuilder<Object>(source).withNode(node, "foo").withNode(otherNode, "bar").build(), new CommandContextBuilder<Object>(source).withNode(node, "foo").withNode(otherNode, "bar").build())
|
||||
.addEqualityGroup(new CommandContextBuilder<Object>(source).withNode(otherNode, "bar").withNode(node, "foo").build(), new CommandContextBuilder<Object>(source).withNode(otherNode, "bar").withNode(node, "foo").build())
|
||||
.testEquals();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInput() throws Exception {
|
||||
CommandContext<Object> context = builder.withNode(literal("foo").build(), "foo").withNode(argument("bar", integer()).build(), "100").withNode(literal("baz").build(), "baz").build();
|
||||
|
||||
assertThat(context.getInput(), is("foo 100 baz"));
|
||||
}
|
||||
}
|
|
@ -61,6 +61,11 @@ public class ArgumentCommandNodeTest extends AbstractCommandNodeTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsage() throws Exception {
|
||||
assertThat(node.getUsageText(), is("<foo>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
Command command = mock(Command.class);
|
||||
|
|
|
@ -64,6 +64,11 @@ public class LiteralCommandNodeTest extends AbstractCommandNodeTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsage() throws Exception {
|
||||
assertThat(node.getUsageText(), is("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
Command command = mock(Command.class);
|
||||
|
|
|
@ -32,6 +32,11 @@ public class RootCommandNodeTest extends AbstractCommandNodeTest {
|
|||
node.addChild(new RootCommandNode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsage() throws Exception {
|
||||
assertThat(node.getUsageText(), is(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
new EqualsTester()
|
||||
|
|
Loading…
Reference in a new issue