de.doncarnage.minecraft.common.commandhandler.manager.impl.DefaultCommandManager.java Source code

Java tutorial

Introduction

Here is the source code for de.doncarnage.minecraft.common.commandhandler.manager.impl.DefaultCommandManager.java

Source

/*
 *     BasePlugin
 *   Copyright (C) 2014 Viciouss <http://www.doncarnage.de>
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public
 *   License as published by the Free Software Foundation; either
 *   version 3 of the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 *   USA
 */

package de.doncarnage.minecraft.common.commandhandler.manager.impl;

import de.doncarnage.minecraft.common.commandhandler.manager.*;
import de.doncarnage.minecraft.common.commandhandler.tree.Node;
import de.doncarnage.minecraft.common.commandhandler.tree.NodeException;
import de.doncarnage.minecraft.common.commandhandler.tree.NodeTree;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.CommandTree;
import de.doncarnage.minecraft.common.commandhandler.tree.impl.NodeType;
import net.milkbowl.vault.permission.Permission;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class DefaultCommandManager implements CommandManager {

    private static final String COMMAND_FORMAT = "/e%sr - b%s";
    private static final String HELP_AVAILABLE_COMMANDS = "eAvailable commands:";
    private static final String NO_PERMISSION_TO_USE_ANY = "eYou do not have permission to use any of these commands.";
    private static final String PARAMETER_COULD_NOT_BE_INTERPRETED = "eOne of the parameters could not be interpreted. Please have a look at the usage and try again.";
    private static final String CHECK_SYNTAX = "eAn internal error occured, please check your command and try again. If the command is correct, contact the plugin author.";
    private static final String COMMAND_DOES_NOT_EXIST = "eThis command does not exist.";
    private static final String NO_PERMISSION = "eYou do not have permission to use this command.";
    private static final String ERROR_EXECUTING_COMMAND = "eThere has been an error executing your command. Please report this to the plugin author.";
    private NodeTree<ExtractedCommandBean> nodeTree;
    private Permission perms;

    public DefaultCommandManager(Permission perms) {
        nodeTree = new CommandTree<>();
        this.perms = perms;
    }

    public DefaultCommandManager() {
        this(null);
    }

    @Override
    public void registerCommand(CommandHolder commandHolder) {
        List<ExtractedCommandBean> commandList = extractAllCommands(commandHolder);
        registerCommands(commandList);
    }

    private void registerCommands(List<ExtractedCommandBean> commandList) {
        CommandRegistrationException ex = null;
        for (ExtractedCommandBean command : commandList) {
            try {
                nodeTree.addContent(command);
            } catch (NodeException e) {
                if (ex == null) {
                    ex = new CommandRegistrationException();
                }
                ex.addNewMessage(e.getMessage());
            }
        }
        if (ex != null) {
            throw ex;
        }
    }

    private List<ExtractedCommandBean> extractAllCommands(CommandHolder commandHolder) {
        List<ExtractedCommandBean> commandList = new ArrayList<>();
        for (Method method : commandHolder.getClass().getMethods()) {
            if (method.isAnnotationPresent(ExecutableCommand.class)) {
                checkForRightMethodFormat(commandHolder, method);
                ExtractedCommandBean ecb = new ExtractedCommandBean();
                ExecutableCommand ec = method.getAnnotation(ExecutableCommand.class);
                ecb.setCmd(ec.cmd());
                ecb.setDescription(ec.desc());
                ecb.setParams(ec.params());
                ecb.setPermissions(ec.permission());
                ecb.setExecutable(method);
                ecb.setInstance(commandHolder);
                if (validate(ecb)) {
                    commandList.add(ecb);
                }
            }
        }
        return commandList;
    }

    private void checkForRightMethodFormat(CommandHolder commandHolder, Method method) {
        List<Class<?>> params = Arrays.asList(method.getParameterTypes());
        if (params.size() != 2 || !params.contains(CommandSender.class) || !params.contains(CommandContext.class)) {
            String paramString = buildParamString(params);
            throw new IllegalStateException(String.format(
                    "The method %s in class %s does not have the needed arguments. Current arguments (%d): %s",
                    method.getName(), commandHolder.getClass().getName(), params.size(), paramString));
        }
    }

    private String buildParamString(List<Class<?>> params) {
        StringBuilder sb = new StringBuilder();
        for (Class<?> param : params) {
            if (sb.length() > 0)
                sb.append(", ");
            sb.append(param.getName());
        }
        return sb.toString();
    }

    private boolean validate(ExtractedCommandBean ecb) {
        return !StringUtils.isEmpty(ecb.getCmd());
    }

    @Override
    public boolean executeCommandForString(CommandSender sender, Command command, String label, String[] args) {
        try {
            String commandString = buildCommandString(command, args);
            Node<ExtractedCommandBean> cbNode = nodeTree.resolveContentNode(commandString);
            ExtractedCommandBean extractedCommandBean = cbNode.getContent();
            if (extractedCommandBean == null) {
                sendHelpToPlayer(sender, cbNode, label);
                return true;
            }
            if (!hasPermissions(sender, extractedCommandBean.getPermissions())) {
                sender.sendMessage(NO_PERMISSION);
                return true;
            }
            Method executable = extractedCommandBean.getExecutable();
            try {
                CommandContext context = createContext(extractedCommandBean, command, label, args);
                executable.invoke(extractedCommandBean.getInstance(), sender, context);
                return true;
            } catch (IllegalAccessException e) {
                sendMessageToPlayer(sender, ERROR_EXECUTING_COMMAND);
                return true;
            } catch (IllegalArgumentException e) {
                sendMessageToPlayer(sender, ERROR_EXECUTING_COMMAND);
                return true;
            } catch (InvocationTargetException e) {
                if (e.getTargetException().getClass().equals(ParameterParsingException.class)) {
                    sender.sendMessage(PARAMETER_COULD_NOT_BE_INTERPRETED);
                    sendHelpToPlayer(sender, cbNode, label);
                } else {
                    sender.getServer().getLogger().warning(
                            String.format("An error occurred while executing the command %s", commandString));
                    e.printStackTrace();
                    sendMessageToPlayer(sender, CHECK_SYNTAX);
                }
                return true;
            }
        } catch (NodeException e) {
            sendMessageToPlayer(sender, COMMAND_DOES_NOT_EXIST);
            return true;
        }
    }

    private String buildCommandString(Command command, String[] args) {
        return String.format("%s %s", command.getName(), StringUtils.join(args, " "));
    }

    private void sendHelpToPlayer(CommandSender sender, Node<ExtractedCommandBean> node, String label) {
        List<String> helpList = new ArrayList<>();
        helpList.add(HELP_AVAILABLE_COMMANDS);
        recursiveAddHelp(sender, node, helpList);
        if (helpList.size() == 1) {
            sender.sendMessage(NO_PERMISSION_TO_USE_ANY);
            return;
        }
        for (String line : helpList) {
            sender.sendMessage(String.format(line, label));
        }
    }

    private void recursiveAddHelp(CommandSender sender, Node<ExtractedCommandBean> node, List<String> helpList) {
        if (node == null)
            return;
        ExtractedCommandBean bean = node.getContent();
        if (bean != null && !StringUtils.isEmpty(bean.getDescription())
                && hasPermissions(sender, bean.getPermissions())) {
            helpList.add(String.format(COMMAND_FORMAT, beautifyCommand(bean.getPathStringWithCustomCommand("%s")),
                    bean.getDescription()));
        }
        for (Node<ExtractedCommandBean> child : node.getAllChildren()) {
            recursiveAddHelp(sender, child, helpList);
        }
    }

    private String beautifyCommand(String pathString) {
        String[] splitted = pathString.split(" ");
        for (int i = 0; i < splitted.length; i++) {
            if (splitted[i].startsWith(":")) {
                splitted[i] = splitted[i].replace(":", "<").concat(">");
            } else if (splitted[i].startsWith("_")) {
                splitted[i] = splitted[i].replace("_", "[").concat("]");
            } else if (splitted[i].startsWith("*")) {
                splitted[i] = splitted[i].replace("*", "[").concat("] ...");
            } else if (splitted[i].startsWith("+")) {
                splitted[i] = splitted[i].replace("+", "<").concat("> ...");
            }
        }
        return StringUtils.join(splitted, " ");
    }

    private boolean hasPermissions(CommandSender sender, String[] permissions) {

        boolean permsPluginActive = perms != null;

        for (String permission : permissions) {
            boolean permsByPlugin = permsPluginActive && perms.has(sender, permission);
            boolean permsByBukkit = sender.hasPermission(permission);
            if (!StringUtils.isEmpty(permission) && !permsByPlugin && !permsByBukkit) {
                return false;
            }
        }
        return true;
    }

    private void sendMessageToPlayer(CommandSender sender, String string) {
        sender.sendMessage(string);
    }

    CommandContext createContext(ExtractedCommandBean ret, Command command, String label, String[] args) {
        DefaultCommandContext cc = new DefaultCommandContext(command, label);
        String[] commandPath = StringUtils.split(ret.getPathString(), " ");
        for (int i = 0; i < args.length; i++) {
            // if i is bigger then the command path length it's a repeatable node, add all of them
            if (i + 1 >= commandPath.length || NodeType.isParamNodeName(commandPath[i + 1])) {
                cc.addParameter(args[i]);
            }
        }
        return cc;
    }

    @Override
    public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
        Node<ExtractedCommandBean> output = nodeTree.getLastNodeOnPath(buildCommandString(command, args));
        int numberOfResolvedArgs = output.getNodeDepth() - 1;
        String nextCommand;
        if (args.length < numberOfResolvedArgs) {
            return Collections.emptyList();
        } else if (args.length == numberOfResolvedArgs) {
            nextCommand = args[numberOfResolvedArgs - 1];
            output = output.getParent();
        } else {
            nextCommand = args[numberOfResolvedArgs];
        }
        return getListOfMatchingNodes(output, nextCommand);
    }

    private List<String> getListOfMatchingNodes(Node<ExtractedCommandBean> output, String nextCommand) {
        List<Node<ExtractedCommandBean>> children = output.getAllChildren();
        if (children.size() > 0 && !children.get(0).isParam()) {
            List<String> options = new ArrayList<>();
            for (Node<ExtractedCommandBean> child : children) {
                if (child.getName().length() > nextCommand.length()
                        && child.getName().substring(0, nextCommand.length()).equalsIgnoreCase(nextCommand)) {
                    options.add(child.getName());
                }
            }
            return options;
        }
        return Collections.emptyList();
    }

}