blob: 1cc05ecc92cb090b7f1bd6efefed39fc2ea3c261 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.enhance;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.Localizer.Message;
import org.apache.openjpa.lib.util.Reflectable;
import org.apache.openjpa.lib.util.StringUtil;
import org.apache.openjpa.lib.util.collections.AbstractReferenceMap.ReferenceStrength;
import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap;
import org.apache.openjpa.util.GeneralException;
import org.apache.openjpa.util.UserException;
/**
* Reflection utilities used to support and augment enhancement. Used both
* at enhancement time and at runtime.
*
* @author Abe White
*/
public class Reflection {
private static final Localizer _loc = Localizer.forPackage
(Reflection.class);
// Weak HashMap caches of getter/setter/beanProperty methods
private static Map<Class<?>, Map<String, Method>> getterMethodCache =
new ConcurrentReferenceHashMap(ReferenceStrength.WEAK, ReferenceStrength.HARD);
private static Map<Class<?>, Map<String, Method>> setterMethodCache =
new ConcurrentReferenceHashMap(ReferenceStrength.WEAK, ReferenceStrength.HARD);
private static Map<Class<?>, Set<String>> beanPropertiesNameCache =
new ConcurrentReferenceHashMap(ReferenceStrength.WEAK, ReferenceStrength.HARD);
private static Method getGetterMethod(Class<?> cls, String prop) {
Method rtnMethod = null;
Map<String, Method> clsMap = getterMethodCache.get(cls);
if (clsMap != null) {
rtnMethod = clsMap.get(prop);
}
return rtnMethod;
}
private static void setGetterMethod(Class<?> cls, String prop,
Method method) {
Map<String, Method> clsMap = getterMethodCache.get(cls);
if (clsMap == null) {
clsMap = new ConcurrentReferenceHashMap(ReferenceStrength.HARD,
ReferenceStrength.WEAK);
getterMethodCache.put(cls, clsMap);
}
clsMap.put(prop, method);
}
private static Method getSetterMethod(Class<?> cls, String prop) {
Method rtnMethod = null;
Map<String, Method> clsMap = setterMethodCache.get(cls);
if (clsMap != null) {
rtnMethod = clsMap.get(prop);
}
return rtnMethod;
}
private static void setSetterMethod(Class<?> cls, String prop,
Method method) {
Map<String, Method> clsMap = setterMethodCache.get(cls);
if (clsMap == null) {
clsMap = new ConcurrentReferenceHashMap(ReferenceStrength.HARD,
ReferenceStrength.WEAK);
setterMethodCache.put(cls, clsMap);
}
clsMap.put(prop, method);
}
/**
* Return the getter method matching the given property name, optionally
* throwing an exception if none.
*/
public static Method findGetter(Class cls, String prop, boolean mustExist) {
Method m = getGetterMethod(cls, prop);
if (m != null) {
return m;
}
String capProp = StringUtil.capitalize(prop);
try {
// this algorithm searches for a get<prop> or is<prop> method in
// a breadth-first manner.
for (Class c = cls; c != null && c != Object.class;
c = c.getSuperclass()) {
m = getDeclaredMethod(c, "get" + capProp, null);
if (m != null) {
setGetterMethod(cls, prop, m);
return m;
} else {
m = getDeclaredMethod(c, "is" + capProp, null);
if (m != null && (m.getReturnType() == boolean.class
|| m.getReturnType() == Boolean.class)) {
setGetterMethod(cls, prop, m);
return m;
} else {
m = getDeclaredMethod(c, "get" + prop, null);
if (m != null) {
setGetterMethod(cls, prop, m);
return m;
} else {
m = getDeclaredMethod(c, "is" + prop, null);
if (m != null
&& (m.getReturnType() == boolean.class || m.getReturnType() == Boolean.class)) {
setGetterMethod(cls, prop, m);
return m;
}
}
}
}
}
} catch (Exception e) {
throw new GeneralException(e);
}
if (mustExist)
throw new UserException(_loc.get("bad-getter", cls, prop));
return null;
}
/**
* Return the setter method matching the given property name, optionally
* throwing an exception if none. The property must also have a getter.
*/
public static Method findSetter(Class cls, String prop, boolean mustExist) {
Method getter = findGetter(cls, prop, mustExist);
return (getter == null) ? null
: findSetter(cls, prop, getter.getReturnType(), mustExist);
}
/**
* Return the setter method matching the given property name, optionally
* throwing an exception if none.
*/
public static Method findSetter(Class cls, String prop, Class param,
boolean mustExist) {
Method m = getSetterMethod(cls, prop);
if (m != null) {
return m;
}
String name = "set" + StringUtil.capitalize(prop);
try {
for (Class c = cls; c != null && c != Object.class;
c = c.getSuperclass()) {
m = getDeclaredMethod(c, name, param);
if (m != null) {
setSetterMethod(cls, prop, m);
return m;
}
}
} catch (Exception e) {
throw new GeneralException(e);
}
if (mustExist)
throw new UserException(_loc.get("bad-setter", cls, prop));
return null;
}
/**
* Invokes <code>cls.getDeclaredMethods()</code>, and returns the method
* that matches the <code>name</code> and <code>param</code> arguments.
* Avoids the exception thrown by <code>Class.getDeclaredMethod()</code>
* for performance reasons. <code>param</code> may be null. Additionally,
* if there are multiple methods with different return types, this will
* return the method defined in the least-derived class.
*
* @since 0.9.8
*/
static Method getDeclaredMethod(Class cls, String name,
Class param) {
Method[] methods = (Method[]) AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredMethodsAction(cls));
Method candidate = null;
for (Method method : methods) {
if (name.equals(method.getName())) {
Class[] methodParams = method.getParameterTypes();
if (param == null && methodParams.length == 0)
candidate = mostDerived(method, candidate);
else if (param != null && methodParams.length == 1
&& param.equals(methodParams[0]))
candidate = mostDerived(method, candidate);
}
}
return candidate;
}
static Method mostDerived(Method meth1, Method meth2) {
if (meth1 == null)
return meth2;
if (meth2 == null)
return meth1;
Class cls2 = meth2.getDeclaringClass();
Class cls1 = meth1.getDeclaringClass();
if (cls1.equals(cls2)) {
Class ret1 = meth1.getReturnType();
Class ret2 = meth2.getReturnType();
if (ret1.isAssignableFrom(ret2))
return meth2;
else if (ret2.isAssignableFrom(ret1))
return meth1;
else
throw new IllegalArgumentException(
_loc.get("most-derived-unrelated-same-type", meth1, meth2)
.getMessage());
} else {
if (cls1.isAssignableFrom(cls2))
return meth2;
else if (cls2.isAssignableFrom(cls1))
return meth1;
else
throw new IllegalArgumentException(
_loc.get("most-derived-unrelated", meth1, meth2)
.getMessage());
}
}
/**
* Return the field with the given name, optionally throwing an exception
* if none.
*/
public static Field findField(Class cls, String name, boolean mustExist) {
try {
Field f;
for (Class c = cls; c != null && c != Object.class;
c = c.getSuperclass()) {
f = getDeclaredField(c, name);
if (f != null)
return f;
}
} catch (Exception e) {
throw new GeneralException(e);
}
if (mustExist)
throw new UserException(_loc.get("bad-field", cls, name));
return null;
}
/**
* Invokes <code>cls.getDeclaredFields()</code>, and returns the field
* that matches the <code>name</code> argument. Avoids the exception
* thrown by <code>Class.getDeclaredField()</code> for performance reasons.
*
* @since 0.9.8
*/
private static Field getDeclaredField(Class cls, String name) {
Field[] fields = AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredFieldsAction(cls));
for (Field field : fields) {
if (name.equals(field.getName()))
return field;
}
return null;
}
/**
* Return the value of the given field in the given object.
*/
public static Object get(Object target, Field field) {
if (target == null || field == null)
return null;
makeAccessible(field, field.getModifiers());
try {
return field.get(target);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-field", target, field));
}
}
/**
* Get the value of the given named field or a corresponding getter method.
*
* @return null if the field does not exist and mustExist is set to false or
* the given target is null.
*
* @exception UserException if mustExist is true and the field or getter
* method is non-existent
*/
public static Object getValue(Object obj, String prop, boolean mustExist) {
if (obj == null)
return null;
Class cls = obj.getClass();
Field field = findField(cls, prop, false);
if (field != null)
return get(obj, field);
Method getter = findGetter(cls, prop, false);
if (getter != null)
return get(obj, getter);
if (mustExist)
throw new UserException(_loc.get("bad-field", cls, prop));
return null; // should not reach
}
/**
* Make the given member accessible if it isn't already.
*/
private static void makeAccessible(AccessibleObject ao, int mods) {
try {
if (!Modifier.isPublic(mods) && !ao.isAccessible())
AccessController.doPrivileged(J2DoPrivHelper
.setAccessibleAction(ao, true));
} catch (SecurityException se) {
throw new UserException(_loc.get("reflect-security", ao)).
setFatal(true);
}
}
/**
* Wrap the given reflection exception as a runtime exception.
*/
private static RuntimeException wrapReflectionException(Throwable t, Message message) {
if (t instanceof InvocationTargetException)
t = ((InvocationTargetException) t).getTargetException();
t.initCause(new IllegalArgumentException(message.getMessage()));
if (t instanceof RuntimeException)
return (RuntimeException) t;
return new GeneralException(t);
}
/**
* Return the value of the given field in the given object.
*/
public static boolean getBoolean(Object target, Field field) {
if (target == null || field == null)
return false;
makeAccessible(field, field.getModifiers());
try {
return field.getBoolean(target);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-field", target, field));
}
}
/**
* Return the value of the given field in the given object.
*/
public static byte getByte(Object target, Field field) {
if (target == null || field == null)
return (byte) 0;
makeAccessible(field, field.getModifiers());
try {
return field.getByte(target);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-field", target, field));
}
}
/**
* Return the value of the given field in the given object.
*/
public static char getChar(Object target, Field field) {
if (target == null || field == null)
return (char) 0;
makeAccessible(field, field.getModifiers());
try {
return field.getChar(target);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-field", target, field));
}
}
/**
* Return the value of the given field in the given object.
*/
public static double getDouble(Object target, Field field) {
if (target == null || field == null)
return 0D;
makeAccessible(field, field.getModifiers());
try {
return field.getDouble(target);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-field", target, field));
}
}
/**
* Return the value of the given field in the given object.
*/
public static float getFloat(Object target, Field field) {
if (target == null || field == null)
return 0F;
makeAccessible(field, field.getModifiers());
try {
return field.getFloat(target);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-field", target, field));
}
}
/**
* Return the value of the given field in the given object.
*/
public static int getInt(Object target, Field field) {
if (target == null || field == null)
return 0;
makeAccessible(field, field.getModifiers());
try {
return field.getInt(target);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-field", target, field));
}
}
/**
* Return the value of the given field in the given object.
*/
public static long getLong(Object target, Field field) {
if (target == null || field == null)
return 0L;
makeAccessible(field, field.getModifiers());
try {
return field.getLong(target);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-field", target, field));
}
}
/**
* Return the value of the given field in the given object.
*/
public static short getShort(Object target, Field field) {
if (target == null || field == null)
return (short) 0;
makeAccessible(field, field.getModifiers());
try {
return field.getShort(target);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-field", target, field));
}
}
/**
* Return the return value of the given getter in the given object.
*/
public static Object get(Object target, Method getter) {
if (target == null || getter == null)
return null;
makeAccessible(getter, getter.getModifiers());
try {
return getter.invoke(target, (Object[]) null);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("get-method", target, getter));
}
}
/**
* Return the return value of the given getter in the given object.
*/
public static boolean getBoolean(Object target, Method getter) {
Object o = get(target, getter);
return (o == null) ? false : (Boolean) o;
}
/**
* Return the return value of the given getter in the given object.
*/
public static byte getByte(Object target, Method getter) {
Object o = get(target, getter);
return (o == null) ? (byte) 0 : ((Number) o).byteValue();
}
/**
* Return the return value of the given getter in the given object.
*/
public static char getChar(Object target, Method getter) {
Object o = get(target, getter);
return (o == null) ? (char) 0 : (Character) o;
}
/**
* Return the return value of the given getter in the given object.
*/
public static double getDouble(Object target, Method getter) {
Object o = get(target, getter);
return (o == null) ? 0D : ((Number) o).doubleValue();
}
/**
* Return the return value of the given getter in the given object.
*/
public static float getFloat(Object target, Method getter) {
Object o = get(target, getter);
return (o == null) ? 0F : ((Number) o).floatValue();
}
/**
* Return the return value of the given getter in the given object.
*/
public static int getInt(Object target, Method getter) {
Object o = get(target, getter);
return (o == null) ? 0 : ((Number) o).intValue();
}
/**
* Return the return value of the given getter in the given object.
*/
public static long getLong(Object target, Method getter) {
Object o = get(target, getter);
return (o == null) ? 0L : ((Number) o).longValue();
}
/**
* Return the return value of the given getter in the given object.
*/
public static short getShort(Object target, Method getter) {
Object o = get(target, getter);
return (o == null) ? (short) 0 : ((Number) o).shortValue();
}
/**
* Set the value of the given field in the given object.
*/
public static void set(Object target, Field field, Object value) {
if (target == null || field == null)
return;
makeAccessible(field, field.getModifiers());
try {
field.set(target, value);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-field", new Object[]{target, field, value,
value == null ? "" : value.getClass()}));
}
}
/**
* Set the value of the given field in the given object.
*/
public static void set(Object target, Field field, boolean value) {
if (target == null || field == null)
return;
makeAccessible(field, field.getModifiers());
try {
field.setBoolean(target, value);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-field", new Object[]{target, field, value, "boolean"}));
}
}
/**
* Set the value of the given field in the given object.
*/
public static void set(Object target, Field field, byte value) {
if (target == null || field == null)
return;
makeAccessible(field, field.getModifiers());
try {
field.setByte(target, value);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-field", new Object[]{target, field, value, "byte"}));
}
}
/**
* Set the value of the given field in the given object.
*/
public static void set(Object target, Field field, char value) {
if (target == null || field == null)
return;
makeAccessible(field, field.getModifiers());
try {
field.setChar(target, value);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-field", new Object[]{target, field, value, "char"}));
}
}
/**
* Set the value of the given field in the given object.
*/
public static void set(Object target, Field field, double value) {
if (target == null || field == null)
return;
makeAccessible(field, field.getModifiers());
try {
field.setDouble(target, value);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-field", new Object[]{target, field, value, "double"}));
}
}
/**
* Set the value of the given field in the given object.
*/
public static void set(Object target, Field field, float value) {
if (target == null || field == null)
return;
makeAccessible(field, field.getModifiers());
try {
field.setFloat(target, value);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-field", new Object[]{target, field, value, "float"}));
}
}
/**
* Set the value of the given field in the given object.
*/
public static void set(Object target, Field field, int value) {
if (target == null || field == null)
return;
makeAccessible(field, field.getModifiers());
try {
field.setInt(target, value);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-field", new Object[]{target, field, value, "int"}));
}
}
/**
* Set the value of the given field in the given object.
*/
public static void set(Object target, Field field, long value) {
if (target == null || field == null)
return;
makeAccessible(field, field.getModifiers());
try {
field.setLong(target, value);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-field", new Object[]{target, field, value, "long"}));
}
}
/**
* Set the value of the given field in the given object.
*/
public static void set(Object target, Field field, short value) {
if (target == null || field == null)
return;
makeAccessible(field, field.getModifiers());
try {
field.setShort(target, value);
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-field", new Object[]{target, field, value, "short"}));
}
}
/**
* Set the value of the given field in the given object.
* Same behavior as above methods, but parameter ordering is rearranged
* to simplify usage from generated bytecodes.
*
* @since 1.0.0
*/
public static void set(Object target, Object value, Field field) {
set(target, field, value);
}
/**
* Set the value of the given field in the given object.
* Same behavior as above methods, but parameter ordering is rearranged
* to simplify usage from generated bytecodes.
*
* @since 1.0.0
*/
public static void set(Object target, boolean value, Field field) {
set(target, field, value);
}
/**
* Set the value of the given field in the given object.
* Same behavior as above methods, but parameter ordering is rearranged
* to simplify usage from generated bytecodes.
*
* @since 1.0.0
*/
public static void set(Object target, byte value, Field field) {
set(target, field, value);
}
/**
* Set the value of the given field in the given object.
* Same behavior as above methods, but parameter ordering is rearranged
* to simplify usage from generated bytecodes.
*
* @since 1.0.0
*/
public static void set(Object target, char value, Field field) {
set(target, field, value);
}
/**
* Set the value of the given field in the given object.
* Same behavior as above methods, but parameter ordering is rearranged
* to simplify usage from generated bytecodes.
*
* @since 1.0.0
*/
public static void set(Object target, double value, Field field) {
set(target, field, value);
}
/**
* Set the value of the given field in the given object.
* Same behavior as above methods, but parameter ordering is rearranged
* to simplify usage from generated bytecodes.
*
* @since 1.0.0
*/
public static void set(Object target, float value, Field field) {
set(target, field, value);
}
/**
* Set the value of the given field in the given object.
* Same behavior as above methods, but parameter ordering is rearranged
* to simplify usage from generated bytecodes.
*
* @since 1.0.0
*/
public static void set(Object target, int value, Field field) {
set(target, field, value);
}
/**
* Set the value of the given field in the given object.
* Same behavior as above methods, but parameter ordering is rearranged
* to simplify usage from generated bytecodes.
*
* @since 1.0.0
*/
public static void set(Object target, long value, Field field) {
set(target, field, value);
}
/**
* Set the value of the given field in the given object.
* Same behavior as above methods, but parameter ordering is rearranged
* to simplify usage from generated bytecodes.
*
* @since 1.0.0
*/
public static void set(Object target, short value, Field field) {
set(target, field, value);
}
/**
* Invoke the given setter on the given object.
*/
public static void set(Object target, Method setter, Object value) {
if (target == null || setter == null)
return;
makeAccessible(setter, setter.getModifiers());
try {
setter.invoke(target, new Object[] { value });
} catch (Throwable t) {
throw wrapReflectionException(t, _loc.get("set-method", new Object[]{target, setter, value,
value == null ? "" : value.getClass()}));
}
}
/**
* Invoke the given setter on the given object.
*/
public static void set(Object target, Method setter, boolean value) {
set(target, setter, (value) ? Boolean.TRUE : Boolean.FALSE);
}
/**
* Invoke the given setter on the given object.
*/
public static void set(Object target, Method setter, byte value) {
set(target, setter, Byte.valueOf(value));
}
/**
* Invoke the given setter on the given object.
*/
public static void set(Object target, Method setter, char value) {
set(target, setter, Character.valueOf(value));
}
/**
* Invoke the given setter on the given object.
*/
public static void set(Object target, Method setter, double value) {
set(target, setter, new Double(value));
}
/**
* Invoke the given setter on the given object.
*/
public static void set(Object target, Method setter, float value) {
set(target, setter, new Float(value));
}
/**
* Invoke the given setter on the given object.
*/
public static void set(Object target, Method setter, int value) {
set(target, setter, Integer.valueOf(value));
}
/**
* Invoke the given setter on the given object.
*/
public static void set(Object target, Method setter, long value) {
set(target, setter, Long.valueOf(value));
}
/**
* Invoke the given setter on the given object.
*/
public static void set(Object target, Method setter, short value) {
set(target, setter, Short.valueOf(value));
}
/**
* Gets all bean-style property names of the given Class or its superclass.
* A bean-style property 'abc' exists in Class C iff C has declared
* following pair of methods:
* public void setAbc(Y y) or public C setAbc(Y y)
* public Y getAbc();
*
* If a getter property is annotated with {@link Reflectable}, then
* it is ignored.
*
*/
public static Set<String> getBeanStylePropertyNames(Class<?> c) {
if (c == null)
return Collections.emptySet();
Set<String> result = beanPropertiesNameCache.get(c);
if (result != null) {
return result;
}
Method[] methods = c.getMethods();
if (methods == null || methods.length < 2)
return Collections.emptySet();
result = new TreeSet<>();
for (Method m : methods) {
if (m.getName().startsWith("get")) {
if (!canReflect(m))
continue;
String prop = StringUtil.capitalize(m.getName()
.substring("get".length()));
Class<?> rtype = m.getReturnType();
try {
Method setter = c.getMethod("set"+prop, new Class<?>[]{rtype});
if (setter.getReturnType() == void.class ||
setter.getReturnType().isAssignableFrom(c))
result.add(prop);
} catch (NoSuchMethodException e) {
}
}
}
beanPropertiesNameCache.put(c, result);
return result;
}
/**
* Gets all public field names of the given Class.
*
*/
public static Set<String> getPublicFieldNames(Class c) {
if (c == null)
return Collections.EMPTY_SET;
Field[] fields = c.getFields();
if (fields == null || fields.length == 0)
return Collections.EMPTY_SET;
Set<String> result = new TreeSet<>();
for (Field f : fields) {
if (canReflect(f))
result.add(f.getName());
}
return result;
}
/**
* Gets values of all field f the given class such that f exactly
* match the given modifiers and are of given type (Object implies any type)
* unless f is annotated as {@link Reflectable}.
*
*/
public static <T> Set<T> getFieldValues(Class c, int mods, Class<T> t){
if (c == null)
return Collections.EMPTY_SET;
Field[] fields = c.getFields();
if (fields == null || fields.length == 0)
return Collections.EMPTY_SET;
Set<T> result = new TreeSet<>();
for (Field f : fields) {
if (mods == f.getModifiers()
&& (t == Object.class || t.isAssignableFrom(f.getType()))
&& canReflect(f)) {
try {
result.add((T)f.get(null));
} catch (IllegalArgumentException | IllegalAccessException e) {
}
}
}
return result;
}
/**
* Affirms if the given member is selected for reflection. The decision is
* based on the following truth table on both the class-level and
* member-level annotation (null annotation represents MAYBE)
*
* Class member
* MAYBE MAYBE YES
* MAYBE YES YES
* MAYBE NO NO
*
* YES MAYBE YES
* YES YES YES
* YES NO NO
*
* NO YES YES
* NO MAYBE NO
* NO NO NO
*
*/
static boolean canReflect(Reflectable cls, Reflectable member) {
if (cls == null || cls.value()) {
return member == null || member.value();
} else {
return member != null && member.value();
}
}
/**
* Affirms if the original declaration the given field is annotated
* for reflection.
*/
static boolean canReflect(Field field) {
Class cls = field.getDeclaringClass();
return canReflect((Reflectable)cls.getAnnotation(Reflectable.class),
field.getAnnotation(Reflectable.class));
}
/**
* Affirms if the original declaration the given method is annotated
* for reflection.
*/
static boolean canReflect(Method method) {
Class cls = getDeclaringClass(method);
if (cls != method.getDeclaringClass())
method = getDeclaringMethod(cls, method);
return canReflect((Reflectable)cls.getAnnotation(Reflectable.class),
method.getAnnotation(Reflectable.class));
}
/**
* Gets the declaring class of the given method signature but also checks
* if the method is declared in an interface. If yes, then returns the
* interface.
*/
public static Class getDeclaringClass(Method m) {
if (m == null)
return null;
Class cls = m.getDeclaringClass();
Class[] intfs = cls.getInterfaces();
for (Class intf : intfs) {
if (getDeclaringMethod(intf, m) != null)
cls = intf;
}
return cls;
}
/**
* Gets the method in the given class that has the same signature of the
* given method, if exists. Otherwise, null.
*/
public static Method getDeclaringMethod(Class c, Method m) {
try {
Method m0 = c.getMethod(m.getName(), m.getParameterTypes());
return m0;
} catch (Exception e) {
return null;
}
}
}