Skip to content

Commit 48ab1ab

Browse files
committed
Context-aware CommandNode#canUse method and Fix childless redirects
issue Mojang#46 fix PaperMC#1 PaperMC#2
1 parent 4c8d2ab commit 48ab1ab

9 files changed

+81
-17
lines changed

src/main/java/com/mojang/brigadier/CommandDispatcher.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -332,19 +332,33 @@ private ParseResults<S> parseNodes(final CommandNode<S> node, final StringReader
332332
final CommandContextBuilder<S> childContext = new CommandContextBuilder<>(this, source, child.getRedirect(), reader.getCursor());
333333
final ParseResults<S> parse = parseNodes(child.getRedirect(), reader, childContext);
334334
context.withChild(parse.getContext());
335-
return new ParseResults<>(context, parse.getReader(), parse.getExceptions());
335+
final ParseResults<S> redirect = new ParseResults<>(context, parse.getReader(), parse.getExceptions());
336+
if (child.canUse(redirect)) {
337+
return redirect;
338+
}
336339
} else {
337340
final ParseResults<S> parse = parseNodes(child, reader, context);
341+
if (!child.canUse(parse)) {
342+
continue;
343+
}
338344
if (potentials == null) {
339345
potentials = new ArrayList<>(1);
340346
}
341347
potentials.add(parse);
342348
}
343349
} else {
350+
final CommandNode<S> redirect = child.getRedirect();
351+
if (redirect != null && redirect.getCommand() != null) {
352+
context.withCommand(redirect.getCommand());
353+
}
354+
final ParseResults<S> parse = new ParseResults<>(context, reader, Collections.emptyMap());
355+
if (!child.canUse(parse)) {
356+
continue;
357+
}
344358
if (potentials == null) {
345359
potentials = new ArrayList<>(1);
346360
}
347-
potentials.add(new ParseResults<>(context, reader, Collections.emptyMap()));
361+
potentials.add(parse);
348362
}
349363
}
350364

src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java

+11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package com.mojang.brigadier.builder;
55

66
import com.mojang.brigadier.Command;
7+
import com.mojang.brigadier.ParseResults;
78
import com.mojang.brigadier.RedirectModifier;
89
import com.mojang.brigadier.SingleRedirectModifier;
910
import com.mojang.brigadier.tree.CommandNode;
@@ -17,6 +18,7 @@ public abstract class ArgumentBuilder<S, T extends ArgumentBuilder<S, T>> {
1718
private final RootCommandNode<S> arguments = new RootCommandNode<>();
1819
private Command<S> command;
1920
private Predicate<S> requirement = s -> true;
21+
private Predicate<ParseResults<S>> contextRequirement = parse -> true;
2022
private CommandNode<S> target;
2123
private RedirectModifier<S> modifier = null;
2224
private boolean forks;
@@ -61,6 +63,15 @@ public Predicate<S> getRequirement() {
6163
return requirement;
6264
}
6365

66+
public T requiresWithContext(final Predicate<ParseResults<S>> requirement) {
67+
this.contextRequirement = requirement;
68+
return getThis();
69+
}
70+
71+
public Predicate<ParseResults<S>> getContextRequirement() {
72+
return contextRequirement;
73+
}
74+
6475
public T redirect(final CommandNode<S> target) {
6576
return forward(target, null, false);
6677
}

src/main/java/com/mojang/brigadier/builder/LiteralArgumentBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public String getLiteral() {
2828

2929
@Override
3030
public LiteralCommandNode<S> build() {
31-
final LiteralCommandNode<S> result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork());
31+
final LiteralCommandNode<S> result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getContextRequirement(), getRedirect(), getRedirectModifier(), isFork());
3232

3333
for (final CommandNode<S> argument : getArguments()) {
3434
result.addChild(argument);

src/main/java/com/mojang/brigadier/builder/RequiredArgumentBuilder.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ public String getName() {
4545
}
4646

4747
public ArgumentCommandNode<S, T> build() {
48-
final ArgumentCommandNode<S, T> result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider());
49-
48+
final ArgumentCommandNode<S, T> result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getContextRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider());
5049
for (final CommandNode<S> argument : getArguments()) {
5150
result.addChild(argument);
5251
}

src/main/java/com/mojang/brigadier/context/CommandContext.java

+4
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ public List<ParsedCommandNode<S>> getNodes() {
158158
return nodes;
159159
}
160160

161+
public Map<String, ParsedArgument<S, ?>> getArguments() {
162+
return arguments;
163+
}
164+
161165
public boolean hasNodes() {
162166
return !nodes.isEmpty();
163167
}

src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package com.mojang.brigadier.tree;
55

66
import com.mojang.brigadier.Command;
7+
import com.mojang.brigadier.ParseResults;
78
import com.mojang.brigadier.RedirectModifier;
89
import com.mojang.brigadier.StringReader;
910
import com.mojang.brigadier.arguments.ArgumentType;
@@ -28,6 +29,13 @@ public class ArgumentCommandNode<S, T> extends CommandNode<S> {
2829
private final ArgumentType<T> type;
2930
private final SuggestionProvider<S> customSuggestions;
3031

32+
public ArgumentCommandNode(final String name, final ArgumentType<T> type, final Command<S> command, final Predicate<S> requirement, final Predicate<ParseResults<S>> contextRequirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final SuggestionProvider<S> customSuggestions) {
33+
super(command, requirement, contextRequirement, redirect, modifier, forks);
34+
this.name = name;
35+
this.type = type;
36+
this.customSuggestions = customSuggestions;
37+
}
38+
3139
public ArgumentCommandNode(final String name, final ArgumentType<T> type, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final SuggestionProvider<S> customSuggestions) {
3240
super(command, requirement, redirect, modifier, forks);
3341
this.name = name;

src/main/java/com/mojang/brigadier/tree/CommandNode.java

+20
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import com.mojang.brigadier.AmbiguityConsumer;
77
import com.mojang.brigadier.Command;
8+
import com.mojang.brigadier.ParseResults;
89
import com.mojang.brigadier.RedirectModifier;
910
import com.mojang.brigadier.StringReader;
1011
import com.mojang.brigadier.builder.ArgumentBuilder;
@@ -28,6 +29,7 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
2829
private final Map<String, LiteralCommandNode<S>> literals = new LinkedHashMap<>();
2930
private final Map<String, ArgumentCommandNode<S, ?>> arguments = new LinkedHashMap<>();
3031
private final Predicate<S> requirement;
32+
private final Predicate<ParseResults<S>> contextRequirement;
3133
private final CommandNode<S> redirect;
3234
private final RedirectModifier<S> modifier;
3335
private final boolean forks;
@@ -36,6 +38,16 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
3638
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
3739
this.command = command;
3840
this.requirement = requirement;
41+
this.contextRequirement = parse -> true;
42+
this.redirect = redirect;
43+
this.modifier = modifier;
44+
this.forks = forks;
45+
}
46+
47+
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final Predicate<ParseResults<S>> contextRequirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
48+
this.command = command;
49+
this.requirement = requirement;
50+
this.contextRequirement = contextRequirement;
3951
this.redirect = redirect;
4052
this.modifier = modifier;
4153
this.forks = forks;
@@ -65,6 +77,10 @@ public boolean canUse(final S source) {
6577
return requirement.test(source);
6678
}
6779

80+
public boolean canUse(final ParseResults<S> parse) {
81+
return contextRequirement.test(parse);
82+
}
83+
6884
public void addChild(final CommandNode<S> node) {
6985
if (node instanceof RootCommandNode) {
7086
throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode");
@@ -138,6 +154,10 @@ public Predicate<S> getRequirement() {
138154
return requirement;
139155
}
140156

157+
public Predicate<ParseResults<S>> getContextRequirement() {
158+
return contextRequirement;
159+
}
160+
141161
public abstract String getName();
142162

143163
public abstract String getUsageText();

src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package com.mojang.brigadier.tree;
55

66
import com.mojang.brigadier.Command;
7+
import com.mojang.brigadier.ParseResults;
78
import com.mojang.brigadier.RedirectModifier;
89
import com.mojang.brigadier.StringReader;
910
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@@ -30,6 +31,12 @@ public LiteralCommandNode(final String literal, final Command<S> command, final
3031
this.literalLowerCase = literal.toLowerCase(Locale.ROOT);
3132
}
3233

34+
public LiteralCommandNode(final String literal, final Command<S> command, final Predicate<S> requirement, final Predicate<ParseResults<S>> contextRequirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
35+
super(command, requirement, contextRequirement, redirect, modifier, forks);
36+
this.literal = literal;
37+
this.literalLowerCase = literal.toLowerCase(Locale.ROOT);
38+
}
39+
3340
public String getLiteral() {
3441
return literal;
3542
}

src/test/java/com/mojang/brigadier/CommandDispatcherTest.java

+13-12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.mojang.brigadier.context.CommandContext;
1111
import com.mojang.brigadier.context.CommandContextBuilder;
1212
import com.mojang.brigadier.exceptions.CommandSyntaxException;
13+
import com.mojang.brigadier.tree.CommandNode;
1314
import com.mojang.brigadier.tree.LiteralCommandNode;
1415
import com.mojang.brigadier.tree.RootCommandNode;
1516
import org.hamcrest.CustomMatcher;
@@ -27,23 +28,13 @@
2728
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
2829
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
2930
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
30-
import static org.hamcrest.Matchers.equalTo;
31-
import static org.hamcrest.Matchers.hasProperty;
32-
import static org.hamcrest.Matchers.is;
33-
import static org.hamcrest.Matchers.notNullValue;
34-
import static org.hamcrest.Matchers.nullValue;
31+
import static org.hamcrest.Matchers.*;
3532
import static org.junit.Assert.assertThat;
3633
import static org.junit.Assert.fail;
3734
import static org.mockito.Matchers.argThat;
3835
import static org.mockito.Matchers.eq;
3936
import static org.mockito.Mockito.any;
40-
import static org.mockito.Mockito.mock;
41-
import static org.mockito.Mockito.never;
42-
import static org.mockito.Mockito.times;
43-
import static org.mockito.Mockito.verify;
44-
import static org.mockito.Mockito.verifyNoMoreInteractions;
45-
import static org.mockito.Mockito.verifyZeroInteractions;
46-
import static org.mockito.Mockito.when;
37+
import static org.mockito.Mockito.*;
4738

4839
@RunWith(MockitoJUnitRunner.class)
4940
public class CommandDispatcherTest {
@@ -215,6 +206,16 @@ public void testParseIncompleteArgument() {
215206
assertThat(parse.getContext().getNodes().size(), is(1));
216207
}
217208

209+
@Test
210+
public void testParseChildlessRedirect() throws Exception {
211+
final CommandNode<Object> target = subject.register(literal("foo").executes(command));
212+
final CommandNode<Object> redirect = subject.register(literal("redirect").redirect(target));
213+
214+
final ParseResults<Object> parse = subject.parse("redirect", source);
215+
assertThat(parse.getContext().getCommand(), equalTo(target.getCommand()));
216+
assertThat(parse.getContext().getNodes().get(0).getNode(), equalTo(redirect));
217+
}
218+
218219
@SuppressWarnings("unchecked")
219220
@Test
220221
public void testExecuteAmbiguiousParentSubcommand() throws Exception {

0 commit comments

Comments
 (0)