net.sf.juffrou.util.reflect.ReflectionUtil Maven / Gradle / Ivy
Show all versions of juffrou-reflect Show documentation
package net.sf.juffrou.util.reflect;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Utility methods to provide information over generic types.
* When a generic class is extended we often need to know which types were used to typify the generic.
* @author cemartins
*/
public final class ReflectionUtil {
/**
* Get the underlying class for a type, or null if the type is a variable type.
*
* @param type
* the type
* @return the underlying class
*/
public static Class> getClass(Type type) {
if (type instanceof Class) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
return getClass(((ParameterizedType) type).getRawType());
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
Class> componentClass = getClass(componentType);
if (componentClass != null) {
return Array.newInstance(componentClass, 0).getClass();
} else {
return null;
}
} else {
if(type instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) type;
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
return type.getClass();
}
else {
return null;
}
}
}
/**
* Get the actual type arguments a child class has used to extend a generic base class.
*
* @param baseClass
* the base class
* @param childClass
* the child class
* @return a list of the raw classes for the actual type arguments.
*/
public static List> getTypeArguments(Class baseClass, Class extends T> childClass) {
Map resolvedTypes = new HashMap();
Type type = childClass;
// start walking up the inheritance hierarchy until we hit baseClass
while (type != null && !getClass(type).equals(baseClass)) {
if (type instanceof Class) {
// there is no useful information for us in raw types, so just keep going.
type = ((Class) type).getGenericSuperclass();
} else {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class> rawType = (Class) parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable>[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++) {
resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
}
if (!rawType.equals(baseClass)) {
type = rawType.getGenericSuperclass();
}
}
}
// finally, for each actual type argument provided to baseClass, determine (if possible)
// the raw class for that type argument.
Type[] actualTypeArguments;
if (type instanceof Class) {
actualTypeArguments = ((Class) type).getTypeParameters();
} else {
if(type != null) {
actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
}
else {
actualTypeArguments = null;
}
}
List> typeArgumentsAsClasses = new ArrayList>();
if(actualTypeArguments == null || actualTypeArguments.length == 0) {
for(Type baseType : resolvedTypes.values()) {
typeArgumentsAsClasses.add(getClass(baseType));
}
}
else {
// resolve types by chasing down type variables.
for (Type baseType : actualTypeArguments) {
while (resolvedTypes.containsKey(baseType)) {
baseType = resolvedTypes.get(baseType);
}
typeArgumentsAsClasses.add(getClass(baseType));
}
}
return typeArgumentsAsClasses;
}
/**
* Get the type parameters and the corresponding actual type arguments a child class has used to extend a generic base class.
*
* @param baseClass
* the base class
* @param childClass
* the child class
* @return A map where the keys are TypeVariable (the type variables declared by the generic declaration) and the values are the Type objects representing the actual type argument the corresponds.
*/
public static Map, Type> getTypeArgumentsMap(Class baseClass, Class extends T> childClass) {
Map, Type> resolvedTypes = new HashMap, Type>();
Type type = childClass;
// start walking up the inheritance hierarchy until we hit baseClass
while (type != null && !getClass(type).equals(baseClass)) {
if (type instanceof Class) {
// there is no useful information for us in raw types, so just keep going.
type = ((Class) type).getGenericSuperclass();
} else {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class> rawType = (Class) parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable>[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++) {
resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
}
if (!rawType.equals(baseClass)) {
type = rawType.getGenericSuperclass();
}
}
}
return resolvedTypes;
}
/**
* Checks whether a type is a simple java type.
* Simple java types are primitives, classes included in the "java" package, Interfaces and Enumerations.
* Simple java types cannot be java beans, so if this method returns true, you cannot create a BeanWrapper
* around the specified type.
* @param type the type to test
* @return true if is a simple java type (is not a bean); false if it is not a simple type (may still not be a bean).
*/
public static boolean isSimpleType(Type type) {
if(! (type instanceof Class))
return false;
Class> clazz = (Class>) type;
return clazz.isPrimitive() || clazz.isEnum() || clazz.isInterface() || clazz.getName().startsWith("java");
}
/**
* Transform a Java bean into a Map where the keys are the property names.
* If there are nested beans, then the key will be the path of property names in the form "prop1.prop2.prop2".
* Properties with null values are not put in the map.
* @param bean The bean to transform
* @return a Map with keys and values corresponding to the bean.
* @see {@link #getMapFromBean(BeanWrapperFactory factory, Object bean)} for increased performance.
*/
public static Map getMapFromBean(Object bean) {
return getMapFromBean(new BeanWrapperFactory(), bean);
}
/**
* Transform a Java bean into a Map where the keys are the property names.
* If there are nested beans, then the key will be the path of property names in the form "prop1.prop2.prop2".
* Properties with null values are not put in the map.
* @param factory a BeanWrapperFactory instance to cache introspection information. Increases performance if re-used.
* @param bean The bean to transform
* @return a Map with keys and values corresponding to the bean.
* @see {@link #getBeanFromMap(BeanWrapperFactory, Map, Object)}
*/
public static Map getMapFromBean(BeanWrapperFactory factory, Object bean) {
Map beanMap = new HashMap();
Map