001package co.aikar.commands;
002
003import com.mojang.brigadier.Command;
004import com.mojang.brigadier.arguments.ArgumentType;
005import com.mojang.brigadier.arguments.BoolArgumentType;
006import com.mojang.brigadier.arguments.DoubleArgumentType;
007import com.mojang.brigadier.arguments.FloatArgumentType;
008import com.mojang.brigadier.arguments.IntegerArgumentType;
009import com.mojang.brigadier.arguments.StringArgumentType;
010import com.mojang.brigadier.builder.LiteralArgumentBuilder;
011import com.mojang.brigadier.builder.RequiredArgumentBuilder;
012import com.mojang.brigadier.suggestion.SuggestionProvider;
013import com.mojang.brigadier.tree.CommandNode;
014import com.mojang.brigadier.tree.LiteralCommandNode;
015
016import java.util.HashMap;
017import java.util.Map;
018import java.util.function.BiPredicate;
019import java.util.function.Predicate;
020
021/**
022 * Handles registering of commands into brigadier
023 *
024 * @param <S>
025 * @author MiniDigger
026 * @deprecated Unstable API
027 */
028@Deprecated
029@UnstableAPI
030public class ACFBrigadierManager<S> {
031
032    protected final CommandManager<?, ?, ?, ?, ?, ?> manager;
033
034    private final Map<Class<?>, ArgumentType<?>> arguments = new HashMap<>();
035
036    /**
037     * Constructs a new brigadier manager, utilizing the currently active command manager
038     *
039     * @param manager
040     */
041    ACFBrigadierManager(CommandManager<?, ?, ?, ?, ?, ?> manager) {
042        manager.verifyUnstableAPI("brigadier");
043
044        this.manager = manager;
045
046        // TODO support stuff like min max via brigadier?
047        registerArgument(String.class, StringArgumentType.word());
048        registerArgument(float.class, FloatArgumentType.floatArg());
049        registerArgument(Float.class, FloatArgumentType.floatArg());
050        registerArgument(double.class, DoubleArgumentType.doubleArg());
051        registerArgument(Double.class, DoubleArgumentType.doubleArg());
052        registerArgument(boolean.class, BoolArgumentType.bool());
053        registerArgument(Boolean.class, BoolArgumentType.bool());
054        registerArgument(int.class, IntegerArgumentType.integer());
055        registerArgument(Integer.class, IntegerArgumentType.integer());
056        // We use integer for long due to Bungee bug, plus should really be considered same on client
057        registerArgument(long.class, IntegerArgumentType.integer());
058        registerArgument(Long.class, IntegerArgumentType.integer());
059    }
060
061    <T> void registerArgument(Class<T> clazz, ArgumentType<?> type) {
062        arguments.put(clazz, type);
063    }
064
065    ArgumentType<Object> getArgumentTypeByClazz(CommandParameter param) {
066        if (param.consumesRest) {
067            //noinspection unchecked
068            return (ArgumentType<Object>) (ArgumentType<?>) StringArgumentType.greedyString();
069        }
070        //noinspection unchecked
071        return (ArgumentType<Object>) arguments.getOrDefault(param.getType(), StringArgumentType.string());
072    }
073
074    /**
075     * Registers the given RootCommand into the given brigadir command node, utilizing the provided suggestion provider, executor and permission predicate.<br>
076     * <p>
077     * It recreates the root command node!
078     */
079    LiteralCommandNode<S> register(RootCommand rootCommand,
080                                   LiteralCommandNode<S> root,
081                                   SuggestionProvider<S> suggestionProvider,
082                                   Command<S> executor,
083                                   BiPredicate<RootCommand, S> permCheckerRoot,
084                                   BiPredicate<RegisteredCommand, S> permCheckerSub) {
085        // recreate root to get rid of bukkits default arg
086        LiteralArgumentBuilder<S> rootBuilder = LiteralArgumentBuilder.<S>literal(root.getLiteral())
087                .requires(sender -> permCheckerRoot.test(rootCommand, sender));
088
089        root = rootBuilder.build();
090        boolean isForwardingCommand = rootCommand.getDefCommand() instanceof ForwardingCommand;
091
092        for (Map.Entry<String, RegisteredCommand> subCommand : rootCommand.getSubCommands().entries()) {
093            if ((BaseCommand.isSpecialSubcommand(subCommand.getKey()) && !isForwardingCommand) || (!subCommand.getKey().equals("help") && subCommand.getValue().prefSubCommand.equals("help"))) {
094                // don't register stuff like __catchunknown and don't help command aliases
095                continue;
096            }
097
098            // handle sub sub commands
099            String commandName = subCommand.getKey();
100            CommandNode<S> currentParent = root;
101            CommandNode<S> subCommandNode;
102            Predicate<S> subPermChecker = sender -> permCheckerSub.test(subCommand.getValue(), sender);
103            if (!isForwardingCommand) {
104                if (commandName.contains(" ")) {
105                    String[] split = ACFPatterns.SPACE.split(commandName);
106                    for (int i = 0; i < split.length - 1; i++) {
107                        if (currentParent.getChild(split[i]) == null) {
108                            LiteralCommandNode<S> sub = LiteralArgumentBuilder.<S>literal(split[i])
109                                    .requires(subPermChecker).build();
110                            currentParent.addChild(sub);
111                            currentParent = sub;
112                        } else {
113                            currentParent = currentParent.getChild(split[i]);
114                        }
115                    }
116                    commandName = split[split.length - 1];
117                }
118
119                subCommandNode = currentParent.getChild(commandName);
120                if (subCommandNode == null) {
121                    LiteralArgumentBuilder<S> argumentBuilder = LiteralArgumentBuilder.<S>literal(commandName)
122                            .requires(subPermChecker);
123
124                    // if we have no params, this command is actually executable
125                    if (subCommand.getValue().consumeInputResolvers == 0) {
126                        argumentBuilder.executes(executor);
127                    }
128                    subCommandNode = argumentBuilder.build();
129                }
130            } else {
131                subCommandNode = root;
132            }
133
134            CommandNode<S> paramNode = subCommandNode;
135            CommandParameter[] parameters = subCommand.getValue().parameters;
136            for (int i = 0; i < parameters.length; i++) {
137                CommandParameter param = parameters[i];
138                CommandParameter nextParam = param.getNextParam();
139                if (param.isCommandIssuer() || (param.canExecuteWithoutInput() && nextParam != null && !nextParam.canExecuteWithoutInput())) {
140                    continue;
141                }
142                RequiredArgumentBuilder<S, Object> builder = RequiredArgumentBuilder
143                        .<S, Object>argument(param.getName(), getArgumentTypeByClazz(param))
144                        .suggests(suggestionProvider)
145                        .requires(sender -> permCheckerSub.test(subCommand.getValue(), sender));
146
147                if (nextParam != null && nextParam.canExecuteWithoutInput()) {
148                    builder.executes(executor);
149                }
150
151                CommandNode<S> subSubCommand = builder.build();
152                paramNode.addChild(subSubCommand);
153                paramNode = subSubCommand;
154            }
155
156            if (!isForwardingCommand) {
157                currentParent.addChild(subCommandNode);
158            }
159        }
160
161        return root;
162    }
163
164}