/** * Copyright 2014 Opower, Inc. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ package com.opower.rest.client.generator.util; 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.HashMap; import java.util.Map; /** * Type conversions and generic type manipulations * * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * */ public class Types { 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"); } private static final Type[] EMPTY_TYPE_ARRAY = {}; 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; } } public static Class getTemplateParameterOfInterface(Class base, Class desiredInterface) { Object rtn = getSomething(base, desiredInterface); if (rtn != null && rtn instanceof Class) return (Class) rtn; return null; } private static Object getSomething(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 = getSomething(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; } 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; } }