/*
** DroidPlugin Project
**
** Copyright(c) 2015 Andy Zhang <zhangyong232@gmail.com>
**
** This file is part of DroidPlugin.
**
** DroidPlugin is free software: you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation, either
** version 3 of the License, or (at your option) any later version.
**
** DroidPlugin is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with DroidPlugin. If not, see <http://www.gnu.org/licenses/lgpl.txt>
**
**/
package com.morgoo.droidplugin.reflect;
import android.text.TextUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
/**
* Created by Andy Zhang(zhangyong232@gmail.com)ClassUtils on 2015/3/25.
*/
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.isTrue(cls != null, "The class must not be null");
Validate.isTrue(!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.isTrue(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.isTrue(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.isTrue(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.isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, true);
Validate.isTrue(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.isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, forceAccess);
Validate.isTrue(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.isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getField(cls, fieldName, true);
Validate.isTrue(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.isTrue(field != null, "The field must not be null");
Validate.isTrue(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.isTrue(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.isTrue(field != null, "The field must not be null");
Validate.isTrue(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.isTrue(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.isTrue(cls != null, "The class must not be null");
Validate.isTrue(!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.isTrue(target != null, "target object must not be null");
final Class<?> cls = target.getClass();
final Field field = getDeclaredField(cls, fieldName, true);
Validate.isTrue(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);
}
}