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