001package fr.aumgn.bukkitutils.command.executor;
002
003import java.lang.reflect.InvocationTargetException;
004import java.lang.reflect.Method;
005import java.util.HashSet;
006import java.util.Set;
007import java.util.logging.Level;
008
009import org.bukkit.Bukkit;
010import org.bukkit.ChatColor;
011import org.bukkit.command.CommandExecutor;
012import org.bukkit.command.CommandSender;
013import org.bukkit.entity.Player;
014
015import fr.aumgn.bukkitutils.command.Command;
016import fr.aumgn.bukkitutils.command.Commands;
017import fr.aumgn.bukkitutils.command.CommandsMessages;
018import fr.aumgn.bukkitutils.command.args.CommandArgs;
019import fr.aumgn.bukkitutils.command.args.CommandArgsParser;
020import fr.aumgn.bukkitutils.command.exception.CommandException;
021import fr.aumgn.bukkitutils.command.exception.CommandUsageError;
022import fr.aumgn.bukkitutils.glob.exceptions.GlobException;
023import fr.aumgn.bukkitutils.glob.exceptions.UnbalancedSquareBracketException;
024
025public class MethodCommandExecutor implements CommandExecutor {
026
027    private final CommandsMessages messages;
028    private final Commands instance;
029    private final Method preExecute;
030    private final Method method;
031    private final int min;
032    private final int max;
033    private final Set<Character> flags;
034    private final Set<Character> argsFlags;
035    private final boolean isPlayerCommand;
036
037    public MethodCommandExecutor(CommandsMessages messages, Commands instance,
038            Method preExecute, Method method, int min, int max, String flags,
039            String argsFlags) {
040        this.messages = messages;
041        this.instance = instance;
042        this.preExecute = preExecute;
043        this.method = method;
044
045        if (method.getParameterTypes().length > 1) {
046            this.min = min;
047            this.max = max;
048        } else {
049            this.min = -1;
050            this.max = 0;
051        }
052        this.flags = new HashSet<Character>();
053        for (char flag : flags.toCharArray()) {
054            this.flags.add(flag);
055        }
056        this.argsFlags = new HashSet<Character>();
057        for (char flag : argsFlags.toCharArray()) {
058            this.argsFlags.add(flag);
059        }
060        this.isPlayerCommand = Player.class.isAssignableFrom(
061                method.getParameterTypes()[0]);
062    }
063
064    public MethodCommandExecutor(CommandsMessages messages, Commands instance,
065            Method preExecute, Method method, Command command) {
066        this(messages, instance, preExecute, method, command.min(),
067                command.max(), command.flags(), command.argsFlags());
068    }
069
070    @Override
071    public boolean onCommand(CommandSender sender,
072            org.bukkit.command.Command cmd, String lbl, String[] rawArgs) {
073        if (isPlayerCommand && !(sender instanceof Player)) {
074            sender.sendMessage(ChatColor.RED + messages.playerOnly());
075            return true;
076        }
077        try {
078            CommandArgs args;
079            if (min >= 0) {
080                args = getArgs(rawArgs);
081            } else {
082                if (rawArgs.length > 0) {
083                    throw new CommandUsageError(
084                            messages.tooManyArguments(rawArgs.length, 0));
085                }
086                args = null;
087            }
088            callCommand(lbl, sender, args);
089        } catch (GlobException exc) {
090            handleGlobException(exc);
091        } catch (CommandUsageError error) {
092            sender.sendMessage(ChatColor.RED + error.getMessage());
093            return false;
094        } catch (Throwable thr) {
095            sender.sendMessage(ChatColor.RED + thr.getMessage());
096        }
097        return true;
098    }
099
100    private CommandArgs getArgs(String[] rawArgs){
101        CommandArgsParser parser = new CommandArgsParser(messages, rawArgs);
102        parser.validate(flags, argsFlags, min, max);
103        return new CommandArgs(messages, parser);
104    }
105
106    private void callCommand(String name, CommandSender sender,
107            CommandArgs args) throws Throwable {
108        try {
109            if (preExecute != null) {
110                preExecute.invoke(instance, sender, args);
111            }
112            if (args != null) {
113                method.invoke(instance, sender, args);
114            } else {
115                method.invoke(instance, sender);
116            }
117        } catch (InvocationTargetException exc) {
118            Throwable cause = exc.getCause();
119            if (cause instanceof CommandException) {
120                throw cause;
121            }
122            unhandledError(name, args, cause);
123        } catch (IllegalArgumentException exc) {
124            unhandledError(name, args, exc);
125        } catch (IllegalAccessException exc) {
126            unhandledError(name, args, exc);
127        }
128    }
129
130    private void handleGlobException(GlobException exc) {
131        if (exc instanceof UnbalancedSquareBracketException) {
132            UnbalancedSquareBracketException usbExc;
133            usbExc = ((UnbalancedSquareBracketException) exc);
134            throw new CommandUsageError(
135                    messages.globUnbalancedSquareBracket(usbExc.getGlob()));
136        }
137
138        throw new RuntimeException(exc);
139    }
140
141    private void unhandledError(String name, CommandArgs args, Throwable exc) {
142        if (!(exc instanceof org.bukkit.command.CommandException)) {
143            Bukkit.getLogger().severe(
144                    "Exception occured while executing \""+ name + "\"");
145            if (args != null) {
146                if (args.hasFlags()) {
147                    StringBuilder flagsString = new StringBuilder();
148                    for (char flag : args.flags()) {
149                        flagsString.append(flag);
150                    }
151                    Bukkit.getLogger().severe(
152                            "Flags : " + flagsString.toString());
153                }
154                if (args.length() > 0) {
155                    StringBuilder arguments = new StringBuilder();
156                    for (String arg : args.asList()) {
157                        arguments.append(arg);
158                        arguments.append(" ");
159                    }
160                    Bukkit.getLogger().severe(
161                            "Arguments : " + arguments.toString());
162                }
163            }
164        }
165        Bukkit.getLogger().log(Level.SEVERE, "Exception : ", exc);
166    }
167}