208 lines
7.0 KiB
Java
208 lines
7.0 KiB
Java
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;
|
|
// CraftBukkit start
|
|
public void removeCommand(String name) {
|
|
children.remove(name);
|
|
literals.remove(name);
|
|
arguments.remove(name);
|
|
}
|
|
// CraftBukkit end
|
|
|
|
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();
|
|
}
|