diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java index d2c40fd..3205fe6 100644 --- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java +++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java @@ -332,19 +332,33 @@ public class CommandDispatcher { final CommandContextBuilder childContext = new CommandContextBuilder<>(this, source, child.getRedirect(), reader.getCursor()); final ParseResults parse = parseNodes(child.getRedirect(), reader, childContext); context.withChild(parse.getContext()); - return new ParseResults<>(context, parse.getReader(), parse.getExceptions()); + final ParseResults redirect = new ParseResults<>(context, parse.getReader(), parse.getExceptions()); + if (child.canUse(redirect)) { + return redirect; + } } else { final ParseResults parse = parseNodes(child, reader, context); + if (!child.canUse(parse)) { + continue; + } if (potentials == null) { potentials = new ArrayList<>(1); } potentials.add(parse); } } else { + final CommandNode redirect = child.getRedirect(); + if (redirect != null && redirect.getCommand() != null) { + context.withCommand(redirect.getCommand()); + } + final ParseResults parse = new ParseResults<>(context, reader, Collections.emptyMap()); + if (!child.canUse(parse)) { + continue; + } if (potentials == null) { potentials = new ArrayList<>(1); } - potentials.add(new ParseResults<>(context, reader, Collections.emptyMap())); + potentials.add(parse); } } diff --git a/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java b/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java index 899008b..92f351e 100644 --- a/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java +++ b/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java @@ -4,6 +4,7 @@ package com.mojang.brigadier.builder; import com.mojang.brigadier.Command; +import com.mojang.brigadier.ParseResults; import com.mojang.brigadier.RedirectModifier; import com.mojang.brigadier.SingleRedirectModifier; import com.mojang.brigadier.tree.CommandNode; @@ -17,6 +18,7 @@ public abstract class ArgumentBuilder> { private final RootCommandNode arguments = new RootCommandNode<>(); private Command command; private Predicate requirement = s -> true; + private Predicate> contextRequirement = parse -> true; private CommandNode target; private RedirectModifier modifier = null; private boolean forks; @@ -61,6 +63,15 @@ public abstract class ArgumentBuilder> { return requirement; } + public T requiresWithContext(final Predicate> requirement) { + this.contextRequirement = requirement; + return getThis(); + } + + public Predicate> getContextRequirement() { + return contextRequirement; + } + public T redirect(final CommandNode target) { return forward(target, null, false); } diff --git a/src/main/java/com/mojang/brigadier/builder/LiteralArgumentBuilder.java b/src/main/java/com/mojang/brigadier/builder/LiteralArgumentBuilder.java index 779f760..a565760 100644 --- a/src/main/java/com/mojang/brigadier/builder/LiteralArgumentBuilder.java +++ b/src/main/java/com/mojang/brigadier/builder/LiteralArgumentBuilder.java @@ -28,7 +28,7 @@ public class LiteralArgumentBuilder extends ArgumentBuilder build() { - final LiteralCommandNode result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork()); + final LiteralCommandNode result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getContextRequirement(), getRedirect(), getRedirectModifier(), isFork()); for (final CommandNode argument : getArguments()) { result.addChild(argument); diff --git a/src/main/java/com/mojang/brigadier/builder/RequiredArgumentBuilder.java b/src/main/java/com/mojang/brigadier/builder/RequiredArgumentBuilder.java index a09b9e1..18e5d29 100644 --- a/src/main/java/com/mojang/brigadier/builder/RequiredArgumentBuilder.java +++ b/src/main/java/com/mojang/brigadier/builder/RequiredArgumentBuilder.java @@ -45,8 +45,7 @@ public class RequiredArgumentBuilder extends ArgumentBuilder build() { - final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); - + final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getContextRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); for (final CommandNode argument : getArguments()) { result.addChild(argument); } diff --git a/src/main/java/com/mojang/brigadier/context/CommandContext.java b/src/main/java/com/mojang/brigadier/context/CommandContext.java index e291360..0d007b0 100644 --- a/src/main/java/com/mojang/brigadier/context/CommandContext.java +++ b/src/main/java/com/mojang/brigadier/context/CommandContext.java @@ -158,6 +158,10 @@ public class CommandContext { return nodes; } + public Map> getArguments() { + return arguments; + } + public boolean hasNodes() { return !nodes.isEmpty(); } diff --git a/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java b/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java index 3ef555a..171e050 100644 --- a/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java @@ -4,6 +4,7 @@ package com.mojang.brigadier.tree; import com.mojang.brigadier.Command; +import com.mojang.brigadier.ParseResults; import com.mojang.brigadier.RedirectModifier; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; @@ -28,6 +29,13 @@ public class ArgumentCommandNode extends CommandNode { private final ArgumentType type; private final SuggestionProvider customSuggestions; + public ArgumentCommandNode(final String name, final ArgumentType type, final Command command, final Predicate requirement, final Predicate> contextRequirement, final CommandNode redirect, final RedirectModifier modifier, final boolean forks, final SuggestionProvider customSuggestions) { + super(command, requirement, contextRequirement, redirect, modifier, forks); + this.name = name; + this.type = type; + this.customSuggestions = customSuggestions; + } + public ArgumentCommandNode(final String name, final ArgumentType type, final Command command, final Predicate requirement, final CommandNode redirect, final RedirectModifier modifier, final boolean forks, final SuggestionProvider customSuggestions) { super(command, requirement, redirect, modifier, forks); this.name = name; diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java index 47f8e3d..ff5d944 100644 --- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java @@ -5,6 +5,7 @@ package com.mojang.brigadier.tree; import com.mojang.brigadier.AmbiguityConsumer; import com.mojang.brigadier.Command; +import com.mojang.brigadier.ParseResults; import com.mojang.brigadier.RedirectModifier; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.builder.ArgumentBuilder; @@ -28,6 +29,7 @@ public abstract class CommandNode implements Comparable> { private final Map> literals = new LinkedHashMap<>(); private final Map> arguments = new LinkedHashMap<>(); private final Predicate requirement; + private final Predicate> contextRequirement; private final CommandNode redirect; private final RedirectModifier modifier; private final boolean forks; @@ -36,6 +38,16 @@ public abstract class CommandNode implements Comparable> { protected CommandNode(final Command command, final Predicate requirement, final CommandNode redirect, final RedirectModifier modifier, final boolean forks) { this.command = command; this.requirement = requirement; + this.contextRequirement = parse -> true; + this.redirect = redirect; + this.modifier = modifier; + this.forks = forks; + } + + protected CommandNode(final Command command, final Predicate requirement, final Predicate> contextRequirement, final CommandNode redirect, final RedirectModifier modifier, final boolean forks) { + this.command = command; + this.requirement = requirement; + this.contextRequirement = contextRequirement; this.redirect = redirect; this.modifier = modifier; this.forks = forks; @@ -65,6 +77,10 @@ public abstract class CommandNode implements Comparable> { return requirement.test(source); } + public boolean canUse(final ParseResults parse) { + return contextRequirement.test(parse); + } + public void addChild(final CommandNode node) { if (node instanceof RootCommandNode) { throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode"); @@ -138,6 +154,10 @@ public abstract class CommandNode implements Comparable> { return requirement; } + public Predicate> getContextRequirement() { + return contextRequirement; + } + public abstract String getName(); public abstract String getUsageText(); diff --git a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java index 85a8904..71ecfa0 100644 --- a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java @@ -4,6 +4,7 @@ package com.mojang.brigadier.tree; import com.mojang.brigadier.Command; +import com.mojang.brigadier.ParseResults; import com.mojang.brigadier.RedirectModifier; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -30,6 +31,12 @@ public class LiteralCommandNode extends CommandNode { this.literalLowerCase = literal.toLowerCase(Locale.ROOT); } + public LiteralCommandNode(final String literal, final Command command, final Predicate requirement, final Predicate> contextRequirement, final CommandNode redirect, final RedirectModifier modifier, final boolean forks) { + super(command, requirement, contextRequirement, redirect, modifier, forks); + this.literal = literal; + this.literalLowerCase = literal.toLowerCase(Locale.ROOT); + } + public String getLiteral() { return literal; } diff --git a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java index 1cd3f88..e516dfc 100644 --- a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java +++ b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java @@ -10,6 +10,7 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.RootCommandNode; import org.hamcrest.CustomMatcher; @@ -27,23 +28,13 @@ import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger; 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.equalTo; -import static org.hamcrest.Matchers.hasProperty; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class CommandDispatcherTest { @@ -215,6 +206,16 @@ public class CommandDispatcherTest { assertThat(parse.getContext().getNodes().size(), is(1)); } + @Test + public void testParseChildlessRedirect() throws Exception { + final CommandNode target = subject.register(literal("foo").executes(command)); + final CommandNode redirect = subject.register(literal("redirect").redirect(target)); + + final ParseResults parse = subject.parse("redirect", source); + assertThat(parse.getContext().getCommand(), equalTo(target.getCommand())); + assertThat(parse.getContext().getNodes().get(0).getNode(), equalTo(redirect)); + } + @SuppressWarnings("unchecked") @Test public void testExecuteAmbiguiousParentSubcommand() throws Exception {