de.matzefratze123.heavyspleef.commands.base.CommandManagerService.java Source code

Java tutorial

Introduction

Here is the source code for de.matzefratze123.heavyspleef.commands.base.CommandManagerService.java

Source

/*
 * This file is part of HeavySpleef.
 * Copyright (c) 2014-2015 matzefratze123
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package de.matzefratze123.heavyspleef.commands.base;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang.Validate;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;

import com.google.common.collect.Maps;

public abstract class CommandManagerService implements CommandExecutor {

    private static final Properties DEFAULT_MESSAGES;
    private static final String[] HELP_IDENTIFIERS = { "?", "help" };

    static {
        DEFAULT_MESSAGES = new Properties();
        DEFAULT_MESSAGES.put("message.player_only", "This command can only be executed by a player!");
        DEFAULT_MESSAGES.put("message.no_permission", "You don't have permission to execute this command!");
        DEFAULT_MESSAGES.put("message.description_format", "Description: %s");
        DEFAULT_MESSAGES.put("message.usage_format", "Usage: %s");
    }

    private final JavaPlugin plugin;
    private final Logger logger;
    private Object[] args;
    private Instantiator instantiator;
    private Map<String, CommandContainer> commandMap;
    private Map<Class<?>, Transformer<?>> transformers;

    public CommandManagerService(JavaPlugin plugin, Logger logger, Object... args) {
        this.plugin = plugin;
        this.logger = logger;
        this.args = args;
        this.instantiator = new UnsafeInstantiator();
        this.commandMap = Maps.newHashMap();
        this.transformers = Maps.newHashMap(BaseTransformers.BASE_TRANSFORMERS);
    }

    public abstract boolean checkPermission(CommandSender sender, String permission);

    public abstract String getMessage(String key, String... messageArgs);

    public String getMessage0(String key, String... messageArgs) {
        String message = getMessage(key, messageArgs);
        if (message == null) {
            message = DEFAULT_MESSAGES.getProperty(key);
        }

        return message;
    }

    public void registerCommands(Class<?> clazz) {
        registerCommands(clazz, null);
    }

    public void registerCommands(Class<?> clazz, CommandContainer base) {
        Validate.notNull(clazz);

        Set<CommandContainer> commands = CommandContainer.create(clazz, instantiator, logger);
        Iterator<CommandContainer> iterator = commands.iterator();

        while (iterator.hasNext()) {
            CommandContainer command = iterator.next();

            if (base == null) {
                if (commandMap.containsKey(command.getName())) {
                    logger.warning("duplicate command " + command.getName() + "!");
                    continue;
                }

                commandMap.put(command.getName(), command);

                PluginCommand bukkitCommand = plugin.getCommand(command.getName());
                if (bukkitCommand != null) {
                    bukkitCommand.setExecutor(this);
                } else {
                    logger.warning("command " + command.getName()
                            + " registered but could not find a matching command for plugin " + plugin.getName()
                            + ". Did you forget to add the command to your plugin.yml?");
                }
            } else {
                // Just add it as a child
                base.addChild(command);
            }
        }
    }

    public CommandContainer containerOf(String path) {
        Validate.notNull(path, "path cannot be null");
        Validate.isTrue(!path.isEmpty(), "path cannot be empty");

        String[] pathComponents = path.split("/");

        CommandContainer current = commandMap.get(pathComponents[0]);
        if (current == null) {
            return null;
        }

        for (int i = 1; i < pathComponents.length; i++) {
            CommandContainer child = current.child(pathComponents[i]);

            if (child != null) {
                current = child;
            } else {
                break;
            }
        }

        return current;
    }

    public CommandContainer getCommand(String baseCommand) {
        return commandMap.get(baseCommand);
    }

    public <T> void registerTransformer(Class<T> returnType, Transformer<T> transformer) {
        Validate.notNull(returnType);
        Validate.notNull(transformer);

        transformers.put(returnType, transformer);
    }

    @SuppressWarnings("unchecked")
    public <T> Transformer<T> getTransformer(Class<T> clazz) {
        return (Transformer<T>) transformers.get(clazz);
    }

    public void setInstantiator(Instantiator instantiator) {
        this.instantiator = instantiator;
    }

    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
        String name = cmd.getName();
        if (!commandMap.containsKey(name)) {
            //That command doesn't belong to us
            return true;
        }

        CommandContainer command = commandMap.get(name);

        //Try to find the deepest available sub-command
        int index = 0;
        boolean subFound;

        if (args.length > 0) {
            do {
                subFound = false;

                Set<CommandContainer> childs = command.getChildCommands();

                if (childs != null) {
                    for (CommandContainer child : childs) {
                        if (child.getName().equals(args[index])) {
                            command = child;
                            index++;
                            subFound = true;
                            break;
                        }
                    }
                }
            } while (index < args.length && subFound);
        }

        if (!(sender instanceof Player) && command.isPlayerOnly()) {
            sender.sendMessage(getMessage0("message.player_only"));
            return true;
        }

        if (!command.getPermission().isEmpty() && !checkPermission(sender, command.getPermission())) {
            sender.sendMessage(getMessage0("message.no_permission"));
            return true;
        }

        //Cut the args to be suitable to the sub-command-deepness
        String[] cutArgs = new String[args.length - index];
        System.arraycopy(args, index, cutArgs, 0, args.length - index);

        if (cutArgs.length > 0 && isHelpArg(cutArgs[0])) {
            String description = command.getDescription();

            if (description.isEmpty() && !command.getDescriptionRef().isEmpty()) {
                description = getMessage(command.getDescriptionRef());
            }

            sender.sendMessage(getMessage0("message.description_format", command.getDescription()));
            return true;
        }

        if (cutArgs.length < command.getMinArgs()) {
            sender.sendMessage(getMessage0("message.usage_format", command.getUsage()));
            return true;
        }

        CommandContext context = new CommandContext(this, cutArgs, command, sender);
        executeCommand(context);

        return true;
    }

    protected void executeCommand(CommandContext context) {
        Method method = context.getCommand().getCommandMethod();
        Object instance = context.getCommand().getCommandClassInstance();

        boolean accessible = method.isAccessible();
        method.setAccessible(true);

        //Analyse the method
        //Default method format is: methodName(CommandContext)

        try {
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 0) {
                //No parameters in this method, so just invoke it
                method.invoke(instance);
            } else {
                Object[] parameterValues = new Object[parameterTypes.length];

                for (int i = 0; i < parameterTypes.length; i++) {
                    Class<?> parameterType = parameterTypes[i];

                    if (parameterType == CommandContext.class) {
                        parameterValues[i] = context;
                    } else if (plugin.getClass() == parameterType) {
                        parameterValues[i] = plugin;
                    } else if (parameterType.isPrimitive()) {
                        parameterValues[i] = getDefaultPrimitiveValue(parameterType);
                    } else {
                        for (Object arg : args) {
                            if (parameterType.isInstance(arg)) {
                                parameterValues[i] = arg;
                                break;
                            }
                        }
                    }
                }

                method.invoke(instance, parameterValues);
            }
        } catch (InvocationTargetException e) {
            Throwable cause = e.getCause();

            if (cause instanceof CommandException) {
                ((CommandException) cause).sendToPlayer(context.getSender());
            } else {
                logger.log(Level.SEVERE,
                        "Unhandled exception executing command \"" + context.getCommand().getName() + "\"", cause);
            }
        } catch (IllegalAccessException | IllegalArgumentException e) {
            logger.log(Level.SEVERE,
                    "Could not invoke command method for '" + context.getCommand().getFullyQualifiedName() + "'",
                    e);
        } catch (Exception e) {
            logger.log(Level.SEVERE,
                    "Unhandled exception executing command '" + context.getCommand().getFullyQualifiedName() + "'",
                    e);
        } finally {
            method.setAccessible(accessible);
        }
    }

    private Object getDefaultPrimitiveValue(Class<?> clazz) {
        if (!clazz.isPrimitive()) {
            return null;
        }

        Object value = null;

        if (clazz == int.class || clazz == long.class || clazz == short.class || clazz == byte.class
                || clazz == double.class || clazz == float.class) {
            value = 0;
        } else if (clazz == boolean.class) {
            value = false;
        }

        return value;
    }

    private boolean isHelpArg(String arg) {
        for (String identifier : HELP_IDENTIFIERS) {
            if (identifier.equalsIgnoreCase(arg)) {
                return true;
            }
        }

        return false;
    }

}