// Path: src/main/java/org/tyrannyofheaven/bukkit/util/permissions/PermissionUtils.java
// public static void requireAllPermissions(Permissible permissible, String... permissions) {
//     if (!hasAllPermissions(permissible, permissions))
//         throw new PermissionException(true, false, permissions);
// }
// 
// Path: src/main/java/org/tyrannyofheaven/bukkit/util/permissions/PermissionUtils.java
// public static void requireOnePermission(Permissible permissible, String... permissions) {
//     requireOnePermission(permissible, false, permissions);
// }
// 
// Path: src/main/java/org/tyrannyofheaven/bukkit/util/ToHLoggingUtils.java
// public class ToHLoggingUtils {
// 
//     private ToHLoggingUtils() {
//         throw new AssertionError("Don't instantiate me!");
//     }
// 
//     // Create a log message
//     private static String createLogMessage(String format, Object... args) {
//         if (format == null)
//             return null;
//         else
//             return String.format(format, args);
//     }
// 
//     /**
//      * Log a message.
//      * 
//      * @param plugin the plugin
//      * @param level the log level
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void log(Plugin plugin, Level level, String format, Object... args) {
//         Logger logger = plugin.getLogger();
//         if (logger.isLoggable(level)) { // Avoid unnecessary String.format() calls
//             if (args.length > 0 && args[args.length - 1] instanceof Throwable) {
//                 // Last argument is a Throwable, treat accordingly
//                 logger.log(level, createLogMessage(format, Arrays.copyOf(args, args.length - 1)), (Throwable)args[args.length - 1]);
//             }
//             else {
//                 logger.log(level, createLogMessage(format, args));
//             }
//         }
//     }
// 
//     /**
//      * Log a message at INFO level.
//      * 
//      * @param plugin the plugin
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void log(Plugin plugin, String format, Object... args) {
//         log(plugin, Level.INFO, format, args);
//     }
// 
//     /**
//      * Log a message at CONFIG level.
//      * 
//      * @param plugin the plugin
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void debug(Plugin plugin, String format, Object... args) {
//         log(plugin, Level.CONFIG, format, args);
//     }
// 
//     /**
//      * Log a message at WARNING level.
//      * 
//      * @param plugin the plugin
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void warn(Plugin plugin, String format, Object... args) {
//         log(plugin, Level.WARNING, format, args);
//     }
// 
//     /**
//      * Log a message at SEVERE level.
//      * 
//      * @param plugin the plugin
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void error(Plugin plugin, String format, Object... args) {
//         log(plugin, Level.SEVERE, format, args);
//     }
// 
// }


import static org.tyrannyofheaven.bukkit.util.permissions.PermissionUtils.requireAllPermissions;
import static org.tyrannyofheaven.bukkit.util.permissions.PermissionUtils.requireOnePermission;
import java.lang.annotation.Annotation;
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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.WeakHashMap;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabExecutor;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.StringUtil;
import org.tyrannyofheaven.bukkit.util.ToHLoggingUtils;
     * @param args command arguments
     */
    void execute(CommandSender sender, String name, String label, String[] args) throws Throwable {
        execute(sender, name, label, args, null, null);
    }

    /**
     * Executes the named command.
     * 
     * @param sender the command sender
     * @param name the name of the command to execute
     * @param args command arguments
     * @param invChain an InvocationChain or null
     * @param session a CommandSession or null
     */
    void execute(CommandSender sender, String name, String label, String[] args, InvocationChain invChain, CommandSession session) throws Throwable {
        if (invChain == null)
            invChain = new InvocationChain();
        if (session == null)
            session = new CommandSession();

        CommandMetaData cmd = commandMap.get(name);
        if (cmd == null)
            throw new ParseException("Unknown command: %s", name);

        // Check permissions
        if (cmd.isRequireAll()) {
            requireAllPermissions(sender, cmd.getPermissions());
        }
        else {
// Path: src/main/java/org/tyrannyofheaven/bukkit/util/permissions/PermissionUtils.java
// public static void requireAllPermissions(Permissible permissible, String... permissions) {
//     if (!hasAllPermissions(permissible, permissions))
//         throw new PermissionException(true, false, permissions);
// }
// 
// Path: src/main/java/org/tyrannyofheaven/bukkit/util/permissions/PermissionUtils.java
// public static void requireOnePermission(Permissible permissible, String... permissions) {
//     requireOnePermission(permissible, false, permissions);
// }
// 
// Path: src/main/java/org/tyrannyofheaven/bukkit/util/ToHLoggingUtils.java
// public class ToHLoggingUtils {
// 
//     private ToHLoggingUtils() {
//         throw new AssertionError("Don't instantiate me!");
//     }
// 
//     // Create a log message
//     private static String createLogMessage(String format, Object... args) {
//         if (format == null)
//             return null;
//         else
//             return String.format(format, args);
//     }
// 
//     /**
//      * Log a message.
//      * 
//      * @param plugin the plugin
//      * @param level the log level
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void log(Plugin plugin, Level level, String format, Object... args) {
//         Logger logger = plugin.getLogger();
//         if (logger.isLoggable(level)) { // Avoid unnecessary String.format() calls
//             if (args.length > 0 && args[args.length - 1] instanceof Throwable) {
//                 // Last argument is a Throwable, treat accordingly
//                 logger.log(level, createLogMessage(format, Arrays.copyOf(args, args.length - 1)), (Throwable)args[args.length - 1]);
//             }
//             else {
//                 logger.log(level, createLogMessage(format, args));
//             }
//         }
//     }
// 
//     /**
//      * Log a message at INFO level.
//      * 
//      * @param plugin the plugin
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void log(Plugin plugin, String format, Object... args) {
//         log(plugin, Level.INFO, format, args);
//     }
// 
//     /**
//      * Log a message at CONFIG level.
//      * 
//      * @param plugin the plugin
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void debug(Plugin plugin, String format, Object... args) {
//         log(plugin, Level.CONFIG, format, args);
//     }
// 
//     /**
//      * Log a message at WARNING level.
//      * 
//      * @param plugin the plugin
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void warn(Plugin plugin, String format, Object... args) {
//         log(plugin, Level.WARNING, format, args);
//     }
// 
//     /**
//      * Log a message at SEVERE level.
//      * 
//      * @param plugin the plugin
//      * @param format the format message
//      * @param args the format args. Note that if the last argument is a
//      *   Throwable, it is removed and passed to {@link java.util.logging.Logger#log(Level, String, Throwable)}.
//      *   It will not be available for the format.
//      */
//     public static void error(Plugin plugin, String format, Object... args) {
//         log(plugin, Level.SEVERE, format, args);
//     }
// 
// }


// Path: src/main/java/org/tyrannyofheaven/bukkit/util/command/HandlerExecutor.java
import static org.tyrannyofheaven.bukkit.util.permissions.PermissionUtils.requireAllPermissions;
import static org.tyrannyofheaven.bukkit.util.permissions.PermissionUtils.requireOnePermission;
import java.lang.annotation.Annotation;
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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.WeakHashMap;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabExecutor;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.StringUtil;
import org.tyrannyofheaven.bukkit.util.ToHLoggingUtils;

     * @param args command arguments
     */
    void execute(CommandSender sender, String name, String label, String[] args) throws Throwable {
        execute(sender, name, label, args, null, null);
    }

    /**
     * Executes the named command.
     * 
     * @param sender the command sender
     * @param name the name of the command to execute
     * @param args command arguments
     * @param invChain an InvocationChain or null
     * @param session a CommandSession or null
     */
    void execute(CommandSender sender, String name, String label, String[] args, InvocationChain invChain, CommandSession session) throws Throwable {
        if (invChain == null)
            invChain = new InvocationChain();
        if (session == null)
            session = new CommandSession();

        CommandMetaData cmd = commandMap.get(name);
        if (cmd == null)
            throw new ParseException("Unknown command: %s", name);

        // Check permissions
        if (cmd.isRequireAll()) {
            requireAllPermissions(sender, cmd.getPermissions());
        }
        else {
            requireOnePermission(sender, cmd.isCheckNegations(), cmd.getPermissions());