package com.gffny.ldrbrd.common.utils; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Common utils on classes for reflection. * * @author jdgaffney * */ public final class ClassUtils { private static final Logger LOG = LoggerFactory.getLogger(ClassUtils.class); // Constructor declaration below is strictly to prevent SONAR warning // regarding presence of a public // or default constructor in a utility class. private ClassUtils() { throw new UnsupportedOperationException( "Instantiation of an instance of utility class: %s" + this.getClass().getName() + " is forbidden"); } /** * 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 <T> List<Class<?>> getTypeArguments(final Class<T> baseClass, final Class<? extends T> childClass) { final Map<Type, Type> resolvedTypes = new HashMap<Type, Type>(); Type type = childClass; // start walking up the inheritance hierarchy until we hit baseClass while (!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 { final ParameterizedType parameterizedType = (ParameterizedType) type; final Class<?> rawType = (Class<?>) parameterizedType .getRawType(); final Type[] actualTypeArguments = parameterizedType .getActualTypeArguments(); final 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 { actualTypeArguments = ((ParameterizedType) type) .getActualTypeArguments(); } final List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>(); // resolve types by chasing down type variables. if (actualTypeArguments != null) { for (Type baseType : actualTypeArguments) { while (resolvedTypes.containsKey(baseType)) { baseType = resolvedTypes.get(baseType); } typeArgumentsAsClasses.add(getClass(baseType)); } } else { LOG.warn("Arguments passed from the actual object is null"); } return typeArgumentsAsClasses; } /** * 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(final Type type) { if (type instanceof Class<?>) { return (Class<?>) type; } else if (type instanceof ParameterizedType) { return getClass(((ParameterizedType) type).getRawType()); } else if (type instanceof GenericArrayType) { final Type componentType = ((GenericArrayType) type) .getGenericComponentType(); final Class<?> componentClass = getClass(componentType); if (componentClass != null) { return Array.newInstance(componentClass, 0).getClass(); } else { return null; } } else { return null; } } }