001package com.google.inject.internal;
002
003import com.google.common.base.Objects;
004import static com.google.common.base.Preconditions.checkArgument;
005import static com.google.common.base.Preconditions.checkNotNull;
006import java.io.Serializable;
007import java.lang.reflect.Array;
008import java.lang.reflect.GenericArrayType;
009import java.lang.reflect.ParameterizedType;
010import java.lang.reflect.Type;
011import java.lang.reflect.TypeVariable;
012import java.lang.reflect.WildcardType;
013import java.util.Arrays;
014
015/**
016 * Static methods for working with types that we aren't publishing in the
017 * public {@code Types} API.
018 *
019 * @author jessewilson@google.com (Jesse Wilson)
020 */
021public class MoreTypes {
022
023  public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
024
025  private MoreTypes() {}
026
027  /**
028   * Returns true if {@code type} is free from type variables.
029   */
030  private static boolean isFullySpecified(Type type) {
031    if (type instanceof Class) {
032      return true;
033
034    } else if (type instanceof CompositeType) {
035      return ((CompositeType) type).isFullySpecified();
036
037    } else if (type instanceof TypeVariable){
038      return false;
039
040    } else {
041      return ((CompositeType) canonicalize(type)).isFullySpecified();
042    }
043  }
044
045  /**
046   * Returns a type that is functionally equal but not necessarily equal
047   * according to {@link Object#equals(Object) Object.equals()}. The returned
048   * type is {@link Serializable}.
049   */
050  public static Type canonicalize(Type type) {
051    if (type instanceof Class) {
052      Class<?> c = (Class<?>) type;
053      return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
054
055    } else if (type instanceof CompositeType) {
056      return type;
057
058    } else if (type instanceof ParameterizedType) {
059      ParameterizedType p = (ParameterizedType) type;
060      return new ParameterizedTypeImpl(p.getOwnerType(),
061          p.getRawType(), p.getActualTypeArguments());
062
063    } else if (type instanceof GenericArrayType) {
064      GenericArrayType g = (GenericArrayType) type;
065      return new GenericArrayTypeImpl(g.getGenericComponentType());
066
067    } else if (type instanceof WildcardType) {
068      WildcardType w = (WildcardType) type;
069      return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
070
071    } else {
072      // type is either serializable as-is or unsupported
073      return type;
074    }
075  }
076
077  public static Class<?> getRawType(Type type) {
078    if (type instanceof Class<?>) {
079      // type is a normal class.
080      return (Class<?>) type;
081
082    } else if (type instanceof ParameterizedType) {
083      ParameterizedType parameterizedType = (ParameterizedType) type;
084
085      // I'm not exactly sure why getRawType() returns Type instead of Class.
086      // Neal isn't either but suspects some pathological case related
087      // to nested classes exists.
088      Type rawType = parameterizedType.getRawType();
089      checkArgument(rawType instanceof Class,
090          "Expected a Class, but <%s> is of type %s", type, type.getClass().getName());
091      return (Class<?>) rawType;
092
093    } else if (type instanceof GenericArrayType) {
094      Type componentType = ((GenericArrayType)type).getGenericComponentType();
095      return Array.newInstance(getRawType(componentType), 0).getClass();
096
097    } else if (type instanceof TypeVariable) {
098      // we could use the variable's bounds, but that'll won't work if there are multiple.
099      // having a raw type that's more general than necessary is okay  
100      return Object.class;
101
102    } else {
103      throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
104          + "GenericArrayType, but <" + type + "> is of type " + type.getClass().getName());
105    }
106  }
107
108  /**
109   * Returns true if {@code a} and {@code b} are equal.
110   */
111  public static boolean equals(Type a, Type b) {
112    if (a == b) {
113      // also handles (a == null && b == null)
114      return true;
115
116    } else if (a instanceof Class) {
117      // Class already specifies equals().
118      return a.equals(b);
119
120    } else if (a instanceof ParameterizedType) {
121      if (!(b instanceof ParameterizedType)) {
122        return false;
123      }
124
125      // TODO: save a .clone() call
126      ParameterizedType pa = (ParameterizedType) a;
127      ParameterizedType pb = (ParameterizedType) b;
128      return Objects.equal(pa.getOwnerType(), pb.getOwnerType())
129          && pa.getRawType().equals(pb.getRawType())
130          && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
131
132    } else if (a instanceof GenericArrayType) {
133      if (!(b instanceof GenericArrayType)) {
134        return false;
135      }
136
137      GenericArrayType ga = (GenericArrayType) a;
138      GenericArrayType gb = (GenericArrayType) b;
139      return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
140
141    } else if (a instanceof WildcardType) {
142      if (!(b instanceof WildcardType)) {
143        return false;
144      }
145
146      WildcardType wa = (WildcardType) a;
147      WildcardType wb = (WildcardType) b;
148      return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
149          && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
150
151    } else if (a instanceof TypeVariable) {
152      if (!(b instanceof TypeVariable)) {
153        return false;
154      }
155      TypeVariable<?> va = (TypeVariable) a;
156      TypeVariable<?> vb = (TypeVariable) b;
157      return va.getGenericDeclaration() == vb.getGenericDeclaration()
158          && va.getName().equals(vb.getName());
159
160    } else {
161      // This isn't a type we support. Could be a generic array type, wildcard type, etc.
162      return false;
163    }
164  }
165
166  private static int hashCodeOrZero(Object o) {
167    return o != null ? o.hashCode() : 0;
168  }
169
170  public static String typeToString(Type type) {
171    return type instanceof Class ? ((Class) type).getName() : type.toString();
172  }
173
174  public static class ParameterizedTypeImpl
175      implements ParameterizedType, Serializable, CompositeType {
176    private final Type ownerType;
177    private final Type rawType;
178    private final Type[] typeArguments;
179
180    public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
181      // require an owner type if the raw type needs it
182      if (rawType instanceof Class<?>) {
183        Class rawTypeAsClass = (Class) rawType;
184        checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null,
185            "No owner type for enclosed %s", rawType);
186        checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null,
187            "Owner type for unenclosed %s", rawType);
188      }
189
190      this.ownerType = ownerType == null ? null : canonicalize(ownerType);
191      this.rawType = canonicalize(rawType);
192      this.typeArguments = typeArguments.clone();
193      for (int t = 0; t < this.typeArguments.length; t++) {
194        checkNotNull(this.typeArguments[t], "type parameter");
195        checkNotPrimitive(this.typeArguments[t], "type parameters");
196        this.typeArguments[t] = canonicalize(this.typeArguments[t]);
197      }
198    }
199
200    public Type[] getActualTypeArguments() {
201      return typeArguments.clone();
202    }
203
204    public Type getRawType() {
205      return rawType;
206    }
207
208    public Type getOwnerType() {
209      return ownerType;
210    }
211
212    public boolean isFullySpecified() {
213      if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) {
214        return false;
215      }
216
217      if (!MoreTypes.isFullySpecified(rawType)) {
218        return false;
219      }
220
221      for (Type type : typeArguments) {
222        if (!MoreTypes.isFullySpecified(type)) {
223          return false;
224        }
225      }
226
227      return true;
228    }
229
230    @Override public boolean equals(Object other) {
231      return other instanceof ParameterizedType
232          && MoreTypes.equals(this, (ParameterizedType) other);
233    }
234
235    @Override public int hashCode() {
236      return Arrays.hashCode(typeArguments)
237          ^ rawType.hashCode()
238          ^ hashCodeOrZero(ownerType);
239    }
240
241    @Override public String toString() {
242      StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
243      stringBuilder.append(typeToString(rawType));
244
245      if (typeArguments.length == 0) {
246        return stringBuilder.toString();
247      }
248
249      stringBuilder.append("<").append(typeToString(typeArguments[0]));
250      for (int i = 1; i < typeArguments.length; i++) {
251        stringBuilder.append(", ").append(typeToString(typeArguments[i]));
252      }
253      return stringBuilder.append(">").toString();
254    }
255
256    private static final long serialVersionUID = 0;
257  }
258
259  public static class GenericArrayTypeImpl
260      implements GenericArrayType, Serializable, CompositeType {
261    private final Type componentType;
262
263    public GenericArrayTypeImpl(Type componentType) {
264      this.componentType = canonicalize(componentType);
265    }
266
267    public Type getGenericComponentType() {
268      return componentType;
269    }
270
271    public boolean isFullySpecified() {
272      return MoreTypes.isFullySpecified(componentType);
273    }
274
275    @Override public boolean equals(Object o) {
276      return o instanceof GenericArrayType
277          && MoreTypes.equals(this, (GenericArrayType) o);
278    }
279
280    @Override public int hashCode() {
281      return componentType.hashCode();
282    }
283
284    @Override public String toString() {
285      return typeToString(componentType) + "[]";
286    }
287
288    private static final long serialVersionUID = 0;
289  }
290
291  /**
292   * The WildcardType interface supports multiple upper bounds and multiple
293   * lower bounds. We only support what the Java 6 language needs - at most one
294   * bound. If a lower bound is set, the upper bound must be Object.class.
295   */
296  public static class WildcardTypeImpl implements WildcardType, Serializable, CompositeType {
297    private final Type upperBound;
298    private final Type lowerBound;
299
300    public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
301      checkArgument(lowerBounds.length <= 1, "Must have at most one lower bound.");
302      checkArgument(upperBounds.length == 1, "Must have exactly one upper bound.");
303
304      if (lowerBounds.length == 1) {
305        checkNotNull(lowerBounds[0], "lowerBound");
306        checkNotPrimitive(lowerBounds[0], "wildcard bounds");
307        checkArgument(upperBounds[0] == Object.class, "bounded both ways");
308        this.lowerBound = canonicalize(lowerBounds[0]);
309        this.upperBound = Object.class;
310
311      } else {
312        checkNotNull(upperBounds[0], "upperBound");
313        checkNotPrimitive(upperBounds[0], "wildcard bounds");
314        this.lowerBound = null;
315        this.upperBound = canonicalize(upperBounds[0]);
316      }
317    }
318
319    public Type[] getUpperBounds() {
320      return new Type[] { upperBound };
321    }
322
323    public Type[] getLowerBounds() {
324      return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
325    }
326
327    public boolean isFullySpecified() {
328      return MoreTypes.isFullySpecified(upperBound)
329          && (lowerBound == null || MoreTypes.isFullySpecified(lowerBound));
330    }
331
332    @Override public boolean equals(Object other) {
333      return other instanceof WildcardType
334          && MoreTypes.equals(this, (WildcardType) other);
335    }
336
337    @Override public int hashCode() {
338      // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());  
339      return (lowerBound != null ? 31 + lowerBound.hashCode() : 1)
340          ^ (31 + upperBound.hashCode());
341    }
342
343    @Override public String toString() {
344      if (lowerBound != null) {
345        return "? super " + typeToString(lowerBound);
346      } else if (upperBound == Object.class) {
347        return "?";
348      } else {
349        return "? extends " + typeToString(upperBound);
350      }
351    }
352
353    private static final long serialVersionUID = 0;
354  }
355
356  private static void checkNotPrimitive(Type type, String use) {
357    checkArgument(!(type instanceof Class<?>) || !((Class) type).isPrimitive(),
358        "Primitive types are not allowed in %s: %s", use, type);
359  }
360
361  /** A type formed from other types, such as arrays, parameterized types or wildcard types */
362  private interface CompositeType {
363    /** Returns true if there are no type variables in this type. */
364    boolean isFullySpecified();
365  }
366}