001/*
002 * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
003 *
004 *  Permission is hereby granted, free of charge, to any person obtaining
005 *  a copy of this software and associated documentation files (the
006 *  "Software"), to deal in the Software without restriction, including
007 *  without limitation the rights to use, copy, modify, merge, publish,
008 *  distribute, sublicense, and/or sell copies of the Software, and to
009 *  permit persons to whom the Software is furnished to do so, subject to
010 *  the following conditions:
011 *
012 *  The above copyright notice and this permission notice shall be
013 *  included in all copies or substantial portions of the Software.
014 *
015 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
016 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
017 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
018 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
019 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
020 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
021 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
022 */
023
024package co.aikar.commands;
025
026import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
027import com.google.common.collect.SetMultimap;
028
029import java.util.ArrayList;
030import java.util.HashSet;
031import java.util.List;
032import java.util.Set;
033
034import static co.aikar.commands.BaseCommand.CATCHUNKNOWN;
035import static co.aikar.commands.BaseCommand.DEFAULT;
036
037public interface RootCommand {
038    void addChild(BaseCommand command);
039
040    CommandManager getManager();
041
042    SetMultimap<String, RegisteredCommand> getSubCommands();
043
044    List<BaseCommand> getChildren();
045
046    String getCommandName();
047
048    default void addChildShared(List<BaseCommand> children, SetMultimap<String, RegisteredCommand> subCommands, BaseCommand command) {
049        command.subCommands.entries().forEach(e -> {
050            String key = e.getKey();
051            RegisteredCommand registeredCommand = e.getValue();
052            if (key.equals(DEFAULT) || key.equals(BaseCommand.CATCHUNKNOWN)) {
053                return;
054            }
055            Set<RegisteredCommand> registered = subCommands.get(key);
056            if (!registered.isEmpty()) {
057                BaseCommand prevBase = registered.iterator().next().scope;
058                if (prevBase != registeredCommand.scope) {
059                    this.getManager().log(LogLevel.ERROR, "ACF Error: " + command.getName() + " registered subcommand " + key + " for root command " + getCommandName() + " - but it is already defined in " + prevBase.getName());
060                    this.getManager().log(LogLevel.ERROR, "2 subcommands of the same prefix may not be spread over 2 different classes. Ignoring this.");
061                    return;
062                }
063            }
064            subCommands.put(key, registeredCommand);
065        });
066
067        children.add(command);
068    }
069
070    /**
071     * @return If this root command can be summarized to a single required permission node to use it, returns that value. If any RegisteredCommand is permission-less, or has multiple required permission nodes, null is returned.
072     */
073    default String getUniquePermission() {
074        Set<String> permissions = new HashSet<>();
075        for (BaseCommand child : getChildren()) {
076            for (RegisteredCommand<?> value : child.subCommands.values()) {
077                Set<String> requiredPermissions = value.getRequiredPermissions();
078                if (requiredPermissions.isEmpty()) {
079                    return null;
080                } else {
081                    permissions.addAll(requiredPermissions);
082                }
083            }
084        }
085        return permissions.size() == 1 ? permissions.iterator().next() : null;
086    }
087
088    default boolean hasAnyPermission(CommandIssuer issuer) {
089        List<BaseCommand> children = getChildren();
090        if (children.isEmpty()) {
091            return true;
092        }
093
094        for (BaseCommand child : children) {
095            if (!child.hasPermission(issuer)) {
096                continue;
097            }
098            for (RegisteredCommand value : child.getRegisteredCommands()) {
099                if (value.hasPermission(issuer)) {
100                    return true;
101                }
102            }
103        }
104        return false;
105    }
106
107    default BaseCommand execute(CommandIssuer sender, String commandLabel, String[] args) {
108        BaseCommand command = getBaseCommand(args);
109
110        command.execute(sender, commandLabel, args);
111        return command;
112    }
113
114    default BaseCommand getBaseCommand(String[] args) {
115        SetMultimap<String, RegisteredCommand> subCommands = getSubCommands();
116        RegisteredCommand command;
117        for (int i = args.length; i >= 0; i--) {
118            String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase();
119            command = ACFUtil.getFirstElement(subCommands.get(checkSub));
120            if (command != null) {
121                return command.scope;
122            }
123        }
124
125        command = ACFUtil.getFirstElement(subCommands.get(DEFAULT));
126        if (command != null) {
127            if (args.length == 0 || command.consumeInputResolvers > 0) {
128                return command.scope;
129            }
130        }
131
132        command = ACFUtil.getFirstElement(subCommands.get(CATCHUNKNOWN));
133        if (command != null) {
134            return command.scope;
135        }
136        return getDefCommand();
137    }
138
139    default List<String> getTabCompletions(CommandIssuer sender, String alias, String[] args) {
140        return getTabCompletions(sender, alias, args, false);
141    }
142
143    default List<String> getTabCompletions(CommandIssuer sender, String alias, String[] args, boolean commandsOnly) {
144        return getTabCompletions(sender, alias, args, commandsOnly, false);
145    }
146
147    default List<String> getTabCompletions(CommandIssuer sender, String alias, String[] args, boolean commandsOnly, boolean isAsync) {
148        Set<String> completions = new HashSet<>();
149        getChildren().forEach(child -> {
150            if (!commandsOnly) {
151                completions.addAll(child.tabComplete(sender, alias, args, isAsync));
152            }
153            completions.addAll(child.getCommandsForCompletion(sender, args));
154        });
155        return new ArrayList<>(completions);
156    }
157
158
159    default RegisteredCommand getDefaultRegisteredCommand() {
160        BaseCommand defCommand = this.getDefCommand();
161        if (defCommand != null) {
162            return defCommand.getDefaultRegisteredCommand();
163        }
164        return null;
165    }
166
167    default BaseCommand getDefCommand() {
168        return null;
169    }
170
171
172    default String getDescription() {
173        final RegisteredCommand cmd = this.getDefaultRegisteredCommand();
174        if (cmd != null) {
175            return cmd.getHelpText();
176        }
177        BaseCommand defCommand = getDefCommand();
178        if (defCommand != null && defCommand.description != null) {
179            return defCommand.description;
180        }
181        return "";
182    }
183
184
185    default String getUsage() {
186        final RegisteredCommand cmd = this.getDefaultRegisteredCommand();
187        if (cmd != null) {
188            return cmd.syntaxText != null ? cmd.syntaxText : "";
189        }
190        return "";
191    }
192}