SHARE
TWEET

Untitled

a guest May 21st, 2016 14 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. package de.klein5.TowerDefense.datawatcher;
  2.  
  3. import java.lang.reflect.Constructor;
  4. import java.lang.reflect.Field;
  5. import java.lang.reflect.Method;
  6. import java.util.Arrays;
  7. import java.util.regex.Matcher;
  8. import java.util.regex.Pattern;
  9.  
  10. import org.bukkit.Bukkit;
  11.  
  12. /**
  13.  * An utility class that simplifies reflection in Bukkit plugins.
  14.  *
  15.  * @author Kristian
  16.  */
  17. public final class Reflection {
  18.     /**
  19.      * An interface for invoking a specific constructor.
  20.      */
  21.     public interface ConstructorInvoker {
  22.         /**
  23.          * Invoke a constructor for a specific class.
  24.          *
  25.          * @param arguments - the arguments to pass to the constructor.
  26.          * @return The constructed object.
  27.          */
  28.         public Object invoke(Object... arguments);
  29.     }
  30.  
  31.     /**
  32.      * An interface for invoking a specific method.
  33.      */
  34.     public interface MethodInvoker {
  35.         /**
  36.          * Invoke a method on a specific target object.
  37.          *
  38.          * @param target - the target object, or NULL for a static method.
  39.          * @param arguments - the arguments to pass to the method.
  40.          * @return The return value, or NULL if is void.
  41.          */
  42.         public Object invoke(Object target, Object... arguments);
  43.     }
  44.  
  45.     /**
  46.      * An interface for retrieving the field content.
  47.      *
  48.      * @param <T> - field type.
  49.      */
  50.     public interface FieldAccessor<T> {
  51.         /**
  52.          * Retrieve the content of a field.
  53.          *
  54.          * @param target - the target object, or NULL for a static field.
  55.          * @return The value of the field.
  56.          */
  57.         public T get(Object target);
  58.  
  59.         /**
  60.          * Set the content of a field.
  61.          *
  62.          * @param target - the target object, or NULL for a static field.
  63.          * @param value - the new value of the field.
  64.          */
  65.         public void set(Object target, Object value);
  66.  
  67.         /**
  68.          * Determine if the given object has this field.
  69.          *
  70.          * @param target - the object to test.
  71.          * @return TRUE if it does, FALSE otherwise.
  72.          */
  73.         public boolean hasField(Object target);
  74.     }
  75.  
  76.     // Deduce the net.minecraft.server.v* package
  77.     private static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
  78.     private static String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server");
  79.     private static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", "");
  80.  
  81.     // Variable replacement
  82.     private static Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}");
  83.  
  84.     private Reflection() {
  85.         // Seal class
  86.     }
  87.  
  88.     /**
  89.      * Retrieve a field accessor for a specific field type and name.
  90.      *
  91.      * @param target - the target type.
  92.      * @param name - the name of the field, or NULL to ignore.
  93.      * @param fieldType - a compatible field type.
  94.      * @return The field accessor.
  95.      */
  96.     public static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType) {
  97.         return getField(target, name, fieldType, 0);
  98.     }
  99.  
  100.     /**
  101.      * Retrieve a field accessor for a specific field type and name.
  102.      *
  103.      * @param className - lookup name of the class, see {@link #getClass(String)}.
  104.      * @param name - the name of the field, or NULL to ignore.
  105.      * @param fieldType - a compatible field type.
  106.      * @return The field accessor.
  107.      */
  108.     public static <T> FieldAccessor<T> getField(String className, String name, Class<T> fieldType) {
  109.         return getField(getClass(className), name, fieldType, 0);
  110.     }
  111.  
  112.     /**
  113.      * Retrieve a field accessor for a specific field type and name.
  114.      *
  115.      * @param target - the target type.
  116.      * @param fieldType - a compatible field type.
  117.      * @param index - the number of compatible fields to skip.
  118.      * @return The field accessor.
  119.      */
  120.     public static <T> FieldAccessor<T> getField(Class<?> target, Class<T> fieldType, int index) {
  121.         return getField(target, null, fieldType, index);
  122.     }
  123.  
  124.     /**
  125.      * Retrieve a field accessor for a specific field type and name.
  126.      *
  127.      * @param className - lookup name of the class, see {@link #getClass(String)}.
  128.      * @param fieldType - a compatible field type.
  129.      * @param index - the number of compatible fields to skip.
  130.      * @return The field accessor.
  131.      */
  132.     public static <T> FieldAccessor<T> getField(String className, Class<T> fieldType, int index) {
  133.         return getField(getClass(className), fieldType, index);
  134.     }
  135.  
  136.     // Common method
  137.     private static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType, int index) {
  138.         for (final Field field : target.getDeclaredFields()) {
  139.             if ((name == null || field.getName().equals(name)) && fieldType.isAssignableFrom(field.getType()) && index-- <= 0) {
  140.                 field.setAccessible(true);
  141.  
  142.                 // A function for retrieving a specific field value
  143.                 return new FieldAccessor<T>() {
  144.  
  145.                     @Override
  146.                     @SuppressWarnings("unchecked")
  147.                     public T get(Object target) {
  148.                         try {
  149.                             return (T) field.get(target);
  150.                         } catch (IllegalAccessException e) {
  151.                             throw new RuntimeException("Cannot access reflection.", e);
  152.                         }
  153.                     }
  154.  
  155.                     @Override
  156.                     public void set(Object target, Object value) {
  157.                         try {
  158.                             field.set(target, value);
  159.                         } catch (IllegalAccessException e) {
  160.                             throw new RuntimeException("Cannot access reflection.", e);
  161.                         }
  162.                     }
  163.  
  164.                     @Override
  165.                     public boolean hasField(Object target) {
  166.                         // target instanceof DeclaringClass
  167.                         return field.getDeclaringClass().isAssignableFrom(target.getClass());
  168.                     }
  169.                 };
  170.             }
  171.         }
  172.  
  173.         // Search in parent classes
  174.         if (target.getSuperclass() != null)
  175.             return getField(target.getSuperclass(), name, fieldType, index);
  176.  
  177.         throw new IllegalArgumentException("Cannot find field with type " + fieldType);
  178.     }
  179.  
  180.     /**
  181.      * Search for the first publically and privately defined method of the given name and parameter count.
  182.      *
  183.      * @param className - lookup name of the class, see {@link #getClass(String)}.
  184.      * @param methodName - the method name, or NULL to skip.
  185.      * @param params - the expected parameters.
  186.      * @return An object that invokes this specific method.
  187.      * @throws IllegalStateException If we cannot find this method.
  188.      */
  189.     public static MethodInvoker getMethod(String className, String methodName, Class<?>... params) {
  190.         return getTypedMethod(getClass(className), methodName, null, params);
  191.     }
  192.  
  193.     /**
  194.      * Search for the first publically and privately defined method of the given name and parameter count.
  195.      *
  196.      * @param clazz - a class to start with.
  197.      * @param methodName - the method name, or NULL to skip.
  198.      * @param params - the expected parameters.
  199.      * @return An object that invokes this specific method.
  200.      * @throws IllegalStateException If we cannot find this method.
  201.      */
  202.     public static MethodInvoker getMethod(Class<?> clazz, String methodName, Class<?>... params) {
  203.         return getTypedMethod(clazz, methodName, null, params);
  204.     }
  205.  
  206.     /**
  207.      * Search for the first publically and privately defined method of the given name and parameter count.
  208.      *
  209.      * @param clazz - a class to start with.
  210.      * @param methodName - the method name, or NULL to skip.
  211.      * @param returnType - the expected return type, or NULL to ignore.
  212.      * @param params - the expected parameters.
  213.      * @return An object that invokes this specific method.
  214.      * @throws IllegalStateException If we cannot find this method.
  215.      */
  216.     public static MethodInvoker getTypedMethod(Class<?> clazz, String methodName, Class<?> returnType, Class<?>... params) {
  217.         for (final Method method : clazz.getDeclaredMethods()) {
  218.             if ((methodName == null || method.getName().equals(methodName))
  219.                     && (returnType == null || method.getReturnType().equals(returnType))
  220.                     && Arrays.equals(method.getParameterTypes(), params)) {
  221.                 method.setAccessible(true);
  222.  
  223.                 return new MethodInvoker() {
  224.  
  225.                     @Override
  226.                     public Object invoke(Object target, Object... arguments) {
  227.                         try {
  228.                             return method.invoke(target, arguments);
  229.                         } catch (Exception e) {
  230.                             throw new RuntimeException("Cannot invoke method " + method, e);
  231.                         }
  232.                     }
  233.  
  234.                 };
  235.             }
  236.         }
  237.  
  238.         // Search in every superclass
  239.         if (clazz.getSuperclass() != null)
  240.             return getMethod(clazz.getSuperclass(), methodName, params);
  241.  
  242.         throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params)));
  243.     }
  244.  
  245.     /**
  246.      * Search for the first publically and privately defined constructor of the given name and parameter count.
  247.      *
  248.      * @param className - lookup name of the class, see {@link #getClass(String)}.
  249.      * @param params - the expected parameters.
  250.      * @return An object that invokes this constructor.
  251.      * @throws IllegalStateException If we cannot find this method.
  252.      */
  253.     public static ConstructorInvoker getConstructor(String className, Class<?>... params) {
  254.         return getConstructor(getClass(className), params);
  255.     }
  256.  
  257.     /**
  258.      * Search for the first publically and privately defined constructor of the given name and parameter count.
  259.      *
  260.      * @param clazz - a class to start with.
  261.      * @param params - the expected parameters.
  262.      * @return An object that invokes this constructor.
  263.      * @throws IllegalStateException If we cannot find this method.
  264.      */
  265.     public static ConstructorInvoker getConstructor(Class<?> clazz, Class<?>... params) {
  266.         for (final Constructor<?> constructor : clazz.getDeclaredConstructors()) {
  267.             if (Arrays.equals(constructor.getParameterTypes(), params)) {
  268.                 constructor.setAccessible(true);
  269.  
  270.                 return new ConstructorInvoker() {
  271.  
  272.                     @Override
  273.                     public Object invoke(Object... arguments) {
  274.                         try {
  275.                             return constructor.newInstance(arguments);
  276.                         } catch (Exception e) {
  277.                             throw new RuntimeException("Cannot invoke constructor " + constructor, e);
  278.                         }
  279.                     }
  280.  
  281.                 };
  282.             }
  283.         }
  284.  
  285.         throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params)));
  286.     }
  287.  
  288.     /**
  289.      * Retrieve a class from its full name, without knowing its type on compile time.
  290.      * <p>
  291.      * This is useful when looking up fields by a NMS or OBC type.
  292.      * <p>
  293.      *
  294.      * @see {@link #getClass()} for more information.
  295.      * @param lookupName - the class name with variables.
  296.      * @return The class.
  297.      */
  298.     public static Class<Object> getUntypedClass(String lookupName) {
  299.         @SuppressWarnings({ "rawtypes", "unchecked" })
  300.         Class<Object> clazz = (Class) getClass(lookupName);
  301.         return clazz;
  302.     }
  303.  
  304.     /**
  305.      * Retrieve a class from its full name.
  306.      * <p>
  307.      * Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table:
  308.      * <p>
  309.      * <table border="1">
  310.      * <tr>
  311.      * <th>Variable</th>
  312.      * <th>Content</th>
  313.      * </tr>
  314.      * <tr>
  315.      * <td>{nms}</td>
  316.      * <td>Actual package name of net.minecraft.server.VERSION</td>
  317.      * </tr>
  318.      * <tr>
  319.      * <td>{obc}</td>
  320.      * <td>Actual pacakge name of org.bukkit.craftbukkit.VERSION</td>
  321.      * </tr>
  322.      * <tr>
  323.      * <td>{version}</td>
  324.      * <td>The current Minecraft package VERSION, if any.</td>
  325.      * </tr>
  326.      * </table>
  327.      *
  328.      * @param lookupName - the class name with variables.
  329.      * @return The looked up class.
  330.      * @throws IllegalArgumentException If a variable or class could not be found.
  331.      */
  332.     public static Class<?> getClass(String lookupName) {
  333.         return getCanonicalClass(expandVariables(lookupName));
  334.     }
  335.  
  336.     /**
  337.      * Retrieve a class in the net.minecraft.server.VERSION.* package.
  338.      *
  339.      * @param name - the name of the class, excluding the package.
  340.      * @throws IllegalArgumentException If the class doesn't exist.
  341.      */
  342.     public static Class<?> getMinecraftClass(String name) {
  343.         return getCanonicalClass(NMS_PREFIX + "." + name);
  344.     }
  345.  
  346.     /**
  347.      * Retrieve a class in the org.bukkit.craftbukkit.VERSION.* package.
  348.      *
  349.      * @param name - the name of the class, excluding the package.
  350.      * @throws IllegalArgumentException If the class doesn't exist.
  351.      */
  352.     public static Class<?> getCraftBukkitClass(String name) {
  353.         return getCanonicalClass(OBC_PREFIX + "." + name);
  354.     }
  355.  
  356.     /**
  357.      * Retrieve a class by its canonical name.
  358.      *
  359.      * @param canonicalName - the canonical name.
  360.      * @return The class.
  361.      */
  362.     private static Class<?> getCanonicalClass(String canonicalName) {
  363.         try {
  364.             return Class.forName(canonicalName);
  365.         } catch (ClassNotFoundException e) {
  366.             throw new IllegalArgumentException("Cannot find " + canonicalName, e);
  367.         }
  368.     }
  369.  
  370.     /**
  371.      * Expand variables such as "{nms}" and "{obc}" to their corresponding packages.
  372.      *
  373.      * @param name - the full name of the class.
  374.      * @return The expanded string.
  375.      */
  376.     private static String expandVariables(String name) {
  377.         StringBuffer output = new StringBuffer();
  378.         Matcher matcher = MATCH_VARIABLE.matcher(name);
  379.  
  380.         while (matcher.find()) {
  381.             String variable = matcher.group(1);
  382.             String replacement = "";
  383.  
  384.             // Expand all detected variables
  385.             if ("nms".equalsIgnoreCase(variable))
  386.                 replacement = NMS_PREFIX;
  387.             else if ("obc".equalsIgnoreCase(variable))
  388.                 replacement = OBC_PREFIX;
  389.             else if ("version".equalsIgnoreCase(variable))
  390.                 replacement = VERSION;
  391.             else
  392.                 throw new IllegalArgumentException("Unknown variable: " + variable);
  393.  
  394.             // Assume the expanded variables are all packages, and append a dot
  395.             if (replacement.length() > 0 && matcher.end() < name.length() && name.charAt(matcher.end()) != '.')
  396.                 replacement += ".";
  397.             matcher.appendReplacement(output, Matcher.quoteReplacement(replacement));
  398.         }
  399.  
  400.         matcher.appendTail(output);
  401.         return output.toString();
  402.     }
  403. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top