Home | History | Annotate | Download | only in inject
      1 /*
      2  * Copyright (C) 2006 Google Inc.
      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.inject;
     18 
     19 import static com.google.common.base.Preconditions.checkArgument;
     20 import static com.google.common.base.Preconditions.checkNotNull;
     21 import static com.google.inject.internal.MoreTypes.canonicalize;
     22 
     23 import com.google.common.collect.ImmutableList;
     24 import com.google.inject.internal.MoreTypes;
     25 import com.google.inject.util.Types;
     26 import java.lang.reflect.Constructor;
     27 import java.lang.reflect.Field;
     28 import java.lang.reflect.GenericArrayType;
     29 import java.lang.reflect.Member;
     30 import java.lang.reflect.Method;
     31 import java.lang.reflect.ParameterizedType;
     32 import java.lang.reflect.Type;
     33 import java.lang.reflect.TypeVariable;
     34 import java.lang.reflect.WildcardType;
     35 import java.util.List;
     36 
     37 /**
     38  * Represents a generic type {@code T}. Java doesn't yet provide a way to represent generic types,
     39  * so this class does. Forces clients to create a subclass of this class which enables retrieval of
     40  * the type information even at runtime.
     41  *
     42  * <p>For example, to create a type literal for {@code List<String>}, you can create an empty
     43  * anonymous inner class:
     44  *
     45  * <p>{@code TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};}
     46  *
     47  * <p>Along with modeling generic types, this class can resolve type parameters. For example, to
     48  * figure out what type {@code keySet()} returns on a {@code Map<Integer, String>}, use this code:
     49  *
     50  * <pre>{@code
     51  * TypeLiteral<Map<Integer, String>> mapType
     52  *     = new TypeLiteral<Map<Integer, String>>() {};
     53  * TypeLiteral<?> keySetType
     54  *     = mapType.getReturnType(Map.class.getMethod("keySet"));
     55  * System.out.println(keySetType); // prints "Set<Integer>"
     56  * }</pre>
     57  *
     58  * @author crazybob (at) google.com (Bob Lee)
     59  * @author jessewilson (at) google.com (Jesse Wilson)
     60  */
     61 public class TypeLiteral<T> {
     62 
     63   final Class<? super T> rawType;
     64   final Type type;
     65   final int hashCode;
     66 
     67   /**
     68    * Constructs a new type literal. Derives represented class from type parameter.
     69    *
     70    * <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
     71    * anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
     72    */
     73   @SuppressWarnings("unchecked")
     74   protected TypeLiteral() {
     75     this.type = getSuperclassTypeParameter(getClass());
     76     this.rawType = (Class<? super T>) MoreTypes.getRawType(type);
     77     this.hashCode = type.hashCode();
     78   }
     79 
     80   /** Unsafe. Constructs a type literal manually. */
     81   @SuppressWarnings("unchecked")
     82   TypeLiteral(Type type) {
     83     this.type = canonicalize(checkNotNull(type, "type"));
     84     this.rawType = (Class<? super T>) MoreTypes.getRawType(this.type);
     85     this.hashCode = this.type.hashCode();
     86   }
     87 
     88   /**
     89    * Returns the type from super class's type parameter in {@link MoreTypes#canonicalize(Type)
     90    * canonical form}.
     91    */
     92   static Type getSuperclassTypeParameter(Class<?> subclass) {
     93     Type superclass = subclass.getGenericSuperclass();
     94     if (superclass instanceof Class) {
     95       throw new RuntimeException("Missing type parameter.");
     96     }
     97     ParameterizedType parameterized = (ParameterizedType) superclass;
     98     return canonicalize(parameterized.getActualTypeArguments()[0]);
     99   }
    100 
    101   /** Gets type literal from super class's type parameter. */
    102   static TypeLiteral<?> fromSuperclassTypeParameter(Class<?> subclass) {
    103     return new TypeLiteral<Object>(getSuperclassTypeParameter(subclass));
    104   }
    105 
    106   /**
    107    * Returns the raw (non-generic) type for this type.
    108    *
    109    * @since 2.0
    110    */
    111   public final Class<? super T> getRawType() {
    112     return rawType;
    113   }
    114 
    115   /** Gets underlying {@code Type} instance. */
    116   public final Type getType() {
    117     return type;
    118   }
    119 
    120   /** Gets the type of this type's provider. */
    121   @SuppressWarnings("unchecked")
    122   final TypeLiteral<Provider<T>> providerType() {
    123     // This cast is safe and wouldn't generate a warning if Type had a type
    124     // parameter.
    125     return (TypeLiteral<Provider<T>>) get(Types.providerOf(getType()));
    126   }
    127 
    128   @Override
    129   public final int hashCode() {
    130     return this.hashCode;
    131   }
    132 
    133   @Override
    134   public final boolean equals(Object o) {
    135     return o instanceof TypeLiteral<?> && MoreTypes.equals(type, ((TypeLiteral) o).type);
    136   }
    137 
    138   @Override
    139   public final String toString() {
    140     return MoreTypes.typeToString(type);
    141   }
    142 
    143   /** Gets type literal for the given {@code Type} instance. */
    144   public static TypeLiteral<?> get(Type type) {
    145     return new TypeLiteral<Object>(type);
    146   }
    147 
    148   /** Gets type literal for the given {@code Class} instance. */
    149   public static <T> TypeLiteral<T> get(Class<T> type) {
    150     return new TypeLiteral<T>(type);
    151   }
    152 
    153   /** Returns an immutable list of the resolved types. */
    154   private List<TypeLiteral<?>> resolveAll(Type[] types) {
    155     TypeLiteral<?>[] result = new TypeLiteral<?>[types.length];
    156     for (int t = 0; t < types.length; t++) {
    157       result[t] = resolve(types[t]);
    158     }
    159     return ImmutableList.copyOf(result);
    160   }
    161 
    162   /** Resolves known type parameters in {@code toResolve} and returns the result. */
    163   TypeLiteral<?> resolve(Type toResolve) {
    164     return TypeLiteral.get(resolveType(toResolve));
    165   }
    166 
    167   Type resolveType(Type toResolve) {
    168     // this implementation is made a little more complicated in an attempt to avoid object-creation
    169     while (true) {
    170       if (toResolve instanceof TypeVariable) {
    171         TypeVariable original = (TypeVariable) toResolve;
    172         toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
    173         if (toResolve == original) {
    174           return toResolve;
    175         }
    176 
    177       } else if (toResolve instanceof GenericArrayType) {
    178         GenericArrayType original = (GenericArrayType) toResolve;
    179         Type componentType = original.getGenericComponentType();
    180         Type newComponentType = resolveType(componentType);
    181         return componentType == newComponentType ? original : Types.arrayOf(newComponentType);
    182 
    183       } else if (toResolve instanceof ParameterizedType) {
    184         ParameterizedType original = (ParameterizedType) toResolve;
    185         Type ownerType = original.getOwnerType();
    186         Type newOwnerType = resolveType(ownerType);
    187         boolean changed = newOwnerType != ownerType;
    188 
    189         Type[] args = original.getActualTypeArguments();
    190         for (int t = 0, length = args.length; t < length; t++) {
    191           Type resolvedTypeArgument = resolveType(args[t]);
    192           if (resolvedTypeArgument != args[t]) {
    193             if (!changed) {
    194               args = args.clone();
    195               changed = true;
    196             }
    197             args[t] = resolvedTypeArgument;
    198           }
    199         }
    200 
    201         return changed
    202             ? Types.newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
    203             : original;
    204 
    205       } else if (toResolve instanceof WildcardType) {
    206         WildcardType original = (WildcardType) toResolve;
    207         Type[] originalLowerBound = original.getLowerBounds();
    208         Type[] originalUpperBound = original.getUpperBounds();
    209 
    210         if (originalLowerBound.length == 1) {
    211           Type lowerBound = resolveType(originalLowerBound[0]);
    212           if (lowerBound != originalLowerBound[0]) {
    213             return Types.supertypeOf(lowerBound);
    214           }
    215         } else if (originalUpperBound.length == 1) {
    216           Type upperBound = resolveType(originalUpperBound[0]);
    217           if (upperBound != originalUpperBound[0]) {
    218             return Types.subtypeOf(upperBound);
    219           }
    220         }
    221         return original;
    222 
    223       } else {
    224         return toResolve;
    225       }
    226     }
    227   }
    228 
    229   /**
    230    * Returns the generic form of {@code supertype}. For example, if this is {@code
    231    * ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
    232    * Iterable.class}.
    233    *
    234    * @param supertype a superclass of, or interface implemented by, this.
    235    * @since 2.0
    236    */
    237   public TypeLiteral<?> getSupertype(Class<?> supertype) {
    238     checkArgument(
    239         supertype.isAssignableFrom(rawType), "%s is not a supertype of %s", supertype, this.type);
    240     return resolve(MoreTypes.getGenericSupertype(type, rawType, supertype));
    241   }
    242 
    243   /**
    244    * Returns the resolved generic type of {@code field}.
    245    *
    246    * @param field a field defined by this or any superclass.
    247    * @since 2.0
    248    */
    249   public TypeLiteral<?> getFieldType(Field field) {
    250     checkArgument(
    251         field.getDeclaringClass().isAssignableFrom(rawType),
    252         "%s is not defined by a supertype of %s",
    253         field,
    254         type);
    255     return resolve(field.getGenericType());
    256   }
    257 
    258   /**
    259    * Returns the resolved generic parameter types of {@code methodOrConstructor}.
    260    *
    261    * @param methodOrConstructor a method or constructor defined by this or any supertype.
    262    * @since 2.0
    263    */
    264   public List<TypeLiteral<?>> getParameterTypes(Member methodOrConstructor) {
    265     Type[] genericParameterTypes;
    266 
    267     if (methodOrConstructor instanceof Method) {
    268       Method method = (Method) methodOrConstructor;
    269       checkArgument(
    270           method.getDeclaringClass().isAssignableFrom(rawType),
    271           "%s is not defined by a supertype of %s",
    272           method,
    273           type);
    274       genericParameterTypes = method.getGenericParameterTypes();
    275 
    276     } else if (methodOrConstructor instanceof Constructor) {
    277       Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
    278       checkArgument(
    279           constructor.getDeclaringClass().isAssignableFrom(rawType),
    280           "%s does not construct a supertype of %s",
    281           constructor,
    282           type);
    283       genericParameterTypes = constructor.getGenericParameterTypes();
    284 
    285     } else {
    286       throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
    287     }
    288 
    289     return resolveAll(genericParameterTypes);
    290   }
    291 
    292   /**
    293    * Returns the resolved generic exception types thrown by {@code constructor}.
    294    *
    295    * @param methodOrConstructor a method or constructor defined by this or any supertype.
    296    * @since 2.0
    297    */
    298   public List<TypeLiteral<?>> getExceptionTypes(Member methodOrConstructor) {
    299     Type[] genericExceptionTypes;
    300 
    301     if (methodOrConstructor instanceof Method) {
    302       Method method = (Method) methodOrConstructor;
    303       checkArgument(
    304           method.getDeclaringClass().isAssignableFrom(rawType),
    305           "%s is not defined by a supertype of %s",
    306           method,
    307           type);
    308       genericExceptionTypes = method.getGenericExceptionTypes();
    309 
    310     } else if (methodOrConstructor instanceof Constructor) {
    311       Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
    312       checkArgument(
    313           constructor.getDeclaringClass().isAssignableFrom(rawType),
    314           "%s does not construct a supertype of %s",
    315           constructor,
    316           type);
    317       genericExceptionTypes = constructor.getGenericExceptionTypes();
    318 
    319     } else {
    320       throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
    321     }
    322 
    323     return resolveAll(genericExceptionTypes);
    324   }
    325 
    326   /**
    327    * Returns the resolved generic return type of {@code method}.
    328    *
    329    * @param method a method defined by this or any supertype.
    330    * @since 2.0
    331    */
    332   public TypeLiteral<?> getReturnType(Method method) {
    333     checkArgument(
    334         method.getDeclaringClass().isAssignableFrom(rawType),
    335         "%s is not defined by a supertype of %s",
    336         method,
    337         type);
    338     return resolve(method.getGenericReturnType());
    339   }
    340 }
    341