package com.alibaba.fastjson.util;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
public class FieldInfo implements Comparable<FieldInfo> {
private final String name;
private final Method method;
private final Field field;
private final Class<?> fieldClass;
private final Type fieldType;
private final Class<?> declaringClass;
private boolean getOnly = false;
public FieldInfo(String name, Class<?> declaringClass, Class<?> fieldClass, Type fieldType, Field field){
this.name = name;
this.declaringClass = declaringClass;
this.fieldClass = fieldClass;
this.fieldType = fieldType;
this.method = null;
this.field = field;
if (field != null) {
field.setAccessible(true);
}
}
public FieldInfo(String name, Method method, Field field){
this(name, method, field, null, null);
}
public FieldInfo(String name, Method method, Field field, Class<?> clazz, Type type){
this.name = name;
this.method = method;
this.field = field;
if (method != null) {
method.setAccessible(true);
}
if (field != null) {
field.setAccessible(true);
}
Type fieldType;
Class<?> fieldClass;
if (method != null) {
if (method.getParameterTypes().length == 1) {
fieldClass = method.getParameterTypes()[0];
fieldType = method.getGenericParameterTypes()[0];
} else {
fieldClass = method.getReturnType();
fieldType = method.getGenericReturnType();
getOnly = true;
}
this.declaringClass = method.getDeclaringClass();
} else {
fieldClass = field.getType();
fieldType = field.getGenericType();
this.declaringClass = field.getDeclaringClass();
}
if (clazz != null && fieldClass == Object.class && fieldType instanceof TypeVariable) {
TypeVariable<?> tv = (TypeVariable<?>) fieldType;
Type genericFieldType = getInheritGenericType(clazz, tv);
if (genericFieldType != null) {
this.fieldClass = TypeUtils.getClass(genericFieldType);
this.fieldType = genericFieldType;
return;
}
}
Type genericFieldType = getFieldType(clazz, type, fieldType);
if (genericFieldType != fieldType) {
if (genericFieldType instanceof ParameterizedType) {
fieldClass = TypeUtils.getClass(genericFieldType);
} else if (genericFieldType instanceof Class) {
fieldClass = TypeUtils.getClass(genericFieldType);
}
}
this.fieldType = genericFieldType;
this.fieldClass = fieldClass;
}
public static Type getFieldType(Class<?> clazz, Type type, Type fieldType) {
if (clazz == null || type == null) {
return fieldType;
}
if (!(type instanceof ParameterizedType)) {
return fieldType;
}
if (fieldType instanceof TypeVariable) {
ParameterizedType paramType = (ParameterizedType) type;
TypeVariable<?> typeVar = (TypeVariable<?>) fieldType;
for (int i = 0; i < clazz.getTypeParameters().length; ++i) {
if (clazz.getTypeParameters()[i].getName().equals(typeVar.getName())) {
fieldType = paramType.getActualTypeArguments()[i];
return fieldType;
}
}
}
if (fieldType instanceof ParameterizedType) {
ParameterizedType parameterizedFieldType = (ParameterizedType) fieldType;
Type[] arguments = parameterizedFieldType.getActualTypeArguments();
boolean changed = false;
for (int i = 0; i < arguments.length; ++i) {
Type feildTypeArguement = arguments[i];
if (feildTypeArguement instanceof TypeVariable) {
TypeVariable<?> typeVar = (TypeVariable<?>) feildTypeArguement;
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
for (int j = 0; j < clazz.getTypeParameters().length; ++j) {
if (clazz.getTypeParameters()[j].getName().equals(typeVar.getName())) {
arguments[i] = parameterizedType.getActualTypeArguments()[j];
changed = true;
}
}
}
}
}
if (changed) {
fieldType = new ParameterizedTypeImpl(arguments, parameterizedFieldType.getOwnerType(),
parameterizedFieldType.getRawType());
return fieldType;
}
}
return fieldType;
}
public static Type getInheritGenericType(Class<?> clazz, TypeVariable<?> tv) {
Type type = null;
GenericDeclaration gd = tv.getGenericDeclaration();
do {
type = clazz.getGenericSuperclass();
if (type == null) {
return null;
}
if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
if (ptype.getRawType() == gd) {
TypeVariable<?>[] tvs = gd.getTypeParameters();
Type[] types = ptype.getActualTypeArguments();
for (int i = 0; i < tvs.length; i++) {
if (tvs[i] == tv) return types[i];
}
return null;
}
}
clazz = TypeUtils.getClass(type);
} while (type != null);
return null;
}
public String toString() {
return this.name;
}
public Class<?> getDeclaringClass() {
return declaringClass;
}
public Class<?> getFieldClass() {
return fieldClass;
}
public Type getFieldType() {
return fieldType;
}
public String getName() {
return name;
}
public Method getMethod() {
return method;
}
public Field getField() {
return field;
}
public int compareTo(FieldInfo o) {
return this.name.compareTo(o.name);
}
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
T annotation = null;
if (method != null) {
annotation = method.getAnnotation(annotationClass);
}
if (annotation == null) {
if (field != null) {
annotation = field.getAnnotation(annotationClass);
}
}
return annotation;
}
public Object get(Object javaObject) throws IllegalAccessException, InvocationTargetException {
if (method != null) {
Object value = method.invoke(javaObject, new Object[0]);
return value;
}
return field.get(javaObject);
}
public void set(Object javaObject, Object value) throws IllegalAccessException, InvocationTargetException {
if (method != null) {
method.invoke(javaObject, new Object[] { value });
return;
}
field.set(javaObject, value);
}
public void setAccessible(boolean flag) throws SecurityException {
if (method != null) {
method.setAccessible(flag);
return;
}
field.setAccessible(flag);
}
public boolean isGetOnly() {
return getOnly;
}
}