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 implements Comparable> { private Map> children = Maps.newLinkedHashMap(); private Map> literals = Maps.newLinkedHashMap(); private Map> arguments = Maps.newLinkedHashMap(); private final Predicate requirement; private final CommandNode redirect; private final RedirectModifier modifier; private final boolean forks; private Command command; // CraftBukkit start public void removeCommand(String name) { children.remove(name); literals.remove(name); arguments.remove(name); } // CraftBukkit end protected CommandNode(final Command command, final Predicate requirement, final CommandNode redirect, final RedirectModifier modifier, final boolean forks) { this.command = command; this.requirement = requirement; this.redirect = redirect; this.modifier = modifier; this.forks = forks; } public Command getCommand() { return command; } public Collection> getChildren() { return children.values(); } public CommandNode getChild(final String name) { return children.get(name); } public CommandNode getRedirect() { return redirect; } public RedirectModifier 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 node) { if (node instanceof RootCommandNode) { throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode"); } final CommandNode 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 grandchild : node.getChildren()) { child.addChild(grandchild); } } else { children.put(node.getName(), node); if (node instanceof LiteralCommandNode) { literals.put(node.getName(), (LiteralCommandNode) node); } else if (node instanceof ArgumentCommandNode) { arguments.put(node.getName(), (ArgumentCommandNode) 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 consumer) { Set matches = Sets.newHashSet(); for (final CommandNode child : children.values()) { for (final CommandNode 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 that = (CommandNode) 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 getRequirement() { return requirement; } public abstract String getName(); public abstract String getUsageText(); public abstract void parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandSyntaxException; public abstract CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException; public abstract ArgumentBuilder createBuilder(); protected abstract String getSortedKey(); public Collection> 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 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 o) { return ComparisonChain .start() .compareTrueFirst(this instanceof LiteralCommandNode, o instanceof LiteralCommandNode) .compare(getSortedKey(), o.getSortedKey()) .result(); } public boolean isFork() { return forks; } public abstract Collection getExamples(); }