View Javadoc
1   /*
2    * Copyright (C) 2011 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.reflect;
16  
17  import static com.google.common.base.Preconditions.checkArgument;
18  import static com.google.common.base.Preconditions.checkNotNull;
19  import static com.google.common.collect.Iterables.transform;
20  
21  import com.google.common.annotations.VisibleForTesting;
22  import com.google.common.base.Function;
23  import com.google.common.base.Joiner;
24  import com.google.common.base.Objects;
25  import com.google.common.base.Predicates;
26  import com.google.common.collect.ImmutableList;
27  import com.google.common.collect.ImmutableMap;
28  import com.google.common.collect.Iterables;
29  import java.io.Serializable;
30  import java.lang.reflect.AnnotatedElement;
31  import java.lang.reflect.Array;
32  import java.lang.reflect.GenericArrayType;
33  import java.lang.reflect.GenericDeclaration;
34  import java.lang.reflect.InvocationHandler;
35  import java.lang.reflect.InvocationTargetException;
36  import java.lang.reflect.Method;
37  import java.lang.reflect.ParameterizedType;
38  import java.lang.reflect.Proxy;
39  import java.lang.reflect.Type;
40  import java.lang.reflect.TypeVariable;
41  import java.lang.reflect.WildcardType;
42  import java.security.AccessControlException;
43  import java.util.Arrays;
44  import java.util.Collection;
45  import java.util.Map;
46  import java.util.concurrent.atomic.AtomicReference;
47  import javax.annotation.Nullable;
48  
49  /**
50   * Utilities for working with {@link Type}.
51   *
52   * @author Ben Yu
53   */
54  final class Types {
55  
56    /** Class#toString without the "class " and "interface " prefixes */
57    private static final Function<Type, String> TYPE_NAME =
58        new Function<Type, String>() {
59          @Override
60          public String apply(Type from) {
61            return JavaVersion.CURRENT.typeName(from);
62          }
63        };
64  
65    private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null");
66  
67    /** Returns the array type of {@code componentType}. */
68    static Type newArrayType(Type componentType) {
69      if (componentType instanceof WildcardType) {
70        WildcardType wildcard = (WildcardType) componentType;
71        Type[] lowerBounds = wildcard.getLowerBounds();
72        checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds.");
73        if (lowerBounds.length == 1) {
74          return supertypeOf(newArrayType(lowerBounds[0]));
75        } else {
76          Type[] upperBounds = wildcard.getUpperBounds();
77          checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound.");
78          return subtypeOf(newArrayType(upperBounds[0]));
79        }
80      }
81      return JavaVersion.CURRENT.newArrayType(componentType);
82    }
83  
84    /**
85     * Returns a type where {@code rawType} is parameterized by {@code arguments} and is owned by
86     * {@code ownerType}.
87     */
88    static ParameterizedType newParameterizedTypeWithOwner(
89        @Nullable Type ownerType, Class<?> rawType, Type... arguments) {
90      if (ownerType == null) {
91        return newParameterizedType(rawType, arguments);
92      }
93      // ParameterizedTypeImpl constructor already checks, but we want to throw NPE before IAE
94      checkNotNull(arguments);
95      checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType);
96      return new ParameterizedTypeImpl(ownerType, rawType, arguments);
97    }
98  
99    /**
100    * Returns a type where {@code rawType} is parameterized by {@code arguments}.
101    */
102   static ParameterizedType newParameterizedType(Class<?> rawType, Type... arguments) {
103     return new ParameterizedTypeImpl(
104         ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments);
105   }
106 
107   /** Decides what owner type to use for constructing {@link ParameterizedType} from a raw class. */
108   private enum ClassOwnership {
109     OWNED_BY_ENCLOSING_CLASS {
110       @Nullable
111       @Override
112       Class<?> getOwnerType(Class<?> rawType) {
113         return rawType.getEnclosingClass();
114       }
115     },
116     LOCAL_CLASS_HAS_NO_OWNER {
117       @Nullable
118       @Override
119       Class<?> getOwnerType(Class<?> rawType) {
120         if (rawType.isLocalClass()) {
121           return null;
122         } else {
123           return rawType.getEnclosingClass();
124         }
125       }
126     };
127 
128     @Nullable
129     abstract Class<?> getOwnerType(Class<?> rawType);
130 
131     static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior();
132 
133     private static ClassOwnership detectJvmBehavior() {
134       class LocalClass<T> {}
135       Class<?> subclass = new LocalClass<String>() {}.getClass();
136       ParameterizedType parameterizedType = (ParameterizedType) subclass.getGenericSuperclass();
137       for (ClassOwnership behavior : ClassOwnership.values()) {
138         if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) {
139           return behavior;
140         }
141       }
142       throw new AssertionError();
143     }
144   }
145 
146   /**
147    * Returns a new {@link TypeVariable} that belongs to {@code declaration} with {@code name} and
148    * {@code bounds}.
149    */
150   static <D extends GenericDeclaration> TypeVariable<D> newArtificialTypeVariable(
151       D declaration, String name, Type... bounds) {
152     return newTypeVariableImpl(
153         declaration, name, (bounds.length == 0) ? new Type[] {Object.class} : bounds);
154   }
155 
156   /** Returns a new {@link WildcardType} with {@code upperBound}. */
157   @VisibleForTesting
158   static WildcardType subtypeOf(Type upperBound) {
159     return new WildcardTypeImpl(new Type[0], new Type[] {upperBound});
160   }
161 
162   /** Returns a new {@link WildcardType} with {@code lowerBound}. */
163   @VisibleForTesting
164   static WildcardType supertypeOf(Type lowerBound) {
165     return new WildcardTypeImpl(new Type[] {lowerBound}, new Type[] {Object.class});
166   }
167 
168   /**
169    * Returns human readable string representation of {@code type}.
170    * <ul>
171    * <li>For array type {@code Foo[]}, {@code "com.mypackage.Foo[]"} are returned.
172    * <li>For any class, {@code theClass.getName()} are returned.
173    * <li>For all other types, {@code type.toString()} are returned.
174    * </ul>
175    */
176   static String toString(Type type) {
177     return (type instanceof Class) ? ((Class<?>) type).getName() : type.toString();
178   }
179 
180   @Nullable
181   static Type getComponentType(Type type) {
182     checkNotNull(type);
183     final AtomicReference<Type> result = new AtomicReference<>();
184     new TypeVisitor() {
185       @Override
186       void visitTypeVariable(TypeVariable<?> t) {
187         result.set(subtypeOfComponentType(t.getBounds()));
188       }
189 
190       @Override
191       void visitWildcardType(WildcardType t) {
192         result.set(subtypeOfComponentType(t.getUpperBounds()));
193       }
194 
195       @Override
196       void visitGenericArrayType(GenericArrayType t) {
197         result.set(t.getGenericComponentType());
198       }
199 
200       @Override
201       void visitClass(Class<?> t) {
202         result.set(t.getComponentType());
203       }
204     }.visit(type);
205     return result.get();
206   }
207 
208   /**
209    * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null
210    * otherwise.
211    */
212   @Nullable
213   private static Type subtypeOfComponentType(Type[] bounds) {
214     for (Type bound : bounds) {
215       Type componentType = getComponentType(bound);
216       if (componentType != null) {
217         // Only the first bound can be a class or array.
218         // Bounds after the first can only be interfaces.
219         if (componentType instanceof Class) {
220           Class<?> componentClass = (Class<?>) componentType;
221           if (componentClass.isPrimitive()) {
222             return componentClass;
223           }
224         }
225         return subtypeOf(componentType);
226       }
227     }
228     return null;
229   }
230 
231   private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable {
232 
233     private final Type componentType;
234 
235     GenericArrayTypeImpl(Type componentType) {
236       this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType);
237     }
238 
239     @Override
240     public Type getGenericComponentType() {
241       return componentType;
242     }
243 
244     @Override
245     public String toString() {
246       return Types.toString(componentType) + "[]";
247     }
248 
249     @Override
250     public int hashCode() {
251       return componentType.hashCode();
252     }
253 
254     @Override
255     public boolean equals(Object obj) {
256       if (obj instanceof GenericArrayType) {
257         GenericArrayType that = (GenericArrayType) obj;
258         return Objects.equal(getGenericComponentType(), that.getGenericComponentType());
259       }
260       return false;
261     }
262 
263     private static final long serialVersionUID = 0;
264   }
265 
266   private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable {
267 
268     private final Type ownerType;
269     private final ImmutableList<Type> argumentsList;
270     private final Class<?> rawType;
271 
272     ParameterizedTypeImpl(@Nullable Type ownerType, Class<?> rawType, Type[] typeArguments) {
273       checkNotNull(rawType);
274       checkArgument(typeArguments.length == rawType.getTypeParameters().length);
275       disallowPrimitiveType(typeArguments, "type parameter");
276       this.ownerType = ownerType;
277       this.rawType = rawType;
278       this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments);
279     }
280 
281     @Override
282     public Type[] getActualTypeArguments() {
283       return toArray(argumentsList);
284     }
285 
286     @Override
287     public Type getRawType() {
288       return rawType;
289     }
290 
291     @Override
292     public Type getOwnerType() {
293       return ownerType;
294     }
295 
296     @Override
297     public String toString() {
298       StringBuilder builder = new StringBuilder();
299       if (ownerType != null && JavaVersion.CURRENT.jdkTypeDuplicatesOwnerName()) {
300         builder.append(JavaVersion.CURRENT.typeName(ownerType)).append('.');
301       }
302       return builder
303           .append(rawType.getName())
304           .append('<')
305           .append(COMMA_JOINER.join(transform(argumentsList, TYPE_NAME)))
306           .append('>')
307           .toString();
308     }
309 
310     @Override
311     public int hashCode() {
312       return (ownerType == null ? 0 : ownerType.hashCode())
313           ^ argumentsList.hashCode()
314           ^ rawType.hashCode();
315     }
316 
317     @Override
318     public boolean equals(Object other) {
319       if (!(other instanceof ParameterizedType)) {
320         return false;
321       }
322       ParameterizedType that = (ParameterizedType) other;
323       return getRawType().equals(that.getRawType())
324           && Objects.equal(getOwnerType(), that.getOwnerType())
325           && Arrays.equals(getActualTypeArguments(), that.getActualTypeArguments());
326     }
327 
328     private static final long serialVersionUID = 0;
329   }
330 
331   private static <D extends GenericDeclaration> TypeVariable<D> newTypeVariableImpl(
332       D genericDeclaration, String name, Type[] bounds) {
333     TypeVariableImpl<D> typeVariableImpl =
334         new TypeVariableImpl<D>(genericDeclaration, name, bounds);
335     @SuppressWarnings("unchecked")
336     TypeVariable<D> typeVariable =
337         Reflection.newProxy(
338             TypeVariable.class, new TypeVariableInvocationHandler(typeVariableImpl));
339     return typeVariable;
340   }
341 
342   /**
343    * Invocation handler to work around a compatibility problem between Java 7 and Java 8.
344    *
345    * <p>Java 8 introduced a new method {@code getAnnotatedBounds()} in the {@link TypeVariable}
346    * interface, whose return type {@code AnnotatedType[]} is also new in Java 8. That means that we
347    * cannot implement that interface in source code in a way that will compile on both Java 7 and
348    * Java 8. If we include the {@code getAnnotatedBounds()} method then its return type means it
349    * won't compile on Java 7, while if we don't include the method then the compiler will complain
350    * that an abstract method is unimplemented. So instead we use a dynamic proxy to get an
351    * implementation. If the method being called on the {@code TypeVariable} instance has the same
352    * name as one of the public methods of {@link TypeVariableImpl}, the proxy calls the same method
353    * on its instance of {@code TypeVariableImpl}. Otherwise it throws
354    * {@link UnsupportedOperationException}; this should only apply to {@code getAnnotatedBounds()}.
355    * This does mean that users on Java 8 who obtain an instance of {@code TypeVariable} from
356    * {@link TypeResolver#resolveType} will not be able to call {@code getAnnotatedBounds()} on it,
357    * but that should hopefully be rare.
358    *
359    * <p>This workaround should be removed at a distant future time when we no longer support Java
360    * versions earlier than 8.
361    */
362   private static final class TypeVariableInvocationHandler implements InvocationHandler {
363     private static final ImmutableMap<String, Method> typeVariableMethods;
364 
365     static {
366       ImmutableMap.Builder<String, Method> builder = ImmutableMap.builder();
367       for (Method method : TypeVariableImpl.class.getMethods()) {
368         if (method.getDeclaringClass().equals(TypeVariableImpl.class)) {
369           try {
370             method.setAccessible(true);
371           } catch (AccessControlException e) {
372             // OK: the method is accessible to us anyway. The setAccessible call is only for
373             // unusual execution environments where that might not be true.
374           }
375           builder.put(method.getName(), method);
376         }
377       }
378       typeVariableMethods = builder.build();
379     }
380 
381     private final TypeVariableImpl<?> typeVariableImpl;
382 
383     TypeVariableInvocationHandler(TypeVariableImpl<?> typeVariableImpl) {
384       this.typeVariableImpl = typeVariableImpl;
385     }
386 
387     @Override
388     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
389       String methodName = method.getName();
390       Method typeVariableMethod = typeVariableMethods.get(methodName);
391       if (typeVariableMethod == null) {
392         throw new UnsupportedOperationException(methodName);
393       } else {
394         try {
395           return typeVariableMethod.invoke(typeVariableImpl, args);
396         } catch (InvocationTargetException e) {
397           throw e.getCause();
398         }
399       }
400     }
401   }
402 
403   private static final class TypeVariableImpl<D extends GenericDeclaration> {
404 
405     private final D genericDeclaration;
406     private final String name;
407     private final ImmutableList<Type> bounds;
408 
409     TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) {
410       disallowPrimitiveType(bounds, "bound for type variable");
411       this.genericDeclaration = checkNotNull(genericDeclaration);
412       this.name = checkNotNull(name);
413       this.bounds = ImmutableList.copyOf(bounds);
414     }
415 
416     public Type[] getBounds() {
417       return toArray(bounds);
418     }
419 
420     public D getGenericDeclaration() {
421       return genericDeclaration;
422     }
423 
424     public String getName() {
425       return name;
426     }
427 
428     public String getTypeName() {
429       return name;
430     }
431 
432     @Override
433     public String toString() {
434       return name;
435     }
436 
437     @Override
438     public int hashCode() {
439       return genericDeclaration.hashCode() ^ name.hashCode();
440     }
441 
442     @Override
443     public boolean equals(Object obj) {
444       if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) {
445         // equal only to our TypeVariable implementation with identical bounds
446         if (obj != null
447             && Proxy.isProxyClass(obj.getClass())
448             && Proxy.getInvocationHandler(obj) instanceof TypeVariableInvocationHandler) {
449           TypeVariableInvocationHandler typeVariableInvocationHandler =
450               (TypeVariableInvocationHandler) Proxy.getInvocationHandler(obj);
451           TypeVariableImpl<?> that = typeVariableInvocationHandler.typeVariableImpl;
452           return name.equals(that.getName())
453               && genericDeclaration.equals(that.getGenericDeclaration())
454               && bounds.equals(that.bounds);
455         }
456         return false;
457       } else {
458         // equal to any TypeVariable implementation regardless of bounds
459         if (obj instanceof TypeVariable) {
460           TypeVariable<?> that = (TypeVariable<?>) obj;
461           return name.equals(that.getName())
462               && genericDeclaration.equals(that.getGenericDeclaration());
463         }
464         return false;
465       }
466     }
467   }
468 
469   static final class WildcardTypeImpl implements WildcardType, Serializable {
470 
471     private final ImmutableList<Type> lowerBounds;
472     private final ImmutableList<Type> upperBounds;
473 
474     WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
475       disallowPrimitiveType(lowerBounds, "lower bound for wildcard");
476       disallowPrimitiveType(upperBounds, "upper bound for wildcard");
477       this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds);
478       this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds);
479     }
480 
481     @Override
482     public Type[] getLowerBounds() {
483       return toArray(lowerBounds);
484     }
485 
486     @Override
487     public Type[] getUpperBounds() {
488       return toArray(upperBounds);
489     }
490 
491     @Override
492     public boolean equals(Object obj) {
493       if (obj instanceof WildcardType) {
494         WildcardType that = (WildcardType) obj;
495         return lowerBounds.equals(Arrays.asList(that.getLowerBounds()))
496             && upperBounds.equals(Arrays.asList(that.getUpperBounds()));
497       }
498       return false;
499     }
500 
501     @Override
502     public int hashCode() {
503       return lowerBounds.hashCode() ^ upperBounds.hashCode();
504     }
505 
506     @Override
507     public String toString() {
508       StringBuilder builder = new StringBuilder("?");
509       for (Type lowerBound : lowerBounds) {
510         builder.append(" super ").append(JavaVersion.CURRENT.typeName(lowerBound));
511       }
512       for (Type upperBound : filterUpperBounds(upperBounds)) {
513         builder.append(" extends ").append(JavaVersion.CURRENT.typeName(upperBound));
514       }
515       return builder.toString();
516     }
517 
518     private static final long serialVersionUID = 0;
519   }
520 
521   private static Type[] toArray(Collection<Type> types) {
522     return types.toArray(new Type[types.size()]);
523   }
524 
525   private static Iterable<Type> filterUpperBounds(Iterable<Type> bounds) {
526     return Iterables.filter(bounds, Predicates.not(Predicates.<Type>equalTo(Object.class)));
527   }
528 
529   private static void disallowPrimitiveType(Type[] types, String usedAs) {
530     for (Type type : types) {
531       if (type instanceof Class) {
532         Class<?> cls = (Class<?>) type;
533         checkArgument(!cls.isPrimitive(), "Primitive type '%s' used as %s", cls, usedAs);
534       }
535     }
536   }
537 
538   /** Returns the {@code Class} object of arrays with {@code componentType}. */
539   static Class<?> getArrayClass(Class<?> componentType) {
540     // TODO(user): This is not the most efficient way to handle generic
541     // arrays, but is there another way to extract the array class in a
542     // non-hacky way (i.e. using String value class names- "[L...")?
543     return Array.newInstance(componentType, 0).getClass();
544   }
545 
546   // TODO(benyu): Once behavior is the same for all Java versions we support, delete this.
547   enum JavaVersion {
548     JAVA6 {
549       @Override
550       GenericArrayType newArrayType(Type componentType) {
551         return new GenericArrayTypeImpl(componentType);
552       }
553 
554       @Override
555       Type usedInGenericType(Type type) {
556         checkNotNull(type);
557         if (type instanceof Class) {
558           Class<?> cls = (Class<?>) type;
559           if (cls.isArray()) {
560             return new GenericArrayTypeImpl(cls.getComponentType());
561           }
562         }
563         return type;
564       }
565     },
566     JAVA7 {
567       @Override
568       Type newArrayType(Type componentType) {
569         if (componentType instanceof Class) {
570           return getArrayClass((Class<?>) componentType);
571         } else {
572           return new GenericArrayTypeImpl(componentType);
573         }
574       }
575 
576       @Override
577       Type usedInGenericType(Type type) {
578         return checkNotNull(type);
579       }
580     },
581     JAVA8 {
582       @Override
583       Type newArrayType(Type componentType) {
584         return JAVA7.newArrayType(componentType);
585       }
586 
587       @Override
588       Type usedInGenericType(Type type) {
589         return JAVA7.usedInGenericType(type);
590       }
591 
592       @Override
593       String typeName(Type type) {
594         try {
595           Method getTypeName = Type.class.getMethod("getTypeName");
596           return (String) getTypeName.invoke(type);
597         } catch (NoSuchMethodException e) {
598           throw new AssertionError("Type.getTypeName should be available in Java 8");
599         } catch (InvocationTargetException | IllegalAccessException e) {
600           throw new RuntimeException(e);
601         }
602       }
603     },
604     JAVA9 {
605       @Override
606       Type newArrayType(Type componentType) {
607         return JAVA8.newArrayType(componentType);
608       }
609 
610       @Override
611       Type usedInGenericType(Type type) {
612         return JAVA8.usedInGenericType(type);
613       }
614 
615       @Override
616       String typeName(Type type) {
617         return JAVA8.typeName(type);
618       }
619 
620       @Override
621       boolean jdkTypeDuplicatesOwnerName() {
622         return false;
623       }
624     };
625 
626     static final JavaVersion CURRENT;
627 
628     static {
629       if (AnnotatedElement.class.isAssignableFrom(TypeVariable.class)) {
630         if (new TypeCapture<Map.Entry<String, int[][]>>() {}.capture()
631             .toString()
632             .contains("java.util.Map.java.util.Map")) {
633           CURRENT = JAVA8;
634         } else {
635           CURRENT = JAVA9;
636         }
637       } else if (new TypeCapture<int[]>() {}.capture() instanceof Class) {
638         CURRENT = JAVA7;
639       } else {
640         CURRENT = JAVA6;
641       }
642     }
643 
644     abstract Type newArrayType(Type componentType);
645 
646     abstract Type usedInGenericType(Type type);
647 
648     String typeName(Type type) {
649       return Types.toString(type);
650     }
651 
652     boolean jdkTypeDuplicatesOwnerName() {
653       return true;
654     }
655 
656     final ImmutableList<Type> usedInGenericType(Type[] types) {
657       ImmutableList.Builder<Type> builder = ImmutableList.builder();
658       for (Type type : types) {
659         builder.add(usedInGenericType(type));
660       }
661       return builder.build();
662     }
663   }
664 
665   /**
666    * Per <a href="https://code.google.com/p/guava-libraries/issues/detail?id=1635">issue 1635</a>,
667    * In JDK 1.7.0_51-b13, {@link TypeVariableImpl#equals(Object)} is changed to no longer be equal
668    * to custom TypeVariable implementations. As a result, we need to make sure our TypeVariable
669    * implementation respects symmetry. Moreover, we don't want to reconstruct a native type variable
670    * {@code <A>} using our implementation unless some of its bounds have changed in resolution. This
671    * avoids creating unequal TypeVariable implementation unnecessarily. When the bounds do change,
672    * however, it's fine for the synthetic TypeVariable to be unequal to any native TypeVariable
673    * anyway.
674    */
675   static final class NativeTypeVariableEquals<X> {
676     static final boolean NATIVE_TYPE_VARIABLE_ONLY =
677         !NativeTypeVariableEquals.class.getTypeParameters()[0]
678             .equals(newArtificialTypeVariable(NativeTypeVariableEquals.class, "X"));
679   }
680 
681   private Types() {}
682 }