package org.jboss.resteasy.util;
import org.jboss.resteasy.resteasy_jaxrs.i18n.Messages;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class Types
{
@SuppressWarnings(value = "unchecked")
public static boolean isA(Class clazz, ParameterizedType pType)
{
return clazz.isAssignableFrom((Class) pType.getRawType());
}
public static Class getArgumentType(ParameterizedType pType, int index)
{
return (Class) pType.getActualTypeArguments()[index];
}
public static Class getTemplateParameterOfInterface(Class base, Class desiredInterface)
{
Object rtn = searchForInterfaceTemplateParameter(base, desiredInterface);
if (rtn != null && rtn instanceof Class) return (Class) rtn;
return null;
}
private static Object searchForInterfaceTemplateParameter(Class base, Class desiredInterface)
{
for (int i = 0; i < base.getInterfaces().length; i++)
{
Class intf = base.getInterfaces()[i];
if (intf.equals(desiredInterface))
{
Type generic = base.getGenericInterfaces()[i];
if (generic instanceof ParameterizedType)
{
ParameterizedType p = (ParameterizedType) generic;
Type type = p.getActualTypeArguments()[0];
Class rtn = getRawTypeNoException(type);
if (rtn != null) return rtn;
return type;
}
else
{
return null;
}
}
}
if (base.getSuperclass() == null || base.getSuperclass().equals(Object.class)) return null;
Object rtn = searchForInterfaceTemplateParameter(base.getSuperclass(), desiredInterface);
if (rtn == null || rtn instanceof Class) return rtn;
if (!(rtn instanceof TypeVariable)) return null;
String name = ((TypeVariable) rtn).getName();
int index = -1;
TypeVariable[] variables = base.getSuperclass().getTypeParameters();
if (variables == null || variables.length < 1) return null;
for (int i = 0; i < variables.length; i++)
{
if (variables[i].getName().equals(name)) index = i;
}
if (index == -1) return null;
Type genericSuperclass = base.getGenericSuperclass();
if (!(genericSuperclass instanceof ParameterizedType)) return null;
ParameterizedType pt = (ParameterizedType) genericSuperclass;
Type type = pt.getActualTypeArguments()[index];
Class clazz = getRawTypeNoException(type);
if (clazz != null) return clazz;
return type;
}
@SuppressWarnings(value = "unchecked")
public static boolean isCompatible(Method method, Method intfMethod)
{
if (method == intfMethod) return true;
if (!method.getName().equals(intfMethod.getName())) return false;
if (method.getParameterTypes().length != intfMethod.getParameterTypes().length) return false;
for (int i = 0; i < method.getParameterTypes().length; i++)
{
Class rootParam = method.getParameterTypes()[i];
Class intfParam = intfMethod.getParameterTypes()[i];
if (!intfParam.isAssignableFrom(rootParam)) return false;
}
return true;
}
@SuppressWarnings(value = "unchecked")
public static Method getImplementingMethod(Class clazz, Method intfMethod)
{
Class<?> declaringClass = intfMethod.getDeclaringClass();
if (declaringClass.equals(clazz)) return intfMethod;
Class[] paramTypes = intfMethod.getParameterTypes();
if (declaringClass.getTypeParameters().length > 0 && paramTypes.length > 0)
{
Type[] intfTypes = findParameterizedTypes(clazz, declaringClass);
Map<String, Type> typeVarMap = new HashMap<String, Type>();
TypeVariable<? extends Class<?>>[] vars = declaringClass.getTypeParameters();
for (int i = 0; i < vars.length; i++)
{
if (intfTypes != null && i < intfTypes.length)
{
typeVarMap.put(vars[i].getName(), intfTypes[i]);
}
else
{
typeVarMap.put(vars[i].getName(), vars[i].getGenericDeclaration());
}
}
Type[] paramGenericTypes = intfMethod.getGenericParameterTypes();
paramTypes = new Class[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++)
{
if (paramGenericTypes[i] instanceof TypeVariable)
{
TypeVariable tv = (TypeVariable)paramGenericTypes[i];
Type t = typeVarMap.get(tv.getName());
if (t == null)
{
throw new RuntimeException(Messages.MESSAGES.unableToResolveTypeVariable());
}
paramTypes[i] = getRawType(t);
}
else
{
paramTypes[i] = getRawType(paramGenericTypes[i]);
}
}
}
try
{
return clazz.getMethod(intfMethod.getName(), paramTypes);
}
catch (NoSuchMethodException e)
{
}
try
{
Method tmp = clazz.getMethod(intfMethod.getName(), intfMethod.getParameterTypes());
return tmp;
}
catch (NoSuchMethodException e)
{
}
return intfMethod;
}
public static Method getImplementedInterfaceMethod(final Class<?> root, final Class<?> iface, final Method implementation)
{
Method overriddenInterfaceMethod = findOverriddenMethod(root, iface, implementation);
if (overriddenInterfaceMethod != null) {
return overriddenInterfaceMethod;
}
for (Class<?> extended : iface.getInterfaces()) {
overriddenInterfaceMethod = getImplementedInterfaceMethod(root, extended, implementation);
if (overriddenInterfaceMethod != null) {
return overriddenInterfaceMethod;
}
}
return null;
}
public static Method findOverriddenMethod(final Class<?> root, final Class<?> superClass, final Method implementation)
{
try
{
final Method method = superClass.getDeclaredMethod(implementation.getName(), implementation.getParameterTypes());
return method;
}
catch (NoSuchMethodException e)
{
}
final Map<TypeVariable<?>, Type> genericTypeMap = populateParameterizedMap(root, superClass);
for (Method superClassMethod : superClass.getDeclaredMethods())
{
if (superClassMethod.isSynthetic())
{
continue;
}
if (!implementation.getName().equals(superClassMethod.getName()) || implementation.getParameterCount() != superClassMethod.getParameterCount())
{
continue;
}
final Type[] actualMethodParameters = extractTypeVariables(genericTypeMap, superClassMethod.getGenericParameterTypes());
if (Arrays.equals(implementation.getGenericParameterTypes(), actualMethodParameters))
{
return superClassMethod;
}
}
return null;
}
public static Class<?> getRawType(Type type)
{
if (type instanceof Class<?>)
{
return (Class<?>) type;
}
else if (type instanceof ParameterizedType)
{
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
return (Class<?>) rawType;
}
else if (type instanceof GenericArrayType)
{
final GenericArrayType genericArrayType = (GenericArrayType) type;
final Class<?> componentRawType = getRawType(genericArrayType.getGenericComponentType());
return Array.newInstance(componentRawType, 0).getClass();
}
else if (type instanceof TypeVariable)
{
final TypeVariable typeVar = (TypeVariable) type;
if (typeVar.getBounds() != null && typeVar.getBounds().length > 0)
{
return getRawType(typeVar.getBounds()[0]);
}
}
else if (type instanceof WildcardType)
{
WildcardType wildcardType = (WildcardType) type;
Type[] upperBounds = wildcardType.getUpperBounds();
if (upperBounds != null && upperBounds.length > 0)
{
return getRawType(upperBounds[0]);
}
}
throw new RuntimeException(Messages.MESSAGES.unableToDetermineBaseClass());
}
public static Class<?> getRawTypeNoException(Type type)
{
if (type instanceof Class<?>)
{
return (Class<?>) type;
}
else if (type instanceof ParameterizedType)
{
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
return (Class<?>) rawType;
}
else if (type instanceof GenericArrayType)
{
final GenericArrayType genericArrayType = (GenericArrayType) type;
final Class<?> componentRawType = getRawType(genericArrayType.getGenericComponentType());
return Array.newInstance(componentRawType, 0).getClass();
}
return null;
}
public static Class<?> getTypeArgument(Type genericType)
{
if (!(genericType instanceof ParameterizedType)) return null;
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Class<?> typeArg = (Class<?>) parameterizedType.getActualTypeArguments()[0];
return typeArg;
}
public static Class getCollectionBaseType(Class type, Type genericType)
{
if (genericType instanceof ParameterizedType)
{
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Type componentGenericType = parameterizedType.getActualTypeArguments()[0];
return getRawType(componentGenericType);
}
else if (genericType instanceof GenericArrayType)
{
final GenericArrayType genericArrayType = (GenericArrayType) genericType;
Type componentGenericType = genericArrayType.getGenericComponentType();
return getRawType(componentGenericType);
}
else if (type.isArray())
{
return type.getComponentType();
}
return null;
}
public static Class getMapKeyType(Type genericType)
{
if (genericType instanceof ParameterizedType)
{
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Type componentGenericType = parameterizedType.getActualTypeArguments()[0];
return getRawType(componentGenericType);
}
return null;
}
public static Class getMapValueType(Type genericType)
{
if (genericType instanceof ParameterizedType)
{
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Type componentGenericType = parameterizedType.getActualTypeArguments()[1];
return getRawType(componentGenericType);
}
return null;
}
public static Type resolveTypeVariables(Class<?> root, Type type)
{
if (type instanceof TypeVariable)
{
Type newType = resolveTypeVariable(root, (TypeVariable)type);
return (newType == null) ? type : newType;
}
else if (type instanceof ParameterizedType)
{
final ParameterizedType param = (ParameterizedType)type;
final Type[] actuals = new Type[param.getActualTypeArguments().length];
for (int i = 0; i < actuals.length; i++)
{
Type newType = resolveTypeVariables(root, param.getActualTypeArguments()[i]);
actuals[i] = newType == null ? param.getActualTypeArguments()[i] : newType;
}
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments()
{
return actuals;
}
@Override
public Type getRawType()
{
return param.getRawType();
}
@Override
public Type getOwnerType()
{
return param.getOwnerType();
}
};
}
else if (type instanceof GenericArrayType)
{
GenericArrayType arrayType = (GenericArrayType)type;
final Type componentType = resolveTypeVariables(root, arrayType.getGenericComponentType());
if (componentType == null) return type;
return new GenericArrayType()
{
@Override
public Type getGenericComponentType()
{
return componentType;
}
};
}
else
{
return type;
}
}
public static Type resolveTypeVariable(Class<?> root, TypeVariable<?> typeVariable)
{
if (typeVariable.getGenericDeclaration() instanceof Class<?>)
{
Class<?> classDeclaringTypeVariable = (Class<?>) typeVariable.getGenericDeclaration();
Type[] types = findParameterizedTypes(root, classDeclaringTypeVariable);
if (types == null) return null;
for (int i = 0; i < types.length; i++)
{
TypeVariable<?> tv = classDeclaringTypeVariable.getTypeParameters()[i];
if (tv.equals(typeVariable))
{
return types[i];
}
}
}
return null;
}
public static Type[] getActualTypeArgumentsOfAnInterface(Class<?> classToSearch, Class<?> interfaceToFind)
{
Type[] types = findParameterizedTypes(classToSearch, interfaceToFind);
if (types == null) throw new RuntimeException(Messages.MESSAGES.unableToFindTypeArguments(interfaceToFind));
return types;
}
private static final Type[] EMPTY_TYPE_ARRAY = {};
public static Type[] findParameterizedTypes(Class<?> root, Class<?> searchedFor)
{
if (searchedFor.isInterface())
{
return findInterfaceParameterizedTypes(root, null, searchedFor);
}
return findClassParameterizedTypes(root, null, searchedFor);
}
public static Type[] findClassParameterizedTypes(Class<?> root, ParameterizedType rootType, Class<?> searchedForClass)
{
if (Object.class.equals(root)) return null;
Map<TypeVariable<?>, Type> typeVarMap = populateParameterizedMap(root, rootType);
Class<?> superclass = root.getSuperclass();
Type genericSuper = root.getGenericSuperclass();
if (superclass.equals(searchedForClass))
{
return extractTypes(typeVarMap, genericSuper);
}
if (genericSuper instanceof ParameterizedType)
{
ParameterizedType intfParam = (ParameterizedType) genericSuper;
Type[] types = findClassParameterizedTypes(superclass, intfParam, searchedForClass);
if (types != null)
{
return extractTypeVariables(typeVarMap, types);
}
}
else
{
Type[] types = findClassParameterizedTypes(superclass, null, searchedForClass);
if (types != null)
{
return types;
}
}
return null;
}
private static Map<TypeVariable<?>, Type> populateParameterizedMap(Class<?> root, ParameterizedType rootType)
{
Map<TypeVariable<?>, Type> typeVarMap = new HashMap<>();
if (rootType != null)
{
TypeVariable<? extends Class<?>>[] vars = root.getTypeParameters();
for (int i = 0; i < vars.length; i++)
{
typeVarMap.put(vars[i], rootType.getActualTypeArguments()[i]);
}
}
return typeVarMap;
}
public static Map<TypeVariable<?>, Type> populateParameterizedMap(final Class<?> root, final Class<?> superClass)
{
final Type[] types = Types.findParameterizedTypes(root, superClass);
final TypeVariable<? extends Class<?>>[] typeParameters = superClass.getTypeParameters();
final Map<TypeVariable<?>, Type> genericTypeMap = new HashMap<>();
if (typeParameters != null && types != null)
{
int i = 0;
while (i < types.length)
{
genericTypeMap.put(typeParameters[i], types[i]);
i++;
}
while (i < typeParameters.length)
{
genericTypeMap.put(typeParameters[i], typeParameters[i].getGenericDeclaration());
i++;
}
}
return genericTypeMap;
}
public static Type[] findInterfaceParameterizedTypes(Class<?> root, ParameterizedType rootType, Class<?> searchedForInterface)
{
Map<TypeVariable<?>, Type> typeVarMap = populateParameterizedMap(root, rootType);
for (int i = 0; i < root.getInterfaces().length; i++)
{
Class<?> sub = root.getInterfaces()[i];
Type genericSub = root.getGenericInterfaces()[i];
if (sub.equals(searchedForInterface))
{
return extractTypes(typeVarMap, genericSub);
}
}
for (int i = 0; i < root.getInterfaces().length; i++)
{
Type genericSub = root.getGenericInterfaces()[i];
Class<?> sub = root.getInterfaces()[i];
Type[] types = recurseSuperclassForInterface(searchedForInterface, typeVarMap, genericSub, sub);
if (types != null) return types;
}
if (root.isInterface()) return null;
Class<?> superclass = root.getSuperclass();
Type genericSuper = root.getGenericSuperclass();
return recurseSuperclassForInterface(searchedForInterface, typeVarMap, genericSuper, superclass);
}
private static Type[] recurseSuperclassForInterface(Class<?> searchedForInterface, Map<TypeVariable<?>, Type> typeVarMap, Type genericSub, Class<?> sub)
{
if (genericSub instanceof ParameterizedType)
{
ParameterizedType intfParam = (ParameterizedType) genericSub;
Type[] types = findInterfaceParameterizedTypes(sub, intfParam, searchedForInterface);
if (types != null)
{
return extractTypeVariables(typeVarMap, types);
}
}
else
{
Type[] types = findInterfaceParameterizedTypes(sub, null, searchedForInterface);
if (types != null)
{
return types;
}
}
return null;
}
private static Type[] (final Map<TypeVariable<?>, Type> typeVarMap, final Type[] types)
{
final Type[] resolvedMethodParameterTypes = new Type[types.length];
for (int i = 0; i < types.length; i++)
{
final Type methodParameterType = types[i];
if (methodParameterType instanceof TypeVariable<?>)
{
resolvedMethodParameterTypes[i] = typeVarMap.get(methodParameterType);
}
else
{
resolvedMethodParameterTypes[i] = methodParameterType;
}
}
return resolvedMethodParameterTypes;
}
private static Type[] (Map<TypeVariable<?>, Type> typeVarMap, Type genericSub)
{
if (genericSub instanceof ParameterizedType)
{
ParameterizedType param = (ParameterizedType) genericSub;
Type[] types = param.getActualTypeArguments();
Type[] returnTypes = extractTypeVariables(typeVarMap, types);
return returnTypes;
}
else
{
return EMPTY_TYPE_ARRAY;
}
}
}