• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.collect.Iterables.transform;
22 
23 import com.google.common.annotations.VisibleForTesting;
24 import com.google.common.base.Function;
25 import com.google.common.base.Joiner;
26 import com.google.common.base.Objects;
27 import com.google.common.base.Predicates;
28 import com.google.common.collect.ImmutableList;
29 import com.google.common.collect.Iterables;
30 
31 import java.io.Serializable;
32 import java.lang.reflect.Array;
33 import java.lang.reflect.GenericArrayType;
34 import java.lang.reflect.GenericDeclaration;
35 import java.lang.reflect.ParameterizedType;
36 import java.lang.reflect.Type;
37 import java.lang.reflect.TypeVariable;
38 import java.lang.reflect.WildcardType;
39 import java.util.Arrays;
40 import java.util.Collection;
41 import java.util.concurrent.atomic.AtomicReference;
42 
43 import javax.annotation.Nullable;
44 
45 /**
46  * Utilities for working with {@link Type}.
47  *
48  * @author Ben Yu
49  */
50 final class Types {
51 
52   /** Class#toString without the "class " and "interface " prefixes */
53   private static final Function<Type, String> TYPE_TO_STRING =
54       new Function<Type, String>() {
55         @Override public String apply(Type from) {
56           return Types.toString(from);
57         }
58       };
59 
60   private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null");
61 
62   /** Returns the array type of {@code componentType}. */
newArrayType(Type componentType)63   static Type newArrayType(Type componentType) {
64     if (componentType instanceof WildcardType) {
65       WildcardType wildcard = (WildcardType) componentType;
66       Type[] lowerBounds = wildcard.getLowerBounds();
67       checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds.");
68       if (lowerBounds.length == 1) {
69         return supertypeOf(newArrayType(lowerBounds[0]));
70       } else {
71         Type[] upperBounds = wildcard.getUpperBounds();
72         checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound.");
73         return subtypeOf(newArrayType(upperBounds[0]));
74       }
75     }
76     return JavaVersion.CURRENT.newArrayType(componentType);
77   }
78 
79   /**
80    * Returns a type where {@code rawType} is parameterized by
81    * {@code arguments} and is owned by {@code ownerType}.
82    */
newParameterizedTypeWithOwner( @ullable Type ownerType, Class<?> rawType, Type... arguments)83   static ParameterizedType newParameterizedTypeWithOwner(
84       @Nullable Type ownerType, Class<?> rawType, Type... arguments) {
85     if (ownerType == null) {
86       return newParameterizedType(rawType, arguments);
87     }
88     // ParameterizedTypeImpl constructor already checks, but we want to throw NPE before IAE
89     checkNotNull(arguments);
90     checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType);
91     return new ParameterizedTypeImpl(ownerType, rawType, arguments);
92   }
93 
94   /**
95    * Returns a type where {@code rawType} is parameterized by
96    * {@code arguments}.
97    */
newParameterizedType(Class<?> rawType, Type... arguments)98   static ParameterizedType newParameterizedType(Class<?> rawType, Type... arguments) {
99       return new ParameterizedTypeImpl(
100           ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments);
101   }
102 
103   /** Decides what owner type to use for constructing {@link ParameterizedType} from a raw class. */
104   private enum ClassOwnership {
105 
106     OWNED_BY_ENCLOSING_CLASS {
107       @Nullable
108       @Override
getOwnerType(Class<?> rawType)109       Class<?> getOwnerType(Class<?> rawType) {
110         return rawType.getEnclosingClass();
111       }
112     },
113     LOCAL_CLASS_HAS_NO_OWNER {
114       @Nullable
115       @Override
getOwnerType(Class<?> rawType)116       Class<?> getOwnerType(Class<?> rawType) {
117         if (rawType.isLocalClass()) {
118           return null;
119         } else {
120           return rawType.getEnclosingClass();
121         }
122       }
123     };
124 
getOwnerType(Class<?> rawType)125     @Nullable abstract Class<?> getOwnerType(Class<?> rawType);
126 
127     static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior();
128 
detectJvmBehavior()129     private static ClassOwnership detectJvmBehavior() {
130       class LocalClass<T> {}
131       Class<?> subclass = new LocalClass<String>() {}.getClass();
132       ParameterizedType parameterizedType = (ParameterizedType)
133           subclass.getGenericSuperclass();
134       for (ClassOwnership behavior : ClassOwnership.values()) {
135         if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) {
136           return behavior;
137         }
138       }
139       throw new AssertionError();
140     }
141   }
142 
143   /**
144    * Returns a new {@link TypeVariable} that belongs to {@code declaration} with
145    * {@code name} and {@code bounds}.
146    */
newArtificialTypeVariable( D declaration, String name, Type... bounds)147   static <D extends GenericDeclaration> TypeVariable<D> newArtificialTypeVariable(
148       D declaration, String name, Type... bounds) {
149     return new TypeVariableImpl<D>(
150         declaration,
151         name,
152         (bounds.length == 0)
153             ? new Type[] { Object.class }
154             : bounds);
155   }
156 
157   /** Returns a new {@link WildcardType} with {@code upperBound}. */
subtypeOf(Type upperBound)158   @VisibleForTesting 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}. */
supertypeOf(Type lowerBound)163   @VisibleForTesting static WildcardType supertypeOf(Type lowerBound) {
164     return new WildcardTypeImpl(new Type[] { lowerBound }, new Type[] { Object.class });
165   }
166 
167   /**
168    * Returns human readable string representation of {@code type}.
169    * <ul>
170    * <li> For array type {@code Foo[]}, {@code "com.mypackage.Foo[]"} are
171    * 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    */
toString(Type type)176   static String toString(Type type) {
177     return (type instanceof Class)
178         ? ((Class<?>) type).getName()
179         : type.toString();
180   }
181 
getComponentType(Type type)182   @Nullable static Type getComponentType(Type type) {
183     checkNotNull(type);
184     final AtomicReference<Type> result = new AtomicReference<Type>();
185     new TypeVisitor() {
186       @Override void visitTypeVariable(TypeVariable<?> t) {
187         result.set(subtypeOfComponentType(t.getBounds()));
188       }
189       @Override void visitWildcardType(WildcardType t) {
190         result.set(subtypeOfComponentType(t.getUpperBounds()));
191       }
192       @Override void visitGenericArrayType(GenericArrayType t) {
193         result.set(t.getGenericComponentType());
194       }
195       @Override void visitClass(Class<?> t) {
196         result.set(t.getComponentType());
197       }
198     }.visit(type);
199     return result.get();
200   }
201 
202   /**
203    * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null
204    * otherwise.
205    */
subtypeOfComponentType(Type[] bounds)206   @Nullable private static Type subtypeOfComponentType(Type[] bounds) {
207     for (Type bound : bounds) {
208       Type componentType = getComponentType(bound);
209       if (componentType != null) {
210         // Only the first bound can be a class or array.
211         // Bounds after the first can only be interfaces.
212         if (componentType instanceof Class) {
213           Class<?> componentClass = (Class<?>) componentType;
214           if (componentClass.isPrimitive()) {
215             return componentClass;
216           }
217         }
218         return subtypeOf(componentType);
219       }
220     }
221     return null;
222   }
223 
224   private static final class GenericArrayTypeImpl
225       implements GenericArrayType, Serializable {
226 
227     private final Type componentType;
228 
GenericArrayTypeImpl(Type componentType)229     GenericArrayTypeImpl(Type componentType) {
230       this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType);
231     }
232 
getGenericComponentType()233     @Override public Type getGenericComponentType() {
234       return componentType;
235     }
236 
toString()237     @Override public String toString() {
238       return Types.toString(componentType) + "[]";
239     }
240 
hashCode()241     @Override public int hashCode() {
242       return componentType.hashCode();
243     }
244 
equals(Object obj)245     @Override public boolean equals(Object obj) {
246       if (obj instanceof GenericArrayType) {
247         GenericArrayType that = (GenericArrayType) obj;
248         return Objects.equal(
249             getGenericComponentType(), that.getGenericComponentType());
250       }
251       return false;
252     }
253 
254     private static final long serialVersionUID = 0;
255   }
256 
257   private static final class ParameterizedTypeImpl
258       implements ParameterizedType, Serializable {
259 
260     private final Type ownerType;
261     private final ImmutableList<Type> argumentsList;
262     private final Class<?> rawType;
263 
ParameterizedTypeImpl( @ullable Type ownerType, Class<?> rawType, Type[] typeArguments)264     ParameterizedTypeImpl(
265         @Nullable Type ownerType, Class<?> rawType, Type[] typeArguments) {
266       checkNotNull(rawType);
267       checkArgument(typeArguments.length == rawType.getTypeParameters().length);
268       disallowPrimitiveType(typeArguments, "type parameter");
269       this.ownerType = ownerType;
270       this.rawType = rawType;
271       this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments);
272     }
273 
getActualTypeArguments()274     @Override public Type[] getActualTypeArguments() {
275       return toArray(argumentsList);
276     }
277 
getRawType()278     @Override public Type getRawType() {
279       return rawType;
280     }
281 
getOwnerType()282     @Override public Type getOwnerType() {
283       return ownerType;
284     }
285 
toString()286     @Override public String toString() {
287       StringBuilder builder = new StringBuilder();
288       if (ownerType != null) {
289         builder.append(Types.toString(ownerType)).append('.');
290       }
291       builder.append(rawType.getName())
292           .append('<')
293           .append(COMMA_JOINER.join(transform(argumentsList, TYPE_TO_STRING)))
294           .append('>');
295       return builder.toString();
296     }
297 
hashCode()298     @Override public int hashCode() {
299       return (ownerType == null ? 0 : ownerType.hashCode())
300           ^ argumentsList.hashCode() ^ rawType.hashCode();
301     }
302 
equals(Object other)303     @Override public boolean equals(Object other) {
304       if (!(other instanceof ParameterizedType)) {
305         return false;
306       }
307       ParameterizedType that = (ParameterizedType) other;
308       return getRawType().equals(that.getRawType())
309           && Objects.equal(getOwnerType(), that.getOwnerType())
310           && Arrays.equals(
311               getActualTypeArguments(), that.getActualTypeArguments());
312     }
313 
314     private static final long serialVersionUID = 0;
315   }
316 
317   private static final class TypeVariableImpl<D extends GenericDeclaration>
318       implements TypeVariable<D> {
319 
320     private final D genericDeclaration;
321     private final String name;
322     private final ImmutableList<Type> bounds;
323 
TypeVariableImpl(D genericDeclaration, String name, Type[] bounds)324     TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) {
325       disallowPrimitiveType(bounds, "bound for type variable");
326       this.genericDeclaration = checkNotNull(genericDeclaration);
327       this.name = checkNotNull(name);
328       this.bounds = ImmutableList.copyOf(bounds);
329     }
330 
getBounds()331     @Override public Type[] getBounds() {
332       return toArray(bounds);
333     }
334 
getGenericDeclaration()335     @Override public D getGenericDeclaration() {
336       return genericDeclaration;
337     }
338 
getName()339     @Override public String getName() {
340       return name;
341     }
342 
toString()343     @Override public String toString() {
344       return name;
345     }
346 
hashCode()347     @Override public int hashCode() {
348       return genericDeclaration.hashCode() ^ name.hashCode();
349     }
350 
equals(Object obj)351     @Override public boolean equals(Object obj) {
352       if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) {
353         // equal only to our TypeVariable implementation with identical bounds
354         if (obj instanceof TypeVariableImpl) {
355           TypeVariableImpl<?> that = (TypeVariableImpl<?>) obj;
356           return name.equals(that.getName())
357               && genericDeclaration.equals(that.getGenericDeclaration())
358               && bounds.equals(that.bounds);
359         }
360         return false;
361       } else {
362         // equal to any TypeVariable implementation regardless of bounds
363         if (obj instanceof TypeVariable) {
364           TypeVariable<?> that = (TypeVariable<?>) obj;
365           return name.equals(that.getName())
366               && genericDeclaration.equals(that.getGenericDeclaration());
367         }
368         return false;
369       }
370     }
371   }
372 
373   static final class WildcardTypeImpl implements WildcardType, Serializable {
374 
375     private final ImmutableList<Type> lowerBounds;
376     private final ImmutableList<Type> upperBounds;
377 
WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds)378     WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
379       disallowPrimitiveType(lowerBounds, "lower bound for wildcard");
380       disallowPrimitiveType(upperBounds, "upper bound for wildcard");
381       this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds);
382       this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds);
383     }
384 
getLowerBounds()385     @Override public Type[] getLowerBounds() {
386       return toArray(lowerBounds);
387     }
388 
getUpperBounds()389     @Override public Type[] getUpperBounds() {
390       return toArray(upperBounds);
391     }
392 
equals(Object obj)393     @Override public boolean equals(Object obj) {
394       if (obj instanceof WildcardType) {
395         WildcardType that = (WildcardType) obj;
396         return lowerBounds.equals(Arrays.asList(that.getLowerBounds()))
397             && upperBounds.equals(Arrays.asList(that.getUpperBounds()));
398       }
399       return false;
400     }
401 
hashCode()402     @Override public int hashCode() {
403       return lowerBounds.hashCode() ^ upperBounds.hashCode();
404     }
405 
toString()406     @Override public String toString() {
407       StringBuilder builder = new StringBuilder("?");
408       for (Type lowerBound : lowerBounds) {
409         builder.append(" super ").append(Types.toString(lowerBound));
410       }
411       for (Type upperBound : filterUpperBounds(upperBounds)) {
412         builder.append(" extends ").append(Types.toString(upperBound));
413       }
414       return builder.toString();
415     }
416 
417     private static final long serialVersionUID = 0;
418   }
419 
toArray(Collection<Type> types)420   private static Type[] toArray(Collection<Type> types) {
421     return types.toArray(new Type[types.size()]);
422   }
423 
filterUpperBounds(Iterable<Type> bounds)424   private static Iterable<Type> filterUpperBounds(Iterable<Type> bounds) {
425     return Iterables.filter(
426         bounds, Predicates.not(Predicates.<Type>equalTo(Object.class)));
427   }
428 
disallowPrimitiveType(Type[] types, String usedAs)429   private static void disallowPrimitiveType(Type[] types, String usedAs) {
430     for (Type type : types) {
431       if (type instanceof Class) {
432         Class<?> cls = (Class<?>) type;
433         checkArgument(!cls.isPrimitive(),
434             "Primitive type '%s' used as %s", cls, usedAs);
435       }
436     }
437   }
438 
439   /** Returns the {@code Class} object of arrays with {@code componentType}. */
getArrayClass(Class<?> componentType)440   static Class<?> getArrayClass(Class<?> componentType) {
441     // TODO(user): This is not the most efficient way to handle generic
442     // arrays, but is there another way to extract the array class in a
443     // non-hacky way (i.e. using String value class names- "[L...")?
444     return Array.newInstance(componentType, 0).getClass();
445   }
446 
447   // TODO(benyu): Once we are on Java 7, delete this abstraction
448   enum JavaVersion {
449 
450     JAVA6 {
newArrayType(Type componentType)451       @Override GenericArrayType newArrayType(Type componentType) {
452         return new GenericArrayTypeImpl(componentType);
453       }
usedInGenericType(Type type)454       @Override Type usedInGenericType(Type type) {
455         checkNotNull(type);
456         if (type instanceof Class) {
457           Class<?> cls = (Class<?>) type;
458           if (cls.isArray()) {
459             return new GenericArrayTypeImpl(cls.getComponentType());
460           }
461         }
462         return type;
463       }
464     },
465     JAVA7 {
newArrayType(Type componentType)466       @Override Type newArrayType(Type componentType) {
467         if (componentType instanceof Class) {
468           return getArrayClass((Class<?>) componentType);
469         } else {
470           return new GenericArrayTypeImpl(componentType);
471         }
472       }
usedInGenericType(Type type)473       @Override Type usedInGenericType(Type type) {
474         return checkNotNull(type);
475       }
476     }
477     ;
478 
479     static final JavaVersion CURRENT =
480         (new TypeCapture<int[]>() {}.capture() instanceof Class)
481         ? JAVA7 : JAVA6;
newArrayType(Type componentType)482     abstract Type newArrayType(Type componentType);
usedInGenericType(Type type)483     abstract Type usedInGenericType(Type type);
484 
usedInGenericType(Type[] types)485     final ImmutableList<Type> usedInGenericType(Type[] types) {
486       ImmutableList.Builder<Type> builder = ImmutableList.builder();
487       for (Type type : types) {
488         builder.add(usedInGenericType(type));
489       }
490       return builder.build();
491     }
492   }
493 
494   /**
495    * Per https://code.google.com/p/guava-libraries/issues/detail?id=1635,
496    * In JDK 1.7.0_51-b13, TypeVariableImpl.equals() is changed to no longer be equal to custom
497    * TypeVariable implementations. As a result, we need to make sure our TypeVariable implementation
498    * respects symmetry.
499    * Moreover, we don't want to reconstruct a native type variable <A> using our implementation
500    * unless some of its bounds have changed in resolution. This avoids creating unequal TypeVariable
501    * implementation unnecessarily. When the bounds do change, however, it's fine for the synthetic
502    * TypeVariable to be unequal to any native TypeVariable anyway.
503    */
504   static final class NativeTypeVariableEquals<X> {
505     static final boolean NATIVE_TYPE_VARIABLE_ONLY =
506         !NativeTypeVariableEquals.class.getTypeParameters()[0].equals(
507             newArtificialTypeVariable(NativeTypeVariableEquals.class, "X"));
508   }
509 
Types()510   private Types() {}
511 }
512