Finish second-to-last commit
[unical.git] / gson / com / google / gson / internal / $Gson$Types.java
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.gson.internal;
18
19 import static com.google.gson.internal.$Gson$Preconditions.checkArgument;
20 import static com.google.gson.internal.$Gson$Preconditions.checkNotNull;
21
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;
32 import java.util.Map;
33 import java.util.NoSuchElementException;
34 import java.util.Properties;
35
36 /**
37 * Static methods for working with types.
38 *
39 * @author Bob Lee
40 * @author Jesse Wilson
41 */
42 public final class $Gson$Types {
43 static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
44
45 private $Gson$Types() {}
46
47 /**
48 * Returns a new parameterized type, applying {@code typeArguments} to
49 * {@code rawType} and enclosed by {@code ownerType}.
50 *
51 * @return a {@link java.io.Serializable serializable} parameterized type.
52 */
53 public static ParameterizedType newParameterizedTypeWithOwner(
54 Type ownerType, Type rawType, Type... typeArguments) {
55 return new ParameterizedTypeImpl(ownerType, rawType, typeArguments);
56 }
57
58 /**
59 * Returns an array type whose elements are all instances of
60 * {@code componentType}.
61 *
62 * @return a {@link java.io.Serializable serializable} generic array type.
63 */
64 public static GenericArrayType arrayOf(Type componentType) {
65 return new GenericArrayTypeImpl(componentType);
66 }
67
68 /**
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}.
73 */
74 public static WildcardType subtypeOf(Type bound) {
75 return new WildcardTypeImpl(new Type[] { bound }, EMPTY_TYPE_ARRAY);
76 }
77
78 /**
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 ?
81 * super String}.
82 */
83 public static WildcardType supertypeOf(Type bound) {
84 return new WildcardTypeImpl(new Type[] { Object.class }, new Type[] { bound });
85 }
86
87 /**
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}.
91 */
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;
96
97 } else if (type instanceof ParameterizedType) {
98 ParameterizedType p = (ParameterizedType) type;
99 return new ParameterizedTypeImpl(p.getOwnerType(),
100 p.getRawType(), p.getActualTypeArguments());
101
102 } else if (type instanceof GenericArrayType) {
103 GenericArrayType g = (GenericArrayType) type;
104 return new GenericArrayTypeImpl(g.getGenericComponentType());
105
106 } else if (type instanceof WildcardType) {
107 WildcardType w = (WildcardType) type;
108 return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
109
110 } else {
111 // type is either serializable as-is or unsupported
112 return type;
113 }
114 }
115
116 public static Class<?> getRawType(Type type) {
117 if (type instanceof Class<?>) {
118 // type is a normal class.
119 return (Class<?>) type;
120
121 } else if (type instanceof ParameterizedType) {
122 ParameterizedType parameterizedType = (ParameterizedType) type;
123
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;
130
131 } else if (type instanceof GenericArrayType) {
132 Type componentType = ((GenericArrayType)type).getGenericComponentType();
133 return Array.newInstance(getRawType(componentType), 0).getClass();
134
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
138 return Object.class;
139
140 } else if (type instanceof WildcardType) {
141 return getRawType(((WildcardType) type).getUpperBounds()[0]);
142
143 } else {
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);
147 }
148 }
149
150 static boolean equal(Object a, Object b) {
151 return a == b || (a != null && a.equals(b));
152 }
153
154 /**
155 * Returns true if {@code a} and {@code b} are equal.
156 */
157 public static boolean equals(Type a, Type b) {
158 if (a == b) {
159 // also handles (a == null && b == null)
160 return true;
161
162 } else if (a instanceof Class) {
163 // Class already specifies equals().
164 return a.equals(b);
165
166 } else if (a instanceof ParameterizedType) {
167 if (!(b instanceof ParameterizedType)) {
168 return false;
169 }
170
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());
177
178 } else if (a instanceof GenericArrayType) {
179 if (!(b instanceof GenericArrayType)) {
180 return false;
181 }
182
183 GenericArrayType ga = (GenericArrayType) a;
184 GenericArrayType gb = (GenericArrayType) b;
185 return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
186
187 } else if (a instanceof WildcardType) {
188 if (!(b instanceof WildcardType)) {
189 return false;
190 }
191
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());
196
197 } else if (a instanceof TypeVariable) {
198 if (!(b instanceof TypeVariable)) {
199 return false;
200 }
201 TypeVariable<?> va = (TypeVariable<?>) a;
202 TypeVariable<?> vb = (TypeVariable<?>) b;
203 return va.getGenericDeclaration() == vb.getGenericDeclaration()
204 && va.getName().equals(vb.getName());
205
206 } else {
207 // This isn't a type we support. Could be a generic array type, wildcard type, etc.
208 return false;
209 }
210 }
211
212 private static int hashCodeOrZero(Object o) {
213 return o != null ? o.hashCode() : 0;
214 }
215
216 public static String typeToString(Type type) {
217 return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
218 }
219
220 /**
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>}.
224 */
225 static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve) {
226 if (toResolve == rawType) {
227 return context;
228 }
229
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);
238 }
239 }
240 }
241
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);
250 }
251 rawType = rawSupertype;
252 }
253 }
254
255 // we can't resolve this further
256 return toResolve;
257 }
258
259 /**
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
262 * Iterable.class}.
263 *
264 * @param supertype a superclass of, or interface implemented by, this.
265 */
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));
270 }
271
272 /**
273 * Returns the component type of this array type.
274 * @throws ClassCastException if this type is not an array.
275 */
276 public static Type getArrayComponentType(Type array) {
277 return array instanceof GenericArrayType
278 ? ((GenericArrayType) array).getGenericComponentType()
279 : ((Class<?>) array).getComponentType();
280 }
281
282 /**
283 * Returns the element type of this collection type.
284 * @throws IllegalArgumentException if this type is not a collection.
285 */
286 public static Type getCollectionElementType(Type context, Class<?> contextRawType) {
287 Type collectionType = getSupertype(context, contextRawType, Collection.class);
288
289 if (collectionType instanceof WildcardType) {
290 collectionType = ((WildcardType)collectionType).getUpperBounds()[0];
291 }
292 if (collectionType instanceof ParameterizedType) {
293 return ((ParameterizedType) collectionType).getActualTypeArguments()[0];
294 }
295 return Object.class;
296 }
297
298 /**
299 * Returns a two element array containing this map's key and value types in
300 * positions 0 and 1 respectively.
301 */
302 public static Type[] getMapKeyAndValueTypes(Type context, Class<?> contextRawType) {
303 /*
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>.
307 */
308 if (context == Properties.class) {
309 return new Type[] { String.class, String.class }; // TODO: test subclasses of Properties!
310 }
311
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();
317 }
318 return new Type[] { Object.class, Object.class };
319 }
320
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
323 while (true) {
324 if (toResolve instanceof TypeVariable) {
325 TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
326 toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
327 if (toResolve == typeVariable) {
328 return toResolve;
329 }
330
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
336 ? original
337 : arrayOf(newComponentType);
338
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
344 ? original
345 : arrayOf(newComponentType);
346
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;
352
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]) {
357 if (!changed) {
358 args = args.clone();
359 changed = true;
360 }
361 args[t] = resolvedTypeArgument;
362 }
363 }
364
365 return changed
366 ? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
367 : original;
368
369 } else if (toResolve instanceof WildcardType) {
370 WildcardType original = (WildcardType) toResolve;
371 Type[] originalLowerBound = original.getLowerBounds();
372 Type[] originalUpperBound = original.getUpperBounds();
373
374 if (originalLowerBound.length == 1) {
375 Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]);
376 if (lowerBound != originalLowerBound[0]) {
377 return supertypeOf(lowerBound);
378 }
379 } else if (originalUpperBound.length == 1) {
380 Type upperBound = resolve(context, contextRawType, originalUpperBound[0]);
381 if (upperBound != originalUpperBound[0]) {
382 return subtypeOf(upperBound);
383 }
384 }
385 return original;
386
387 } else {
388 return toResolve;
389 }
390 }
391 }
392
393 static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
394 Class<?> declaredByRaw = declaringClassOf(unknown);
395
396 // we can't reduce this further
397 if (declaredByRaw == null) {
398 return unknown;
399 }
400
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];
405 }
406
407 return unknown;
408 }
409
410 private static int indexOf(Object[] array, Object toFind) {
411 for (int i = 0; i < array.length; i++) {
412 if (toFind.equals(array[i])) {
413 return i;
414 }
415 }
416 throw new NoSuchElementException();
417 }
418
419 /**
420 * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
421 * a class.
422 */
423 private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
424 GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
425 return genericDeclaration instanceof Class
426 ? (Class<?>) genericDeclaration
427 : null;
428 }
429
430 private static void checkNotPrimitive(Type type) {
431 checkArgument(!(type instanceof Class<?>) || !((Class<?>) type).isPrimitive());
432 }
433
434 private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable {
435 private final Type ownerType;
436 private final Type rawType;
437 private final Type[] typeArguments;
438
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);
445 }
446
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]);
454 }
455 }
456
457 public Type[] getActualTypeArguments() {
458 return typeArguments.clone();
459 }
460
461 public Type getRawType() {
462 return rawType;
463 }
464
465 public Type getOwnerType() {
466 return ownerType;
467 }
468
469 @Override public boolean equals(Object other) {
470 return other instanceof ParameterizedType
471 && $Gson$Types.equals(this, (ParameterizedType) other);
472 }
473
474 @Override public int hashCode() {
475 return Arrays.hashCode(typeArguments)
476 ^ rawType.hashCode()
477 ^ hashCodeOrZero(ownerType);
478 }
479
480 @Override public String toString() {
481 StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
482 stringBuilder.append(typeToString(rawType));
483
484 if (typeArguments.length == 0) {
485 return stringBuilder.toString();
486 }
487
488 stringBuilder.append("<").append(typeToString(typeArguments[0]));
489 for (int i = 1; i < typeArguments.length; i++) {
490 stringBuilder.append(", ").append(typeToString(typeArguments[i]));
491 }
492 return stringBuilder.append(">").toString();
493 }
494
495 private static final long serialVersionUID = 0;
496 }
497
498 private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable {
499 private final Type componentType;
500
501 public GenericArrayTypeImpl(Type componentType) {
502 this.componentType = canonicalize(componentType);
503 }
504
505 public Type getGenericComponentType() {
506 return componentType;
507 }
508
509 @Override public boolean equals(Object o) {
510 return o instanceof GenericArrayType
511 && $Gson$Types.equals(this, (GenericArrayType) o);
512 }
513
514 @Override public int hashCode() {
515 return componentType.hashCode();
516 }
517
518 @Override public String toString() {
519 return typeToString(componentType) + "[]";
520 }
521
522 private static final long serialVersionUID = 0;
523 }
524
525 /**
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.
529 */
530 private static final class WildcardTypeImpl implements WildcardType, Serializable {
531 private final Type upperBound;
532 private final Type lowerBound;
533
534 public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
535 checkArgument(lowerBounds.length <= 1);
536 checkArgument(upperBounds.length == 1);
537
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;
544
545 } else {
546 checkNotNull(upperBounds[0]);
547 checkNotPrimitive(upperBounds[0]);
548 this.lowerBound = null;
549 this.upperBound = canonicalize(upperBounds[0]);
550 }
551 }
552
553 public Type[] getUpperBounds() {
554 return new Type[] { upperBound };
555 }
556
557 public Type[] getLowerBounds() {
558 return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
559 }
560
561 @Override public boolean equals(Object other) {
562 return other instanceof WildcardType
563 && $Gson$Types.equals(this, (WildcardType) other);
564 }
565
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());
570 }
571
572 @Override public String toString() {
573 if (lowerBound != null) {
574 return "? super " + typeToString(lowerBound);
575 } else if (upperBound == Object.class) {
576 return "?";
577 } else {
578 return "? extends " + typeToString(upperBound);
579 }
580 }
581
582 private static final long serialVersionUID = 0;
583 }
584 }
This page took 0.039464 seconds and 4 git commands to generate.