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

Java tutorial

Introduction

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

Source

/*
 * This file is part of HeavySpleef.
 * Copyright (c) 2014-2016 Matthias Werning
 *
 * 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.xaniox.heavyspleef.commands.base;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang.Validate;
import org.bukkit.command.Command;
import org.bukkit.command.*;
import org.bukkit.plugin.java.JavaPlugin;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

public class CommandManagerService implements CommandExecutor, TabCompleter {

    private static Map<Class<?>, Transformer<?>> TRANSFORMERS = Maps.newHashMap(BaseTransformers.BASE_TRANSFORMERS);

    private final JavaPlugin plugin;
    private final Logger logger;
    private final DefaultCommandExecution execution;
    private List<Object> args;
    private Instantiator instantiator;
    private MessageBundle messageBundle;
    private PermissionChecker permissionChecker;
    private Map<String, CommandContainer> commandMap;

    public CommandManagerService(JavaPlugin plugin, Logger logger, MessageBundle.MessageProvider messageProvider,
            PermissionChecker permissionChecker, Object... args) {
        this.plugin = plugin;
        this.logger = logger;
        this.args = Lists.newArrayList(args);
        this.instantiator = new UnsafeInstantiator();
        this.commandMap = Maps.newHashMap();

        InputStream defaultMessagesStream = getClass().getResourceAsStream("/command_messages.yml");
        this.messageBundle = new MessageBundle(messageProvider, defaultMessagesStream);
        this.permissionChecker = permissionChecker;
        this.execution = new DefaultCommandExecution(plugin, null);
    }

    public DefaultCommandExecution getExecution() {
        return execution;
    }

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

        TRANSFORMERS.put(returnType, transformer);
    }

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

    public void addArgument(Object object) {
        args.add(object);
    }

    public void removeArgument(Object object) {
        args.remove(object);
    }

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

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

        Set<CommandContainer> commands = CommandContainer.create(clazz, base, instantiator, execution, 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 void unregisterCommands(Class<?> clazz, CommandContainer base) {
        Iterator<CommandContainer> iterator;

        if (base == null) {
            iterator = commandMap.values().iterator();
        } else {
            iterator = base.getChildCommands().iterator();
        }

        unregisterRecursively(clazz, iterator);
    }

    private void unregisterRecursively(Class<?> clazz, Iterator<CommandContainer> iterator) {
        while (iterator.hasNext()) {
            CommandContainer container = iterator.next();
            Method method = container.getCommandMethod();
            Set<CommandContainer> childs = container.getChildCommands();

            if (method.getDeclaringClass() == clazz) {
                iterator.remove();
            } else if (childs != null && !childs.isEmpty()) {
                unregisterRecursively(clazz, childs.iterator());
            }
        }
    }

    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 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;
        }

        SearchResult result = searchCommand(name, args);
        if (result == null || result.container == null) {
            sender.sendMessage(messageBundle.getMessage("message-unknown-command"));
            return true;
        }

        CommandContainer command = result.container;
        int deepness = result.deepness;

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

        CommandContext context = new CommandContext(cutArgs, command, sender);
        command.execute(context, messageBundle, permissionChecker, this.args.toArray(new Object[this.args.size()]));
        return true;
    }

    @Override
    public List<String> onTabComplete(CommandSender sender, Command cmd, String alias, String[] args) {
        String name = cmd.getName();
        if (!commandMap.containsKey(name)) {
            //That command doesn't belong to us
            return null;
        }

        SearchResult result = searchCommand(name, args);
        if (result == null || result.container == null) {
            return null;
        }

        CommandContainer container = result.container;
        int deepness = result.deepness;

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

        CommandContext context = new CommandContext(cutArgs, container, sender);
        List<String> tabCompletes = container.tabComplete(context, permissionChecker,
                this.args.toArray(new Object[this.args.size()]));
        if (tabCompletes == null) {
            tabCompletes = Lists.newArrayList();
        }

        if (args.length > 0 && !args[args.length - 1].isEmpty()) {
            //Remove unrelevant completes
            String lastArgument = args[args.length - 1];

            Iterator<String> iterator = tabCompletes.iterator();
            while (iterator.hasNext()) {
                String complete = iterator.next().toLowerCase();

                if (!complete.startsWith(lastArgument.toLowerCase())) {
                    iterator.remove();
                }
            }
        }

        return tabCompletes;
    }

    public SearchResult searchCommand(String name, String[] args) {
        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);
        }

        SearchResult result = new SearchResult();
        result.container = command;
        result.deepness = index;

        return result;
    }

    public class SearchResult {

        private CommandContainer container;
        private int deepness;

        public CommandContainer getContainer() {
            return container;
        }

        public int getDeepness() {
            return deepness;
        }

    }

}