| /** |
| * Copyright (C) 2008 Google 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.google.inject.internal; |
| |
| import static com.google.common.base.Preconditions.checkArgument; |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Maps; |
| import com.google.inject.util.Types; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.GenericDeclaration; |
| import java.lang.reflect.Member; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.lang.reflect.TypeVariable; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Applies known information to generic types to discover more precise generic |
| * types. For example, the Java reflection API exposes the return type of |
| * {@code Collection.iterator()} as {@code Iterator<E>}. This class leverages |
| * more precise type (such as {@code Set<String>}) to expose a more precise |
| * return type: {@code Iterator<String>}. To accomplish this, type resolver |
| * fills the generic parameter {@code E} with the known type {@code String}. |
| * Parameters whose types are unavailable will not be resolved. |
| * |
| * <p>This class is not threadsafe. |
| * |
| * @author jessewilson@google.com (Jesse Wilson) |
| */ |
| public final class TypeResolver { |
| |
| /** the type being resolved, such as {@code List<String>} */ |
| public final Type type; |
| |
| /** maps from type variables to the types they resolve to */ |
| private final Map<TypeVariable, Type> index = Maps.newHashMap(); |
| |
| /** types to resolved types */ |
| private final Map<Type, Type> cache = Maps.newHashMap(); |
| |
| /** raw supertypes to the corresponding resolved generic types */ |
| private final Map<Class<?>, Type> implementedTypes = Maps.newHashMap(); |
| |
| /** |
| * Creates a new type resolver that can resolve the members of {@code type}. |
| */ |
| public TypeResolver(Type type) { |
| this.type = type; |
| resolveRecursive(type); |
| } |
| |
| /** |
| * Resolves and stores all generic information for {@code type} and all of |
| * its implemented interfaces and supertypes. |
| */ |
| private void resolveRecursive(Type type) { |
| Class<?> rawType = MoreTypes.getRawType(type); |
| implementedTypes.put(rawType, resolve(type)); |
| |
| Type supertype = rawType.getGenericSuperclass(); |
| if (supertype != null) { |
| resolveRecursive(supertype); |
| } |
| for (Type genericInterface : rawType.getGenericInterfaces()) { |
| resolveRecursive(genericInterface); |
| } |
| } |
| |
| /** |
| * Projects the known type information from this resolver on {@code type} and |
| * returns the result. |
| */ |
| private Type resolve(Type type) { |
| checkNotNull(type, "type"); |
| Type alreadyResolved = cache.get(type); |
| |
| if (alreadyResolved != null) { |
| return alreadyResolved; |
| |
| } else if (type instanceof ParameterizedType) { |
| ParameterizedType parameterizedType = (ParameterizedType) type; |
| Class<?> rawType = MoreTypes.getRawType(parameterizedType); |
| TypeVariable<? extends Class<?>>[] typeVariables = rawType.getTypeParameters(); |
| Type[] arguments = parameterizedType.getActualTypeArguments(); |
| Type[] resolvedArguments = new Type[typeVariables.length]; |
| for (int v = 0; v < typeVariables.length; v++) { |
| resolvedArguments[v] = resolve(arguments[v]); |
| index.put(typeVariables[v], resolvedArguments[v]); |
| } |
| |
| Type resolvedRawType = resolve(rawType); |
| Type ownerType = parameterizedType.getOwnerType(); |
| Type resolvedOwnerType = ownerType != null ? resolve(ownerType) : null; |
| Type resolved = Types.newParameterizedTypeWithOwner(resolvedOwnerType, |
| resolvedRawType, resolvedArguments); |
| cache.put(type, resolved); |
| return resolved; |
| |
| } else if (type instanceof TypeVariable) { |
| TypeVariable typeVariable = (TypeVariable) type; |
| GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); |
| Type result = typeVariable; |
| if (genericDeclaration instanceof Class) { |
| Type resolved = index.get(typeVariable); |
| result = resolved != null ? resolved : result; |
| } |
| cache.put(type, result); |
| return result; |
| |
| } else if (type instanceof Class) { |
| cache.put(type, type); |
| return type; |
| |
| } else if (type instanceof GenericArrayType) { |
| GenericArrayType arrayType = (GenericArrayType) type; |
| Type componentTypeResolved = resolve(arrayType.getGenericComponentType()); |
| GenericArrayType resolved = Types.arrayOf(componentTypeResolved); |
| cache.put(type, resolved); |
| return resolved; |
| |
| } else { |
| return type; |
| } |
| } |
| |
| /** |
| * Returns an immutable list of the resolved types. |
| */ |
| private List<Type> resolveAll(Type[] types) { |
| Type[] result = new Type[types.length]; |
| for (int t = 0; t < types.length; t++) { |
| result[t] = resolve(types[t]); |
| } |
| return ImmutableList.of(result); |
| } |
| |
| /** |
| * Returns the type used to resolve other types. Ideally this is a fully |
| * specified type such as {@code List<String>}. |
| */ |
| public Type getType() { |
| return type; |
| } |
| |
| /** |
| * Returns the raw form of the type being resolved, such as {@code List}. |
| */ |
| public Class<?> getRawType() { |
| return MoreTypes.getRawType(type); |
| } |
| |
| /** |
| * Returns the generic equivalent of {@code supertype}. For example, if this |
| * is a resolver of {@code ArrayList<String>}, this method returns {@code |
| * Iterable<String>} given the input {@code Iterable.class}. |
| * |
| * @param supertype a superclass of, or interface implemented by, this |
| * resolver's type. |
| */ |
| public Type getSupertype(Class<?> supertype) { |
| Type type = implementedTypes.get(supertype); |
| checkArgument(type != null, |
| "%s is not a supertype of %s", supertype, this.type); |
| return type; |
| } |
| |
| /** |
| * Returns the resolved generic type of {@code field}. |
| * |
| * @param field a field defined by this resolver's type or any of its |
| * superclasses. |
| */ |
| public Type getFieldType(Field field) { |
| checkArgument(implementedTypes.containsKey(field.getDeclaringClass()), |
| "%s is not defined by a supertype of %s", field, type); |
| return resolve(field.getGenericType()); |
| } |
| |
| /** |
| * Returns a list of the resolved generic exception types of {@code |
| * constructor}. |
| * |
| * @param constructor a constructor defined by this resolver's type or any of |
| * its superclasses. |
| */ |
| public List<Type> getExceptionTypes(Constructor constructor) { |
| checkArgument(implementedTypes.containsKey(constructor.getDeclaringClass()), |
| "%s does not construct a supertype of %s", constructor, type); |
| return resolveAll(constructor.getGenericExceptionTypes()); |
| } |
| |
| /** |
| * Returns a list of the resolved generic parameter types of {@code methodOrConstructor}. |
| * |
| * @param methodOrConstructor a method or constructor defined by this resolver's type, its |
| * superclasses or implemented interfaces. |
| */ |
| public List<Type> getParameterTypes(Member methodOrConstructor) { |
| Type[] genericParameterTypes; |
| |
| if (methodOrConstructor instanceof Method) { |
| Method method = (Method) methodOrConstructor; |
| checkArgument(implementedTypes.containsKey(method.getDeclaringClass()), |
| "%s is not defined by a supertype of %s", method, type); |
| genericParameterTypes = method.getGenericParameterTypes(); |
| |
| } else if (methodOrConstructor instanceof Constructor) { |
| Constructor constructor = (Constructor) methodOrConstructor; |
| checkArgument(implementedTypes.containsKey(constructor.getDeclaringClass()), |
| "%s does not construct a supertype of %s", constructor, type); |
| genericParameterTypes = constructor.getGenericParameterTypes(); |
| |
| } else { |
| throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor); |
| } |
| |
| return resolveAll(genericParameterTypes); |
| } |
| |
| /** |
| * Returns a list of the resolved generic exception types of {@code method}. |
| * |
| * @param method a method defined by this resolver's type, its superclasses |
| * or implemented interfaces. |
| */ |
| public List<Type> getExceptionTypes(Method method) { |
| checkArgument(implementedTypes.containsKey(method.getDeclaringClass()), |
| "%s is not defined by a supertype of %s", method, type); |
| return resolveAll(method.getGenericExceptionTypes()); |
| } |
| |
| /** |
| * Returns the resolved generic return type of {@code method}. |
| * |
| * @param method a method defined by this resolver's type, its superclasses |
| * or implemented interfaces. |
| */ |
| public Type getReturnType(Method method) { |
| checkArgument(implementedTypes.containsKey(method.getDeclaringClass()), |
| "%s is not defined by a supertype of %s", method, type); |
| return resolve(method.getGenericReturnType()); |
| } |
| |
| @Override public boolean equals(Object obj) { |
| return obj instanceof TypeResolver |
| && getType().equals(((TypeResolver) obj).getType()); |
| } |
| |
| @Override public int hashCode() { |
| return 79 * type.hashCode(); |
| } |
| |
| @Override public String toString() { |
| return "Resolver:" + type; |
| } |
| } |