summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/mojang/brigadier/tree/CommandNode.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/mojang/brigadier/tree/CommandNode.java')
-rw-r--r--src/main/java/com/mojang/brigadier/tree/CommandNode.java200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
new file mode 100644
index 00000000..1196df6b
--- /dev/null
+++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
@@ -0,0 +1,200 @@
+package com.mojang.brigadier.tree;
+
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.mojang.brigadier.AmbiguityConsumer;
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.RedirectModifier;
+import com.mojang.brigadier.StringReader;
+import com.mojang.brigadier.builder.ArgumentBuilder;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.context.CommandContextBuilder;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.suggestion.Suggestions;
+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import net.minecraft.server.CommandListenerWrapper; // CraftBukkit
+
+public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
+ private Map<String, CommandNode<S>> children = Maps.newLinkedHashMap();
+ private Map<String, LiteralCommandNode<S>> literals = Maps.newLinkedHashMap();
+ private Map<String, ArgumentCommandNode<S, ?>> arguments = Maps.newLinkedHashMap();
+ private final Predicate<S> requirement;
+ private final CommandNode<S> redirect;
+ private final RedirectModifier<S> modifier;
+ private final boolean forks;
+ private Command<S> command;
+
+ protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
+ this.command = command;
+ this.requirement = requirement;
+ this.redirect = redirect;
+ this.modifier = modifier;
+ this.forks = forks;
+ }
+
+ public Command<S> getCommand() {
+ return command;
+ }
+
+ public Collection<CommandNode<S>> getChildren() {
+ return children.values();
+ }
+
+ public CommandNode<S> getChild(final String name) {
+ return children.get(name);
+ }
+
+ public CommandNode<S> getRedirect() {
+ return redirect;
+ }
+
+ public RedirectModifier<S> getRedirectModifier() {
+ return modifier;
+ }
+
+ public boolean canUse(final S source) {
+ // CraftBukkit start
+ if (source instanceof CommandListenerWrapper) {
+ try {
+ ((CommandListenerWrapper) source).currentCommand = this;
+ return requirement.test(source);
+ } finally {
+ ((CommandListenerWrapper) source).currentCommand = null;
+ }
+ }
+ // CraftBukkit end
+ return requirement.test(source);
+ }
+
+ public void addChild(final CommandNode<S> node) {
+ if (node instanceof RootCommandNode) {
+ throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode");
+ }
+
+ final CommandNode<S> child = children.get(node.getName());
+ if (child != null) {
+ // We've found something to merge onto
+ if (node.getCommand() != null) {
+ child.command = node.getCommand();
+ }
+ for (final CommandNode<S> grandchild : node.getChildren()) {
+ child.addChild(grandchild);
+ }
+ } else {
+ children.put(node.getName(), node);
+ if (node instanceof LiteralCommandNode) {
+ literals.put(node.getName(), (LiteralCommandNode<S>) node);
+ } else if (node instanceof ArgumentCommandNode) {
+ arguments.put(node.getName(), (ArgumentCommandNode<S, ?>) node);
+ }
+ }
+
+ children = children.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
+ }
+
+ public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
+ Set<String> matches = Sets.newHashSet();
+
+ for (final CommandNode<S> child : children.values()) {
+ for (final CommandNode<S> sibling : children.values()) {
+ if (child == sibling) {
+ continue;
+ }
+
+ for (final String input : child.getExamples()) {
+ if (sibling.isValidInput(input)) {
+ matches.add(input);
+ }
+ }
+
+ if (matches.size() > 0) {
+ consumer.ambiguous(this, child, sibling, matches);
+ matches = Sets.newHashSet();
+ }
+ }
+
+ child.findAmbiguities(consumer);
+ }
+ }
+
+ protected abstract boolean isValidInput(final String input);
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CommandNode)) return false;
+
+ final CommandNode<S> that = (CommandNode<S>) o;
+
+ if (!children.equals(that.children)) return false;
+ if (command != null ? !command.equals(that.command) : that.command != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * children.hashCode() + (command != null ? command.hashCode() : 0);
+ }
+
+ public Predicate<S> getRequirement() {
+ return requirement;
+ }
+
+ public abstract String getName();
+
+ public abstract String getUsageText();
+
+ public abstract void parse(StringReader reader, CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException;
+
+ public abstract CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) throws CommandSyntaxException;
+
+ public abstract ArgumentBuilder<S, ?> createBuilder();
+
+ protected abstract String getSortedKey();
+
+ public Collection<? extends CommandNode<S>> getRelevantNodes(final StringReader input) {
+ if (literals.size() > 0) {
+ final int cursor = input.getCursor();
+ while (input.canRead() && input.peek() != ' ') {
+ input.skip();
+ }
+ final String text = input.getString().substring(cursor, input.getCursor());
+ input.setCursor(cursor);
+ final LiteralCommandNode<S> literal = literals.get(text);
+ if (literal != null) {
+ return Collections.singleton(literal);
+ } else {
+ return arguments.values();
+ }
+ } else {
+ return arguments.values();
+ }
+ }
+
+ @Override
+ public int compareTo(final CommandNode<S> o) {
+ return ComparisonChain
+ .start()
+ .compareTrueFirst(this instanceof LiteralCommandNode, o instanceof LiteralCommandNode)
+ .compare(getSortedKey(), o.getSortedKey())
+ .result();
+ }
+
+ public boolean isFork() {
+ return forks;
+ }
+
+ public abstract Collection<String> getExamples();
+}