package me.ele.amigo.reflect;
import android.text.TextUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
public class FieldUtils {
private static Map<String, Field> sFieldCache = new HashMap<String, Field>();
private static String getKey(Class<?> cls, String fieldName) {
StringBuilder sb = new StringBuilder();
sb.append(cls.toString()).append("#").append(fieldName);
return sb.toString();
}
private static Field getField(Class<?> cls, String fieldName, final boolean forceAccess) {
Validate.assertTrue(cls != null, "The class must not be null");
Validate.assertTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty");
String key = getKey(cls, fieldName);
Field cachedField;
synchronized (sFieldCache) {
cachedField = sFieldCache.get(key);
}
if (cachedField != null) {
if (forceAccess && !cachedField.isAccessible()) {
cachedField.setAccessible(true);
}
return cachedField;
}
// check up the superclass hierarchy
for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
try {
final Field field = acls.getDeclaredField(fieldName);
// getDeclaredField checks for non-public scopes as well
// and it returns accurate results
if (!Modifier.isPublic(field.getModifiers())) {
if (forceAccess) {
field.setAccessible(true);
} else {
continue;
}
}
synchronized (sFieldCache) {
sFieldCache.put(key, field);
}
return field;
} catch (final NoSuchFieldException ex) { // NOPMD
// ignore
}
}
// check the public interface case. This must be manually searched for
// incase there is a public supersuperclass field hidden by a private/package
// superclass field.
Field match = null;
for (final Class<?> class1 : Utils.getAllInterfaces(cls)) {
try {
final Field test = class1.getField(fieldName);
Validate.assertTrue(match == null, "Reference to field %s is ambiguous relative to %s"
+ "; a matching field exists on two or more implemented interfaces.",
fieldName, cls);
match = test;
} catch (final NoSuchFieldException ex) { // NOPMD
// ignore
}
}
synchronized (sFieldCache) {
sFieldCache.put(key, match);
}
return match;
}
public static Object readField(final Field field, final Object target, final boolean
forceAccess) throws IllegalAccessException {
Validate.assertTrue(field != null, "The field must not be null");
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
MemberUtils.setAccessibleWorkaround(field);
}
return field.get(target);
}
public static void writeField(final Field field, final Object target, final Object value,
final boolean forceAccess)
throws IllegalAccessException {
Validate.assertTrue(field != null, "The field must not be null");
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
MemberUtils.setAccessibleWorkaround(field);
}
field.set(target, value);
}
public static Object readField(final Field field, final Object target) throws
IllegalAccessException {
return readField(field, target, true);
}
public static Field getField(final Class<?> cls, final String fieldName) {
return getField(cls, fieldName, true);
}
public static Object readField(final Object target, final String fieldName) throws
IllegalAccessException {
Validate.assertTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, true);
Validate.assertTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
// already forced access above, don't repeat it here:
return readField(field, target, false);
}
public static Object readField(final Object target, final String fieldName, final boolean
forceAccess) throws IllegalAccessException {
Validate.assertTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, forceAccess);
Validate.assertTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
// already forced access above, don't repeat it here:
return readField(field, target, forceAccess);
}
public static void writeField(final Object target, final String fieldName, final Object
value) throws IllegalAccessException {
writeField(target, fieldName, value, true);
}
public static void writeField(final Object target, final String fieldName, final Object
value, final boolean forceAccess) throws IllegalAccessException {
Validate.assertTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, true);
Validate.assertTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(),
fieldName);
// already forced access above, don't repeat it here:
writeField(field, target, value, forceAccess);
}
public static void writeField(final Field field, final Object target, final Object value)
throws IllegalAccessException {
writeField(field, target, value, true);
}
public static Object readStaticField(final Field field, final boolean forceAccess) throws
IllegalAccessException {
Validate.assertTrue(field != null, "The field must not be null");
Validate.assertTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static",
field.getName());
return readField(field, (Object) null, forceAccess);
}
public static Object readStaticField(final Class<?> cls, final String fieldName) throws
IllegalAccessException {
final Field field = getField(cls, fieldName, true);
Validate.assertTrue(field != null, "Cannot locate field '%s' on %s", fieldName, cls);
// already forced access above, don't repeat it here:
return readStaticField(field, true);
}
public static void writeStaticField(final Field field, final Object value, final boolean
forceAccess) throws IllegalAccessException {
Validate.assertTrue(field != null, "The field must not be null");
Validate.assertTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static",
field.getDeclaringClass().getName(),
field.getName());
writeField(field, (Object) null, value, forceAccess);
}
public static void writeStaticField(final Class<?> cls, final String fieldName, final Object
value) throws IllegalAccessException {
final Field field = getField(cls, fieldName, true);
Validate.assertTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
// already forced access above, don't repeat it here:
writeStaticField(field, value, true);
}
public static Field getDeclaredField(final Class<?> cls, final String fieldName, final
boolean forceAccess) {
Validate.assertTrue(cls != null, "The class must not be null");
Validate.assertTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty");
try {
// only consider the specified class by using getDeclaredField()
final Field field = cls.getDeclaredField(fieldName);
if (!MemberUtils.isAccessible(field)) {
if (forceAccess) {
field.setAccessible(true);
} else {
return null;
}
}
return field;
} catch (final NoSuchFieldException e) { // NOPMD
// ignore
}
return null;
}
public static void writeDeclaredField(final Object target, final String fieldName, final
Object value) throws IllegalAccessException {
Validate.assertTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getDeclaredField(cls, fieldName, true);
Validate.assertTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(),
fieldName);
// already forced access above, don't repeat it here:
writeField(field, target, value, false);
}
}