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