/* * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.beans; 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 java.util.HashMap; import java.util.Map; import sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl; import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; /** * This is utility class to resolve types. * * @since 1.7 * * @author Eamonn McManus * @author Sergey Malenkov */ public final class TypeResolver { private static final WeakCache> CACHE = new WeakCache<>(); /** * Replaces the given {@code type} in an inherited method * with the actual type it has in the given {@code inClass}. * *

Although type parameters are not inherited by subclasses in the Java * language, they are effectively inherited when using reflection. * For example, if you declare an interface like this...

* *
     * public interface StringToIntMap extends Map<String,Integer> {}
     * 
* *

...then StringToIntMap.class.getMethods() will show that it has methods * like put(K,V) even though StringToIntMap has no type parameters. The K * and V variables are the ones declared by Map, so * {@link TypeVariable#getGenericDeclaration()} will return Map.class.

* *

The purpose of this method is to take a Type from a possibly-inherited * method and replace it with the correct Type for the inheriting class. * So given parameters of K and StringToIntMap.class in the above example, * this method will return String.

* * @param inClass the base class used to resolve * @param type the type to resolve * @return a resolved type * * @see #getActualType(Class) * @see #resolve(Type,Type) */ public static Type resolveInClass(Class inClass, Type type) { return resolve(getActualType(inClass), type); } /** * Replaces all {@code types} in the given array * with the actual types they have in the given {@code inClass}. * * @param inClass the base class used to resolve * @param types the array of types to resolve * @return an array of resolved types * * @see #getActualType(Class) * @see #resolve(Type,Type[]) */ public static Type[] resolveInClass(Class inClass, Type[] types) { return resolve(getActualType(inClass), types); } /** * Replaces type variables of the given {@code formal} type * with the types they stand for in the given {@code actual} type. * *

A ParameterizedType is a class with type parameters, and the values * of those parameters. For example, Map<K,V> is a generic class, and * a corresponding ParameterizedType might look like * Map<K=String,V=Integer>. Given such a ParameterizedType, this method * will replace K with String, or List<K> with List<String;, or * List<? super K> with List<? super String>.

* *

The {@code actual} argument to this method can also be a Class. * In this case, either it is equivalent to a ParameterizedType with * no parameters (for example, Integer.class), or it is equivalent to * a "raw" ParameterizedType (for example, Map.class). In the latter * case, every type parameter declared or inherited by the class is replaced * by its "erasure". For a type parameter declared as <T>, the erasure * is Object. For a type parameter declared as <T extends Number>, * the erasure is Number.

* *

Although type parameters are not inherited by subclasses in the Java * language, they are effectively inherited when using reflection. * For example, if you declare an interface like this...

* *
     * public interface StringToIntMap extends Map<String,Integer> {}
     * 
* *

...then StringToIntMap.class.getMethods() will show that it has methods * like put(K,V) even though StringToIntMap has no type parameters. The K * and V variables are the ones declared by Map, so * {@link TypeVariable#getGenericDeclaration()} will return {@link Map Map.class}.

* *

For this reason, this method replaces inherited type parameters too. * Therefore if this method is called with {@code actual} being * StringToIntMap.class and {@code formal} being the K from Map, * it will return {@link String String.class}.

* *

In the case where {@code actual} is a "raw" ParameterizedType, the * inherited type parameters will also be replaced by their erasures. * The erasure of a Class is the Class itself, so a "raw" subinterface of * StringToIntMap will still show the K from Map as String.class. But * in a case like this... * *

     * public interface StringToIntListMap extends Map<String,List<Integer>> {}
     * public interface RawStringToIntListMap extends StringToIntListMap {}
     * 
* *

...the V inherited from Map will show up as List<Integer> in * StringToIntListMap, but as plain List in RawStringToIntListMap.

* * @param actual the type that supplies bindings for type variables * @param formal the type where occurrences of the variables * in {@code actual} will be replaced by the corresponding bound values * @return a resolved type */ public static Type resolve(Type actual, Type formal) { if (formal instanceof Class) { return formal; } if (formal instanceof GenericArrayType) { Type comp = ((GenericArrayType) formal).getGenericComponentType(); comp = resolve(actual, comp); return (comp instanceof Class) ? Array.newInstance((Class) comp, 0).getClass() : GenericArrayTypeImpl.make(comp); } if (formal instanceof ParameterizedType) { ParameterizedType fpt = (ParameterizedType) formal; Type[] actuals = resolve(actual, fpt.getActualTypeArguments()); return ParameterizedTypeImpl.make( (Class) fpt.getRawType(), actuals, fpt.getOwnerType()); } if (formal instanceof WildcardType) { WildcardType fwt = (WildcardType) formal; Type[] upper = resolve(actual, fwt.getUpperBounds()); Type[] lower = resolve(actual, fwt.getLowerBounds()); return new WildcardTypeImpl(upper, lower); } if (formal instanceof TypeVariable) { Map map; synchronized (CACHE) { map = CACHE.get(actual); if (map == null) { map = new HashMap<>(); prepare(map, actual); CACHE.put(actual, map); } } Type result = map.get(formal); if (result == null || result.equals(formal)) { return formal; } result = fixGenericArray(result); // A variable can be bound to another variable that is itself bound // to something. For example, given: // class Super {...} // class Mid extends Super {...} // class Sub extends Mid // the variable T is bound to X, which is in turn bound to String. // So if we have to resolve T, we need the tail recursion here. return resolve(actual, result); } throw new IllegalArgumentException("Bad Type kind: " + formal.getClass()); } /** * Replaces type variables of all formal types in the given array * with the types they stand for in the given {@code actual} type. * * @param actual the type that supplies bindings for type variables * @param formals the array of types to resolve * @return an array of resolved types */ public static Type[] resolve(Type actual, Type[] formals) { int length = formals.length; Type[] actuals = new Type[length]; for (int i = 0; i < length; i++) { actuals[i] = resolve(actual, formals[i]); } return actuals; } /** * Converts the given {@code type} to the corresponding class. * This method implements the concept of type erasure, * that is described in section 4.6 of * The Java™ Language Specification. * * @param type the array of types to convert * @return a corresponding class */ public static Class erase(Type type) { if (type instanceof Class) { return (Class) type; } if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; return (Class) pt.getRawType(); } if (type instanceof TypeVariable) { TypeVariable tv = (TypeVariable)type; Type[] bounds = tv.getBounds(); return (0 < bounds.length) ? erase(bounds[0]) : Object.class; } if (type instanceof WildcardType) { WildcardType wt = (WildcardType)type; Type[] bounds = wt.getUpperBounds(); return (0 < bounds.length) ? erase(bounds[0]) : Object.class; } if (type instanceof GenericArrayType) { GenericArrayType gat = (GenericArrayType)type; return Array.newInstance(erase(gat.getGenericComponentType()), 0).getClass(); } throw new IllegalArgumentException("Unknown Type kind: " + type.getClass()); } /** * Converts all {@code types} in the given array * to the corresponding classes. * * @param types the array of types to convert * @return an array of corresponding classes * * @see #erase(Type) */ public static Class[] erase(Type[] types) { int length = types.length; Class[] classes = new Class[length]; for (int i = 0; i < length; i++) { classes[i] = TypeResolver.erase(types[i]); } return classes; } /** * Fills the map from type parameters * to types as seen by the given {@code type}. * The method is recursive because the {@code type} * inherits mappings from its parent classes and interfaces. * The {@code type} can be either a {@link Class Class} * or a {@link ParameterizedType ParameterizedType}. * If it is a {@link Class Class}, it is either equivalent * to a {@link ParameterizedType ParameterizedType} with no parameters, * or it represents the erasure of a {@link ParameterizedType ParameterizedType}. * * @param map the mappings of all type variables * @param type the next type in the hierarchy */ private static void prepare(Map map, Type type) { Class raw = (Class)((type instanceof Class) ? type : ((ParameterizedType)type).getRawType()); TypeVariable[] formals = raw.getTypeParameters(); Type[] actuals = (type instanceof Class) ? formals : ((ParameterizedType)type).getActualTypeArguments(); assert formals.length == actuals.length; for (int i = 0; i < formals.length; i++) { map.put(formals[i], actuals[i]); } Type gSuperclass = raw.getGenericSuperclass(); if (gSuperclass != null) { prepare(map, gSuperclass); } for (Type gInterface : raw.getGenericInterfaces()) { prepare(map, gInterface); } // If type is the raw version of a parameterized class, we type-erase // all of its type variables, including inherited ones. if (type instanceof Class && formals.length > 0) { for (Map.Entry entry : map.entrySet()) { entry.setValue(erase(entry.getValue())); } } } /** * Replaces a {@link GenericArrayType GenericArrayType} * with plain array class where it is possible. * Bug 5041784 * is that arrays of non-generic type sometimes show up * as {@link GenericArrayType GenericArrayType} when using reflection. * For example, a {@code String[]} might show up * as a {@link GenericArrayType GenericArrayType} * where {@link GenericArrayType#getGenericComponentType getGenericComponentType} * is {@code String.class}. This violates the specification, * which says that {@link GenericArrayType GenericArrayType} * is used when the component type is a type variable or parameterized type. * We fit the specification here. * * @param type the type to fix * @return a corresponding type for the generic array type, * or the same type as {@code type} */ private static Type fixGenericArray(Type type) { if (type instanceof GenericArrayType) { Type comp = ((GenericArrayType)type).getGenericComponentType(); comp = fixGenericArray(comp); if (comp instanceof Class) { return Array.newInstance((Class)comp, 0).getClass(); } } return type; } /** * Replaces a {@link Class Class} with type parameters * with a {@link ParameterizedType ParameterizedType} * where every parameter is bound to itself. * When calling {@link #resolveInClass} in the context of {@code inClass}, * we can't just pass {@code inClass} as the {@code actual} parameter, * because if {@code inClass} has type parameters * that would be interpreted as accessing the raw type, * so we would get unwanted erasure. * This is why we bind each parameter to itself. * If {@code inClass} does have type parameters and has methods * where those parameters appear in the return type or argument types, * we will correctly leave those types alone. * * @param inClass the base class used to resolve * @return a parameterized type for the class, * or the same class as {@code inClass} */ private static Type getActualType(Class inClass) { Type[] params = inClass.getTypeParameters(); return (params.length == 0) ? inClass : ParameterizedTypeImpl.make( inClass, params, inClass.getEnclosingClass()); } }