Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2008 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.internal;
     18 
     19 import static com.google.common.base.Preconditions.checkArgument;
     20 import static com.google.common.base.Preconditions.checkNotNull;
     21 
     22 import com.google.common.base.Objects;
     23 import com.google.common.collect.ImmutableMap;
     24 import com.google.inject.ConfigurationException;
     25 import com.google.inject.Key;
     26 import com.google.inject.TypeLiteral;
     27 import com.google.inject.util.Types;
     28 import java.io.Serializable;
     29 import java.lang.reflect.Array;
     30 import java.lang.reflect.GenericArrayType;
     31 import java.lang.reflect.GenericDeclaration;
     32 import java.lang.reflect.ParameterizedType;
     33 import java.lang.reflect.Type;
     34 import java.lang.reflect.TypeVariable;
     35 import java.lang.reflect.WildcardType;
     36 import java.util.Arrays;
     37 import java.util.NoSuchElementException;
     38 
     39 /**
     40  * Static methods for working with types that we aren't publishing in the public {@code Types} API.
     41  *
     42  * @author jessewilson (at) google.com (Jesse Wilson)
     43  */
     44 public class MoreTypes {
     45 
     46   public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
     47 
     48   private MoreTypes() {}
     49 
     50   private static final ImmutableMap<TypeLiteral<?>, TypeLiteral<?>> PRIMITIVE_TO_WRAPPER =
     51       new ImmutableMap.Builder<TypeLiteral<?>, TypeLiteral<?>>()
     52           .put(TypeLiteral.get(boolean.class), TypeLiteral.get(Boolean.class))
     53           .put(TypeLiteral.get(byte.class), TypeLiteral.get(Byte.class))
     54           .put(TypeLiteral.get(short.class), TypeLiteral.get(Short.class))
     55           .put(TypeLiteral.get(int.class), TypeLiteral.get(Integer.class))
     56           .put(TypeLiteral.get(long.class), TypeLiteral.get(Long.class))
     57           .put(TypeLiteral.get(float.class), TypeLiteral.get(Float.class))
     58           .put(TypeLiteral.get(double.class), TypeLiteral.get(Double.class))
     59           .put(TypeLiteral.get(char.class), TypeLiteral.get(Character.class))
     60           .put(TypeLiteral.get(void.class), TypeLiteral.get(Void.class))
     61           .build();
     62 
     63   /**
     64    * Returns a key that doesn't hold any references to parent classes. This is necessary for
     65    * anonymous keys, so ensure we don't hold a ref to the containing module (or class) forever.
     66    */
     67   public static <T> Key<T> canonicalizeKey(Key<T> key) {
     68     // If we know this isn't a subclass, return as-is.
     69     // Otherwise, recreate the key to avoid the subclass
     70     if (key.getClass() == Key.class) {
     71       return key;
     72     } else if (key.getAnnotation() != null) {
     73       return Key.get(key.getTypeLiteral(), key.getAnnotation());
     74     } else if (key.getAnnotationType() != null) {
     75       return Key.get(key.getTypeLiteral(), key.getAnnotationType());
     76     } else {
     77       return Key.get(key.getTypeLiteral());
     78     }
     79   }
     80 
     81   /**
     82    * Returns an type that's appropriate for use in a key.
     83    *
     84    * <p>If the raw type of {@code typeLiteral} is a {@code javax.inject.Provider}, this returns a
     85    * {@code com.google.inject.Provider} with the same type parameters.
     86    *
     87    * <p>If the type is a primitive, the corresponding wrapper type will be returned.
     88    *
     89    * @throws ConfigurationException if {@code type} contains a type variable
     90    */
     91   public static <T> TypeLiteral<T> canonicalizeForKey(TypeLiteral<T> typeLiteral) {
     92     Type type = typeLiteral.getType();
     93     if (!isFullySpecified(type)) {
     94       Errors errors = new Errors().keyNotFullySpecified(typeLiteral);
     95       throw new ConfigurationException(errors.getMessages());
     96     }
     97 
     98     if (typeLiteral.getRawType() == javax.inject.Provider.class) {
     99       ParameterizedType parameterizedType = (ParameterizedType) type;
    100 
    101       // the following casts are generally unsafe, but com.google.inject.Provider extends
    102       // javax.inject.Provider and is covariant
    103       @SuppressWarnings("unchecked")
    104       TypeLiteral<T> guiceProviderType =
    105           (TypeLiteral<T>)
    106               TypeLiteral.get(Types.providerOf(parameterizedType.getActualTypeArguments()[0]));
    107       return guiceProviderType;
    108     }
    109 
    110     @SuppressWarnings("unchecked")
    111     TypeLiteral<T> wrappedPrimitives = (TypeLiteral<T>) PRIMITIVE_TO_WRAPPER.get(typeLiteral);
    112     if (wrappedPrimitives != null) {
    113       return wrappedPrimitives;
    114     }
    115 
    116     // If we know this isn't a subclass, return as-is.
    117     if (typeLiteral.getClass() == TypeLiteral.class) {
    118       return typeLiteral;
    119     }
    120 
    121     // recreate the TypeLiteral to avoid anonymous TypeLiterals from holding refs to their
    122     // surrounding classes.
    123     @SuppressWarnings("unchecked")
    124     TypeLiteral<T> recreated = (TypeLiteral<T>) TypeLiteral.get(typeLiteral.getType());
    125     return recreated;
    126   }
    127 
    128   /** Returns true if {@code type} is free from type variables. */
    129   private static boolean isFullySpecified(Type type) {
    130     if (type instanceof Class) {
    131       return true;
    132 
    133     } else if (type instanceof CompositeType) {
    134       return ((CompositeType) type).isFullySpecified();
    135 
    136     } else if (type instanceof TypeVariable) {
    137       return false;
    138 
    139     } else {
    140       return ((CompositeType) canonicalize(type)).isFullySpecified();
    141     }
    142   }
    143 
    144   /**
    145    * Returns a type that is functionally equal but not necessarily equal according to {@link
    146    * Object#equals(Object) Object.equals()}. The returned type is {@link Serializable}.
    147    */
    148   public static Type canonicalize(Type type) {
    149     if (type instanceof Class) {
    150       Class<?> c = (Class<?>) type;
    151       return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
    152 
    153     } else if (type instanceof CompositeType) {
    154       return type;
    155 
    156     } else if (type instanceof ParameterizedType) {
    157       ParameterizedType p = (ParameterizedType) type;
    158       return new ParameterizedTypeImpl(
    159           p.getOwnerType(), p.getRawType(), p.getActualTypeArguments());
    160 
    161     } else if (type instanceof GenericArrayType) {
    162       GenericArrayType g = (GenericArrayType) type;
    163       return new GenericArrayTypeImpl(g.getGenericComponentType());
    164 
    165     } else if (type instanceof WildcardType) {
    166       WildcardType w = (WildcardType) type;
    167       return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
    168 
    169     } else {
    170       // type is either serializable as-is or unsupported
    171       return type;
    172     }
    173   }
    174 
    175   public static Class<?> getRawType(Type type) {
    176     if (type instanceof Class<?>) {
    177       // type is a normal class.
    178       return (Class<?>) type;
    179 
    180     } else if (type instanceof ParameterizedType) {
    181       ParameterizedType parameterizedType = (ParameterizedType) type;
    182 
    183       // I'm not exactly sure why getRawType() returns Type instead of Class.
    184       // Neal isn't either but suspects some pathological case related
    185       // to nested classes exists.
    186       Type rawType = parameterizedType.getRawType();
    187       checkArgument(
    188           rawType instanceof Class,
    189           "Expected a Class, but <%s> is of type %s",
    190           type,
    191           type.getClass().getName());
    192       return (Class<?>) rawType;
    193 
    194     } else if (type instanceof GenericArrayType) {
    195       Type componentType = ((GenericArrayType) type).getGenericComponentType();
    196       return Array.newInstance(getRawType(componentType), 0).getClass();
    197 
    198     } else if (type instanceof TypeVariable || type instanceof WildcardType) {
    199       // we could use the variable's bounds, but that'll won't work if there are multiple.
    200       // having a raw type that's more general than necessary is okay
    201       return Object.class;
    202 
    203     } else {
    204       throw new IllegalArgumentException(
    205           "Expected a Class, ParameterizedType, or "
    206               + "GenericArrayType, but <"
    207               + type
    208               + "> is of type "
    209               + type.getClass().getName());
    210     }
    211   }
    212 
    213   /** Returns true if {@code a} and {@code b} are equal. */
    214   public static boolean equals(Type a, Type b) {
    215     if (a == b) {
    216       // also handles (a == null && b == null)
    217       return true;
    218 
    219     } else if (a instanceof Class) {
    220       // Class already specifies equals().
    221       return a.equals(b);
    222 
    223     } else if (a instanceof ParameterizedType) {
    224       if (!(b instanceof ParameterizedType)) {
    225         return false;
    226       }
    227 
    228       // TODO: save a .clone() call
    229       ParameterizedType pa = (ParameterizedType) a;
    230       ParameterizedType pb = (ParameterizedType) b;
    231       return Objects.equal(pa.getOwnerType(), pb.getOwnerType())
    232           && pa.getRawType().equals(pb.getRawType())
    233           && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
    234 
    235     } else if (a instanceof GenericArrayType) {
    236       if (!(b instanceof GenericArrayType)) {
    237         return false;
    238       }
    239 
    240       GenericArrayType ga = (GenericArrayType) a;
    241       GenericArrayType gb = (GenericArrayType) b;
    242       return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
    243 
    244     } else if (a instanceof WildcardType) {
    245       if (!(b instanceof WildcardType)) {
    246         return false;
    247       }
    248 
    249       WildcardType wa = (WildcardType) a;
    250       WildcardType wb = (WildcardType) b;
    251       return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
    252           && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
    253 
    254     } else if (a instanceof TypeVariable) {
    255       if (!(b instanceof TypeVariable)) {
    256         return false;
    257       }
    258       TypeVariable<?> va = (TypeVariable) a;
    259       TypeVariable<?> vb = (TypeVariable) b;
    260       return va.getGenericDeclaration().equals(vb.getGenericDeclaration())
    261           && va.getName().equals(vb.getName());
    262 
    263     } else {
    264       // This isn't a type we support. Could be a generic array type, wildcard type, etc.
    265       return false;
    266     }
    267   }
    268 
    269   private static int hashCodeOrZero(Object o) {
    270     return o != null ? o.hashCode() : 0;
    271   }
    272 
    273   public static String typeToString(Type type) {
    274     return type instanceof Class ? ((Class) type).getName() : type.toString();
    275   }
    276 
    277   /**
    278    * Returns the generic supertype for {@code type}. For example, given a class {@code IntegerSet},
    279    * the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the result when
    280    * the supertype is {@code Collection.class} is {@code Collection<Integer>}.
    281    */
    282   public static Type getGenericSupertype(Type type, Class<?> rawType, Class<?> toResolve) {
    283     if (toResolve == rawType) {
    284       return type;
    285     }
    286 
    287     // we skip searching through interfaces if unknown is an interface
    288     if (toResolve.isInterface()) {
    289       Class[] interfaces = rawType.getInterfaces();
    290       for (int i = 0, length = interfaces.length; i < length; i++) {
    291         if (interfaces[i] == toResolve) {
    292           return rawType.getGenericInterfaces()[i];
    293         } else if (toResolve.isAssignableFrom(interfaces[i])) {
    294           return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
    295         }
    296       }
    297     }
    298 
    299     // check our supertypes
    300     if (!rawType.isInterface()) {
    301       while (rawType != Object.class) {
    302         Class<?> rawSupertype = rawType.getSuperclass();
    303         if (rawSupertype == toResolve) {
    304           return rawType.getGenericSuperclass();
    305         } else if (toResolve.isAssignableFrom(rawSupertype)) {
    306           return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
    307         }
    308         rawType = rawSupertype;
    309       }
    310     }
    311 
    312     // we can't resolve this further
    313     return toResolve;
    314   }
    315 
    316   public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable unknown) {
    317     Class<?> declaredByRaw = declaringClassOf(unknown);
    318 
    319     // we can't reduce this further
    320     if (declaredByRaw == null) {
    321       return unknown;
    322     }
    323 
    324     Type declaredBy = getGenericSupertype(type, rawType, declaredByRaw);
    325     if (declaredBy instanceof ParameterizedType) {
    326       int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
    327       return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
    328     }
    329 
    330     return unknown;
    331   }
    332 
    333   private static int indexOf(Object[] array, Object toFind) {
    334     for (int i = 0; i < array.length; i++) {
    335       if (toFind.equals(array[i])) {
    336         return i;
    337       }
    338     }
    339     throw new NoSuchElementException();
    340   }
    341 
    342   /**
    343    * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
    344    * a class.
    345    */
    346   private static Class<?> declaringClassOf(TypeVariable typeVariable) {
    347     GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
    348     return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
    349   }
    350 
    351   public static class ParameterizedTypeImpl
    352       implements ParameterizedType, Serializable, CompositeType {
    353     private final Type ownerType;
    354     private final Type rawType;
    355     private final Type[] typeArguments;
    356 
    357     public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
    358       // require an owner type if the raw type needs it
    359       ensureOwnerType(ownerType, rawType);
    360 
    361       this.ownerType = ownerType == null ? null : canonicalize(ownerType);
    362       this.rawType = canonicalize(rawType);
    363       this.typeArguments = typeArguments.clone();
    364       for (int t = 0; t < this.typeArguments.length; t++) {
    365         checkNotNull(this.typeArguments[t], "type parameter");
    366         checkNotPrimitive(this.typeArguments[t], "type parameters");
    367         this.typeArguments[t] = canonicalize(this.typeArguments[t]);
    368       }
    369     }
    370 
    371     @Override
    372     public Type[] getActualTypeArguments() {
    373       return typeArguments.clone();
    374     }
    375 
    376     @Override
    377     public Type getRawType() {
    378       return rawType;
    379     }
    380 
    381     @Override
    382     public Type getOwnerType() {
    383       return ownerType;
    384     }
    385 
    386     @Override
    387     public boolean isFullySpecified() {
    388       if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) {
    389         return false;
    390       }
    391 
    392       if (!MoreTypes.isFullySpecified(rawType)) {
    393         return false;
    394       }
    395 
    396       for (Type type : typeArguments) {
    397         if (!MoreTypes.isFullySpecified(type)) {
    398           return false;
    399         }
    400       }
    401 
    402       return true;
    403     }
    404 
    405     @Override
    406     public boolean equals(Object other) {
    407       return other instanceof ParameterizedType
    408           && MoreTypes.equals(this, (ParameterizedType) other);
    409     }
    410 
    411     @Override
    412     public int hashCode() {
    413       return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
    414     }
    415 
    416     @Override
    417     public String toString() {
    418       StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
    419       stringBuilder.append(typeToString(rawType));
    420 
    421       if (typeArguments.length == 0) {
    422         return stringBuilder.toString();
    423       }
    424 
    425       stringBuilder.append("<").append(typeToString(typeArguments[0]));
    426       for (int i = 1; i < typeArguments.length; i++) {
    427         stringBuilder.append(", ").append(typeToString(typeArguments[i]));
    428       }
    429       return stringBuilder.append(">").toString();
    430     }
    431 
    432     private static void ensureOwnerType(Type ownerType, Type rawType) {
    433       if (rawType instanceof Class<?>) {
    434         Class rawTypeAsClass = (Class) rawType;
    435         checkArgument(
    436             ownerType != null || rawTypeAsClass.getEnclosingClass() == null,
    437             "No owner type for enclosed %s",
    438             rawType);
    439         checkArgument(
    440             ownerType == null || rawTypeAsClass.getEnclosingClass() != null,
    441             "Owner type for unenclosed %s",
    442             rawType);
    443       }
    444     }
    445 
    446     private static final long serialVersionUID = 0;
    447   }
    448 
    449   public static class GenericArrayTypeImpl
    450       implements GenericArrayType, Serializable, CompositeType {
    451     private final Type componentType;
    452 
    453     public GenericArrayTypeImpl(Type componentType) {
    454       this.componentType = canonicalize(componentType);
    455     }
    456 
    457     @Override
    458     public Type getGenericComponentType() {
    459       return componentType;
    460     }
    461 
    462     @Override
    463     public boolean isFullySpecified() {
    464       return MoreTypes.isFullySpecified(componentType);
    465     }
    466 
    467     @Override
    468     public boolean equals(Object o) {
    469       return o instanceof GenericArrayType && MoreTypes.equals(this, (GenericArrayType) o);
    470     }
    471 
    472     @Override
    473     public int hashCode() {
    474       return componentType.hashCode();
    475     }
    476 
    477     @Override
    478     public String toString() {
    479       return typeToString(componentType) + "[]";
    480     }
    481 
    482     private static final long serialVersionUID = 0;
    483   }
    484 
    485   /**
    486    * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only
    487    * support what the Java 6 language needs - at most one bound. If a lower bound is set, the upper
    488    * bound must be Object.class.
    489    */
    490   public static class WildcardTypeImpl implements WildcardType, Serializable, CompositeType {
    491     private final Type upperBound;
    492     private final Type lowerBound;
    493 
    494     public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
    495       checkArgument(lowerBounds.length <= 1, "Must have at most one lower bound.");
    496       checkArgument(upperBounds.length == 1, "Must have exactly one upper bound.");
    497 
    498       if (lowerBounds.length == 1) {
    499         checkNotNull(lowerBounds[0], "lowerBound");
    500         checkNotPrimitive(lowerBounds[0], "wildcard bounds");
    501         checkArgument(upperBounds[0] == Object.class, "bounded both ways");
    502         this.lowerBound = canonicalize(lowerBounds[0]);
    503         this.upperBound = Object.class;
    504 
    505       } else {
    506         checkNotNull(upperBounds[0], "upperBound");
    507         checkNotPrimitive(upperBounds[0], "wildcard bounds");
    508         this.lowerBound = null;
    509         this.upperBound = canonicalize(upperBounds[0]);
    510       }
    511     }
    512 
    513     @Override
    514     public Type[] getUpperBounds() {
    515       return new Type[] {upperBound};
    516     }
    517 
    518     @Override
    519     public Type[] getLowerBounds() {
    520       return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY;
    521     }
    522 
    523     @Override
    524     public boolean isFullySpecified() {
    525       return MoreTypes.isFullySpecified(upperBound)
    526           && (lowerBound == null || MoreTypes.isFullySpecified(lowerBound));
    527     }
    528 
    529     @Override
    530     public boolean equals(Object other) {
    531       return other instanceof WildcardType && MoreTypes.equals(this, (WildcardType) other);
    532     }
    533 
    534     @Override
    535     public int hashCode() {
    536       // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
    537       return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
    538     }
    539 
    540     @Override
    541     public String toString() {
    542       if (lowerBound != null) {
    543         return "? super " + typeToString(lowerBound);
    544       } else if (upperBound == Object.class) {
    545         return "?";
    546       } else {
    547         return "? extends " + typeToString(upperBound);
    548       }
    549     }
    550 
    551     private static final long serialVersionUID = 0;
    552   }
    553 
    554   private static void checkNotPrimitive(Type type, String use) {
    555     checkArgument(
    556         !(type instanceof Class<?>) || !((Class) type).isPrimitive(),
    557         "Primitive types are not allowed in %s: %s",
    558         use,
    559         type);
    560   }
    561 
    562   /** A type formed from other types, such as arrays, parameterized types or wildcard types */
    563   private interface CompositeType {
    564     /** Returns true if there are no type variables in this type. */
    565     boolean isFullySpecified();
    566   }
    567 }
    568