/** * Copyright 2013-2016 Guoqiang Chen, Shanghai, China. All rights reserved. * * Author: Guoqiang Chen * Email: subchen@gmail.com * WebURL: https://github.com/subchen * * 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 jetbrick.bean; 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.lang.reflect.WildcardType; import jetbrick.util.ArrayUtils; /** * 根据类型定义,获取泛型信息. * * @author Guoqiang Chen */ public final class TypeResolverUtils { /** * Returns raw class for given <code>type</code>. Use this method with both * regular and generic types. * * @param type - the type to convert * @return the closest class representing the given <code>type</code> * @see #getRawType(java.lang.reflect.Type, Class) */ public static Class<?> getRawType(Type type) { return getRawType(type, null); } /** * Returns raw class for given <code>type</code> when implementation class is known * and it makes difference. * * @param type - given type * @param implClass - implementation clas * @return raw class * @see #resolveVariable(java.lang.reflect.TypeVariable, Class) */ public static Class<?> getRawType(Type type, Class<?> implClass) { if (type instanceof Class) { return (Class<?>) type; } if (type instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType) type; return getRawType(pType.getRawType(), implClass); } if (type instanceof WildcardType) { WildcardType wType = (WildcardType) type; Type[] lowerTypes = wType.getLowerBounds(); if (lowerTypes.length > 0) { return getRawType(lowerTypes[0], implClass); } Type[] upperTypes = wType.getUpperBounds(); if (upperTypes.length != 0) { return getRawType(upperTypes[0], implClass); } return Object.class; } if (type instanceof GenericArrayType) { Type genericComponentType = ((GenericArrayType) type).getGenericComponentType(); Class<?> rawType = getRawType(genericComponentType, implClass); return Array.newInstance(rawType, 0).getClass(); } if (type instanceof TypeVariable) { TypeVariable<?> varType = (TypeVariable<?>) type; if (implClass != null) { Type resolvedType = resolveVariable(varType, implClass); if (resolvedType != null) { return getRawType(resolvedType, null); } } Type[] boundsTypes = varType.getBounds(); if (boundsTypes.length == 0) { return Object.class; } return getRawType(boundsTypes[0], implClass); } return null; } /** * Resolves <code>TypeVariable</code> with given implementation class. * * @param variable - variable * @param implClass - implementation class * @return resolved type */ public static Type resolveVariable(TypeVariable<?> variable, Class<?> implClass) { Class<?> rawType = getRawType(implClass, null); int index = ArrayUtils.indexOf(rawType.getTypeParameters(), variable); if (index >= 0) { return variable; } Class<?>[] interfaces = rawType.getInterfaces(); Type[] genericInterfaces = rawType.getGenericInterfaces(); for (int i = 0; i <= interfaces.length; i++) { Class<?> rawInterface; if (i < interfaces.length) { rawInterface = interfaces[i]; } else { rawInterface = rawType.getSuperclass(); if (rawInterface == null) { continue; } } Type resolved = resolveVariable(variable, rawInterface); if (resolved instanceof Class || resolved instanceof ParameterizedType) { return resolved; } if (resolved instanceof TypeVariable) { TypeVariable<?> typeVariable = (TypeVariable<?>) resolved; index = ArrayUtils.indexOf(rawInterface.getTypeParameters(), typeVariable); if (index < 0) { throw new IllegalArgumentException("Can't resolve type variable:" + typeVariable); } Type type = i < genericInterfaces.length ? genericInterfaces[i] : rawType.getGenericSuperclass(); if (type instanceof Class) { return Object.class; } if (type instanceof ParameterizedType) { return ((ParameterizedType) type).getActualTypeArguments()[index]; } throw new IllegalArgumentException("Unsupported type: " + type); } } return null; } public static Class<?> getComponentType(Type type) { return getComponentType(type, null, -1); } public static Class<?> getComponentType(Type type, Class<?> implClass) { return getComponentType(type, implClass, -1); } public static Class<?> getComponentType(Type type, int index) { return getComponentType(type, null, index); } /** * Returns the component type of the given type. * Returns <code>null</code> if given type does not have a single * component type. For example the following types all have the * component-type MyClass: * <ul> * <li>MyClass[]</li> * <li>List<MyClass></li> * <li>Foo<? extends MyClass></li> * <li>Bar<? super MyClass></li> * <li><T extends MyClass> T[]</li> * </ul> * * Index represents the index of component type, when class supports more then one. * For example, <code>Map<A, B></code> has 2 component types. If index is 0 or positive, * than it represents order of component type. If the value is negative, then it represents * component type counted from the end! Therefore, the default value of <code>-1</code> * always returns the <b>last</b> component type. * * @param type - type * @param implClass - implementation class * @param index - index of component type * @return component type */ public static Class<?> getComponentType(Type type, Class<?> implClass, int index) { if (type instanceof Class) { Class<?> clazz = (Class<?>) type; if (clazz.isArray()) { return clazz.getComponentType(); } } else if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; Type[] generics = pt.getActualTypeArguments(); if (index < 0) { index = generics.length + index; } if (index < generics.length) { return getRawType(generics[index], implClass); } } else if (type instanceof GenericArrayType) { GenericArrayType gat = (GenericArrayType) type; return getRawType(gat.getGenericComponentType(), implClass); } return null; } /** * @see #getComponentType(java.lang.reflect.Type) */ public static Class<?> getGenericSupertype(Class<?> type) { return getComponentType(type.getGenericSuperclass()); } /** * Returns generic supertype for given class and 0-based index. * @see #getComponentType(java.lang.reflect.Type, int) */ public static Class<?> getGenericSupertype(Class<?> type, int index) { return getComponentType(type.getGenericSuperclass(), null, index); } }