// 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;
    void fillInvocationChain(InvocationChain invChain, String label) {
        CommandMetaData cmd = commandMap.get(label);
        if (cmd == null)
            throw new IllegalArgumentException("Unknown command: " + label);
        invChain.addInvocation(label, cmd);
    }

    // Retrieve cached HandlerExecutor for given handler object, creating
    // one if it doesn't exist
    synchronized HandlerExecutor<T> handlerExecutorFor(Object handler) {
        // Check HandlerExecutor cache
        HandlerExecutor<T> he = subCommandMap.get(handler);
        if (he == null) {
            // No HandlerExecutor yet, create a new one
            he = new HandlerExecutor<>(plugin, usageOptions, handler);
            subCommandMap.put(handler, he);
        }
        return he;
    }

    // Create a HelpBuilder associated with this HandlerExecutor
    HelpBuilder getHelpBuilder(InvocationChain rootInvocationChain, Set<String> possibleCommands) {
        return new HelpBuilder(this, rootInvocationChain, usageOptions, possibleCommands);
    }

    // Register top-level commands
    void registerCommands(TabExecutor executor) {
        for (String name : commandList) {
            PluginCommand command = ((JavaPlugin)plugin).getCommand(name);
            if (command == null) {
// 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;

    void fillInvocationChain(InvocationChain invChain, String label) {
        CommandMetaData cmd = commandMap.get(label);
        if (cmd == null)
            throw new IllegalArgumentException("Unknown command: " + label);
        invChain.addInvocation(label, cmd);
    }

    // Retrieve cached HandlerExecutor for given handler object, creating
    // one if it doesn't exist
    synchronized HandlerExecutor<T> handlerExecutorFor(Object handler) {
        // Check HandlerExecutor cache
        HandlerExecutor<T> he = subCommandMap.get(handler);
        if (he == null) {
            // No HandlerExecutor yet, create a new one
            he = new HandlerExecutor<>(plugin, usageOptions, handler);
            subCommandMap.put(handler, he);
        }
        return he;
    }

    // Create a HelpBuilder associated with this HandlerExecutor
    HelpBuilder getHelpBuilder(InvocationChain rootInvocationChain, Set<String> possibleCommands) {
        return new HelpBuilder(this, rootInvocationChain, usageOptions, possibleCommands);
    }

    // Register top-level commands
    void registerCommands(TabExecutor executor) {
        for (String name : commandList) {
            PluginCommand command = ((JavaPlugin)plugin).getCommand(name);
            if (command == null) {
                ToHLoggingUtils.warn(plugin, "Command '%s' not found in plugin.yml -- ignoring", name);