/* * Copyright (c) 2016 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.jersey.protobuf; 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.util.HashMap; import java.util.Map; /** * Copied from resteasy-jaxrs-3.0.7.Final.jar!/org/jboss/resteasy/util/Types.class * <p> * Type conversions and generic type manipulations * </p> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ @SuppressWarnings({ "rawtypes", "unchecked", "OverlyLongMethod" }) public class Types { private Types() {} /** * Is the genericType of a certain class? */ public static boolean isA(Class clazz, ParameterizedType pType) { return clazz.isAssignableFrom((Class) pType.getRawType()); } /** * Gets the index-th type argument. */ 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 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; } /** * See if the two methods are compatible, that is they have the same relative signature * * @param method * @param intfMethod * @return */ 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; } /** * Given a method and a root class, find the actual method declared in the root that implements the method. * * @param clazz * @param intfMethod * @return */ 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 { // Interface type parameters may not have been filled out 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("Unable to resolve type variable"); } 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 Class<?> getRawType(Type type) { if(type instanceof Class<?>) { // type is a normal 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]); } } throw new RuntimeException("Unable to determine base class from Type"); } public static Class<?> getRawTypeNoException(Type type) { if(type instanceof Class<?>) { // type is a normal 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; } /** * Returns the type argument from a parameterized type * * @param genericType * @return null if there is no type parameter */ 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; } } /** * Finds an actual value of a type variable. The method looks in a class hierarchy for a class defining the variable * and returns the value if present. * * @param root * @param typeVariable * @return actual type of the type variable */ 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; } /** * Given a class and an interfaces, go through the class hierarchy to find the interface and return its type arguments. * * @param classToSearch * @param interfaceToFind * @return type arguments of the interface */ public static Type[] getActualTypeArgumentsOfAnInterface(Class<?> classToSearch, Class<?> interfaceToFind) { Type[] types = findParameterizedTypes(classToSearch, interfaceToFind); if(types == null) throw new RuntimeException("Unable to find type arguments of " + interfaceToFind); return types; } private static final Type[] EMPTY_TYPE_ARRAY = { }; /** * Search for the given interface or class within the root's class/interface hierarchy. * If the searched for class/interface is a generic return an array of real types that fill it out. * * @param root * @param searchedFor * @return */ 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<String, 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<String, Type> populateParameterizedMap(Class<?> root, ParameterizedType rootType) { Map<String, Type> typeVarMap = new HashMap<String, Type>(); if(rootType != null) { TypeVariable<? extends Class<?>>[] vars = root.getTypeParameters(); for(int i = 0; i < vars.length; i++) { typeVarMap.put(vars[i].getName(), rootType.getActualTypeArguments()[i]); } } return typeVarMap; } public static Type[] findInterfaceParameterizedTypes(Class<?> root, ParameterizedType rootType, Class<?> searchedForInterface) { Map<String, 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<String, 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[] extractTypeVariables(Map<String, Type> typeVarMap, Type[] types) { for(int j = 0; j < types.length; j++) { if(types[j] instanceof TypeVariable) { TypeVariable tv = (TypeVariable) types[j]; types[j] = typeVarMap.get(tv.getName()); } else { types[j] = types[j]; } } return types; } private static Type[] extractTypes(Map<String, Type> typeVarMap, Type genericSub) { if(genericSub instanceof ParameterizedType) { ParameterizedType param = (ParameterizedType) genericSub; Type[] types = param.getActualTypeArguments(); Type[] returnTypes = new Type[types.length]; System.arraycopy(types, 0, returnTypes, 0, types.length); extractTypeVariables(typeVarMap, returnTypes); return returnTypes; } else { return EMPTY_TYPE_ARRAY; } } }