2 * Copyright (C) 2008 Google Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.google
.gson
.internal
;
19 import static com
.google
.gson
.internal
.$Gson$Preconditions
.checkArgument
;
20 import static com
.google
.gson
.internal
.$Gson$Preconditions
.checkNotNull
;
22 import java
.io
.Serializable
;
23 import java
.lang
.reflect
.Array
;
24 import java
.lang
.reflect
.GenericArrayType
;
25 import java
.lang
.reflect
.GenericDeclaration
;
26 import java
.lang
.reflect
.ParameterizedType
;
27 import java
.lang
.reflect
.Type
;
28 import java
.lang
.reflect
.TypeVariable
;
29 import java
.lang
.reflect
.WildcardType
;
30 import java
.util
.Arrays
;
31 import java
.util
.Collection
;
33 import java
.util
.NoSuchElementException
;
34 import java
.util
.Properties
;
37 * Static methods for working with types.
40 * @author Jesse Wilson
42 public final class $Gson$Types
{
43 static final Type
[] EMPTY_TYPE_ARRAY
= new Type
[] {};
45 private $Gson$
Types() {}
48 * Returns a new parameterized type, applying {@code typeArguments} to
49 * {@code rawType} and enclosed by {@code ownerType}.
51 * @return a {@link java.io.Serializable serializable} parameterized type.
53 public static ParameterizedType
newParameterizedTypeWithOwner(
54 Type ownerType
, Type rawType
, Type
... typeArguments
) {
55 return new ParameterizedTypeImpl(ownerType
, rawType
, typeArguments
);
59 * Returns an array type whose elements are all instances of
60 * {@code componentType}.
62 * @return a {@link java.io.Serializable serializable} generic array type.
64 public static GenericArrayType
arrayOf(Type componentType
) {
65 return new GenericArrayTypeImpl(componentType
);
69 * Returns a type that represents an unknown type that extends {@code bound}.
70 * For example, if {@code bound} is {@code CharSequence.class}, this returns
71 * {@code ? extends CharSequence}. If {@code bound} is {@code Object.class},
72 * this returns {@code ?}, which is shorthand for {@code ? extends Object}.
74 public static WildcardType
subtypeOf(Type bound
) {
75 return new WildcardTypeImpl(new Type
[] { bound
}, EMPTY_TYPE_ARRAY
);
79 * Returns a type that represents an unknown supertype of {@code bound}. For
80 * example, if {@code bound} is {@code String.class}, this returns {@code ?
83 public static WildcardType
supertypeOf(Type bound
) {
84 return new WildcardTypeImpl(new Type
[] { Object
.class }, new Type
[] { bound
});
88 * Returns a type that is functionally equal but not necessarily equal
89 * according to {@link Object#equals(Object) Object.equals()}. The returned
90 * type is {@link java.io.Serializable}.
92 public static Type
canonicalize(Type type
) {
93 if (type
instanceof Class
) {
94 Class
<?
> c
= (Class
<?
>) type
;
95 return c
.isArray() ?
new GenericArrayTypeImpl(canonicalize(c
.getComponentType())) : c
;
97 } else if (type
instanceof ParameterizedType
) {
98 ParameterizedType p
= (ParameterizedType
) type
;
99 return new ParameterizedTypeImpl(p
.getOwnerType(),
100 p
.getRawType(), p
.getActualTypeArguments());
102 } else if (type
instanceof GenericArrayType
) {
103 GenericArrayType g
= (GenericArrayType
) type
;
104 return new GenericArrayTypeImpl(g
.getGenericComponentType());
106 } else if (type
instanceof WildcardType
) {
107 WildcardType w
= (WildcardType
) type
;
108 return new WildcardTypeImpl(w
.getUpperBounds(), w
.getLowerBounds());
111 // type is either serializable as-is or unsupported
116 public static Class
<?
> getRawType(Type type
) {
117 if (type
instanceof Class
<?
>) {
118 // type is a normal class.
119 return (Class
<?
>) type
;
121 } else if (type
instanceof ParameterizedType
) {
122 ParameterizedType parameterizedType
= (ParameterizedType
) type
;
124 // I'm not exactly sure why getRawType() returns Type instead of Class.
125 // Neal isn't either but suspects some pathological case related
126 // to nested classes exists.
127 Type rawType
= parameterizedType
.getRawType();
128 checkArgument(rawType
instanceof Class
);
129 return (Class
<?
>) rawType
;
131 } else if (type
instanceof GenericArrayType
) {
132 Type componentType
= ((GenericArrayType
)type
).getGenericComponentType();
133 return Array
.newInstance(getRawType(componentType
), 0).getClass();
135 } else if (type
instanceof TypeVariable
) {
136 // we could use the variable's bounds, but that won't work if there are multiple.
137 // having a raw type that's more general than necessary is okay
140 } else if (type
instanceof WildcardType
) {
141 return getRawType(((WildcardType
) type
).getUpperBounds()[0]);
144 String className
= type
== null ?
"null" : type
.getClass().getName();
145 throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
146 + "GenericArrayType, but <" + type
+ "> is of type " + className
);
150 static boolean equal(Object a
, Object b
) {
151 return a
== b
|| (a
!= null && a
.equals(b
));
155 * Returns true if {@code a} and {@code b} are equal.
157 public static boolean equals(Type a
, Type b
) {
159 // also handles (a == null && b == null)
162 } else if (a
instanceof Class
) {
163 // Class already specifies equals().
166 } else if (a
instanceof ParameterizedType
) {
167 if (!(b
instanceof ParameterizedType
)) {
171 // TODO: save a .clone() call
172 ParameterizedType pa
= (ParameterizedType
) a
;
173 ParameterizedType pb
= (ParameterizedType
) b
;
174 return equal(pa
.getOwnerType(), pb
.getOwnerType())
175 && pa
.getRawType().equals(pb
.getRawType())
176 && Arrays
.equals(pa
.getActualTypeArguments(), pb
.getActualTypeArguments());
178 } else if (a
instanceof GenericArrayType
) {
179 if (!(b
instanceof GenericArrayType
)) {
183 GenericArrayType ga
= (GenericArrayType
) a
;
184 GenericArrayType gb
= (GenericArrayType
) b
;
185 return equals(ga
.getGenericComponentType(), gb
.getGenericComponentType());
187 } else if (a
instanceof WildcardType
) {
188 if (!(b
instanceof WildcardType
)) {
192 WildcardType wa
= (WildcardType
) a
;
193 WildcardType wb
= (WildcardType
) b
;
194 return Arrays
.equals(wa
.getUpperBounds(), wb
.getUpperBounds())
195 && Arrays
.equals(wa
.getLowerBounds(), wb
.getLowerBounds());
197 } else if (a
instanceof TypeVariable
) {
198 if (!(b
instanceof TypeVariable
)) {
201 TypeVariable
<?
> va
= (TypeVariable
<?
>) a
;
202 TypeVariable
<?
> vb
= (TypeVariable
<?
>) b
;
203 return va
.getGenericDeclaration() == vb
.getGenericDeclaration()
204 && va
.getName().equals(vb
.getName());
207 // This isn't a type we support. Could be a generic array type, wildcard type, etc.
212 private static int hashCodeOrZero(Object o
) {
213 return o
!= null ? o
.hashCode() : 0;
216 public static String
typeToString(Type type
) {
217 return type
instanceof Class ?
((Class
<?
>) type
).getName() : type
.toString();
221 * Returns the generic supertype for {@code supertype}. For example, given a class {@code
222 * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the
223 * result when the supertype is {@code Collection.class} is {@code Collection<Integer>}.
225 static Type
getGenericSupertype(Type context
, Class
<?
> rawType
, Class
<?
> toResolve
) {
226 if (toResolve
== rawType
) {
230 // we skip searching through interfaces if unknown is an interface
231 if (toResolve
.isInterface()) {
232 Class
<?
>[] interfaces
= rawType
.getInterfaces();
233 for (int i
= 0, length
= interfaces
.length
; i
< length
; i
++) {
234 if (interfaces
[i
] == toResolve
) {
235 return rawType
.getGenericInterfaces()[i
];
236 } else if (toResolve
.isAssignableFrom(interfaces
[i
])) {
237 return getGenericSupertype(rawType
.getGenericInterfaces()[i
], interfaces
[i
], toResolve
);
242 // check our supertypes
243 if (!rawType
.isInterface()) {
244 while (rawType
!= Object
.class) {
245 Class
<?
> rawSupertype
= rawType
.getSuperclass();
246 if (rawSupertype
== toResolve
) {
247 return rawType
.getGenericSuperclass();
248 } else if (toResolve
.isAssignableFrom(rawSupertype
)) {
249 return getGenericSupertype(rawType
.getGenericSuperclass(), rawSupertype
, toResolve
);
251 rawType
= rawSupertype
;
255 // we can't resolve this further
260 * Returns the generic form of {@code supertype}. For example, if this is {@code
261 * ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
264 * @param supertype a superclass of, or interface implemented by, this.
266 static Type
getSupertype(Type context
, Class
<?
> contextRawType
, Class
<?
> supertype
) {
267 checkArgument(supertype
.isAssignableFrom(contextRawType
));
268 return resolve(context
, contextRawType
,
269 $Gson$Types
.getGenericSupertype(context
, contextRawType
, supertype
));
273 * Returns the component type of this array type.
274 * @throws ClassCastException if this type is not an array.
276 public static Type
getArrayComponentType(Type array
) {
277 return array
instanceof GenericArrayType
278 ?
((GenericArrayType
) array
).getGenericComponentType()
279 : ((Class
<?
>) array
).getComponentType();
283 * Returns the element type of this collection type.
284 * @throws IllegalArgumentException if this type is not a collection.
286 public static Type
getCollectionElementType(Type context
, Class
<?
> contextRawType
) {
287 Type collectionType
= getSupertype(context
, contextRawType
, Collection
.class);
289 if (collectionType
instanceof WildcardType
) {
290 collectionType
= ((WildcardType
)collectionType
).getUpperBounds()[0];
292 if (collectionType
instanceof ParameterizedType
) {
293 return ((ParameterizedType
) collectionType
).getActualTypeArguments()[0];
299 * Returns a two element array containing this map's key and value types in
300 * positions 0 and 1 respectively.
302 public static Type
[] getMapKeyAndValueTypes(Type context
, Class
<?
> contextRawType
) {
304 * Work around a problem with the declaration of java.util.Properties. That
305 * class should extend Hashtable<String, String>, but it's declared to
306 * extend Hashtable<Object, Object>.
308 if (context
== Properties
.class) {
309 return new Type
[] { String
.class, String
.class }; // TODO: test subclasses of Properties!
312 Type mapType
= getSupertype(context
, contextRawType
, Map
.class);
313 // TODO: strip wildcards?
314 if (mapType
instanceof ParameterizedType
) {
315 ParameterizedType mapParameterizedType
= (ParameterizedType
) mapType
;
316 return mapParameterizedType
.getActualTypeArguments();
318 return new Type
[] { Object
.class, Object
.class };
321 public static Type
resolve(Type context
, Class
<?
> contextRawType
, Type toResolve
) {
322 // this implementation is made a little more complicated in an attempt to avoid object-creation
324 if (toResolve
instanceof TypeVariable
) {
325 TypeVariable
<?
> typeVariable
= (TypeVariable
<?
>) toResolve
;
326 toResolve
= resolveTypeVariable(context
, contextRawType
, typeVariable
);
327 if (toResolve
== typeVariable
) {
331 } else if (toResolve
instanceof Class
&& ((Class
<?
>) toResolve
).isArray()) {
332 Class
<?
> original
= (Class
<?
>) toResolve
;
333 Type componentType
= original
.getComponentType();
334 Type newComponentType
= resolve(context
, contextRawType
, componentType
);
335 return componentType
== newComponentType
337 : arrayOf(newComponentType
);
339 } else if (toResolve
instanceof GenericArrayType
) {
340 GenericArrayType original
= (GenericArrayType
) toResolve
;
341 Type componentType
= original
.getGenericComponentType();
342 Type newComponentType
= resolve(context
, contextRawType
, componentType
);
343 return componentType
== newComponentType
345 : arrayOf(newComponentType
);
347 } else if (toResolve
instanceof ParameterizedType
) {
348 ParameterizedType original
= (ParameterizedType
) toResolve
;
349 Type ownerType
= original
.getOwnerType();
350 Type newOwnerType
= resolve(context
, contextRawType
, ownerType
);
351 boolean changed
= newOwnerType
!= ownerType
;
353 Type
[] args
= original
.getActualTypeArguments();
354 for (int t
= 0, length
= args
.length
; t
< length
; t
++) {
355 Type resolvedTypeArgument
= resolve(context
, contextRawType
, args
[t
]);
356 if (resolvedTypeArgument
!= args
[t
]) {
361 args
[t
] = resolvedTypeArgument
;
366 ?
newParameterizedTypeWithOwner(newOwnerType
, original
.getRawType(), args
)
369 } else if (toResolve
instanceof WildcardType
) {
370 WildcardType original
= (WildcardType
) toResolve
;
371 Type
[] originalLowerBound
= original
.getLowerBounds();
372 Type
[] originalUpperBound
= original
.getUpperBounds();
374 if (originalLowerBound
.length
== 1) {
375 Type lowerBound
= resolve(context
, contextRawType
, originalLowerBound
[0]);
376 if (lowerBound
!= originalLowerBound
[0]) {
377 return supertypeOf(lowerBound
);
379 } else if (originalUpperBound
.length
== 1) {
380 Type upperBound
= resolve(context
, contextRawType
, originalUpperBound
[0]);
381 if (upperBound
!= originalUpperBound
[0]) {
382 return subtypeOf(upperBound
);
393 static Type
resolveTypeVariable(Type context
, Class
<?
> contextRawType
, TypeVariable
<?
> unknown
) {
394 Class
<?
> declaredByRaw
= declaringClassOf(unknown
);
396 // we can't reduce this further
397 if (declaredByRaw
== null) {
401 Type declaredBy
= getGenericSupertype(context
, contextRawType
, declaredByRaw
);
402 if (declaredBy
instanceof ParameterizedType
) {
403 int index
= indexOf(declaredByRaw
.getTypeParameters(), unknown
);
404 return ((ParameterizedType
) declaredBy
).getActualTypeArguments()[index
];
410 private static int indexOf(Object
[] array
, Object toFind
) {
411 for (int i
= 0; i
< array
.length
; i
++) {
412 if (toFind
.equals(array
[i
])) {
416 throw new NoSuchElementException();
420 * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
423 private static Class
<?
> declaringClassOf(TypeVariable
<?
> typeVariable
) {
424 GenericDeclaration genericDeclaration
= typeVariable
.getGenericDeclaration();
425 return genericDeclaration
instanceof Class
426 ?
(Class
<?
>) genericDeclaration
430 private static void checkNotPrimitive(Type type
) {
431 checkArgument(!(type
instanceof Class
<?
>) || !((Class
<?
>) type
).isPrimitive());
434 private static final class ParameterizedTypeImpl
implements ParameterizedType
, Serializable
{
435 private final Type ownerType
;
436 private final Type rawType
;
437 private final Type
[] typeArguments
;
439 public ParameterizedTypeImpl(Type ownerType
, Type rawType
, Type
... typeArguments
) {
440 // require an owner type if the raw type needs it
441 if (rawType
instanceof Class
<?
>) {
442 Class
<?
> rawTypeAsClass
= (Class
<?
>) rawType
;
443 checkArgument(ownerType
!= null || rawTypeAsClass
.getEnclosingClass() == null);
444 checkArgument(ownerType
== null || rawTypeAsClass
.getEnclosingClass() != null);
447 this.ownerType
= ownerType
== null ?
null : canonicalize(ownerType
);
448 this.rawType
= canonicalize(rawType
);
449 this.typeArguments
= typeArguments
.clone();
450 for (int t
= 0; t
< this.typeArguments
.length
; t
++) {
451 checkNotNull(this.typeArguments
[t
]);
452 checkNotPrimitive(this.typeArguments
[t
]);
453 this.typeArguments
[t
] = canonicalize(this.typeArguments
[t
]);
457 public Type
[] getActualTypeArguments() {
458 return typeArguments
.clone();
461 public Type
getRawType() {
465 public Type
getOwnerType() {
469 @Override public boolean equals(Object other
) {
470 return other
instanceof ParameterizedType
471 && $Gson$Types
.equals(this, (ParameterizedType
) other
);
474 @Override public int hashCode() {
475 return Arrays
.hashCode(typeArguments
)
477 ^
hashCodeOrZero(ownerType
);
480 @Override public String
toString() {
481 StringBuilder stringBuilder
= new StringBuilder(30 * (typeArguments
.length
+ 1));
482 stringBuilder
.append(typeToString(rawType
));
484 if (typeArguments
.length
== 0) {
485 return stringBuilder
.toString();
488 stringBuilder
.append("<").append(typeToString(typeArguments
[0]));
489 for (int i
= 1; i
< typeArguments
.length
; i
++) {
490 stringBuilder
.append(", ").append(typeToString(typeArguments
[i
]));
492 return stringBuilder
.append(">").toString();
495 private static final long serialVersionUID
= 0;
498 private static final class GenericArrayTypeImpl
implements GenericArrayType
, Serializable
{
499 private final Type componentType
;
501 public GenericArrayTypeImpl(Type componentType
) {
502 this.componentType
= canonicalize(componentType
);
505 public Type
getGenericComponentType() {
506 return componentType
;
509 @Override public boolean equals(Object o
) {
510 return o
instanceof GenericArrayType
511 && $Gson$Types
.equals(this, (GenericArrayType
) o
);
514 @Override public int hashCode() {
515 return componentType
.hashCode();
518 @Override public String
toString() {
519 return typeToString(componentType
) + "[]";
522 private static final long serialVersionUID
= 0;
526 * The WildcardType interface supports multiple upper bounds and multiple
527 * lower bounds. We only support what the Java 6 language needs - at most one
528 * bound. If a lower bound is set, the upper bound must be Object.class.
530 private static final class WildcardTypeImpl
implements WildcardType
, Serializable
{
531 private final Type upperBound
;
532 private final Type lowerBound
;
534 public WildcardTypeImpl(Type
[] upperBounds
, Type
[] lowerBounds
) {
535 checkArgument(lowerBounds
.length
<= 1);
536 checkArgument(upperBounds
.length
== 1);
538 if (lowerBounds
.length
== 1) {
539 checkNotNull(lowerBounds
[0]);
540 checkNotPrimitive(lowerBounds
[0]);
541 checkArgument(upperBounds
[0] == Object
.class);
542 this.lowerBound
= canonicalize(lowerBounds
[0]);
543 this.upperBound
= Object
.class;
546 checkNotNull(upperBounds
[0]);
547 checkNotPrimitive(upperBounds
[0]);
548 this.lowerBound
= null;
549 this.upperBound
= canonicalize(upperBounds
[0]);
553 public Type
[] getUpperBounds() {
554 return new Type
[] { upperBound
};
557 public Type
[] getLowerBounds() {
558 return lowerBound
!= null ?
new Type
[] { lowerBound
} : EMPTY_TYPE_ARRAY
;
561 @Override public boolean equals(Object other
) {
562 return other
instanceof WildcardType
563 && $Gson$Types
.equals(this, (WildcardType
) other
);
566 @Override public int hashCode() {
567 // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
568 return (lowerBound
!= null ?
31 + lowerBound
.hashCode() : 1)
569 ^
(31 + upperBound
.hashCode());
572 @Override public String
toString() {
573 if (lowerBound
!= null) {
574 return "? super " + typeToString(lowerBound
);
575 } else if (upperBound
== Object
.class) {
578 return "? extends " + typeToString(upperBound
);
582 private static final long serialVersionUID
= 0;