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