/** * Copyright (c) 2011-2012, Lukas Eder, lukas.eder@gmail.com * All rights reserved. * * This software is licensed to you under the Apache License, Version 2.0 * (the "License"); You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * . Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * . Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * . Neither the name "jOOR" nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */
/** * * 为了满足自己的需求, 支持对私有属性和方法进行反射, 包括一直向上父类查找方法或属性, 因为完全暴露了方法和属性, 谨慎使用 * * A wrapper for an {@link Object} or {@link Class} upon which reflective calls * can be made. * <p> * An example of using <code>Reflect</code> is <code><pre> * // Static import all reflection methods to decrease verbosity * import static org.joor.Reflect.*; * * // Wrap an Object / Class / class name with the on() method: * on("java.lang.String") * // Invoke constructors using the create() method: * .create("Hello World") * // Invoke methods using the call() method: * .call("toString") * // Retrieve the wrapped object * * @author Lukas Eder */ publicclassReflect{ privatefinalstatic Logger logger = LoggerFactory.getLogger(Reflect.class);
publicstaticclassParam<T>{ Class<?> type; T value; publicParam(String name, T value){ Class<?> type = null; try { type = Class.forName(name); } catch (ClassNotFoundException e) { thrownew RuntimeException("class name:" + name, e); } this.type = type; this.value = value; }
// --------------------------------------------------------------------- // Static API used as entrance points to the fluent API // ---------------------------------------------------------------------
/** * Wrap a class name. * <p> * This is the same as calling <code>on(Class.forName(name))</code> * * @param name A fully qualified class name * @return A wrapped class object, to be used for further reflection. * * @see #on(Class) */ publicstatic Reflect on(String name){ return on(forName(name)); }
/** * Wrap a class. * <p> * Use this when you want to access static fields and methods on a * {@link Class} object, or as a basis for constructing objects of that * class using {@link #create(Object...)} * * @param clazz The class to be wrapped * @return A wrapped class object, to be used for further reflection. */ publicstatic Reflect on(Class<?> clazz){ returnnew Reflect(clazz); }
/** * Wrap an object. * <p> * Use this when you want to access instance fields and methods on any * {@link Object} * * @param object The object to be wrapped * @return A wrapped object, to be used for further reflection. */ publicstatic Reflect on(Object object){ returnnew Reflect(object); }
/** * Conveniently render an {@link java.lang.reflect.AccessibleObject} accessible * * @param accessible The object to render accessible * @return The argument object rendered accessible */ publicstatic <T extends AccessibleObject> T accessible(T accessible){ if (accessible == null) { returnnull; }
if (!accessible.isAccessible()) { accessible.setAccessible(true); }
return accessible; }
// --------------------------------------------------------------------- // Members // ---------------------------------------------------------------------
/** * The wrapped object */ privatefinal Object object;
/** * A flag indicating whether the wrapped object is a {@link Class} (for * accessing static fields and methods), or any other type of {@link Object} * (for accessing instance fields and methods). */ privatefinalboolean isClass;
// --------------------------------------------------------------------- // Fluent Reflection API // ---------------------------------------------------------------------
/** * Get the wrapped object * * @param <T> A convenience generic parameter for automatic unsafe casting */ @SuppressWarnings("unchecked") public <T> T get(){ return (T) object; }
/** * Set a field value. * <p> * This is roughly equivalent to {@link java.lang.reflect.Field#set(Object, Object)}. If the * wrapped object is a {@link Class}, then this will set a value to a static * member field. If the wrapped object is any other {@link Object}, then * this will set a value to an instance member field. * * @param name The field name * @param value The new field value * @return The same wrapped object, to be used for further reflection. * */ public Reflect set(String name, final Object value){ return doExecute(name, new Executor() { @Override public Reflect execute(Field field)throws Exception { field.set(object, unwrap(value)); return on(object); }}); }
/** * Get a field value. * <p> * This is roughly equivalent to {@link java.lang.reflect.Field#get(Object)}. If the wrapped * object is a {@link Class}, then this will get a value from a static * member field. If the wrapped object is any other {@link Object}, then * this will get a value from an instance member field. * <p> * If you want to "navigate" to a wrapped version of the field, use * {@link #field(String)} instead. * * @param name The field name * @return The field value * * @see #field(String) */ public <T> T get(String name){ return field(name).<T>get(); }
/** * Get a wrapped field. * <p> * This is roughly equivalent to {@link java.lang.reflect.Field#get(Object)}. If the wrapped * object is a {@link Class}, then this will wrap a static member field. If * the wrapped object is any other {@link Object}, then this wrap an * instance member field. * * @param name The field name * @return The wrapped field * */ public Reflect field(String name){ return doExecute(name, new Executor() { @Override public Reflect execute(Field field)throws Exception { return on(field.get(object)); }}); }
private Reflect doExecute(String name, Executor executor){ Class<?> useClazz = type(); Field field = null; do { try { field = useClazz.getDeclaredField(name); field = accessible(field); return executor.execute(field); } catch (NoSuchFieldException e) { // } catch (Exception e) { thrownew RuntimeException("class:" + useClazz + ", name:" + name, e); } if (field == null) { useClazz = useClazz.getSuperclass(); } } while (useClazz != null);
thrownew RuntimeException("don't execute on field[" + name + "]"); }
/** * Get a Map containing field names and wrapped values for the fields' * values. * <p> * If the wrapped object is a {@link Class}, then this will return static * fields. If the wrapped object is any other {@link Object}, then this will * return instance fields. * <p> * These two calls are equivalent <code><pre> * on(object).field("myField"); * on(object).fields().get("myField"); * </pre></code> * * @return A map containing field names and wrapped values. */ public Map<String, Reflect> fields(){ Map<String, Reflect> result = new LinkedHashMap<String, Reflect>();
for (Field field : type().getFields()) { if (!isClass ^ Modifier.isStatic(field.getModifiers())) { String name = field.getName(); result.put(name, field(name)); } }
return result; }
/** * Call a method by its name. * <p> * This is a convenience method for calling * <code>call(name, new Object[0])</code> * * @param name The method name * @return The wrapped method result or the same wrapped object if the * method returns <code>void</code>, to be used for further * reflection. * * @see #call(String, Object...) */ public Reflect call(String name){ return call(name, new Object[0]); }
/** * Call a method by its name. * <p> * This is roughly equivalent to {@link java.lang.reflect.Method#invoke(Object, Object...)}. * If the wrapped object is a {@link Class}, then this will invoke a static * method. If the wrapped object is any other {@link Object}, then this will * invoke an instance method. * <p> * Just like {@link java.lang.reflect.Method#invoke(Object, Object...)}, this will try to wrap * primitive types or unwrap primitive type wrappers if applicable. If * several methods are applicable, by that rule, the first one encountered * is called. i.e. when calling <code><pre> * on(...).call("method", 1, 1); * </pre></code> The first of the following methods will be called: * <code><pre> * public void method(int param1, Integer param2); * public void method(Integer param1, int param2); * public void method(Number param1, Number param2); * public void method(Number param1, Object param2); * public void method(int param1, Object param2); * </pre></code> * * @param name The method name * @param args The method arguments * @return The wrapped method result or the same wrapped object if the * method returns <code>void</code>, to be used for further * reflection. * */ public Reflect call(String name, Object... args){ //return doCall(name, types(args), args); Class<?>[] types; Object[] values = null; if (args == null) { types = new Class[0]; }else { types = new Class[args.length];
for (int i = 0; i < args.length; i++) { Object arg = args[i]; types[i] = (arg instanceof Param) ? ((Param)arg).type : arg.getClass(); }
values = new Object[args.length]; for (int i = 0; i < args.length; i++) { Object arg = args[i]; values[i] = (arg instanceof Param) ? ((Param)arg).value : arg; } }
thrownew RuntimeException("don't execute the method " + type() + "." + name + "(" + Arrays.toString(values) + ")"); }
/** * Call a constructor. * <p> * This is a convenience method for calling * <code>create(new Object[0])</code> * * @return The wrapped new object, to be used for further reflection. * * @see #create(Object...) */ public Reflect create(){ return create(new Object[0]); }
/** * Call a constructor. * <p> * This is roughly equivalent to {@link java.lang.reflect.Constructor#newInstance(Object...)}. * If the wrapped object is a {@link Class}, then this will create a new * object of that class. If the wrapped object is any other {@link Object}, * then this will create a new object of the same type. * <p> * Just like {@link java.lang.reflect.Constructor#newInstance(Object...)}, this will try to * wrap primitive types or unwrap primitive type wrappers if applicable. If * several constructors are applicable, by that rule, the first one * encountered is called. i.e. when calling <code><pre> * on(C.class).create(1, 1); * </pre></code> The first of the following constructors will be applied: * <code><pre> * public C(int param1, Integer param2); * public C(Integer param1, int param2); * public C(Number param1, Number param2); * public C(Number param1, Object param2); * public C(int param1, Object param2); * </pre></code> * * @param args The constructor arguments * @return The wrapped new object, to be used for further reflection. * */ public Reflect create(Object... args){ Class<?>[] types = types(args);
return doCreate(types, args); }
public Reflect create(Param<?>... args){ Class<?>[] types = null; Object[] values = null; if (args == null) { types = new Class[0]; }else { types = new Class[args.length];
for (int i = 0; i < args.length; i++) { types[i] = args[i].type; }
values = new Object[args.length]; for (int i = 0; i < args.length; i++) { values[i] = args[i].value; } }
return doCreate(types, values); }
private Reflect doCreate(Class<?>[] types, Object... args){ // Try invoking the "canonical" constructor, i.e. the one with exact // matching argument types try { Constructor<?> constructor = type().getConstructor(types); return on(constructor, args); }
// If there is no exact match, try to find one that has a "similar" // signature if primitive argument types are converted to their wrappers catch (NoSuchMethodException e) { for (Constructor<?> constructor : type().getDeclaredConstructors()) { if (match(constructor.getParameterTypes(), types)) { return on(constructor, args); } }
thrownew RuntimeException(e); } }
/** * Create a proxy for the wrapped object allowing to typesafely invoke * methods on it using a custom interface * * @param proxyType The interface type that is implemented by the proxy * @return A proxy for the wrapped object */ @SuppressWarnings("unchecked") public <P> P as(Class<P> proxyType){ final InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { return on(object).call(method.getName(), args).get(); } };
return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[] { proxyType }, handler); }
// --------------------------------------------------------------------- // Object API // ---------------------------------------------------------------------
/** * Check whether two arrays of types match, converting primitive types to * their corresponding wrappers. */ privatebooleanmatch(Class<?>[] declaredTypes, Class<?>[] actualTypes){ if (declaredTypes.length == actualTypes.length) { for (int i = 0; i < actualTypes.length; i++) { if (!wrapper(declaredTypes[i]).isAssignableFrom(wrapper(actualTypes[i]))) { returnfalse; } }
/** * Unwrap an object */ privatestatic Object unwrap(Object object){ if (object instanceof Reflect) { return ((Reflect) object).get(); }
return object; }
/** * Get an array of types for an array of objects * * @see Object#getClass() */ privatestatic Class<?>[] types(Object... values) { if (values == null) { returnnew Class[0]; }
Class<?>[] result = new Class[values.length];
for (int i = 0; i < values.length; i++) { result[i] = values[i].getClass(); }
/** * Get the type of the wrapped object. * * @see Object#getClass() */ public Class<?> type() { if (isClass) { return (Class<?>) object; } else { return object.getClass(); } }
/** * Get a wrapper type for a primitive type, or the argument type itself, if * it is not a primitive type. */ privatestatic Class<?> wrapper(Class<?> type) { if (boolean.class == type) { return Boolean.class; } elseif (int.class == type) { return Integer.class; } elseif (long.class == type) { return Long.class; } elseif (short.class == type) { return Short.class; } elseif (byte.class == type) { return Byte.class; } elseif (double.class == type) { return Double.class; } elseif (float.class == type) { return Float.class; } elseif (char.class == type) { return Character.class; } else { return type; } }