package xapi.reflect; import xapi.annotation.api.XApi; import xapi.annotation.compile.MagicMethod; import xapi.inject.X_Inject; import xapi.log.X_Log; import xapi.log.api.LogService; import xapi.reflect.service.ReflectionService; import xapi.util.X_Runtime; import xapi.util.X_String; import static xapi.util.X_String.joinClasses; import javax.inject.Inject; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.TypeVariable; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; /** * A set of static accessor classes to enable reflection in gwt. * <p> * Each method should be treated like GWT.create; you must send a Class literal, * not a class reference. * <p> * Literal: SomeClass.class<br> * Reference: Class<?> someClass; * <p> * Some methods will fail gracefully if you let a reference slip through. Gwt * production compiles will warn you if it can generate a sub-optimal solution, * (aka maps from reference to factory), but will throw an error if it cannot * deliver the functionality. * * @author James X. Nelson (james@wetheinter.net, @james) */ @XApi public class X_Reflect { @Inject static ReflectionService reflectionService = X_Inject.singleton(ReflectionService.class); @Inject static LogService logService; /** * Retrieve an object from a given array using the most efficient solution for * a given platform. In Gwt, this does a direct int index access on the * supplied array object; in a JRE, it uses {@link Array#get(Object, int)}. * <p> * Note that JREs will throw {@link IndexOutOfBoundsException}, while Gwt will * not. * * @param array * -> The array to load from * @param index * -> The index to load from. * @return The object at the specified index. */ public static Object arrayGet(final Object array, final int index) { if (X_Runtime.isJavaScript()) { return jsniGet(array, index); } else { return Array.get(array, index); } } /** * In jvms, we defer to java.lang.reflect.Array; In Gwt, we just return the * .length property. * * @param array * - Any array[] instance; java or js * @return - The number of elements in the []. */ public static int arrayLength(final Object array) { if (X_Runtime.isJavaScript()) { return jsniLength(array); } else { return Array.getLength(array); } } /** * @param c * -> The class whose unique int identifier we should return * <p> * In JVMs, hotswapped classes that should == will have different constIds. * GWT prod overrides returns a field, .constId, that we added to Class.java * via supersource. * <p> * Note that, in both cases, the ID returned is sequential, and will be * neither unique nor stable across multiple runtimes; multiple Gwt * applications will reuse sequential IDs, and multiple classes loaded from * different ClassLoaders or in different JVMs will assign random hashCodes to * each class. */ public static int constId(final Class<?> c) { // if (GWT.isClient()) { // return JsMemberPool.constId(c); // } else { return c.hashCode(); // } } /** * In gwt dev and standard jvms, this just calls * cls.getConstructor(...).newInstance(...); in gwt production, this is a * magic method which will generate calls to new T(params); Note that for Gwt * production to be fully optimized, you must always send class literals * (SomeClass.class) If you send a class reference (a Class<?> object), the * magic method injector will be forced to generate a monolithic helper class. * In gwt production, this method will avoid generating the magic class * metadata. * * @param cls * - The class on which to call .newInstance(); * @param paramSignature * - The constructor parameter signature. The array and it's contents must be * constants. * @param params * - The actual objects (which should be assignable to param signature). * @return A new instance of type T * @throws Throwable * - Standard reflection exceptions in java vms, generator-base exceptions in * js vms. InvocationTargetExceptions are unwrapped for you. This also forces * you to catch Errors, which may very well be thrown by gwt, or by the jvm */ @MagicMethod(documentation="Generated by com.google.gwt.reflect.rebind.injectors.ConstructInjector") public static <T> T construct(final Class<? extends T> cls, final Class<?>[] paramSignature, final Object... params) throws Throwable { assert isAssignable(paramSignature, params) : formatUnassignableError(cls, paramSignature, params); try { return makeAccessible(cls.getDeclaredConstructor(paramSignature)) .newInstance(params); } catch (final InvocationTargetException e) { throw e.getCause(); } } /** * Escapes string content to be a valid string literal. Copied directly from * com.google.gwt.core.ext.Generator#escape(String) * * @return an escaped version of <code>unescaped</code>, suitable for being * enclosed in double quotes in Java source */ public static String escape(final String unescaped) { if (X_Runtime.isJavaScript()) { return nativeEscape(unescaped); } int extra = 0; for (int in = 0, n = unescaped.length(); in < n; ++in) { switch (unescaped.charAt(in)) { case '\0': case '\n': case '\r': case '\"': case '\\': ++extra; break; } } if (extra == 0) { return unescaped; } final char[] oldChars = unescaped.toCharArray(); final char[] newChars = new char[oldChars.length + extra]; for (int in = 0, out = 0, n = oldChars.length; in < n; ++in, ++out) { char c = oldChars[in]; switch (c) { case '\0': newChars[out++] = '\\'; c = '0'; break; case '\n': newChars[out++] = '\\'; c = 'n'; break; case '\r': newChars[out++] = '\\'; c = 'r'; break; case '\"': newChars[out++] = '\\'; c = '"'; break; case '\\': newChars[out++] = '\\'; c = '\\'; break; } newChars[out] = c; } return String.valueOf(newChars); } private native static String nativeEscape(String unescaped) /*-{ return unescaped.replace(/(["'\\])/g, "\\$1").replace(/\n/g,"\\n"); }-*/; /** * Uses reflection to invoke a field getter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @return - cls.get(Declared)Field(name).get(inst); // Checks for declared * methods first Primitive return types will be boxed for you. * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ @SuppressWarnings("unchecked") public static <T> T fieldGet(final Class<?> cls, final String name, final Object inst) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); return (T) field.get(inst); } /** * Uses reflection to invoke a field getter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @return - cls.get(Declared)Field(name).getBoolean(inst); // Checks for * declared methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static boolean fieldGetBoolean(final Class<?> cls, final String name, final Object inst) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); return field.getBoolean(inst); } /** * Uses reflection to invoke a field getter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @return - cls.get(Declared)Field(name).getByte(inst); // Checks for * declared methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static byte fieldGetByte(final Class<?> cls, final String name, final Object inst) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); return field.getByte(inst); } /** * Uses reflection to invoke a field getter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @return - cls.get(Declared)Field(name).getChar(inst); // Checks for * declared methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static char fieldGetChar(final Class<?> cls, final String name, final Object inst) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); return field.getChar(inst); } /** * Uses reflection to invoke a field getter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @return - cls.get(Declared)Field(name).getDouble(inst); // Checks for * declared methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static double fieldGetDouble(final Class<?> cls, final String name, final Object inst) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); return field.getDouble(inst); } /** * Uses reflection to invoke a field getter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @return - cls.get(Declared)Field(name).getFloat(inst); // Checks for * declared methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static float fieldGetFloat(final Class<?> cls, final String name, final Object inst) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); return field.getFloat(inst); } /** * Uses reflection to invoke a field getter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @return - cls.get(Declared)Field(name).getInt(inst); // Checks for declared * methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static int fieldGetInt(final Class<?> cls, final String name, final Object inst) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); return field.getInt(inst); } /** * Uses reflection to invoke a field getter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @return - cls.get(Declared)Field(name).getLong(inst); // Checks for * declared methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static long fieldGetLong(final Class<?> cls, final String name, final Object inst) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); return field.getLong(inst); } /** * Uses reflection to invoke a field setter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @param value * - The long value to set to the field. Calls: * cls.get(Declared)Field(name).setLong(inst, value); // Checks for declared * methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void fieldGetLong(final Class<?> cls, final String name, final Object inst, final long value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); field.setLong(inst, value); } /** * Uses reflection to invoke a field getter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @return - cls.get(Declared)Field(name).getShort(inst); // Checks for * declared methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static short fieldGetShort(final Class<?> cls, final String name, final Object inst) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); return field.getShort(inst); } /** * Uses reflection to invoke a field setter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @param value * - The object value to set to the field. Calls: * cls.get(Declared)Field(name).set(inst, value); // Checks for declared * methods first Primitive boxing will NOT work here! * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void fieldSet(final Class<?> cls, final String name, final Object inst, final Object value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); field.set(inst, value); } /** * Uses reflection to invoke a field setter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @param value * - The boolean value to set to the field. Calls: * cls.get(Declared)Field(name).setBoolean(inst, value); // Checks for * declared methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void fieldSetBoolean(final Class<?> cls, final String name, final Object inst, final boolean value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); field.setBoolean(inst, value); } /** * Uses reflection to invoke a field setter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @param value * - The byte value to set to the field. Calls: * cls.get(Declared)Field(name).setByte(inst, value); // Checks for declared * methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void fieldSetByte(final Class<?> cls, final String name, final Object inst, final byte value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); field.setByte(inst, value); } /** * Uses reflection to invoke a field setter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @param value * - The char value to set to the field. Calls: * cls.get(Declared)Field(name).setChar(inst, value); // Checks for declared * methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void fieldSetChar(final Class<?> cls, final String name, final Object inst, final char value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); field.setChar(inst, value); } /** * Uses reflection to invoke a field setter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @param value * - The double value to set to the field. Calls: * cls.get(Declared)Field(name).setDouble(inst, value); // Checks for declared * methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void fieldSetDouble(final Class<?> cls, final String name, final Object inst, final double value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); field.setDouble(inst, value); } /** * Uses reflection to invoke a field setter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @param value * - The float value to set to the field. Calls: * cls.get(Declared)Field(name).setFloat(inst, value); // Checks for declared * methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void fieldSetFloat(final Class<?> cls, final String name, final Object inst, final float value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); field.setFloat(inst, value); } /** * Uses reflection to invoke a field setter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @param value * - The int value to set to the field. Calls: * cls.get(Declared)Field(name).setInt(inst, value); // Checks for declared * methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void fieldSetInt(final Class<?> cls, final String name, final Object inst, final int value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); field.setInt(inst, value); } /** * Uses reflection to invoke a field setter for you; using this method will * net you convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Field object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" get * objects from your objects. :D All parameters with descriptions starting * with a * will only work in GWT if they are either class literals, or * directly traceable to class literal fields. * * @param cls * - * The class on which to invoke a field get. * @param name * - * The name of the field to get from. * @param inst * - The instance object to get from (null for static fields) * @param value * - The short value to set to the field. Calls: * cls.get(Declared)Field(name).setShort(inst, value); // Checks for declared * methods first * @throws SecurityException * @throws NoSuchFieldException * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void fieldSetShort(final Class<?> cls, final String name, final Object inst, final short value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field field = jvmGetField(cls, name); field.setShort(inst, value); } public static <T> Constructor<T> getDeclaredConstructor(final Class<T> c, final Class<?>... params) { try { return makeAccessible(c.getDeclaredConstructor(params)); } catch (final NoSuchMethodException e) { log("Could not retrieve " + c + "(" + joinClasses(", ", params), e); throw new RuntimeException(e); } } @SuppressWarnings("unchecked") public static <T> Constructor<T>[] getDeclaredConstructors(final Class<T> c) { return Constructor[].class .cast(makeAccessible(c.getDeclaredConstructors())); } public static Field getDeclaredField(final Class<?> c, final String name) { try { return makeAccessible(c.getDeclaredField(name)); } catch (final NoSuchFieldException e) { log("Could not retrieve " + c + "." + name, e); throw new RuntimeException(e); } } public static Field[] getDeclaredFields(final Class<?> c) { return makeAccessible(c.getDeclaredFields()); } public static Method getDeclaredMethod(final Class<?> c, final String name, final Class<?>... params) { try { return makeAccessible(c.getDeclaredMethod(name, params)); } catch (final NoSuchMethodException e) { log( "Could not retrieve " + c + "." + name + "(" + joinClasses(", ", params), e); throw new RuntimeException(e); } } public static Method[] getDeclaredMethods(final Class<?> c) { return makeAccessible(c.getDeclaredMethods()); } public static Package getPackage(final String name) { if (X_Runtime.isJavaScript()) { return Package.getPackage(name); } else { return reflectionService.getPackage(name); } } public static <T> Constructor<T> getPublicConstructor(final Class<T> c, final Class<?>... params) { try { return c.getConstructor(params); } catch (final NoSuchMethodException e) { log( "Could not retrieve " + c + "(" + X_String.joinClasses(", ", params), e); throw new RuntimeException(e); } } @SuppressWarnings("unchecked") public static <T> Constructor<T>[] getPublicConstructors(final Class<T> c) { return Constructor[].class.cast(c.getConstructors()); } public static Field getPublicField(final Class<?> c, final String name) { try { return c.getField(name); } catch (final NoSuchFieldException e) { log("Could not retrieve " + c + "." + name, e); throw new RuntimeException(e); } } public static Field[] getPublicFields(final Class<?> c) { return c.getFields(); } public static Method getPublicMethod(final Class<?> c, final String name, final Class<?>... params) { try { return c.getMethod(name, params); } catch (final NoSuchMethodException e) { log("Could not retrieve " + c + "." + name + "(" + joinClasses(", ", params), e); throw new RuntimeException(e); } } public static Method[] getPublicMethods(final Class<?> c) { return c.getMethods(); } /** * Uses reflection to invoke a method for you; using this method will net you * convenience in a JRE environment, and maximal efficiency in a GWT * environment. By directly using this method, GWT will avoid creating an * actual Method object full of extra metadata you probably don't need, and * will simply generate the jsni accessor method needed to "reflectively" * access your objects. All parameters with descriptions starting with a * * will only work in GWT if they are either class literals, or directly * traceable to class literal fields. * * @param cls * - * The class on which to invoke a method. * @param name * - * The name of the method to invoke * @param paramTypes * - * An array of classes matching that of the method to invoke * @param inst * - The instance object to invoke the method as (null for static methods) * @param params * - The actual parameters to send to the object * @return - null for void methods, Objects or boxed primitives for everything * else. * @throws Throwable * - Throws throwable because InvocationTargetException is unwrapped for you. */ public static Object invokeDefaultMethod(final Class<?> cls, final String name, final Class<?>[] paramTypes, final Object inst, final Object... params) throws Throwable { Method method; try { method = makeAccessible(cls.getDeclaredMethod(name, paramTypes)); } catch (final NoSuchMethodException e) { method = cls.getMethod(name, paramTypes); } return reflectionService.invokeDefaultMethod(inst, method, params); } public static Object invoke(final Class<?> cls, final String name, final Class<?>[] paramTypes, final Object inst, final Object... params) throws Throwable { assert isAssignable(paramTypes, params) : formatUnassignableError(cls, paramTypes, params) + " for method named " + name; Method method; try { method = makeAccessible(cls.getDeclaredMethod(name, paramTypes)); } catch (final NoSuchMethodException e) { method = cls.getMethod(name, paramTypes); } try { if (!X_Runtime.isJavaScript() && inst == null && method.getDeclaringClass().isInterface() && !Modifier.isAbstract(method.getModifiers())) { return reflectionService.invokeDefaultMethod(method, params); } if (method.getReturnType() == void.class) { method.invoke(inst, params); return null; } else { return method.invoke(inst, params); } } catch (final InvocationTargetException e) { throw e.getCause(); } } public static native Object jsniGet(Object array, int index) /*-{ return array[index]; }-*/; public static native int jsniLength(Object array) /*-{ return array.length; }-*/; /** * Ensures that a given class has all its reflection data filled in. A magic * method injection optimizes this in production mode. You MUST send a class * literal for this process to work in production. Work is in progress to * create a monolithic runtime factory, so when a non-constant literal is * encountered, the prod mode implementation can do a runtime lookup of the * type. A flag may be created to allow class refs to fall through and do * nothing, but a do-nothing call should just be erased, not worked around. * Gwt dev and standard jvms will just call standard reflection methods, so * they do nothing to make a class magic. * * @param cls * - The class to enhance in gwt production mode. * @return - The same class, casted to a compatible generic supertype. */ @SuppressWarnings("unchecked") public static <T> Class<T> magicClass(final Class<? extends T> cls) { assert cls != null; return Class.class.cast(cls); } /** * For the time being you MUST send only class literals to this method. * <p> * Returns a new Typed[size], null-initialized and properly typed. Utilizes * standard java array reflection in gwt dev and plain jvms. * <p> * If you want to create multi dimensional arrays with only one dimension * defined, just call SomeType[][][] array = newArray(SomeType[][].class, 2); * <p> * It you need to create primitive arrays, prefer * {@link Array#newInstance(Class, int)}, which returns type Object, and cast * it yourself. Because this type signature is generic, int, double and * friends will auto-box. The only difference between this method and the one * from java.lang.reflect.Array is the return type is typesafely cast for you. * <p> * In gwt production mode, this method call is replaced with new * T[dimensions[0]][dimensions[1]...[]; * <p> * Failing to use a class literal will currently make the compiler fail, and * will eventually resort to a runtime lookup in the ConstPool to get a seed * array to clone. * * @param classLit * - The class for which a new array will be created. * @param size * - The size of the new array. * @return new T[dimension] */ public static <T> T[] newArray(final Class<T> classLit, final int size) { return reflectionService.newArray(classLit, size); } /** * For the time being you MUST send only class literals to this method. * <p> * Returns a two-dimensional array, with the inner two dimensions filled in. * Utilizes standard java array reflection in gwt dev and plain jvms. * <p> * If you want to create complex multi-dimensional arrays this method will * fill in the two inner dimensions of whatever class you send (array classes * welcome). SomeType[][][][] array = newArray(SomeType[][].class, 4, 4); * <p> * It you need to create primitive arrays, or more complex multi-dimensional * arrays, prefer {@link Array#newInstance(Class, int ...)}, which returns * type Object, and cast it yourself. Because this type signature is generic, * int, double and friends will auto-box. The only difference between this * method and the one from java.lang.reflect.Array is the return type is * typesafely cast for you. * <p> * In gwt production mode, this method call is replaced with new * T[dimensions[0]][dimensions[1]...[]; * <p> * Failing to use a class literal will currently make the compiler fail, and * will eventually resort to a runtime lookup in the ConstPool to get a seed * array to clone. * * @param classLit * - The class for which a new array will be created. * @param dim1 * - The size of the new array's inner dimension. * @param dim1 * - The size of the new array's outer dimension. * @return new T[dim1][dim2]; */ public static <T, R extends T> T[][] newArray(final Class<R> classLit, final int dim1, final int dim2) { return reflectionService.newArray(classLit, dim1, dim2); } public static <T extends Throwable> T doThrow(final T exception) throws T { throw exception; } private static int assignableDepth(final Class<?>[] paramSignature, final Object[] params) { if (paramSignature.length != params.length) { return 0; } for (int i = paramSignature.length; i-- > 0;) { final Class<?> sig = paramSignature[i]; final Object param = params[i]; if (sig.isPrimitive()) { if (param == null) { return i; } // Gwt dev gets a crack at handling boxing. if (sig.getName().equalsIgnoreCase(param.getClass().getSimpleName())) { continue; } if (sig == int.class && param instanceof Integer) { continue; } if (sig == char.class && param instanceof Character) { continue; } return i; } else { if (!sig.isAssignableFrom(param.getClass())) { return i; } } } return -1; } //js: @UnsafeNativeLong private static Long boxLong(final long l) { return new Long(l); } private static String formatUnassignableError(final Class<?> cls, final Class<?>[] paramSignature, final Object... params) { final int depth = assignableDepth(paramSignature, params); return "Unassignable parameter signature for class " + cls.getName() + "; mismatch on parameter " + depth + "\n Signature type was " + paramSignature[depth].getName() + "; object was " + (params[depth] == null ? "null" : " a " + params[depth].getClass().getName() + " : " + params[depth]); } private static boolean isAssignable(final Class<?>[] paramSignature, final Object[] params) { return assignableDepth(paramSignature, params) == -1; } private static Field jvmGetField(Class<?> cls, final String name) throws NoSuchFieldException { try { // Prefer the declared, unaccesible field return makeAccessible(cls.getDeclaredField(name)); } catch (final NoSuchFieldException e0) { try { // Next, check superclasses for public fields return cls.getField(name); } catch (final NoSuchFieldException e1) { // Finally, check all superclasses for private/protected fields while (true) { cls = cls.getSuperclass(); if (cls == Object.class || cls == null) { throw new NoSuchFieldException("No field " + name + " in " + cls); } try { return makeAccessible(cls.getDeclaredField(name)); } catch (final NoSuchFieldException e2) { continue; } } } } } private static void log(final String string, final Throwable e) { if (logService == null) { X_Log.error(X_Reflect.class, string, e); } else { logService.error(X_Reflect.class, string, e); } } private static <T extends AccessibleObject> T makeAccessible(final T member) { // TODO use security manager if (!member.isAccessible()) { member.setAccessible(true); } return member; } private static <T extends AccessibleObject> T[] makeAccessible( final T[] members) { for (final T member : members) { makeAccessible(member); } return members; } private static void nullCheck(final Object o) { if (o == null) { throw new NullPointerException("Null is not allowed"); } } //js: @UnsafeNativeLong private static long unboxLong(final Number l) { return l.longValue(); } private X_Reflect() { } public static String className(Object value) { return value == null ? "<null>" : value.getClass().getCanonicalName(); } public static <T> Class<? extends T> getGenericAsClass(Object from) { return getGenericAsClass(from, 0); } public static <T> Class<? extends T> getGenericAsClass(Object from, int position) { assert from != null : "Do not send null objects to X_Reflect.getGenericsAsClass"; final TypeVariable<? extends Class<?>>[] params = from.getClass().getTypeParameters(); assert params.length > position : "Requested a generic type from position " + position +" of type " + from.getClass()+", but no type variable found on object " + from; return (Class<? extends T>) params[position].getGenericDeclaration(); } public static String getFileLoc(Class<?> mainClass) { assert mainClass != null : "Do not send null class to X_Reflect.getFileLoc"; URL loc = null; final ProtectionDomain domain = mainClass.getProtectionDomain(); if (domain != null) { final CodeSource source = mainClass.getProtectionDomain().getCodeSource(); if (source != null) { loc = source.getLocation(); } } if (loc == null) { loc = mainClass.getClassLoader().getResource(mainClass.getCanonicalName().replace('.', '/')+".class"); } boolean isJar = loc.getProtocol().equals("jar") || loc.toExternalForm().contains("jar!"); if (isJar) { // When the main class is in a jar, we need to make sure that jar is on the classpath String jar = loc.toExternalForm().replace("jar:", "").split("jar!")[0] + ".jar"; return jar; } else { // location is a source path; add it directly return loc.toExternalForm().replace("file:", ""); } } }