package cn.mutils.core.beans; import java.lang.annotation.Annotation; import java.lang.ref.WeakReference; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; 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.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import cn.mutils.core.annotation.Ignore; import cn.mutils.core.annotation.Name; import cn.mutils.core.reflect.ArgumentsType; import cn.mutils.core.reflect.GenericInfo; import cn.mutils.core.reflect.ReflectUtil; /** * Bean field of framework reflection */ @SuppressWarnings({"unused", "UnusedAssignment", "UnnecessaryLocalVariable"}) public class BeanField { protected static Map<Class<?>, BeanField[]> sFieldsCache = new ConcurrentHashMap<Class<?>, BeanField[]>(); protected Field mField; protected Method mGetMethod; protected Method mSetMethod; protected String mName; protected Class<?> mOwnerType; // Class who is field owner protected Class<?> mRawType; // Class of field protected Type mGenericType; // Generic type of field protected WeakReference<Type> mGenericTypeSourceRef; // Cache for source generic type protected WeakReference<Class<?>> mRawTypeResultRef; // Cache for result raw type protected WeakReference<Type> mGenericTypeResultRef; // Cache for result generic type public BeanField(Class<?> ownerType, String name, Field field) { mField = field; init(ownerType, name); } public BeanField(Class<?> ownerType, String name, Method getMethod, Method setMethod) { mGetMethod = getMethod; mSetMethod = setMethod; init(ownerType, name); } protected void init(Class<?> ownerType, String name) { mOwnerType = ownerType; mName = name; // Generate raw type Class<?> rawType = null; if (mField != null) { rawType = mField.getType(); mGenericType = mField.getGenericType(); } else { rawType = mGetMethod.getReturnType(); mGenericType = mGetMethod.getGenericReturnType(); } // Fix bug for base class is ParamterizedType while subclass is a normal class if (rawType == Object.class && (mGenericType instanceof TypeVariable)) { TypeVariable<?> typeVariable = (TypeVariable<?>) mGenericType; Type genericFieldType = ReflectUtil.getInheritGenericType(mOwnerType, typeVariable); if (genericFieldType != null) { mRawType = ReflectUtil.getClass(genericFieldType); mGenericType = genericFieldType; return; } } if (mGenericType instanceof Class) { mRawType = (Class<?>) mGenericType; } else { if (mGenericType instanceof ParameterizedType) { mRawType = (Class<?>) ((ParameterizedType) mGenericType).getRawType(); } else { if (mGenericType instanceof TypeVariable) { Type ownerGenericType = mOwnerType.getGenericSuperclass(); if (ownerGenericType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) ownerGenericType; // Map<K,V>: // K is a TypeVariable, // It's name is K,It's index is 0. String n = ((TypeVariable<?>) mGenericType).getName(); GenericInfo info = ReflectUtil.getGenericInfo(n, mOwnerType.getSuperclass(), pt); if (info.parameterizedType != null) { mGenericType = info.parameterizedType; } mRawType = info.rawType; info.clear(); } } // Error happens when mRawType is null. } } } public Class<?> getOwnerType() { return mOwnerType; } public Class<?> getRawType() { return getRawType(null); } // public class Role<ID,NAME> { public ID id; public NAME n; } // public class User { public Role<Integer,String> role; } // User u = new User(); // RawType for "u.role.id" is null by normal java reflection // RawType for "u.role.id" is expected to be "java.lang.Integer" public Class<?> getRawType(Type genericType) { if (mRawType != null) { return mRawType; } else { // Get from cache if (genericType != null && mGenericTypeSourceRef != null) { synchronized (this) { if (ReflectUtil.isAssignable(genericType, mGenericTypeSourceRef.get())) { if (mRawTypeResultRef != null) { Class<?> resultRef = mRawTypeResultRef.get(); if (resultRef != null) { return resultRef; } } } } } Class<?> expectedType = null; if (genericType != null && (genericType instanceof ParameterizedType)) { ParameterizedType pt = (ParameterizedType) genericType; if (mOwnerType.isAssignableFrom((Class<?>) pt.getRawType())) { if (mGenericType instanceof TypeVariable) { String n = ((TypeVariable<?>) mGenericType).getName(); GenericInfo info = ReflectUtil.getGenericInfo(n, mOwnerType, pt); expectedType = info.rawType; info.clear(); } } } expectedType = expectedType != null ? expectedType : Object.class; //Put to cache if (genericType != null) { synchronized (this) { if (mGenericTypeSourceRef != null) { if (!genericType.equals(mGenericTypeSourceRef.get())) { mGenericTypeSourceRef.clear(); mGenericTypeSourceRef = null; mGenericTypeResultRef = null; mRawTypeResultRef = null; } } else { mGenericTypeSourceRef = new WeakReference<Type>(genericType); } mRawTypeResultRef = new WeakReference<Class<?>>(expectedType); } } return expectedType; } } public Type getGenericType() { return getGenericType(null); } public Type getGenericType(Type genericType) { if (!(mGenericType instanceof TypeVariable)) { return mGenericType; } else { // Get from cache if (genericType != null && mGenericTypeSourceRef != null) { synchronized (this) { if (ReflectUtil.isAssignable(genericType, mGenericTypeSourceRef.get())) { if (mGenericTypeResultRef != null) { Type resultRef = mGenericTypeResultRef.get(); if (resultRef != null) { return resultRef; } } } } } Type expectedType = null; if (genericType != null && (genericType instanceof ParameterizedType)) { ParameterizedType pt = (ParameterizedType) genericType; if (mOwnerType.isAssignableFrom((Class<?>) pt.getRawType())) { if (mGenericType instanceof TypeVariable) { String n = ((TypeVariable<?>) mGenericType).getName(); GenericInfo info = ReflectUtil.getGenericInfo(n, mOwnerType, pt); expectedType = info.parameterizedType; info.clear(); } } } expectedType = expectedType != null ? expectedType : mGenericType; //Put to cache if (genericType != null) { synchronized (this) { if (mGenericTypeSourceRef != null) { if (!genericType.equals(mGenericTypeSourceRef.get())) { mGenericTypeSourceRef.clear(); mGenericTypeSourceRef = null; mGenericTypeResultRef = null; mRawTypeResultRef = null; } } else { mGenericTypeSourceRef = new WeakReference<Type>(genericType); } mGenericTypeResultRef = new WeakReference<Type>(expectedType); } } return expectedType; } } public <T extends Annotation> T getAnnotation(Class<T> annotationType) { if (mField != null) { return mField.getAnnotation(annotationType); } else { return mGetMethod.getAnnotation(annotationType); } } public String getName() { return mName; } public Object get(Object object) throws Exception { if (mField != null) { return mField.get(object); } else { return mGetMethod.invoke(object); } } public void set(Object object, Object value) throws Exception { if (mField != null) { mField.set(object, value); } else { mSetMethod.invoke(object, value); } } /** * Get field type by given class and class type */ protected static Type getFieldType(Class<?> clazz, 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(ReflectUtil.getClass(componentTypeX), 0).getClass(); return fieldTypeX; } return fieldType; } if (!ReflectUtil.isGenericParamType(type)) { return fieldType; } if (fieldType instanceof TypeVariable) { String typeVariableName = ((TypeVariable<?>) fieldType).getName(); ParameterizedType paramType = (ParameterizedType) ReflectUtil.getGenericParamType(type); Class<?> paramClass = ReflectUtil.getClass(paramType); for (TypeVariable<?> t : paramClass.getTypeParameters()) { if (typeVariableName.equals(t.getName())) { fieldType = t; return fieldType; } } } if ((fieldType instanceof ParameterizedType) && (type instanceof ParameterizedType)) { ParameterizedType paramFieldType = (ParameterizedType) fieldType; ParameterizedType paramType = (ParameterizedType) type; boolean changed = false; Type[] arguments = paramFieldType.getActualTypeArguments(); TypeVariable<?>[] typeVariables = clazz.getTypeParameters(); for (int i = 0, size4i = arguments.length; i < size4i; i++) { Type argument = arguments[i]; if (argument instanceof TypeVariable) { String typeVariableName = ((TypeVariable<?>) argument).getName(); for (int j = 0, size4j = typeVariables.length; j < size4j; j++) { if (typeVariableName.equals(typeVariables[j].getName())) { arguments[i] = paramType.getActualTypeArguments()[j]; changed = true; } } } } if (changed) { fieldType = new ArgumentsType(arguments, paramType.getOwnerType(), paramType.getRawType()); return fieldType; } } return fieldType; } protected static String getFieldNameFromMethod(String method, int startIndex) { char[] cs = new char[method.length() - startIndex]; for (int i = startIndex, j = 0, size = method.length(); i < size; i++, j++) { char c = method.charAt(i); if (i == startIndex) { c = Character.toLowerCase(c); } cs[j] = c; } return new String(cs); } protected static BeanField[] generateFields(Class<?> cls) { List<BeanField> fields = new ArrayList<BeanField>(); List<String> fieldNames = new ArrayList<String>(); for (Field f : cls.getFields()) { int modifiers = f.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) { continue; } Ignore ignore = f.getAnnotation(Ignore.class); if (ignore != null) { continue; } String name = f.getName(); Name n = f.getAnnotation(Name.class); if (n != null && !n.value().isEmpty()) { name = n.value(); } fields.add(new BeanField(cls, name, f)); fieldNames.add(name); } List<Method> setList = new ArrayList<Method>(); List<Method> getList = new LinkedList<Method>(); for (Method m : cls.getMethods()) { int modifiers = m.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) { continue; } Ignore ignore = m.getAnnotation(Ignore.class); if (ignore != null) { continue; } String name = m.getName(); Class<?>[] paramTypes = m.getParameterTypes(); Class<?> returnType = m.getReturnType(); if (name.startsWith("set")) { if (paramTypes.length != 1 || returnType != Void.TYPE) { continue; } setList.add(m); } if (name.startsWith("get")) { if (paramTypes.length != 0 || returnType == Void.TYPE) { continue; } getList.add(m); } if (name.startsWith("is")) { if (paramTypes.length != 0 || (returnType != Boolean.TYPE && returnType != Boolean.class)) { continue; } getList.add(m); } } for (Method sm : setList) { String sName = sm.getName(); sName = getFieldNameFromMethod(sName, 3); Class<?> sType = sm.getParameterTypes()[0]; for (Method gm : getList) { Class<?> gType = gm.getReturnType(); if (gType != sType) { continue; } String gName = gm.getName(); if (gType == Boolean.TYPE || gType == Boolean.class) { gName = getFieldNameFromMethod(gName, 2); } else { gName = getFieldNameFromMethod(gName, 3); } if (sName.equals(gName)) { String name = gName; Name n = gm.getAnnotation(Name.class); if (n != null && !n.value().isEmpty()) { name = n.value(); } if (!fieldNames.contains(name)) { fields.add(new BeanField(cls, name, gm, sm)); fieldNames.add(name); } getList.remove(gm); break; } } } setList.clear(); getList.clear(); fieldNames.clear(); BeanField[] fieldsArray = new BeanField[fields.size()]; fields.toArray(fieldsArray); fields.clear(); return fieldsArray; } public static BeanField[] getFields(Class<?> cls) { BeanField[] fields = sFieldsCache.get(cls); if (fields == null) { fields = generateFields(cls); sFieldsCache.put(cls, fields); } return fields; } public static BeanField getField(Class<?> cls, String name) { for (BeanField f : getFields(cls)) { String n = f.getName(); if (n.equals(name)) { return f; } } return null; } public static BeanField getField(Object obj, String name) { if (obj == null) { return null; } return getField(obj.getClass(), name); } }