1
16 package com.squareup.moshi.internal;
17
18 import com.squareup.moshi.JsonAdapter;
19 import com.squareup.moshi.JsonClass;
20 import com.squareup.moshi.JsonQualifier;
21 import com.squareup.moshi.Moshi;
22 import com.squareup.moshi.Types;
23 import java.lang.annotation.Annotation;
24 import java.lang.reflect.AnnotatedElement;
25 import java.lang.reflect.Constructor;
26 import java.lang.reflect.GenericArrayType;
27 import java.lang.reflect.GenericDeclaration;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.ParameterizedType;
30 import java.lang.reflect.Type;
31 import java.lang.reflect.TypeVariable;
32 import java.lang.reflect.WildcardType;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.LinkedHashSet;
36 import java.util.NoSuchElementException;
37 import java.util.Set;
38 import javax.annotation.Nullable;
39
40 import static com.squareup.moshi.Types.arrayOf;
41 import static com.squareup.moshi.Types.subtypeOf;
42 import static com.squareup.moshi.Types.supertypeOf;
43
44 public final class Util {
45 public static final Set<Annotation> NO_ANNOTATIONS = Collections.emptySet();
46 public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
47
48 private Util() {
49 }
50
51 public static boolean typesMatch(Type pattern, Type candidate) {
52
53 return Types.equals(pattern, candidate);
54 }
55
56 public static Set<? extends Annotation> jsonAnnotations(AnnotatedElement annotatedElement) {
57 return jsonAnnotations(annotatedElement.getAnnotations());
58 }
59
60 public static Set<? extends Annotation> jsonAnnotations(Annotation[] annotations) {
61 Set<Annotation> result = null;
62 for (Annotation annotation : annotations) {
63 if (annotation.annotationType().isAnnotationPresent(JsonQualifier.class)) {
64 if (result == null) result = new LinkedHashSet<>();
65 result.add(annotation);
66 }
67 }
68 return result != null ? Collections.unmodifiableSet(result) : Util.NO_ANNOTATIONS;
69 }
70
71 public static boolean isAnnotationPresent(
72 Set<? extends Annotation> annotations, Class<? extends Annotation> annotationClass) {
73 if (annotations.isEmpty()) return false;
74 for (Annotation annotation : annotations) {
75 if (annotation.annotationType() == annotationClass) return true;
76 }
77 return false;
78 }
79
80
81 public static boolean hasNullable(Annotation[] annotations) {
82 for (Annotation annotation : annotations) {
83 if (annotation.annotationType().getSimpleName().equals("Nullable")) {
84 return true;
85 }
86 }
87 return false;
88 }
89
90
94 public static boolean isPlatformType(Class<?> rawType) {
95 String name = rawType.getName();
96 return name.startsWith("android.")
97 || name.startsWith("androidx.")
98 || name.startsWith("java.")
99 || name.startsWith("javax.")
100 || name.startsWith("kotlin.")
101 || name.startsWith("scala.");
102 }
103
104
105 public static RuntimeException rethrowCause(InvocationTargetException e) {
106 Throwable cause = e.getTargetException();
107 if (cause instanceof RuntimeException) throw (RuntimeException) cause;
108 if (cause instanceof Error) throw (Error) cause;
109 throw new RuntimeException(cause);
110 }
111
112
116 public static Type canonicalize(Type type) {
117 if (type instanceof Class) {
118 Class<?> c = (Class<?>) type;
119 return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
120
121 } else if (type instanceof ParameterizedType) {
122 if (type instanceof ParameterizedTypeImpl) return type;
123 ParameterizedType p = (ParameterizedType) type;
124 return new ParameterizedTypeImpl(p.getOwnerType(),
125 p.getRawType(), p.getActualTypeArguments());
126
127 } else if (type instanceof GenericArrayType) {
128 if (type instanceof GenericArrayTypeImpl) return type;
129 GenericArrayType g = (GenericArrayType) type;
130 return new GenericArrayTypeImpl(g.getGenericComponentType());
131
132 } else if (type instanceof WildcardType) {
133 if (type instanceof WildcardTypeImpl) return type;
134 WildcardType w = (WildcardType) type;
135 return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
136
137 } else {
138 return type;
139 }
140 }
141
142 public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
143
144 while (true) {
145 if (toResolve instanceof TypeVariable) {
146 TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
147 toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
148 if (toResolve == typeVariable) return toResolve;
149
150 } else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) {
151 Class<?> original = (Class<?>) toResolve;
152 Type componentType = original.getComponentType();
153 Type newComponentType = resolve(context, contextRawType, componentType);
154 return componentType == newComponentType
155 ? original
156 : arrayOf(newComponentType);
157
158 } else if (toResolve instanceof GenericArrayType) {
159 GenericArrayType original = (GenericArrayType) toResolve;
160 Type componentType = original.getGenericComponentType();
161 Type newComponentType = resolve(context, contextRawType, componentType);
162 return componentType == newComponentType
163 ? original
164 : arrayOf(newComponentType);
165
166 } else if (toResolve instanceof ParameterizedType) {
167 ParameterizedType original = (ParameterizedType) toResolve;
168 Type ownerType = original.getOwnerType();
169 Type newOwnerType = resolve(context, contextRawType, ownerType);
170 boolean changed = newOwnerType != ownerType;
171
172 Type[] args = original.getActualTypeArguments();
173 for (int t = 0, length = args.length; t < length; t++) {
174 Type resolvedTypeArgument = resolve(context, contextRawType, args[t]);
175 if (resolvedTypeArgument != args[t]) {
176 if (!changed) {
177 args = args.clone();
178 changed = true;
179 }
180 args[t] = resolvedTypeArgument;
181 }
182 }
183
184 return changed
185 ? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args)
186 : original;
187
188 } else if (toResolve instanceof WildcardType) {
189 WildcardType original = (WildcardType) toResolve;
190 Type[] originalLowerBound = original.getLowerBounds();
191 Type[] originalUpperBound = original.getUpperBounds();
192
193 if (originalLowerBound.length == 1) {
194 Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]);
195 if (lowerBound != originalLowerBound[0]) {
196 return supertypeOf(lowerBound);
197 }
198 } else if (originalUpperBound.length == 1) {
199 Type upperBound = resolve(context, contextRawType, originalUpperBound[0]);
200 if (upperBound != originalUpperBound[0]) {
201 return subtypeOf(upperBound);
202 }
203 }
204 return original;
205
206 } else {
207 return toResolve;
208 }
209 }
210 }
211
212 static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
213 Class<?> declaredByRaw = declaringClassOf(unknown);
214
215
216 if (declaredByRaw == null) return unknown;
217
218 Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw);
219 if (declaredBy instanceof ParameterizedType) {
220 int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
221 return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
222 }
223
224 return unknown;
225 }
226
227
232 public static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve) {
233 if (toResolve == rawType) {
234 return context;
235 }
236
237
238 if (toResolve.isInterface()) {
239 Class<?>[] interfaces = rawType.getInterfaces();
240 for (int i = 0, length = interfaces.length; i < length; i++) {
241 if (interfaces[i] == toResolve) {
242 return rawType.getGenericInterfaces()[i];
243 } else if (toResolve.isAssignableFrom(interfaces[i])) {
244 return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
245 }
246 }
247 }
248
249
250 if (!rawType.isInterface()) {
251 while (rawType != Object.class) {
252 Class<?> rawSupertype = rawType.getSuperclass();
253 if (rawSupertype == toResolve) {
254 return rawType.getGenericSuperclass();
255 } else if (toResolve.isAssignableFrom(rawSupertype)) {
256 return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
257 }
258 rawType = rawSupertype;
259 }
260 }
261
262
263 return toResolve;
264 }
265
266 static int hashCodeOrZero(@Nullable Object o) {
267 return o != null ? o.hashCode() : 0;
268 }
269
270 static String typeToString(Type type) {
271 return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
272 }
273
274 static int indexOf(Object[] array, Object toFind) {
275 for (int i = 0; i < array.length; i++) {
276 if (toFind.equals(array[i])) return i;
277 }
278 throw new NoSuchElementException();
279 }
280
281
285 static @Nullable Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
286 GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
287 return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
288 }
289
290 static void checkNotPrimitive(Type type) {
291 if ((type instanceof Class<?>) && ((Class<?>) type).isPrimitive()) {
292 throw new IllegalArgumentException("Unexpected primitive " + type + ". Use the boxed type.");
293 }
294 }
295
296 public static final class ParameterizedTypeImpl implements ParameterizedType {
297 private final @Nullable Type ownerType;
298 private final Type rawType;
299 public final Type[] typeArguments;
300
301 public ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type... typeArguments) {
302
303 if (rawType instanceof Class<?>) {
304 Class<?> enclosingClass = ((Class<?>) rawType).getEnclosingClass();
305 if (ownerType != null) {
306 if (enclosingClass == null || Types.getRawType(ownerType) != enclosingClass) {
307 throw new IllegalArgumentException(
308 "unexpected owner type for " + rawType + ": " + ownerType);
309 }
310 } else if (enclosingClass != null) {
311 throw new IllegalArgumentException(
312 "unexpected owner type for " + rawType + ": null");
313 }
314 }
315
316 this.ownerType = ownerType == null ? null : canonicalize(ownerType);
317 this.rawType = canonicalize(rawType);
318 this.typeArguments = typeArguments.clone();
319 for (int t = 0; t < this.typeArguments.length; t++) {
320 if (this.typeArguments[t] == null) throw new NullPointerException();
321 checkNotPrimitive(this.typeArguments[t]);
322 this.typeArguments[t] = canonicalize(this.typeArguments[t]);
323 }
324 }
325
326 @Override public Type[] getActualTypeArguments() {
327 return typeArguments.clone();
328 }
329
330 @Override public Type getRawType() {
331 return rawType;
332 }
333
334 @Override public @Nullable Type getOwnerType() {
335 return ownerType;
336 }
337
338 @Override public boolean equals(Object other) {
339 return other instanceof ParameterizedType
340 && Types.equals(this, (ParameterizedType) other);
341 }
342
343 @Override public int hashCode() {
344 return Arrays.hashCode(typeArguments)
345 ^ rawType.hashCode()
346 ^ hashCodeOrZero(ownerType);
347 }
348
349 @Override public String toString() {
350 StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1));
351 result.append(typeToString(rawType));
352
353 if (typeArguments.length == 0) {
354 return result.toString();
355 }
356
357 result.append("<").append(typeToString(typeArguments[0]));
358 for (int i = 1; i < typeArguments.length; i++) {
359 result.append(", ").append(typeToString(typeArguments[i]));
360 }
361 return result.append(">").toString();
362 }
363 }
364
365 public static final class GenericArrayTypeImpl implements GenericArrayType {
366 private final Type componentType;
367
368 public GenericArrayTypeImpl(Type componentType) {
369 this.componentType = canonicalize(componentType);
370 }
371
372 @Override public Type getGenericComponentType() {
373 return componentType;
374 }
375
376 @Override public boolean equals(Object o) {
377 return o instanceof GenericArrayType
378 && Types.equals(this, (GenericArrayType) o);
379 }
380
381 @Override public int hashCode() {
382 return componentType.hashCode();
383 }
384
385 @Override public String toString() {
386 return typeToString(componentType) + "[]";
387 }
388 }
389
390
395 public static final class WildcardTypeImpl implements WildcardType {
396 private final Type upperBound;
397 private final @Nullable Type lowerBound;
398
399 public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
400 if (lowerBounds.length > 1) throw new IllegalArgumentException();
401 if (upperBounds.length != 1) throw new IllegalArgumentException();
402
403 if (lowerBounds.length == 1) {
404 if (lowerBounds[0] == null) throw new NullPointerException();
405 checkNotPrimitive(lowerBounds[0]);
406 if (upperBounds[0] != Object.class) throw new IllegalArgumentException();
407 this.lowerBound = canonicalize(lowerBounds[0]);
408 this.upperBound = Object.class;
409
410 } else {
411 if (upperBounds[0] == null) throw new NullPointerException();
412 checkNotPrimitive(upperBounds[0]);
413 this.lowerBound = null;
414 this.upperBound = canonicalize(upperBounds[0]);
415 }
416 }
417
418 @Override public Type[] getUpperBounds() {
419 return new Type[] { upperBound };
420 }
421
422 @Override public Type[] getLowerBounds() {
423 return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
424 }
425
426 @Override public boolean equals(Object other) {
427 return other instanceof WildcardType
428 && Types.equals(this, (WildcardType) other);
429 }
430
431 @Override public int hashCode() {
432
433 return (lowerBound != null ? 31 + lowerBound.hashCode() : 1)
434 ^ (31 + upperBound.hashCode());
435 }
436
437 @Override public String toString() {
438 if (lowerBound != null) {
439 return "? super " + typeToString(lowerBound);
440 } else if (upperBound == Object.class) {
441 return "?";
442 } else {
443 return "? extends " + typeToString(upperBound);
444 }
445 }
446 }
447
448 public static String typeAnnotatedWithAnnotations(Type type,
449 Set<? extends Annotation> annotations) {
450 return type + (annotations.isEmpty() ? " (with no annotations)" : " annotated " + annotations);
451 }
452
453
457 public static @Nullable JsonAdapter<?> generatedAdapter(Moshi moshi, Type type,
458 Class<?> rawType) {
459 JsonClass jsonClass = rawType.getAnnotation(JsonClass.class);
460 if (jsonClass == null || !jsonClass.generateAdapter()) {
461 return null;
462 }
463 String adapterClassName = rawType.getName().replace("$", "_") + "JsonAdapter";
464 try {
465 @SuppressWarnings("unchecked")
466 Class<? extends JsonAdapter<?>> adapterClass = (Class<? extends JsonAdapter<?>>)
467 Class.forName(adapterClassName, true, rawType.getClassLoader());
468 if (type instanceof ParameterizedType) {
469 Constructor<? extends JsonAdapter<?>> constructor
470 = adapterClass.getDeclaredConstructor(Moshi.class, Type[].class);
471 constructor.setAccessible(true);
472 return constructor.newInstance(moshi, ((ParameterizedType) type).getActualTypeArguments())
473 .nullSafe();
474 } else {
475 Constructor<? extends JsonAdapter<?>> constructor
476 = adapterClass.getDeclaredConstructor(Moshi.class);
477 constructor.setAccessible(true);
478 return constructor.newInstance(moshi).nullSafe();
479 }
480 } catch (ClassNotFoundException e) {
481 throw new RuntimeException(
482 "Failed to find the generated JsonAdapter class for " + rawType, e);
483 } catch (NoSuchMethodException e) {
484 throw new RuntimeException(
485 "Failed to find the generated JsonAdapter constructor for " + rawType, e);
486 } catch (IllegalAccessException e) {
487 throw new RuntimeException(
488 "Failed to access the generated JsonAdapter for " + rawType, e);
489 } catch (InstantiationException e) {
490 throw new RuntimeException(
491 "Failed to instantiate the generated JsonAdapter for " + rawType, e);
492 } catch (InvocationTargetException e) {
493 throw rethrowCause(e);
494 }
495 }
496 }
497