de.bild.codec.ReflectionHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of polymorphia Show documentation
Show all versions of polymorphia Show documentation
A very fast POJO codec for MongoDB (used in conjunction with the Mongo Java Driver) that handles generic types as well as polymorphic class hierarchies
package de.bild.codec;
import de.bild.codec.annotations.Id;
import de.bild.codec.annotations.LockingVersion;
import org.apache.commons.lang3.reflect.TypeUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class ReflectionHelper {
/**
* calculates all fields of class hierarchy
*
* @param type the value type
* @param returnFinalFields indicate if final fields should be retuned as well
* @return all declared and all inherited declared fields of given type
*/
public static List getDeclaredAndInheritedFieldTypePairs(final Type type, final boolean returnFinalFields) {
ArrayList list = new ArrayList<>();
getFieldTypePairsRecursive(type, returnFinalFields, list, null);
return list;
}
private static void getFieldTypePairsRecursive(final Type type, final boolean returnFinalFields, List currentList, Map realTypeMap) {
if (type instanceof ParameterizedType) {
getFieldTypePairsRecursive((ParameterizedType) type, returnFinalFields, currentList, realTypeMap);
} else if (type instanceof Class) {
getFieldTypePairsRecursive(TypeUtils.parameterize((Class) type, ((Class) type).getTypeParameters()), returnFinalFields, currentList, realTypeMap);
} else if (type instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type;
Type upperBoundType = wildcardType.getUpperBounds()[0];
getFieldTypePairsRecursive(upperBoundType, returnFinalFields, currentList, realTypeMap);
} else {
throw new IllegalArgumentException("Unknown type." + type);
}
}
/**
* @param type
* @param returnFinalFields
* @param currentList
* @param realTypeMap
*/
private static void getFieldTypePairsRecursive(final ParameterizedType type, final boolean returnFinalFields, List currentList, Map realTypeMap) {
if (type.getRawType() == Object.class) {
return;
}
Type[] actualTypeArguments = type.getActualTypeArguments();
Type[] inferredTypeArguments = new Type[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
Type currentTypeParameter = actualTypeArguments[i];
inferredTypeArguments[i] = inferRealType(currentTypeParameter, realTypeMap);
}
Class rawClass = (Class) type.getRawType();
// we need to calculate the parameter map for the superclass
Map clazzTypeParameterMap = calculateInferredTypeParameters(rawClass, inferredTypeArguments);
getFieldTypePairsRecursive(rawClass.getGenericSuperclass(), returnFinalFields, currentList, clazzTypeParameterMap);
List validFields = getValidFields(rawClass.getDeclaredFields(), returnFinalFields);
for (Field validField : validFields) {
currentList.add(new FieldTypePair(validField, inferRealType(validField.getGenericType(), clazzTypeParameterMap)));
}
}
/**
* Calculates the map of inferred type parameters for the given class based upon the given array of known type parameters from the class hierarchy
*
* @param clazz the class to work on
* @param actualTypeParameters array of type parameters
* @return a Map of inferred types, that maps type variable names to real types
*/
public static Map calculateInferredTypeParameters(Class clazz, Type[] actualTypeParameters) {
Map newRealTypeMap = new HashMap<>();
TypeVariable[] typeParameters = clazz.getTypeParameters();
for (int i = 0; i < typeParameters.length; i++) {
TypeVariable typeVariable = typeParameters[i];
Type bound = typeVariable.getBounds()[0];
if (actualTypeParameters != null) {
bound = actualTypeParameters[i];
}
newRealTypeMap.put(typeVariable.getName(), inferRealType(bound, newRealTypeMap));
}
return newRealTypeMap;
}
public static Type inferRealType(Type type, Map realTypeMap) {
if (type instanceof GenericArrayType) {
GenericArrayType genericArrayType = (GenericArrayType) type;
Type genericComponentType = genericArrayType.getGenericComponentType();
return TypeUtils.genericArrayType(inferRealType(genericComponentType, realTypeMap));
} else if (type instanceof ParameterizedType) {
ParameterizedType bound = (ParameterizedType) type;
List typeList = new ArrayList<>();
for (int i = 0; i < bound.getActualTypeArguments().length; i++) {
typeList.add(inferRealType(bound.getActualTypeArguments()[i], realTypeMap));
}
return TypeUtils.parameterizeWithOwner(bound.getOwnerType(), (Class>) bound.getRawType(), typeList.toArray(new Type[0]));
} else if (type instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type;
return TypeUtils.wildcardType()
.withLowerBounds(inferRealTypes(wildcardType.getLowerBounds(), realTypeMap))
.withUpperBounds(inferRealTypes(wildcardType.getUpperBounds(), realTypeMap)).build();
} else if (type instanceof TypeVariable) {
if (realTypeMap == null) {
return ((TypeVariable) type).getBounds()[0];
}
return realTypeMap.get(type.getTypeName());
}
return type;
}
private static Type[] inferRealTypes(Type[] bounds, Map realTypeMap) {
Type[] newBounds = new Type[bounds.length];
for (int i = 0; i < bounds.length; i++) {
newBounds[i] = inferRealType(bounds[i], realTypeMap);
}
return newBounds;
}
/**
* Scans the array fields and returns any fields that are not static or (optionally) final.
*
* @param fields the fields to process
* @param returnFinalFields include final fields in the results
* @return the valid fields
*/
private static List getValidFields(final Field[] fields, final boolean returnFinalFields) {
final List validFields = new ArrayList();
for (final Field field : fields) {
if (!Modifier.isStatic(field.getModifiers()) && (returnFinalFields || !Modifier.isFinal(field.getModifiers()))) {
validFields.add(field);
}
}
return validFields;
}
public static List getDeclaredAndInheritedMethods(final Type type) {
List list = new ArrayList<>();
getMethodTypePairsRecursive(type, list, null);
return list;
}
private static void getMethodTypePairsRecursive(final Type type, List currentList, Map realTypeMap) {
if (type instanceof ParameterizedType) {
getMethodTypePairsRecursive((ParameterizedType) type, currentList, realTypeMap);
} else if (type instanceof Class) {
getMethodTypePairsRecursive(TypeUtils.parameterize((Class) type, ((Class) type).getTypeParameters()), currentList, realTypeMap);
} else {
throw new IllegalArgumentException("Unknown type." + type);
}
}
private static void getMethodTypePairsRecursive(ParameterizedType type, List currentList, Map realTypeMap) {
if (type.getRawType() == Object.class) {
return;
}
Type[] actualTypeArguments = type.getActualTypeArguments();
Type[] inferredTypeArguments = new Type[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
Type currentTypeParameter = actualTypeArguments[i];
inferredTypeArguments[i] = inferRealType(currentTypeParameter, realTypeMap);
}
Class rawClass = (Class) type.getRawType();
// we need to calculate the parameter map for the superclass
Map clazzTypeParameterMap = calculateInferredTypeParameters(rawClass, inferredTypeArguments);
getMethodTypePairsRecursive(rawClass.getGenericSuperclass(), currentList, clazzTypeParameterMap);
for (final Method method : rawClass.getDeclaredMethods()) {
if (!Modifier.isStatic(method.getModifiers())) {
currentList.add(new MethodTypePair(method, inferRealType(method.getGenericReturnType(), clazzTypeParameterMap)));
}
}
}
/**
* Returns the field with the given annotation if found in the given class, null otherwise
*
* @param clazz
* @param annotationClass
* @return the field with the given annotation
*/
private static FieldTypePair getAnnotatedFieldIfPresent(final Class clazz, Class extends Annotation> annotationClass) {
for (final FieldTypePair fieldTypePair : getDeclaredAndInheritedFieldTypePairs(clazz, true)) {
Field field = fieldTypePair.getField();
if (field.isAnnotationPresent(annotationClass)) {
return fieldTypePair;
}
}
return null;
}
public static FieldTypePair getIdFieldIfPresent(final Class clazz) {
return getAnnotatedFieldIfPresent(clazz, Id.class);
}
public static FieldTypePair getLockingVersionFieldIfPresent(final Class clazz) {
return getAnnotatedFieldIfPresent(clazz, LockingVersion.class);
}
/**
* Example:
*
* {@code
* static class ValueClass {}
* static class SomeOtherClass extends ArrayList implements List {}
* static class SomeSpecializedList extends SomeOtherClass {}
* }
*
*
* @param type e.g. SomeSpecializedList
* @param interfaceToFind example List.class
* @return the parameterized interface, e.g. {@code List}
*/
public static Type findInterface(Type type, Class interfaceToFind) {
return findInterface(type, interfaceToFind, null);
}
private static Type findInterface(Type type, Class interfaceToFind, Map realTypeMap) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
return findInterface(parameterizedType, interfaceToFind, realTypeMap);
} else if (type instanceof Class) {
Class clazz = (Class) type;
return findInterface(TypeUtils.parameterize(clazz, clazz.getTypeParameters()), interfaceToFind, realTypeMap);
}
return null;
}
private static Type findInterface(ParameterizedType type, Class interfaceToFind, Map realTypeMap) {
if (type.getRawType() == Object.class) {
return null;
}
Class rawClass = (Class) type.getRawType();
if (rawClass == interfaceToFind) {
return inferRealType(type, realTypeMap);
} else {
Type[] actualTypeArguments = type.getActualTypeArguments();
Type[] inferredTypeArguments = new Type[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
Type currentTypeParameter = actualTypeArguments[i];
inferredTypeArguments[i] = inferRealType(currentTypeParameter, realTypeMap);
}
// we need to calculate the parameter map for the superclass
Map clazzTypeParameterMap = calculateInferredTypeParameters(rawClass, inferredTypeArguments);
for (Type genericInterface : rawClass.getGenericInterfaces()) {
Type foundInterface = findInterface(genericInterface, interfaceToFind, clazzTypeParameterMap);
if (foundInterface != null) {
return foundInterface;
}
}
return findInterface(rawClass.getGenericSuperclass(), interfaceToFind, clazzTypeParameterMap);
}
}
/**
* @param type type to be examined
* @return The raw class of the given type or if type is a class, the class itself will be returned
*/
public static Class extractRawClass(Type type) {
return TypeUtils.getRawType(type, null);
}
}