    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}