Package com.bergerkiller.bukkit.common.utils

Source Code of com.bergerkiller.bukkit.common.utils.CommonUtil

package com.bergerkiller.bukkit.common.utils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.zip.ZipEntry;

import net.minecraft.server.IPlayerFileData;
import net.minecraft.util.com.google.common.base.Charsets;
import net.minecraft.util.com.mojang.authlib.GameProfile;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredListener;
import org.bukkit.plugin.SimplePluginManager;

import com.bergerkiller.bukkit.common.Common;
import com.bergerkiller.bukkit.common.StackTraceFilter;
import com.bergerkiller.bukkit.common.config.BasicConfiguration;
import com.bergerkiller.bukkit.common.conversion.ConversionPairs;
import com.bergerkiller.bukkit.common.internal.CommonNMS;
import com.bergerkiller.bukkit.common.internal.CommonPlugin;
import com.bergerkiller.bukkit.common.reflection.FieldAccessor;
import com.bergerkiller.bukkit.common.reflection.SafeField;
import com.bergerkiller.bukkit.common.reflection.SafeMethod;

public class CommonUtil {
  public static final int VIEW = Bukkit.getServer().getViewDistance();
  public static final int VIEWWIDTH = VIEW + VIEW + 1;
  public static final int CHUNKAREA = VIEWWIDTH * VIEWWIDTH;
  public static final int BLOCKVIEW = 32 + (VIEW << 4);
  public static final Thread MAIN_THREAD = Thread.currentThread();
  private static final FieldAccessor<Collection<Plugin>> pluginsField = new SafeField<Collection<Plugin>>(SimplePluginManager.class, "plugins");

  /**
   * Gets a mapping of all commands available on the server, so they can be obtained and executed (dispatched)
   *
   * @return Bukkit command map
   */
  public static CommandMap getCommandMap() {
    return ((CraftServer) Bukkit.getServer()).getCommandMap();
  }

  /**
   * Sends a message to a sender<br>
   * - Empty messages are ignored<br>
   * - Color is stripped from messages to consoles
   *
   * @param sender to send to
   * @param message to send
   */
  public static void sendMessage(Object sender, Object message) {
    if (message != null) {
      String msg = message.toString();
      if (msg.length() > 0 && sender instanceof CommandSender) {
        if (!(sender instanceof Player)) {
          message = ChatColor.stripColor(msg);
        }
        for (String line : msg.split("\n", -1)) {
          ((CommandSender) sender).sendMessage(line);
        }
      }
    }
  }

  /**
   * Sends a message containing a list of items
   *
   * @param sender to send to
   * @param delimiter to use between items
   * @param items to send
   */
  public static void sendListMessage(Object sender, String delimiter, Object[] items) {
    String msgpart = null;
    String item;
    for (Object oitem : items) {
      item = oitem.toString();
      // display it
      if (msgpart == null || msgpart.length() + item.length() < 70) {
        if (msgpart == null) {
          msgpart = item;
        } else {
          msgpart += ChatColor.WHITE + delimiter + item;
        }
      } else {
        sendMessage(sender, msgpart);
        msgpart = item;
      }
    }
    sendMessage(sender, msgpart);
  }

  /**
   * Calls an Event
   *
   * @param event to call
   * @return the input Event
   */
  public static <T extends Event> T callEvent(T event) {
    Bukkit.getServer().getPluginManager().callEvent(event);
    return event;
  }

  /**
   * Checks whether the event handler list of an event has registered listeners<br>
   * If creating an event causes a performance drain, useless event creation can be avoided this way
   *
   * @param handlerList of the Event
   * @return True if handlers are contained, False if not
   */
  public static boolean hasHandlers(HandlerList handlerList) {
    return handlerList.getRegisteredListeners().length > 0;
  }

  /**
   * Sets the Player File Data used to load and save player information on the server
   *
   * @param playerFileData to set to
   */
  public static void setPlayerFileData(Object playerFileData) {
    CommonNMS.getPlayerList().playerFileData = (IPlayerFileData) playerFileData;
  }

  /**
   * Saves the specified human information to file
   *
   * @param human to save
   */
  public static void savePlayer(HumanEntity human) {
    CommonNMS.getPlayerList().playerFileData.save(CommonNMS.getNative(human));
  }

  /**
   * Saves all player information to file
   */
  public static void savePlayers() {
    CommonNMS.getPlayerList().savePlayers();
  }

  /**
   * Shuffle an array of type T
   *
   * @param array to be shuffled
   */
  public static <T> void shuffle(T[] array) {
    int random;
    for (int i = 1; i < array.length; i++) {
      random = (int) (Math.random() * i);
      T temp = array[i - 1];
      array[i - 1] = array[random];
      array[random] = temp;
    }
  }

  /**
   * Add a Set to another Set
   *
   * @param to Set that receives orther set
   * @param from Set to be added to the orther set
   * @return new Set
   */
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public static Set addAllToSet(Set to, Set from) {
    for(Object i : from) {
      to.add(i);
    }
   
    return to;
  }

  /**
   * Gets a list of online players on the server
   *
   * @return online players
   */
  public static Collection<Player> getOnlinePlayers() {
    return ConversionPairs.player.convertAll(CommonNMS.getPlayerList().players);
  }

  /**
   * Checks whether a given command sender has a given permission<br>
   * Vault is used for permissions if available, otherwise super permissions are used
   *
   * @param sender to check
   * @param permissionNode to check (each part is appended with '.' in between)
   * @return True if the sender has permission for the node, False if not
   */
  public static boolean hasPermission(CommandSender sender, String[] permissionNode) {
    return CommonPlugin.getInstance().getPermissionHandler().hasPermission(sender, permissionNode);
  }
 
  /**
   * Get the game profile from a name.
   *
   * @param name to get game profile from.
   * @return Player's GameProfile, OfflinePlayer profile used if player not found.
   */
  public static GameProfile getGameProfile(String name) {
    Player player = Bukkit.getPlayer(name);
    if(player != null) {
      //The right way
      return PlayerUtil.getGameProfile(player);
    }
   
    UUID uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8));
    return new GameProfile(uuid, name);
  }

  /**
   * Checks whether a given command sender has a given permission<br>
   * Vault is used for permissions if available, otherwise super permissions are used
   *
   * @param sender to check
   * @param permissionNode to check
   * @return True if the sender has permission for the node, False if not
   */
  public static boolean hasPermission(CommandSender sender, String permissionNode) {
    return CommonPlugin.getInstance().getPermissionHandler().hasPermission(sender, permissionNode);
  }

  /**
   * Prints the filtered (using the SERVER filter) stack trace of an error
   *
   * @param error to print
   */
  public static void printFilteredStackTrace(Throwable error) {
    StackTraceFilter.SERVER.print(error);
  }

  /**
   * Prints the filtered (using the SERVER filter) stack trace of an error
   *
   * @param error to print
   * @param level of the error
   */
  public static void printFilteredStackTrace(Throwable error, Level level) {
    StackTraceFilter.SERVER.print(error, level);
  }

  /**
   * Removes all stack trace elements after a given method
   *
   * @param elements to filter
   * @param className to filter from
   * @param methodName to filter from
   * @return Filtered stack trace
   */
  public static StackTraceElement[] filterStackTrace(StackTraceElement[] elements, String className, String methodName) {
    ArrayList<StackTraceElement> rval = new ArrayList<StackTraceElement>(elements.length - 1);
    for (StackTraceElement elem : elements) {
      if (elem.getClassName().equals(className) && elem.getMethodName().equals(methodName)) {
        break;
      } else {
        rval.add(elem);
      }
    }
    return rval.toArray(new StackTraceElement[0]);
  }

  /**
   * Checks all plugin files to check if a plugin file exists
   *
   * @param name of the plugin
   * @return Plugin exists?
   */
  public static boolean isPluginInDirectory(String name) {
    File dir = new File("plugins");
    if (!dir.exists()) {
      return false;
    }
    for (File file : dir.listFiles()) {
      try {
        BasicConfiguration config = getPluginConfiguration(file, "plugin.yml");
        String pluginName = config.get("name", String.class);
        if (name.equals(pluginName)) {
          return true;
        }
      } catch(IOException e) {
      }
    }
    return false;
  }

  /**
   * Obtains a new YAML Configuration instance for a yaml file contained within a plugin Jar file
   *
   * @param pluginJarFile to get a YAML configuration instance of
   * @param configResourcePath to the configuration resource
   * @return a new Configuration instance, filled with data from the YAML file
   * @throws IOException if loading fails or the file can not be found
   */
  public static BasicConfiguration getPluginConfiguration(File pluginJarFile, String configResourcePath) throws IOException {
    InputStream stream = getPluginResource(pluginJarFile, configResourcePath);
    try {
      BasicConfiguration config = new BasicConfiguration();
      try {
        config.loadFromStream(stream);
      } catch (Throwable t) {
        throw new IOException("Error in YAML format", t);
      }
      return config;
    } finally {
      stream.close();
    }
  }

  /**
   * Obtains a new InputStream to the resource found in the given Plugin Jar file
   *
   * @param pluginJarFile to get a resource of
   * @param resourcePath to the resource
   * @return an InputStream to the resource
   * @throws IOException if loading fails or the file can not be found
   */
  public static InputStream getPluginResource(File pluginJarFile, String resourcePath) throws IOException {
    // First, find the plugin by Jar file (avoids Jar File decompression times)
    // Then we can use the ClassLoader of this Jar File to load the resource instead
    synchronized (Bukkit.getPluginManager()) {
      for (Plugin plugin : CommonUtil.getPluginsUnsafe()) {
        File file = getPluginJarFile(plugin);
        if (pluginJarFile.equals(file)) {
          InputStream stream = plugin.getResource(resourcePath);
          if (stream == null) {
            throw new IOException("Resource not found: " + resourcePath);
          }
          return stream;
        }
      }
    }

    // Not found, stick to reading the JarFile ourselves
    final JarFile jarFile = new JarFile(pluginJarFile);
    final ZipEntry entry = jarFile.getEntry(resourcePath);
    if (entry == null) {
      jarFile.close();
      throw new IOException("Resource not found: " + resourcePath);
    }
   
    return jarFile.getInputStream(entry);
  }

  /**
   * Gets the Jar File where a given Plugin is loaded from.
   * If the plugin is not in a Jar file, null is returned instead.
   *
   * @param plugin to get the Jar File of
   * @return the Jar File in which the plugin resides, or null if none found
   */
  public static File getPluginJarFile(Plugin plugin) {
    final Class<?> pluginClass = plugin.getClass();
    try {
      URI uri = pluginClass.getProtectionDomain().getCodeSource().getLocation().toURI();
      File file = new File(uri);
      if (file.exists()) {
        return file;
      }
    } catch (Exception e) {
    }
    return null;
  }

  /**
   * Obtains the folder in which plugin-specific information is contained.<br>
   * Usually this folder is <b>/plugins/[pluginname]</b>.<br>
   * This method can be used to properly obtain this folder if the plugin is not initialized yet.
   *
   * @param plugin to get the data folder of
   * @return Plugin data folder (never null)
   */
  public static File getPluginDataFolder(Plugin plugin) {
    File folder = plugin.getDataFolder();
    if (folder == null) {
      File jarFile = getPluginJarFile(plugin);
      if (jarFile == null) {
        throw new RuntimeException("Plugin data folder can not be obtained: Not a valid JAR plugin");
      }
      folder = new File(jarFile.getAbsoluteFile().getParentFile(), plugin.getName());
    }
    return folder;
  }

  /**
   * Removes all stack trace elements after a given method from an error
   *
   * @param error to filter
   * @param className to filter from
   * @param methodName to filter from
   * @return The input error (uncloned, same instance)
   */
  public static <T extends Throwable> T filterStackTrace(T error, String className, String methodName) {
    error.setStackTrace(filterStackTrace(error.getStackTrace(), className, methodName));
    return error;
  }

  /**
   * Removes all stack trace elements after the method that called this function
   *
   * @param elements to filter
   * @return Filtered stack trace
   */
  public static StackTraceElement[] filterStackTrace(StackTraceElement[] elements) {
    // Obtain the calling method and class name
    StackTraceElement[] currStack = Thread.currentThread().getStackTrace();
    for (int i = 1; i < currStack.length; i++) {
      if (!currStack[i].getClassName().equals(CommonUtil.class.getName()) || !currStack[i].getMethodName().equals("filterStackTrace")) {
        return filterStackTrace(elements, currStack[i + 1].getClassName(), currStack[i + 1].getMethodName());
      }
    }
    return elements;
  }

  /**
   * Removes all stack trace elements after the method that called this function from an error
   *
   * @param error to filter
   * @return The input error (uncloned, same instance)
   */
  public static <T extends Throwable> T filterStackTrace(T error) {
    error.setStackTrace(filterStackTrace(error.getStackTrace()));
    return error;
  }

  /**
   * Performs an unsafe cast to a generic type. Be sure to check that
   * the object being cast is actually an instance of the type to cast to.
   * Casting errors will occur while working with the resulting value,
   * not while performing the casting in this method.
   *
   * @param value to cast
   * @return value cast in an unsafe way
   */
  @SuppressWarnings("unchecked")
  public static <T> T unsafeCast(Object value) {
    return (T) value;
  }

  /**
   * Tries to cast the object to the type specified, returning null upon failure
   *
   * @param object to cast
   * @param type to cast to
   * @return The cast object, or null
   */
  public static <T> T tryCast(Object object, Class<T> type) {
    return tryCast(object, type, null);
  }

  /**
   * Tries to cast the object to the type specified, returning def upon failure
   *
   * @param object to cast
   * @param type to cast to
   * @param def to return on cast failure
   * @return The cast object, or def
   */
  public static <T> T tryCast(Object object, Class<T> type, T def) {
    if (type.isInstance(object)) {
      return type.cast(object);
    } else {
      return def;
    }
  }

  /**
   * Schedules a runnable to execute the next Tick<br>
   * The BKCommonLib internal plugin will handle this task<br>
   * This method is thread safe
   *
   * @param runnable to execute
   */
  public static void nextTick(Runnable runnable) {
    if (runnable == null) {
      return;
    }
    if (CommonPlugin.hasInstance()) {
      // Use BKCommonLib next tick task
      CommonPlugin.getInstance().nextTick(runnable);
    } else {
      // Try to find out what plugin this Runnable belongs to
      Plugin plugin = CommonUtil.getPluginByClass(runnable.getClass());
      if (plugin == null) {
        // Well...ain't that a pickle.
        // Maybe there is some other plugin we can dump this to?
        // It's a fallback...it does not have to be fair or perfect!
        synchronized (Bukkit.getPluginManager()) {
          Iterator<Plugin> iter = getPluginsUnsafe().iterator();
          while (iter.hasNext()) {
            plugin = iter.next();
            if (plugin.isEnabled()) {
              Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, runnable);
              return;
            }
          }
        }
        // Well now we just don't know...
        CommonPlugin.LOGGER.log(Level.SEVERE, "Unable to properly schedule next-tick task: " + runnable.getClass().getName());
        CommonPlugin.LOGGER.log(Level.SEVERE, "The task is executed right away instead...we might recover!");
        runnable.run();
      } else {
        // Use the supposed plugin this Class belongs to
        Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, runnable);
      }
    }
  }

  /**
   * Broadcasts a message to all players on a world
   *
   * @param message to send
   */
  public static void broadcast(Object message) {
    if (message != null) {
      for (Player player : getOnlinePlayers()) {
        player.sendMessage(message.toString());
      }
    }
  }

  /**
   * Gets all Plugins running on the server WITHOUT allocating a new array if possible.
   * If there is no performance requirement to avoid array allocation, use {@link #getPlugins()} instead.
   * Only use this method inside a <b>synchronized</b> body around the Plugin Manager, for example:
   * <pre>
   * synchronized (Bukkit.getPluginManager()) {
   *   for (Plugin plugin : CommonUtil.getPluginsUnsafe()) {
   *      System.out.println(plugin.getName());
   *   }
   * }
   * </pre>
   *
   * @return unsafe collection of plugins running on the server
   */
  public static Collection<Plugin> getPluginsUnsafe() {
    final PluginManager man = Bukkit.getPluginManager();
    if (man instanceof SimplePluginManager) {
      return pluginsField.get(man);
    } else {
      return Arrays.asList(man.getPlugins());
    }
  }

  /**
   * Gets all the Plugins running on the Server
   *
   * @return Plugins
   */
  public static Plugin[] getPlugins() {
    return Bukkit.getPluginManager().getPlugins();
  }

  /**
   * Checks whether the plugin of the given name is enabled on the Server
   *
   * @param name to check
   * @return True if the plugin is enabled, False if not
   */
  public static boolean isPluginEnabled(String name) {
    return Bukkit.getPluginManager().isPluginEnabled(name);
  }

  /**
   * Gets a certain Plugin by name
   *
   * @param name of the Plugin
   * @return Plugin
   */
  public static Plugin getPlugin(String name) {
    return Bukkit.getPluginManager().getPlugin(name);
  }

  /**
   * Gets the plugin by inspecting a Class
   *
   * @param clazz
   * @return the Plugin matching the Class, or null if not found
   */
  public static Plugin getPluginByClass(Class<?> clazz) {
    ClassLoader loader = clazz.getClassLoader();
    synchronized (Bukkit.getServer().getPluginManager()) {
      for (Plugin plugin : getPluginsUnsafe()) {
        if (plugin.getClass().getClassLoader() == loader) {
          return plugin;
        }
      }
    }
    return null;
  }

  /**
   * Gets the plugin by inspecting the path to a Class
   *
   * @param classPath of the Class
   * @return the Plugin matching the Class, or null if not found
   */
  public static Plugin getPluginByClass(String classPath) {
    try {
      return getPluginByClass(Class.forName(classPath));
    } catch (ClassNotFoundException e) {
      return null;
    }
  }

  /**
   * Obtains the package path of a given Class Path
   *
   * @param classPath
   * @return package path of the Package the class resides in
   */
  public static String getPackagePath(String classPath) {
    final int idx = classPath.lastIndexOf('.');
    return idx == -1 ? "" : classPath.substring(0, idx);
  }

  /**
   * Finds all plugins involved in an array of stack trace elements.
   *
   * @param stackTrace to find the plugins for
   * @return array of plugins mentioned in the Stack Trace
   */
  public static Plugin[] findPlugins(StackTraceElement[] stackTrace) {
    return findPlugins(Arrays.asList(stackTrace));
  }

  /**
   * Finds all plugins involved in a list of stack trace elements.
   *
   * @param stackTrace to find the plugins for
   * @return array of plugins mentioned in the Stack Trace
   */
  public static Plugin[] findPlugins(List<StackTraceElement> stackTrace) {
    LinkedHashSet<Plugin> found = new LinkedHashSet<Plugin>(3);
    for (StackTraceElement elem : stackTrace) {
      Plugin plugin = getPluginByClass(elem.getClassName());
      if (plugin != null) {
        found.add(plugin);
      }
    }
    return found.toArray(new Plugin[0]);
  }

  /**
   * Tries to get the class at the path specified and
   * applies translations based on the server currently running.
   *
   * @param path to the class
   * @return the class, or null if not found
   */
  public static Class<?> getClass(String path) {
    try {
      if (Common.SERVER == null) {
        return Class.forName(path);
      } else {
        return Class.forName(Common.SERVER.getClassName(path));
      }
    } catch (ClassNotFoundException e) {
      return null;
    }
  }

  /**
   * Tries to get the net.minecraft.server class at the path specified
   *
   * @param name of the NMS class
   * @return the class, or null if not found
   */
  public static Class<?> getNMSClass(String name) {
    return getClass(Common.NMS_ROOT + "." + name);
  }

  /**
   * Tries to get the org.bukkit.craftbukkit class at the path specified
   *
   * @param name of the CB class
   * @return the class, or null if not found
   */
  public static Class<?> getCBClass(String name) {
    return getClass(Common.CB_ROOT + "." + name);
  }

  /**
   * Checks whether a plugin is soft-depending on another plugin
   *
   * @param plugin
   * @param depending plugin
   * @return True if plugin soft-depends on the depending plugin, False if not
   */
  public static boolean isSoftDepending(Plugin plugin, Plugin depending) {
    final List<String> dep = plugin.getDescription().getSoftDepend();
    return !LogicUtil.nullOrEmpty(dep) && dep.contains(depending.getName());
  }

  /**
   * Checks whether a plugin is depending on another plugin
   *
   * @param plugin
   * @param depending plugin
   * @return True if plugin depends on the depending plugin, False if not
   */
  public static boolean isDepending(Plugin plugin, Plugin depending) {
    final List<String> dep = plugin.getDescription().getDepend();
    return !LogicUtil.nullOrEmpty(dep) && dep.contains(depending.getName());
  }

  /**
   * Gets constants of the class type statically defined in the class itself.
   * If the class is an enum, the enumeration constants are returned.
   * Otherwise, only the static fields with theClass type are returned.
   *
   * @param theClass to get the class constants of
   * @return class constants defined in class 'theClass'
   */
  public static <T> T[] getClassConstants(Class<T> theClass) {
    return getClassConstants(theClass, theClass);
  }

  /**
   * Gets constants of the class type statically defined in the class itself.
   * If the type class is an enum, the enumeration constants are returned.
   * Otherwise, only the static fields with the same type as the type parameter are returned.
   *
   * @param theClass to get the class constants of
   * @param type of constants to return from theClass
   * @return class constants defined in class 'theClass'
   */
  @SuppressWarnings("unchecked")
  public static <T> T[] getClassConstants(Class<?> theClass, Class<T> type) {
    if (type.isEnum()) {
      // Get using enum constants
      return type.getEnumConstants();
    } else {
      // Get using reflection
      try {
        Field[] declaredFields = theClass.getDeclaredFields();
        ArrayList<T> constants = new ArrayList<T>(declaredFields.length);
        for (Field field : declaredFields) {
          if (Modifier.isStatic(field.getModifiers()) && type.isAssignableFrom(field.getType())) {
            T constant = (T) field.get(null);
            if (constant != null) {
              constants.add(constant);
            }
          }
        }
        return LogicUtil.toArray(constants, type);
      } catch (Throwable t) {
        t.printStackTrace();
        return LogicUtil.createArray(type, 0);
      }
    }
  }

  /**
   * Checks whether a given value is an instance of one of the types specified
   *
   * @param value to check
   * @param types to check against
   * @return True if value is an instance of one of the types, False if not
   */
  public static boolean isInstance(Object value, Class<?>... types) {
    for (Class<?> type : types) {
      if (type.isInstance(value)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Checks whether the server has officially started running and accepting new players to join.
   *
   * @return True if the server started, False if not and the server is still enabling
   */
  public static boolean isServerStarted() {
    return CommonPlugin.hasInstance() && CommonPlugin.getInstance().isServerStarted();
  }

  /**
   * Obtains the Handler List of an event, throwing an exception if this is not possible
   *
   * @param eventClass to get the HandlerList of
   * @return the HandlerList
   * @throws RuntimeException: Event class has no handler list
   */
  public static HandlerList getEventHandlerList(Class<?> eventClass) {
    try {
      return (HandlerList) eventClass.getDeclaredMethod("getHandlerList").invoke(null);
    } catch (Throwable t) {
    }
    throw new RuntimeException("Class '" + eventClass.getName() + "' does not have a handler list");
  }

  /**
   * Re-orders the event registration to ensure that the listener MONITOR handler is called last, after
   * all other listener handlers have executed. This should only be used if information is changed
   * that could cause other listeners after this handler to malfunction.<br>
   * <br>
   * This method may fail if more than one Monitor handler per Event class is present in a
   * single Listener.
   *
   * @param listener of the event handler
   * @param eventClass that is handled
   */
  public static void queueListenerLast(Listener listener, Class<?> eventClass) {
    setListenerOrder(listener, eventClass, false);
  }

  /**
   * Re-orders the event registration to ensure that the listener LOWEST handler is called first, before
   * all other listener handlers have executed. This should only be used if information is changed
   * that could cause other listeners before this handler to malfunction.<br>
   * This method may fail if more than one Lowest handler per Event class is present in a
   * single Listener.
   *
   * @param listener of the event handler
   * @param eventClass that is handled
   */
  public static void queueListenerFirst(Listener listener, Class<?> eventClass) {
    setListenerOrder(listener, eventClass, true);
  }

  private static void setListenerOrder(Listener listener, Class<?> eventClass, boolean first) {
    HandlerList list = getEventHandlerList(eventClass);
    final EventPriority prio = first ? EventPriority.LOWEST : EventPriority.MONITOR;
    synchronized (list) {
      EnumMap<EventPriority, ArrayList<RegisteredListener>> handlerSlots = SafeField.get(list, "handlerslots");
      ArrayList<RegisteredListener> registeredListenerList = handlerSlots.get(prio);
      int requestedIndex = first ? 0 : (registeredListenerList.size() - 1);

      // Try to find the registered listener
      for (int i = 0; i < registeredListenerList.size(); i++) {
        RegisteredListener registeredListener = registeredListenerList.get(i);
        // Check that the Listener matches
        if (registeredListener.getListener() != listener) {
          continue;
        } else if (i == requestedIndex) {
          // Already in order, do not do anything
          return;
        }
        RegisteredListener[] allListeners = list.getRegisteredListeners();

        // Change order in list
        registeredListenerList.remove(i);
        registeredListenerList.add(requestedIndex, registeredListener);

        // Get the index of the listener in the baked array
        ArrayList<RegisteredListener> newListeners = new ArrayList<RegisteredListener>(allListeners.length);
        if (first) {
          newListeners.add(registeredListener);
          for (RegisteredListener otherListener : allListeners) {
            if (otherListener != registeredListener) {
              newListeners.add(otherListener);
            }
          }
        } else {
          for (RegisteredListener otherListener : allListeners) {
            if (otherListener != registeredListener) {
              newListeners.add(otherListener);
            }
          }
          newListeners.add(registeredListener);
        }
        // Transfer the listeners over
        if (newListeners.toArray(allListeners) != allListeners) {
          // Strange failure, ensure a bake
          list.bake();
        }
        return;
      }
    }
  }

  /**
   * Checks whether a certain method is overrided in a class
   *
   * @param baseClass the method is defined in
   * @param typeInstance to check whether it overrides in the base class
   * @param methodName of the method
   * @param parameterTypes of the method
   * @return True if overrided, False if not
   */
  public static boolean isMethodOverrided(Class<?> baseClass, Object typeInstance, String methodName, Class<?>... parameterTypes) {
    return isMethodOverrided(baseClass, typeInstance.getClass(), methodName, parameterTypes);
  }

  /**
   * Checks whether a certain method is overrided in a class
   *
   * @param baseClass the method is defined in
   * @param type to check whether it overrides in the base class
   * @param methodName of the method
   * @param parameterTypes of the method
   * @return True if overrided, False if not
   */
  public static boolean isMethodOverrided(Class<?> baseClass, Class<?> type, String methodName, Class<?>... parameterTypes) {
    return new SafeMethod<Void>(baseClass, methodName, parameterTypes).isOverridedIn(type);
  }
}
TOP

Related Classes of com.bergerkiller.bukkit.common.utils.CommonUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.