1 /*
2  * Copyright (C) 2014 Square, 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 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     // TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
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// Save an iterator in the common case.
74     for (Annotation annotation : annotations) {
75       if (annotation.annotationType() == annotationClass) return true;
76     }
77     return false;
78   }
79
80   /** Returns true if {@code annotations} has any annotation whose simple name is Nullable. */
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   /**
91    * Returns true if {@code rawType} is built in. We don't reflect on private fields of platform
92    * types because they're unspecified and likely to be different on Java vs. Android.
93    */

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   /** Throws the cause of {@code e}, wrapping it if it is checked. */
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   /**
113    * Returns a type that is functionally equal but not necessarily equal according to {@link
114    * Object#equals(Object) Object.equals()}.
115    */

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; // This type is unsupported!
139     }
140   }
141
142   public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
143     // This implementation is made a little more complicated in an attempt to avoid object-creation.
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     // We can't reduce this further.
216     if (declaredByRaw == nullreturn 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   /**
228    * Returns the generic supertype for {@code supertype}. For example, given a class {@code
229    * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the
230    * result when the supertype is {@code Collection.class} is {@code Collection<Integer>}.
231    */

232   public static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve) {
233     if (toResolve == rawType) {
234       return context;
235     }
236
237     // we skip searching through interfaces if unknown is an interface
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     // check our supertypes
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     // we can't resolve this further
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   /**
282    * Returns the declaring class of {@code typeVariable}, or {@code nullif it was not declared by
283    * a class.
284    */

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       // Require an owner type if the raw type needs it.
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] == nullthrow 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   /**
391    * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only
392    * support what the Java 6 language needs - at most one bound. If a lower bound is set, the upper
393    * bound must be Object.class.
394    */

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] == nullthrow new NullPointerException();
405         checkNotPrimitive(lowerBounds[0]);
406         if (upperBounds[0] != Object.classthrow new IllegalArgumentException();
407         this.lowerBound = canonicalize(lowerBounds[0]);
408         this.upperBound = Object.class;
409
410       } else {
411         if (upperBounds[0] == nullthrow 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       // This equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()).
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   /**
454    * Loads the generated JsonAdapter for classes annotated {@link JsonClass}. This works because it
455    * uses the same naming conventions as {@code JsonClassCodeGenProcessor}.
456    */

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"// We generate types to match.
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