package com.yiqiniu.easytrans.util;
import java.io.Serializable;
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 com.yiqiniu.easytrans.protocol.BusinessIdentifer;
import com.yiqiniu.easytrans.protocol.BusinessProvider;
import com.yiqiniu.easytrans.protocol.EasyTransRequest;
public final class ReflectUtil {
private ReflectUtil(){
}
@SuppressWarnings("unchecked")
public static <P extends EasyTransRequest<R, ?>, R extends Serializable> Class<R> getResultClass(Class<P> paramsClass){
List<Class<?>> pType = getTypeArguments(EasyTransRequest.class,paramsClass);
return (Class<R>) pType.get(0);
}
@SuppressWarnings("unchecked")
public static Class<? extends EasyTransRequest<?, ?>> getRequestClass(Class<? extends BusinessProvider<?>> providerClass){
List<Class<?>> pType = getTypeArguments(BusinessProvider.class, providerClass);
return (Class<? extends EasyTransRequest<?, ?>>) pType.get(0);
}
public static <P extends EasyTransRequest<?,?>> BusinessIdentifer getBusinessIdentifer(Class<P> o){
if(o == null){
return null;
}
return o.getAnnotation(BusinessIdentifer.class);
}
/**
* Get the underlying class for a type, or null if the type is a variable
* type.
*
* @param type
* the type
* @return the underlying class
*/
private 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 {
return null;
}
}
private static <T> Type getNecessaryResolvedTypes(Class<T> baseClass, Class<? extends T> childClass,Map<Type, Type> resolvedTypes){
Type targetType = innerGetMap(baseClass, childClass, resolvedTypes);
if(targetType == null){
throw new RuntimeException("programe error!");
}
return targetType;
}
private static <T> Type innerGetMap(Class<T> baseClass, Type childClass, Map<Type, Type> resolvedTypes) {
Class<?> rawClass = getClass(childClass);
if(rawClass == null){
return null;
}
if(rawClass.equals(baseClass)){
return childClass;
}
if (childClass instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) childClass;
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]);
}
}
{
Type genericSuperclass = rawClass.getGenericSuperclass();
if(genericSuperclass != null){
Class<?> superClass = null;
if (genericSuperclass instanceof ParameterizedType){
superClass = (Class<?>) ((ParameterizedType)genericSuperclass).getRawType();
}else{
superClass = (Class<?>) genericSuperclass;
}
if(baseClass.isAssignableFrom(superClass)){
return innerGetMap(baseClass, genericSuperclass, resolvedTypes);
}
}
}
{
Type[] genericInterfaces = rawClass.getGenericInterfaces();
Class<?> superInterface = null;
for(Type interfaceType :genericInterfaces){
if(interfaceType instanceof ParameterizedType){
superInterface = (Class<?>) ((ParameterizedType)interfaceType).getRawType();
}else{
superInterface = (Class<?>) interfaceType;
}
if(baseClass.isAssignableFrom(superInterface)){
return innerGetMap(baseClass, interfaceType, resolvedTypes);
}
}
}
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 <T> List<Class<?>> getTypeArguments(Class<T> baseClass, Class<? extends T> childClass) {
Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
Type type = getNecessaryResolvedTypes(baseClass, childClass, resolvedTypes);
// 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();
}
List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
// 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;
}
}