package com.alibaba.fastjson.util; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Arrays; import com.alibaba.fastjson.annotation.JSONField; public class FieldInfo implements Comparable<FieldInfo> { public final String name; public final Method method; public final Field field; private int ordinal = 0; public final Class<?> fieldClass; public final Type fieldType; public final Class<?> declaringClass; public final boolean getOnly; public final int serialzeFeatures; public final int parserFeatures; public final String label; private final JSONField fieldAnnotation; private final JSONField methodAnnotation; public final boolean fieldAccess; public final boolean fieldTransient; public final char[] name_chars; public final boolean isEnum; public final boolean jsonDirect; public final boolean unwrapped; public final String format; public final String[] alternateNames; public FieldInfo(String name, // Class<?> declaringClass, // Class<?> fieldClass, // Type fieldType, // Field field, // int ordinal, // int serialzeFeatures, // int parserFeatures){ this.name = name; this.declaringClass = declaringClass; this.fieldClass = fieldClass; this.fieldType = fieldType; this.method = null; this.field = field; this.ordinal = ordinal; this.serialzeFeatures = serialzeFeatures; this.parserFeatures = 0; isEnum = fieldClass.isEnum(); if (field != null) { int modifiers = field.getModifiers(); fieldAccess = (modifiers & Modifier.PUBLIC) != 0 || method == null; fieldTransient = Modifier.isTransient(modifiers); } else { fieldTransient = false; fieldAccess = false; } name_chars = genFieldNameChars(); if (field != null) { TypeUtils.setAccessible(field); } this.label = ""; fieldAnnotation = null; methodAnnotation = null; this.getOnly = false; this.jsonDirect = false; this.unwrapped = false; this.format = null; this.alternateNames = new String[0]; } public FieldInfo(String name, // Method method, // Field field, // Class<?> clazz, // Type type, // int ordinal, // int serialzeFeatures, // int parserFeatures, // JSONField fieldAnnotation, // JSONField methodAnnotation, // String label){ if (field != null) { String fieldName = field.getName(); if (fieldName.equals(name)) { name = fieldName; } } this.name = name; this.method = method; this.field = field; this.ordinal = ordinal; this.serialzeFeatures = serialzeFeatures; this.parserFeatures = parserFeatures; this.fieldAnnotation = fieldAnnotation; this.methodAnnotation = methodAnnotation; if (field != null) { int modifiers = field.getModifiers(); fieldAccess = ((modifiers & Modifier.PUBLIC) != 0 || method == null); fieldTransient = Modifier.isTransient(modifiers) || TypeUtils.isTransient(method); } else { fieldAccess = false; fieldTransient = false; } if (label != null && label.length() > 0) { this.label = label; } else { this.label = ""; } String format = null; JSONField annotation = getAnnotation(); boolean jsonDirect = false; if (annotation != null) { format = annotation.format(); if (format.trim().length() == 0) { format = null; } jsonDirect = annotation.jsonDirect(); unwrapped = annotation.unwrapped(); alternateNames = annotation.alternateNames(); } else { jsonDirect = false; unwrapped = false; alternateNames = new String[0]; } this.format = format; name_chars = genFieldNameChars(); if (method != null) { TypeUtils.setAccessible(method); } if (field != null) { TypeUtils.setAccessible(field); } boolean getOnly = false; Type fieldType; Class<?> fieldClass; if (method != null) { Class<?>[] types; if ((types = method.getParameterTypes()).length == 1) { fieldClass = types[0]; fieldType = method.getGenericParameterTypes()[0]; } else if (types.length == 2 && types[0] == String.class && types[1] == Object.class) { fieldType = fieldClass = types[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(); getOnly = Modifier.isFinal(field.getModifiers()); } this.getOnly = getOnly; this.jsonDirect = jsonDirect && fieldClass == String.class; 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; isEnum = fieldClass.isEnum(); return; } } Type genericFieldType = fieldType; if (!(fieldType instanceof Class)) { genericFieldType = getFieldType(clazz, type != null ? type : clazz, 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; isEnum = fieldClass.isEnum(); } protected char[] genFieldNameChars() { int nameLen = this.name.length(); char[] name_chars = new char[nameLen + 3]; this.name.getChars(0, this.name.length(), name_chars, 1); name_chars[0] = '"'; name_chars[nameLen + 1] = '"'; name_chars[nameLen + 2] = ':'; return name_chars; } @SuppressWarnings("unchecked") public <T extends Annotation> T getAnnation(Class<T> annotationClass) { if (annotationClass == JSONField.class) { return (T) getAnnotation(); } T annotatition = null; if (method != null) { annotatition = method.getAnnotation(annotationClass); } if (annotatition == null && field != null) { annotatition = field.getAnnotation(annotationClass); } return annotatition; } public static Type getFieldType(final Class<?> clazz, final Type type, Type fieldType) { if (clazz == null || type == null) { return fieldType; } if (fieldType instanceof GenericArrayType) { GenericArrayType genericArrayType = (GenericArrayType) fieldType; Type componentType = genericArrayType.getGenericComponentType(); Type componentTypeX = getFieldType(clazz, type, componentType); if (componentType != componentTypeX) { Type fieldTypeX = Array.newInstance(TypeUtils.getClass(componentTypeX), 0).getClass(); return fieldTypeX; } return fieldType; } if (!TypeUtils.isGenericParamType(type)) { return fieldType; } if (fieldType instanceof TypeVariable) { ParameterizedType paramType = (ParameterizedType) TypeUtils.getGenericParamType(type); Class<?> parameterizedClass = TypeUtils.getClass(paramType); final TypeVariable<?> typeVar = (TypeVariable<?>) fieldType; TypeVariable<?>[] typeVariables = parameterizedClass.getTypeParameters(); for (int i = 0; i < typeVariables.length; ++i) { if (typeVariables[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; TypeVariable<?>[] typeVariables = null; Type[] actualTypes = null; ParameterizedType paramType = null; if (type instanceof ParameterizedType) { paramType = (ParameterizedType) type; typeVariables = clazz.getTypeParameters(); } else if(clazz.getGenericSuperclass() instanceof ParameterizedType) { paramType = (ParameterizedType) clazz.getGenericSuperclass(); typeVariables = clazz.getSuperclass().getTypeParameters(); } for (int i = 0; i < arguments.length && paramType != null; ++i) { Type feildTypeArguement = arguments[i]; if (feildTypeArguement instanceof TypeVariable) { TypeVariable<?> typeVar = (TypeVariable<?>) feildTypeArguement; for (int j = 0; j < typeVariables.length; ++j) { if (typeVariables[j].getName().equals(typeVar.getName())) { if (actualTypes == null) { actualTypes = paramType.getActualTypeArguments(); } if (arguments[i] != actualTypes[j]) { arguments[i] = actualTypes[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(); Type superGenericType = clazz.getGenericSuperclass(); do { type = clazz.getGenericSuperclass(); if (type == null) { return null; } if (type instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType) type; Type rawType = ptype.getRawType(); boolean eq = gd.equals(rawType) || (gd instanceof Class && rawType instanceof Class && ((Class) gd).isAssignableFrom((Class) rawType)); if (eq) { TypeVariable<?>[] tvs = gd.getTypeParameters(); Type[] types = ptype.getActualTypeArguments(); for (int i = 0; i < tvs.length; i++) { if (tv.equals(tvs[i])) { return types[i]; } } return null; } } clazz = TypeUtils.getClass(type); } while (type != null); return null; } public String toString() { return this.name; } public Member getMember() { if (method != null) { return method; } else { return field; } } protected Class<?> getDeclaredClass() { if (this.method != null) { return this.method.getDeclaringClass(); } if (this.field != null) { return this.field.getDeclaringClass(); } return null; } public int compareTo(FieldInfo o) { if (this.ordinal < o.ordinal) { return -1; } if (this.ordinal > o.ordinal) { return 1; } int result = this.name.compareTo(o.name); if (result != 0) { return result; } Class<?> thisDeclaringClass = this.getDeclaredClass(); Class<?> otherDeclaringClass = o.getDeclaredClass(); if (thisDeclaringClass != null && otherDeclaringClass != null && thisDeclaringClass != otherDeclaringClass) { if (thisDeclaringClass.isAssignableFrom(otherDeclaringClass)) { return -1; } if (otherDeclaringClass.isAssignableFrom(thisDeclaringClass)) { return 1; } } boolean isSampeType = this.field != null && this.field.getType() == this.fieldClass; boolean oSameType = o.field != null && o.field.getType() == o.fieldClass; if (isSampeType && !oSameType) { return 1; } if (oSameType && !isSampeType) { return -1; } if (o.fieldClass.isPrimitive() && !this.fieldClass.isPrimitive()) { return 1; } if (this.fieldClass.isPrimitive() && !o.fieldClass.isPrimitive()) { return -1; } if (o.fieldClass.getName().startsWith("java.") && !this.fieldClass.getName().startsWith("java.")) { return 1; } if (this.fieldClass.getName().startsWith("java.") && !o.fieldClass.getName().startsWith("java.")) { return -1; } return this.fieldClass.getName().compareTo(o.fieldClass.getName()); } public JSONField getAnnotation() { if (this.fieldAnnotation != null) { return this.fieldAnnotation; } return this.methodAnnotation; } public String getFormat() { return format; } public Object get(Object javaObject) throws IllegalAccessException, InvocationTargetException { if (method != null) { Object value = method.invoke(javaObject); 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() throws SecurityException { if (method != null) { TypeUtils.setAccessible(method); return; } TypeUtils.setAccessible(field); } }