xref: /aosp_15_r20/external/moshi/moshi/src/main/java/com/squareup/moshi/internal/Util.java (revision 238ab3e782f339ab327592a602fa7df0a3f729ad)
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  *    https://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 static com.squareup.moshi.Types.arrayOf;
19 import static com.squareup.moshi.Types.subtypeOf;
20 import static com.squareup.moshi.Types.supertypeOf;
21 
22 import com.squareup.moshi.Json;
23 import com.squareup.moshi.JsonAdapter;
24 import com.squareup.moshi.JsonClass;
25 import com.squareup.moshi.JsonDataException;
26 import com.squareup.moshi.JsonQualifier;
27 import com.squareup.moshi.JsonReader;
28 import com.squareup.moshi.Moshi;
29 import com.squareup.moshi.Types;
30 import java.lang.annotation.Annotation;
31 import java.lang.reflect.AnnotatedElement;
32 import java.lang.reflect.Constructor;
33 import java.lang.reflect.GenericArrayType;
34 import java.lang.reflect.GenericDeclaration;
35 import java.lang.reflect.InvocationTargetException;
36 import java.lang.reflect.ParameterizedType;
37 import java.lang.reflect.Type;
38 import java.lang.reflect.TypeVariable;
39 import java.lang.reflect.WildcardType;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.Collections;
43 import java.util.LinkedHashMap;
44 import java.util.LinkedHashSet;
45 import java.util.Map;
46 import java.util.NoSuchElementException;
47 import java.util.Set;
48 import javax.annotation.Nullable;
49 
50 public final class Util {
51   public static final Set<Annotation> NO_ANNOTATIONS = Collections.emptySet();
52   public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
53   @Nullable public static final Class<?> DEFAULT_CONSTRUCTOR_MARKER;
54   @Nullable private static final Class<? extends Annotation> METADATA;
55 
56   /** A map from primitive types to their corresponding wrapper types. */
57   private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE;
58 
59   static {
60     Class<? extends Annotation> metadata = null;
61     try {
62       //noinspection unchecked
63       metadata = (Class<? extends Annotation>) Class.forName(getKotlinMetadataClassName());
64     } catch (ClassNotFoundException ignored) {
65     }
66     METADATA = metadata;
67 
68     // We look up the constructor marker separately because Metadata might be (justifiably)
69     // stripped by R8/Proguard but the DefaultConstructorMarker is still present.
70     Class<?> defaultConstructorMarker = null;
71     try {
72       defaultConstructorMarker = Class.forName("kotlin.jvm.internal.DefaultConstructorMarker");
73     } catch (ClassNotFoundException ignored) {
74     }
75     DEFAULT_CONSTRUCTOR_MARKER = defaultConstructorMarker;
76 
77     Map<Class<?>, Class<?>> primToWrap = new LinkedHashMap<>(16);
78 
primToWrap.put(boolean.class, Boolean.class)79     primToWrap.put(boolean.class, Boolean.class);
primToWrap.put(byte.class, Byte.class)80     primToWrap.put(byte.class, Byte.class);
primToWrap.put(char.class, Character.class)81     primToWrap.put(char.class, Character.class);
primToWrap.put(double.class, Double.class)82     primToWrap.put(double.class, Double.class);
primToWrap.put(float.class, Float.class)83     primToWrap.put(float.class, Float.class);
primToWrap.put(int.class, Integer.class)84     primToWrap.put(int.class, Integer.class);
primToWrap.put(long.class, Long.class)85     primToWrap.put(long.class, Long.class);
primToWrap.put(short.class, Short.class)86     primToWrap.put(short.class, Short.class);
primToWrap.put(void.class, Void.class)87     primToWrap.put(void.class, Void.class);
88 
89     PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap);
90   }
91 
92   // Extracted as a method with a keep rule to prevent R8 from keeping Kotlin Metada
getKotlinMetadataClassName()93   private static String getKotlinMetadataClassName() {
94     return "kotlin.Metadata";
95   }
96 
Util()97   private Util() {}
98 
jsonName(String declaredName, AnnotatedElement element)99   public static String jsonName(String declaredName, AnnotatedElement element) {
100     return jsonName(declaredName, element.getAnnotation(Json.class));
101   }
102 
jsonName(String declaredName, @Nullable Json annotation)103   public static String jsonName(String declaredName, @Nullable Json annotation) {
104     if (annotation == null) return declaredName;
105     String annotationName = annotation.name();
106     return Json.UNSET_NAME.equals(annotationName) ? declaredName : annotationName;
107   }
108 
typesMatch(Type pattern, Type candidate)109   public static boolean typesMatch(Type pattern, Type candidate) {
110     // TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
111     return Types.equals(pattern, candidate);
112   }
113 
jsonAnnotations(AnnotatedElement annotatedElement)114   public static Set<? extends Annotation> jsonAnnotations(AnnotatedElement annotatedElement) {
115     return jsonAnnotations(annotatedElement.getAnnotations());
116   }
117 
jsonAnnotations(Annotation[] annotations)118   public static Set<? extends Annotation> jsonAnnotations(Annotation[] annotations) {
119     Set<Annotation> result = null;
120     for (Annotation annotation : annotations) {
121       if (annotation.annotationType().isAnnotationPresent(JsonQualifier.class)) {
122         if (result == null) result = new LinkedHashSet<>();
123         result.add(annotation);
124       }
125     }
126     return result != null ? Collections.unmodifiableSet(result) : Util.NO_ANNOTATIONS;
127   }
128 
isAnnotationPresent( Set<? extends Annotation> annotations, Class<? extends Annotation> annotationClass)129   public static boolean isAnnotationPresent(
130       Set<? extends Annotation> annotations, Class<? extends Annotation> annotationClass) {
131     if (annotations.isEmpty()) return false; // Save an iterator in the common case.
132     for (Annotation annotation : annotations) {
133       if (annotation.annotationType() == annotationClass) return true;
134     }
135     return false;
136   }
137 
138   /** Returns true if {@code annotations} has any annotation whose simple name is Nullable. */
hasNullable(Annotation[] annotations)139   public static boolean hasNullable(Annotation[] annotations) {
140     for (Annotation annotation : annotations) {
141       if (annotation.annotationType().getSimpleName().equals("Nullable")) {
142         return true;
143       }
144     }
145     return false;
146   }
147 
148   /**
149    * Returns true if {@code rawType} is built in. We don't reflect on private fields of platform
150    * types because they're unspecified and likely to be different on Java vs. Android.
151    */
isPlatformType(Class<?> rawType)152   public static boolean isPlatformType(Class<?> rawType) {
153     String name = rawType.getName();
154     return name.startsWith("android.")
155         || name.startsWith("androidx.")
156         || name.startsWith("java.")
157         || name.startsWith("javax.")
158         || name.startsWith("kotlin.")
159         || name.startsWith("kotlinx.")
160         || name.startsWith("scala.");
161   }
162 
163   /** Throws the cause of {@code e}, wrapping it if it is checked. */
rethrowCause(InvocationTargetException e)164   public static RuntimeException rethrowCause(InvocationTargetException e) {
165     Throwable cause = e.getTargetException();
166     if (cause instanceof RuntimeException) throw (RuntimeException) cause;
167     if (cause instanceof Error) throw (Error) cause;
168     throw new RuntimeException(cause);
169   }
170 
171   /**
172    * Returns a type that is functionally equal but not necessarily equal according to {@link
173    * Object#equals(Object) Object.equals()}.
174    */
canonicalize(Type type)175   public static Type canonicalize(Type type) {
176     if (type instanceof Class) {
177       Class<?> c = (Class<?>) type;
178       return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
179 
180     } else if (type instanceof ParameterizedType) {
181       if (type instanceof ParameterizedTypeImpl) return type;
182       ParameterizedType p = (ParameterizedType) type;
183       return new ParameterizedTypeImpl(
184           p.getOwnerType(), p.getRawType(), p.getActualTypeArguments());
185 
186     } else if (type instanceof GenericArrayType) {
187       if (type instanceof GenericArrayTypeImpl) return type;
188       GenericArrayType g = (GenericArrayType) type;
189       return new GenericArrayTypeImpl(g.getGenericComponentType());
190 
191     } else if (type instanceof WildcardType) {
192       if (type instanceof WildcardTypeImpl) return type;
193       WildcardType w = (WildcardType) type;
194       return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
195 
196     } else {
197       return type; // This type is unsupported!
198     }
199   }
200 
201   /** If type is a "? extends X" wildcard, returns X; otherwise returns type unchanged. */
removeSubtypeWildcard(Type type)202   public static Type removeSubtypeWildcard(Type type) {
203     if (!(type instanceof WildcardType)) return type;
204 
205     Type[] lowerBounds = ((WildcardType) type).getLowerBounds();
206     if (lowerBounds.length != 0) return type;
207 
208     Type[] upperBounds = ((WildcardType) type).getUpperBounds();
209     if (upperBounds.length != 1) throw new IllegalArgumentException();
210 
211     return upperBounds[0];
212   }
213 
resolve(Type context, Class<?> contextRawType, Type toResolve)214   public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
215     return resolve(context, contextRawType, toResolve, new LinkedHashSet<TypeVariable<?>>());
216   }
217 
resolve( Type context, Class<?> contextRawType, Type toResolve, Collection<TypeVariable<?>> visitedTypeVariables)218   private static Type resolve(
219       Type context,
220       Class<?> contextRawType,
221       Type toResolve,
222       Collection<TypeVariable<?>> visitedTypeVariables) {
223     // This implementation is made a little more complicated in an attempt to avoid object-creation.
224     while (true) {
225       if (toResolve instanceof TypeVariable) {
226         TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
227         if (visitedTypeVariables.contains(typeVariable)) {
228           // cannot reduce due to infinite recursion
229           return toResolve;
230         } else {
231           visitedTypeVariables.add(typeVariable);
232         }
233         toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
234         if (toResolve == typeVariable) return toResolve;
235 
236       } else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) {
237         Class<?> original = (Class<?>) toResolve;
238         Type componentType = original.getComponentType();
239         Type newComponentType =
240             resolve(context, contextRawType, componentType, visitedTypeVariables);
241         return componentType == newComponentType ? original : arrayOf(newComponentType);
242 
243       } else if (toResolve instanceof GenericArrayType) {
244         GenericArrayType original = (GenericArrayType) toResolve;
245         Type componentType = original.getGenericComponentType();
246         Type newComponentType =
247             resolve(context, contextRawType, componentType, visitedTypeVariables);
248         return componentType == newComponentType ? original : arrayOf(newComponentType);
249 
250       } else if (toResolve instanceof ParameterizedType) {
251         ParameterizedType original = (ParameterizedType) toResolve;
252         Type ownerType = original.getOwnerType();
253         Type newOwnerType = resolve(context, contextRawType, ownerType, visitedTypeVariables);
254         boolean changed = newOwnerType != ownerType;
255 
256         Type[] args = original.getActualTypeArguments();
257         for (int t = 0, length = args.length; t < length; t++) {
258           Type resolvedTypeArgument =
259               resolve(context, contextRawType, args[t], visitedTypeVariables);
260           if (resolvedTypeArgument != args[t]) {
261             if (!changed) {
262               args = args.clone();
263               changed = true;
264             }
265             args[t] = resolvedTypeArgument;
266           }
267         }
268 
269         return changed
270             ? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args)
271             : original;
272 
273       } else if (toResolve instanceof WildcardType) {
274         WildcardType original = (WildcardType) toResolve;
275         Type[] originalLowerBound = original.getLowerBounds();
276         Type[] originalUpperBound = original.getUpperBounds();
277 
278         if (originalLowerBound.length == 1) {
279           Type lowerBound =
280               resolve(context, contextRawType, originalLowerBound[0], visitedTypeVariables);
281           if (lowerBound != originalLowerBound[0]) {
282             return supertypeOf(lowerBound);
283           }
284         } else if (originalUpperBound.length == 1) {
285           Type upperBound =
286               resolve(context, contextRawType, originalUpperBound[0], visitedTypeVariables);
287           if (upperBound != originalUpperBound[0]) {
288             return subtypeOf(upperBound);
289           }
290         }
291         return original;
292 
293       } else {
294         return toResolve;
295       }
296     }
297   }
298 
resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown)299   static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
300     Class<?> declaredByRaw = declaringClassOf(unknown);
301 
302     // We can't reduce this further.
303     if (declaredByRaw == null) return unknown;
304 
305     Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw);
306     if (declaredBy instanceof ParameterizedType) {
307       int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
308       return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
309     }
310 
311     return unknown;
312   }
313 
314   /**
315    * Returns the generic supertype for {@code supertype}. For example, given a class {@code
316    * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the
317    * result when the supertype is {@code Collection.class} is {@code Collection<Integer>}.
318    */
getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve)319   public static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve) {
320     if (toResolve == rawType) {
321       return context;
322     }
323 
324     // we skip searching through interfaces if unknown is an interface
325     if (toResolve.isInterface()) {
326       Class<?>[] interfaces = rawType.getInterfaces();
327       for (int i = 0, length = interfaces.length; i < length; i++) {
328         if (interfaces[i] == toResolve) {
329           return rawType.getGenericInterfaces()[i];
330         } else if (toResolve.isAssignableFrom(interfaces[i])) {
331           return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
332         }
333       }
334     }
335 
336     // check our supertypes
337     if (!rawType.isInterface()) {
338       while (rawType != Object.class) {
339         Class<?> rawSupertype = rawType.getSuperclass();
340         if (rawSupertype == toResolve) {
341           return rawType.getGenericSuperclass();
342         } else if (toResolve.isAssignableFrom(rawSupertype)) {
343           return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
344         }
345         rawType = rawSupertype;
346       }
347     }
348 
349     // we can't resolve this further
350     return toResolve;
351   }
352 
hashCodeOrZero(@ullable Object o)353   static int hashCodeOrZero(@Nullable Object o) {
354     return o != null ? o.hashCode() : 0;
355   }
356 
typeToString(Type type)357   static String typeToString(Type type) {
358     return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
359   }
360 
indexOf(Object[] array, Object toFind)361   static int indexOf(Object[] array, Object toFind) {
362     for (int i = 0; i < array.length; i++) {
363       if (toFind.equals(array[i])) return i;
364     }
365     throw new NoSuchElementException();
366   }
367 
368   /**
369    * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
370    * a class.
371    */
declaringClassOf(TypeVariable<?> typeVariable)372   static @Nullable Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
373     GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
374     return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
375   }
376 
checkNotPrimitive(Type type)377   static void checkNotPrimitive(Type type) {
378     if ((type instanceof Class<?>) && ((Class<?>) type).isPrimitive()) {
379       throw new IllegalArgumentException("Unexpected primitive " + type + ". Use the boxed type.");
380     }
381   }
382 
383   public static final class ParameterizedTypeImpl implements ParameterizedType {
384     private final @Nullable Type ownerType;
385     private final Type rawType;
386     public final Type[] typeArguments;
387 
ParameterizedTypeImpl(@ullable Type ownerType, Type rawType, Type... typeArguments)388     public ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type... typeArguments) {
389       // Require an owner type if the raw type needs it.
390       if (rawType instanceof Class<?>) {
391         Class<?> enclosingClass = ((Class<?>) rawType).getEnclosingClass();
392         if (ownerType != null) {
393           if (enclosingClass == null || Types.getRawType(ownerType) != enclosingClass) {
394             throw new IllegalArgumentException(
395                 "unexpected owner type for " + rawType + ": " + ownerType);
396           }
397         } else if (enclosingClass != null) {
398           throw new IllegalArgumentException("unexpected owner type for " + rawType + ": null");
399         }
400       }
401 
402       this.ownerType = ownerType == null ? null : canonicalize(ownerType);
403       this.rawType = canonicalize(rawType);
404       this.typeArguments = typeArguments.clone();
405       for (int t = 0; t < this.typeArguments.length; t++) {
406         if (this.typeArguments[t] == null) throw new NullPointerException();
407         checkNotPrimitive(this.typeArguments[t]);
408         this.typeArguments[t] = canonicalize(this.typeArguments[t]);
409       }
410     }
411 
412     @Override
getActualTypeArguments()413     public Type[] getActualTypeArguments() {
414       return typeArguments.clone();
415     }
416 
417     @Override
getRawType()418     public Type getRawType() {
419       return rawType;
420     }
421 
422     @Override
getOwnerType()423     public @Nullable Type getOwnerType() {
424       return ownerType;
425     }
426 
427     @Override
equals(Object other)428     public boolean equals(Object other) {
429       return other instanceof ParameterizedType && Types.equals(this, (ParameterizedType) other);
430     }
431 
432     @Override
hashCode()433     public int hashCode() {
434       return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
435     }
436 
437     @Override
toString()438     public String toString() {
439       StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1));
440       result.append(typeToString(rawType));
441 
442       if (typeArguments.length == 0) {
443         return result.toString();
444       }
445 
446       result.append("<").append(typeToString(typeArguments[0]));
447       for (int i = 1; i < typeArguments.length; i++) {
448         result.append(", ").append(typeToString(typeArguments[i]));
449       }
450       return result.append(">").toString();
451     }
452   }
453 
454   public static final class GenericArrayTypeImpl implements GenericArrayType {
455     private final Type componentType;
456 
GenericArrayTypeImpl(Type componentType)457     public GenericArrayTypeImpl(Type componentType) {
458       this.componentType = canonicalize(componentType);
459     }
460 
461     @Override
getGenericComponentType()462     public Type getGenericComponentType() {
463       return componentType;
464     }
465 
466     @Override
equals(Object o)467     public boolean equals(Object o) {
468       return o instanceof GenericArrayType && Types.equals(this, (GenericArrayType) o);
469     }
470 
471     @Override
hashCode()472     public int hashCode() {
473       return componentType.hashCode();
474     }
475 
476     @Override
toString()477     public String toString() {
478       return typeToString(componentType) + "[]";
479     }
480   }
481 
482   /**
483    * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only
484    * support what the Java 6 language needs - at most one bound. If a lower bound is set, the upper
485    * bound must be Object.class.
486    */
487   public static final class WildcardTypeImpl implements WildcardType {
488     private final Type upperBound;
489     private final @Nullable Type lowerBound;
490 
WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds)491     public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
492       if (lowerBounds.length > 1) throw new IllegalArgumentException();
493       if (upperBounds.length != 1) throw new IllegalArgumentException();
494 
495       if (lowerBounds.length == 1) {
496         if (lowerBounds[0] == null) throw new NullPointerException();
497         checkNotPrimitive(lowerBounds[0]);
498         if (upperBounds[0] != Object.class) throw new IllegalArgumentException();
499         this.lowerBound = canonicalize(lowerBounds[0]);
500         this.upperBound = Object.class;
501 
502       } else {
503         if (upperBounds[0] == null) throw new NullPointerException();
504         checkNotPrimitive(upperBounds[0]);
505         this.lowerBound = null;
506         this.upperBound = canonicalize(upperBounds[0]);
507       }
508     }
509 
510     @Override
getUpperBounds()511     public Type[] getUpperBounds() {
512       return new Type[] {upperBound};
513     }
514 
515     @Override
getLowerBounds()516     public Type[] getLowerBounds() {
517       return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY;
518     }
519 
520     @Override
equals(Object other)521     public boolean equals(Object other) {
522       return other instanceof WildcardType && Types.equals(this, (WildcardType) other);
523     }
524 
525     @Override
hashCode()526     public int hashCode() {
527       // This equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()).
528       return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
529     }
530 
531     @Override
toString()532     public String toString() {
533       if (lowerBound != null) {
534         return "? super " + typeToString(lowerBound);
535       } else if (upperBound == Object.class) {
536         return "?";
537       } else {
538         return "? extends " + typeToString(upperBound);
539       }
540     }
541   }
542 
typeAnnotatedWithAnnotations( Type type, Set<? extends Annotation> annotations)543   public static String typeAnnotatedWithAnnotations(
544       Type type, Set<? extends Annotation> annotations) {
545     return type + (annotations.isEmpty() ? " (with no annotations)" : " annotated " + annotations);
546   }
547 
548   /**
549    * Loads the generated JsonAdapter for classes annotated {@link JsonClass}. This works because it
550    * uses the same naming conventions as {@code JsonClassCodeGenProcessor}.
551    */
generatedAdapter( Moshi moshi, Type type, Class<?> rawType)552   public static @Nullable JsonAdapter<?> generatedAdapter(
553       Moshi moshi, Type type, Class<?> rawType) {
554     JsonClass jsonClass = rawType.getAnnotation(JsonClass.class);
555     if (jsonClass == null || !jsonClass.generateAdapter()) {
556       return null;
557     }
558     String adapterClassName = Types.generatedJsonAdapterName(rawType.getName());
559     Class<? extends JsonAdapter<?>> adapterClass = null;
560     try {
561       //noinspection unchecked - We generate types to match.
562       adapterClass =
563           (Class<? extends JsonAdapter<?>>)
564               Class.forName(adapterClassName, true, rawType.getClassLoader());
565       Constructor<? extends JsonAdapter<?>> constructor;
566       Object[] args;
567       if (type instanceof ParameterizedType) {
568         Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments();
569         try {
570           // Common case first
571           constructor = adapterClass.getDeclaredConstructor(Moshi.class, Type[].class);
572           args = new Object[] {moshi, typeArgs};
573         } catch (NoSuchMethodException e) {
574           constructor = adapterClass.getDeclaredConstructor(Type[].class);
575           args = new Object[] {typeArgs};
576         }
577       } else {
578         try {
579           // Common case first
580           constructor = adapterClass.getDeclaredConstructor(Moshi.class);
581           args = new Object[] {moshi};
582         } catch (NoSuchMethodException e) {
583           constructor = adapterClass.getDeclaredConstructor();
584           args = new Object[0];
585         }
586       }
587       constructor.setAccessible(true);
588       return constructor.newInstance(args).nullSafe();
589     } catch (ClassNotFoundException e) {
590       throw new RuntimeException("Failed to find the generated JsonAdapter class for " + type, e);
591     } catch (NoSuchMethodException e) {
592       if (!(type instanceof ParameterizedType) && adapterClass.getTypeParameters().length != 0) {
593         throw new RuntimeException(
594             "Failed to find the generated JsonAdapter constructor for '"
595                 + type
596                 + "'. Suspiciously, the type was not parameterized but the target class '"
597                 + adapterClass.getCanonicalName()
598                 + "' is generic. Consider using "
599                 + "Types#newParameterizedType() to define these missing type variables.",
600             e);
601       } else {
602         throw new RuntimeException(
603             "Failed to find the generated JsonAdapter constructor for " + type, e);
604       }
605     } catch (IllegalAccessException e) {
606       throw new RuntimeException("Failed to access the generated JsonAdapter for " + type, e);
607     } catch (InstantiationException e) {
608       throw new RuntimeException("Failed to instantiate the generated JsonAdapter for " + type, e);
609     } catch (InvocationTargetException e) {
610       throw rethrowCause(e);
611     }
612   }
613 
isKotlin(Class<?> targetClass)614   public static boolean isKotlin(Class<?> targetClass) {
615     return METADATA != null && targetClass.isAnnotationPresent(METADATA);
616   }
617 
618   /**
619    * Reflectively looks up the defaults constructor of a kotlin class.
620    *
621    * @param targetClass the target kotlin class to instantiate.
622    * @param <T> the type of {@code targetClass}.
623    * @return the instantiated {@code targetClass} instance.
624    */
lookupDefaultsConstructor(Class<T> targetClass)625   public static <T> Constructor<T> lookupDefaultsConstructor(Class<T> targetClass) {
626     if (DEFAULT_CONSTRUCTOR_MARKER == null) {
627       throw new IllegalStateException(
628           "DefaultConstructorMarker not on classpath. Make sure the "
629               + "Kotlin stdlib is on the classpath.");
630     }
631     Constructor<T> defaultConstructor = findConstructor(targetClass);
632     defaultConstructor.setAccessible(true);
633     return defaultConstructor;
634   }
635 
findConstructor(Class<T> targetClass)636   private static <T> Constructor<T> findConstructor(Class<T> targetClass) {
637     for (Constructor<?> constructor : targetClass.getDeclaredConstructors()) {
638       Class<?>[] paramTypes = constructor.getParameterTypes();
639       if (paramTypes.length != 0
640           && paramTypes[paramTypes.length - 1].equals(DEFAULT_CONSTRUCTOR_MARKER)) {
641         //noinspection unchecked
642         return (Constructor<T>) constructor;
643       }
644     }
645 
646     throw new IllegalStateException("No defaults constructor found for " + targetClass);
647   }
648 
missingProperty( String propertyName, String jsonName, JsonReader reader)649   public static JsonDataException missingProperty(
650       String propertyName, String jsonName, JsonReader reader) {
651     String path = reader.getPath();
652     String message;
653     if (jsonName.equals(propertyName)) {
654       message = String.format("Required value '%s' missing at %s", propertyName, path);
655     } else {
656       message =
657           String.format(
658               "Required value '%s' (JSON name '%s') missing at %s", propertyName, jsonName, path);
659     }
660     return new JsonDataException(message);
661   }
662 
unexpectedNull( String propertyName, String jsonName, JsonReader reader)663   public static JsonDataException unexpectedNull(
664       String propertyName, String jsonName, JsonReader reader) {
665     String path = reader.getPath();
666     String message;
667     if (jsonName.equals(propertyName)) {
668       message = String.format("Non-null value '%s' was null at %s", propertyName, path);
669     } else {
670       message =
671           String.format(
672               "Non-null value '%s' (JSON name '%s') was null at %s", propertyName, jsonName, path);
673     }
674     return new JsonDataException(message);
675   }
676 
677   // Public due to inline access in MoshiKotlinTypesExtensions
boxIfPrimitive(Class<T> type)678   public static <T> Class<T> boxIfPrimitive(Class<T> type) {
679     // cast is safe: long.class and Long.class are both of type Class<Long>
680     @SuppressWarnings("unchecked")
681     Class<T> wrapped = (Class<T>) PRIMITIVE_TO_WRAPPER_TYPE.get(type);
682     return (wrapped == null) ? type : wrapped;
683   }
684 }
685