• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.reflect;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.common.base.Preconditions.checkState;
22 
23 import com.google.common.annotations.Beta;
24 import com.google.common.base.Joiner;
25 import com.google.common.base.Objects;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.Maps;
28 
29 import java.lang.reflect.GenericArrayType;
30 import java.lang.reflect.ParameterizedType;
31 import java.lang.reflect.Type;
32 import java.lang.reflect.TypeVariable;
33 import java.lang.reflect.WildcardType;
34 import java.util.Arrays;
35 import java.util.Map;
36 import java.util.concurrent.atomic.AtomicInteger;
37 
38 import javax.annotation.Nullable;
39 
40 /**
41  * An object of this class encapsulates type mappings from type variables. Mappings are established
42  * with {@link #where} and types are resolved using {@link #resolveType}.
43  *
44  * <p>Note that usually type mappings are already implied by the static type hierarchy (for example,
45  * the {@code E} type variable declared by class {@code List} naturally maps to {@code String} in
46  * the context of {@code class MyStringList implements List<String>}. In such case, prefer to use
47  * {@link TypeToken#resolveType} since it's simpler and more type safe. This class should only be
48  * used when the type mapping isn't implied by the static type hierarchy, but provided through other
49  * means such as an annotation or external configuration file.
50  *
51  * @author Ben Yu
52  * @since 15.0
53  */
54 @Beta
55 public final class TypeResolver {
56 
57   private final TypeTable typeTable;
58 
TypeResolver()59   public TypeResolver() {
60     this.typeTable = new TypeTable();
61   }
62 
TypeResolver(TypeTable typeTable)63   private TypeResolver(TypeTable typeTable) {
64     this.typeTable = typeTable;
65   }
66 
accordingTo(Type type)67   static TypeResolver accordingTo(Type type) {
68     return new TypeResolver().where(TypeMappingIntrospector.getTypeMappings(type));
69   }
70 
71   /**
72    * Returns a new {@code TypeResolver} with type variables in {@code formal} mapping to types in
73    * {@code actual}.
74    *
75    * <p>For example, if {@code formal} is a {@code TypeVariable T}, and {@code actual} is {@code
76    * String.class}, then {@code new TypeResolver().where(formal, actual)} will {@linkplain
77    * #resolveType resolve} {@code ParameterizedType List<T>} to {@code List<String>}, and resolve
78    * {@code Map<T, Something>} to {@code Map<String, Something>} etc. Similarly, {@code formal} and
79    * {@code actual} can be {@code Map<K, V>} and {@code Map<String, Integer>} respectively, or they
80    * can be {@code E[]} and {@code String[]} respectively, or even any arbitrary combination
81    * thereof.
82    *
83    * @param formal The type whose type variables or itself is mapped to other type(s). It's almost
84    *        always a bug if {@code formal} isn't a type variable and contains no type variable. Make
85    *        sure you are passing the two parameters in the right order.
86    * @param actual The type that the formal type variable(s) are mapped to. It can be or contain yet
87    *        other type variables, in which case these type variables will be further resolved if
88    *        corresponding mappings exist in the current {@code TypeResolver} instance.
89    */
where(Type formal, Type actual)90   public TypeResolver where(Type formal, Type actual) {
91     Map<TypeVariableKey, Type> mappings = Maps.newHashMap();
92     populateTypeMappings(mappings, checkNotNull(formal), checkNotNull(actual));
93     return where(mappings);
94   }
95 
96   /** Returns a new {@code TypeResolver} with {@code variable} mapping to {@code type}. */
where(Map<TypeVariableKey, ? extends Type> mappings)97   TypeResolver where(Map<TypeVariableKey, ? extends Type> mappings) {
98     return new TypeResolver(typeTable.where(mappings));
99   }
100 
populateTypeMappings( final Map<TypeVariableKey, Type> mappings, Type from, final Type to)101   private static void populateTypeMappings(
102       final Map<TypeVariableKey, Type> mappings, Type from, final Type to) {
103     if (from.equals(to)) {
104       return;
105     }
106     new TypeVisitor() {
107       @Override void visitTypeVariable(TypeVariable<?> typeVariable) {
108         mappings.put(new TypeVariableKey(typeVariable), to);
109       }
110       @Override void visitWildcardType(WildcardType fromWildcardType) {
111         WildcardType toWildcardType = expectArgument(WildcardType.class, to);
112         Type[] fromUpperBounds = fromWildcardType.getUpperBounds();
113         Type[] toUpperBounds = toWildcardType.getUpperBounds();
114         Type[] fromLowerBounds = fromWildcardType.getLowerBounds();
115         Type[] toLowerBounds = toWildcardType.getLowerBounds();
116         checkArgument(
117             fromUpperBounds.length == toUpperBounds.length
118                 && fromLowerBounds.length == toLowerBounds.length,
119             "Incompatible type: %s vs. %s", fromWildcardType, to);
120         for (int i = 0; i < fromUpperBounds.length; i++) {
121           populateTypeMappings(mappings, fromUpperBounds[i], toUpperBounds[i]);
122         }
123         for (int i = 0; i < fromLowerBounds.length; i++) {
124           populateTypeMappings(mappings, fromLowerBounds[i], toLowerBounds[i]);
125         }
126       }
127       @Override void visitParameterizedType(ParameterizedType fromParameterizedType) {
128         ParameterizedType toParameterizedType = expectArgument(ParameterizedType.class, to);
129         checkArgument(fromParameterizedType.getRawType().equals(toParameterizedType.getRawType()),
130             "Inconsistent raw type: %s vs. %s", fromParameterizedType, to);
131         Type[] fromArgs = fromParameterizedType.getActualTypeArguments();
132         Type[] toArgs = toParameterizedType.getActualTypeArguments();
133         checkArgument(fromArgs.length == toArgs.length,
134             "%s not compatible with %s", fromParameterizedType, toParameterizedType);
135         for (int i = 0; i < fromArgs.length; i++) {
136           populateTypeMappings(mappings, fromArgs[i], toArgs[i]);
137         }
138       }
139       @Override void visitGenericArrayType(GenericArrayType fromArrayType) {
140         Type componentType = Types.getComponentType(to);
141         checkArgument(componentType != null, "%s is not an array type.", to);
142         populateTypeMappings(mappings, fromArrayType.getGenericComponentType(), componentType);
143       }
144       @Override void visitClass(Class<?> fromClass) {
145         // Can't map from a raw class to anything other than itself.
146         // You can't say "assuming String is Integer".
147         // And we don't support "assuming String is T"; user has to say "assuming T is String".
148         throw new IllegalArgumentException("No type mapping from " + fromClass);
149       }
150     }.visit(from);
151   }
152 
153   /**
154    * Resolves all type variables in {@code type} and all downstream types and
155    * returns a corresponding type with type variables resolved.
156    */
resolveType(Type type)157   public Type resolveType(Type type) {
158     checkNotNull(type);
159     if (type instanceof TypeVariable) {
160       return typeTable.resolve((TypeVariable<?>) type);
161     } else if (type instanceof ParameterizedType) {
162       return resolveParameterizedType((ParameterizedType) type);
163     } else if (type instanceof GenericArrayType) {
164       return resolveGenericArrayType((GenericArrayType) type);
165     } else if (type instanceof WildcardType) {
166       return resolveWildcardType((WildcardType) type);
167     } else {
168       // if Class<?>, no resolution needed, we are done.
169       return type;
170     }
171   }
172 
resolveTypes(Type[] types)173   private Type[] resolveTypes(Type[] types) {
174     Type[] result = new Type[types.length];
175     for (int i = 0; i < types.length; i++) {
176       result[i] = resolveType(types[i]);
177     }
178     return result;
179   }
180 
resolveWildcardType(WildcardType type)181   private WildcardType resolveWildcardType(WildcardType type) {
182     Type[] lowerBounds = type.getLowerBounds();
183     Type[] upperBounds = type.getUpperBounds();
184     return new Types.WildcardTypeImpl(
185         resolveTypes(lowerBounds), resolveTypes(upperBounds));
186   }
187 
resolveGenericArrayType(GenericArrayType type)188   private Type resolveGenericArrayType(GenericArrayType type) {
189     Type componentType = type.getGenericComponentType();
190     Type resolvedComponentType = resolveType(componentType);
191     return Types.newArrayType(resolvedComponentType);
192   }
193 
resolveParameterizedType(ParameterizedType type)194   private ParameterizedType resolveParameterizedType(ParameterizedType type) {
195     Type owner = type.getOwnerType();
196     Type resolvedOwner = (owner == null) ? null : resolveType(owner);
197     Type resolvedRawType = resolveType(type.getRawType());
198 
199     Type[] args = type.getActualTypeArguments();
200     Type[] resolvedArgs = resolveTypes(args);
201     return Types.newParameterizedTypeWithOwner(
202         resolvedOwner, (Class<?>) resolvedRawType, resolvedArgs);
203   }
204 
expectArgument(Class<T> type, Object arg)205   private static <T> T expectArgument(Class<T> type, Object arg) {
206     try {
207       return type.cast(arg);
208     } catch (ClassCastException e) {
209       throw new IllegalArgumentException(arg + " is not a " + type.getSimpleName());
210     }
211   }
212 
213   /** A TypeTable maintains mapping from {@link TypeVariable} to types. */
214   private static class TypeTable {
215     private final ImmutableMap<TypeVariableKey, Type> map;
216 
TypeTable()217     TypeTable() {
218       this.map = ImmutableMap.of();
219     }
220 
TypeTable(ImmutableMap<TypeVariableKey, Type> map)221     private TypeTable(ImmutableMap<TypeVariableKey, Type> map) {
222       this.map = map;
223     }
224 
225     /** Returns a new {@code TypeResolver} with {@code variable} mapping to {@code type}. */
where(Map<TypeVariableKey, ? extends Type> mappings)226     final TypeTable where(Map<TypeVariableKey, ? extends Type> mappings) {
227       ImmutableMap.Builder<TypeVariableKey, Type> builder = ImmutableMap.builder();
228       builder.putAll(map);
229       for (Map.Entry<TypeVariableKey, ? extends Type> mapping : mappings.entrySet()) {
230         TypeVariableKey variable = mapping.getKey();
231         Type type = mapping.getValue();
232         checkArgument(!variable.equalsType(type), "Type variable %s bound to itself", variable);
233         builder.put(variable, type);
234       }
235       return new TypeTable(builder.build());
236     }
237 
resolve(final TypeVariable<?> var)238     final Type resolve(final TypeVariable<?> var) {
239       final TypeTable unguarded = this;
240       TypeTable guarded = new TypeTable() {
241         @Override public Type resolveInternal(
242             TypeVariable<?> intermediateVar, TypeTable forDependent) {
243           if (intermediateVar.getGenericDeclaration().equals(var.getGenericDeclaration())) {
244             return intermediateVar;
245           }
246           return unguarded.resolveInternal(intermediateVar, forDependent);
247         }
248       };
249       return resolveInternal(var, guarded);
250     }
251 
252     /**
253      * Resolves {@code var} using the encapsulated type mapping. If it maps to yet another
254      * non-reified type or has bounds, {@code forDependants} is used to do further resolution, which
255      * doesn't try to resolve any type variable on generic declarations that are already being
256      * resolved.
257      *
258      * <p>Should only be called and overridden by {@link #resolve(TypeVariable)}.
259      */
resolveInternal(TypeVariable<?> var, TypeTable forDependants)260     Type resolveInternal(TypeVariable<?> var, TypeTable forDependants) {
261       Type type = map.get(new TypeVariableKey(var));
262       if (type == null) {
263         Type[] bounds = var.getBounds();
264         if (bounds.length == 0) {
265           return var;
266         }
267         Type[] resolvedBounds = new TypeResolver(forDependants).resolveTypes(bounds);
268         /*
269          * We'd like to simply create our own TypeVariable with the newly resolved bounds. There's
270          * just one problem: Starting with JDK 7u51, the JDK TypeVariable's equals() method doesn't
271          * recognize instances of our TypeVariable implementation. This is a problem because users
272          * compare TypeVariables from the JDK against TypeVariables returned by TypeResolver. To
273          * work with all JDK versions, TypeResolver must return the appropriate TypeVariable
274          * implementation in each of the three possible cases:
275          *
276          * 1. Prior to JDK 7u51, the JDK TypeVariable implementation interoperates with ours.
277          * Therefore, we can always create our own TypeVariable.
278          *
279          * 2. Starting with JDK 7u51, the JDK TypeVariable implementations does not interoperate
280          * with ours. Therefore, we have to be careful about whether we create our own TypeVariable:
281          *
282          * 2a. If the resolved types are identical to the original types, then we can return the
283          * original, identical JDK TypeVariable. By doing so, we sidestep the problem entirely.
284          *
285          * 2b. If the resolved types are different from the original types, things are trickier. The
286          * only way to get a TypeVariable instance for the resolved types is to create our own. The
287          * created TypeVariable will not interoperate with any JDK TypeVariable. But this is OK: We
288          * don't _want_ our new TypeVariable to be equal to the JDK TypeVariable because it has
289          * _different bounds_ than the JDK TypeVariable. And it wouldn't make sense for our new
290          * TypeVariable to be equal to any _other_ JDK TypeVariable, either, because any other JDK
291          * TypeVariable must have a different declaration or name. The only TypeVariable that our
292          * new TypeVariable _will_ be equal to is an equivalent TypeVariable that was also created
293          * by us. And that equality is guaranteed to hold because it doesn't involve the JDK
294          * TypeVariable implementation at all.
295          */
296         if (Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY
297             && Arrays.equals(bounds, resolvedBounds)) {
298           return var;
299         }
300         return Types.newArtificialTypeVariable(
301             var.getGenericDeclaration(), var.getName(), resolvedBounds);
302       }
303       // in case the type is yet another type variable.
304       return new TypeResolver(forDependants).resolveType(type);
305     }
306   }
307 
308   private static final class TypeMappingIntrospector extends TypeVisitor {
309 
310     private static final WildcardCapturer wildcardCapturer = new WildcardCapturer();
311 
312     private final Map<TypeVariableKey, Type> mappings = Maps.newHashMap();
313 
314     /**
315      * Returns type mappings using type parameters and type arguments found in
316      * the generic superclass and the super interfaces of {@code contextClass}.
317      */
getTypeMappings( Type contextType)318     static ImmutableMap<TypeVariableKey, Type> getTypeMappings(
319         Type contextType) {
320       TypeMappingIntrospector introspector = new TypeMappingIntrospector();
321       introspector.visit(wildcardCapturer.capture(contextType));
322       return ImmutableMap.copyOf(introspector.mappings);
323     }
324 
visitClass(Class<?> clazz)325     @Override void visitClass(Class<?> clazz) {
326       visit(clazz.getGenericSuperclass());
327       visit(clazz.getGenericInterfaces());
328     }
329 
visitParameterizedType(ParameterizedType parameterizedType)330     @Override void visitParameterizedType(ParameterizedType parameterizedType) {
331       Class<?> rawClass = (Class<?>) parameterizedType.getRawType();
332       TypeVariable<?>[] vars = rawClass.getTypeParameters();
333       Type[] typeArgs = parameterizedType.getActualTypeArguments();
334       checkState(vars.length == typeArgs.length);
335       for (int i = 0; i < vars.length; i++) {
336         map(new TypeVariableKey(vars[i]), typeArgs[i]);
337       }
338       visit(rawClass);
339       visit(parameterizedType.getOwnerType());
340     }
341 
visitTypeVariable(TypeVariable<?> t)342     @Override void visitTypeVariable(TypeVariable<?> t) {
343       visit(t.getBounds());
344     }
345 
visitWildcardType(WildcardType t)346     @Override void visitWildcardType(WildcardType t) {
347       visit(t.getUpperBounds());
348     }
349 
map(final TypeVariableKey var, final Type arg)350     private void map(final TypeVariableKey var, final Type arg) {
351       if (mappings.containsKey(var)) {
352         // Mapping already established
353         // This is possible when following both superClass -> enclosingClass
354         // and enclosingclass -> superClass paths.
355         // Since we follow the path of superclass first, enclosing second,
356         // superclass mapping should take precedence.
357         return;
358       }
359       // First, check whether var -> arg forms a cycle
360       for (Type t = arg; t != null; t = mappings.get(TypeVariableKey.forLookup(t))) {
361         if (var.equalsType(t)) {
362           // cycle detected, remove the entire cycle from the mapping so that
363           // each type variable resolves deterministically to itself.
364           // Otherwise, a F -> T cycle will end up resolving both F and T
365           // nondeterministically to either F or T.
366           for (Type x = arg; x != null; x = mappings.remove(TypeVariableKey.forLookup(x))) {}
367           return;
368         }
369       }
370       mappings.put(var, arg);
371     }
372   }
373 
374   // This is needed when resolving types against a context with wildcards
375   // For example:
376   // class Holder<T> {
377   //   void set(T data) {...}
378   // }
379   // Holder<List<?>> should *not* resolve the set() method to set(List<?> data).
380   // Instead, it should create a capture of the wildcard so that set() rejects any List<T>.
381   private static final class WildcardCapturer {
382 
383     private final AtomicInteger id = new AtomicInteger();
384 
capture(Type type)385     Type capture(Type type) {
386       checkNotNull(type);
387       if (type instanceof Class) {
388         return type;
389       }
390       if (type instanceof TypeVariable) {
391         return type;
392       }
393       if (type instanceof GenericArrayType) {
394         GenericArrayType arrayType = (GenericArrayType) type;
395         return Types.newArrayType(capture(arrayType.getGenericComponentType()));
396       }
397       if (type instanceof ParameterizedType) {
398         ParameterizedType parameterizedType = (ParameterizedType) type;
399         return Types.newParameterizedTypeWithOwner(
400             captureNullable(parameterizedType.getOwnerType()),
401             (Class<?>) parameterizedType.getRawType(),
402             capture(parameterizedType.getActualTypeArguments()));
403       }
404       if (type instanceof WildcardType) {
405         WildcardType wildcardType = (WildcardType) type;
406         Type[] lowerBounds = wildcardType.getLowerBounds();
407         if (lowerBounds.length == 0) { // ? extends something changes to capture-of
408           Type[] upperBounds = wildcardType.getUpperBounds();
409           String name = "capture#" + id.incrementAndGet() + "-of ? extends "
410               + Joiner.on('&').join(upperBounds);
411           return Types.newArtificialTypeVariable(
412               WildcardCapturer.class, name, wildcardType.getUpperBounds());
413         } else {
414           // TODO(benyu): handle ? super T somehow.
415           return type;
416         }
417       }
418       throw new AssertionError("must have been one of the known types");
419     }
420 
captureNullable(@ullable Type type)421     private Type captureNullable(@Nullable Type type) {
422       if (type == null) {
423         return null;
424       }
425       return capture(type);
426     }
427 
capture(Type[] types)428     private Type[] capture(Type[] types) {
429       Type[] result = new Type[types.length];
430       for (int i = 0; i < types.length; i++) {
431         result[i] = capture(types[i]);
432       }
433       return result;
434     }
435   }
436 
437   /**
438    * Wraps around {@code TypeVariable<?>} to ensure that any two type variables are equal as long as
439    * they are declared by the same {@link java.lang.reflect.GenericDeclaration} and have the same
440    * name, even if their bounds differ.
441    *
442    * <p>While resolving a type variable from a {var -> type} map, we don't care whether the
443    * type variable's bound has been partially resolved. As long as the type variable "identity"
444    * matches.
445    *
446    * <p>On the other hand, if for example we are resolving List<A extends B> to
447    * List<A extends String>, we need to compare that <A extends B> is unequal to
448    * <A extends String> in order to decide to use the transformed type instead of the original
449    * type.
450    */
451   static final class TypeVariableKey {
452     private final TypeVariable<?> var;
453 
TypeVariableKey(TypeVariable<?> var)454     TypeVariableKey(TypeVariable<?> var) {
455       this.var = checkNotNull(var);
456     }
457 
hashCode()458     @Override public int hashCode() {
459       return Objects.hashCode(var.getGenericDeclaration(), var.getName());
460     }
461 
equals(Object obj)462     @Override public boolean equals(Object obj) {
463       if (obj instanceof TypeVariableKey) {
464         TypeVariableKey that = (TypeVariableKey) obj;
465         return equalsTypeVariable(that.var);
466       } else {
467         return false;
468       }
469     }
470 
toString()471     @Override public String toString() {
472       return var.toString();
473     }
474 
475     /** Wraps {@code t} in a {@code TypeVariableKey} if it's a type variable. */
forLookup(Type t)476     static Object forLookup(Type t) {
477       if (t instanceof TypeVariable) {
478         return new TypeVariableKey((TypeVariable<?>) t);
479       } else {
480         return null;
481       }
482     }
483 
484     /**
485      * Returns true if {@code type} is a {@code TypeVariable} with the same name and declared by
486      * the same {@code GenericDeclaration}.
487      */
equalsType(Type type)488     boolean equalsType(Type type) {
489       if (type instanceof TypeVariable) {
490         return equalsTypeVariable((TypeVariable<?>) type);
491       } else {
492         return false;
493       }
494     }
495 
equalsTypeVariable(TypeVariable<?> that)496     private boolean equalsTypeVariable(TypeVariable<?> that) {
497       return var.getGenericDeclaration().equals(that.getGenericDeclaration())
498           && var.getName().equals(that.getName());
499     }
500   }
501 }
502