mirror of
https://github.com/fankes/moshi.git
synced 2025-10-18 23:49:21 +08:00
Convert util package to Kotlin (#1461)
* Rename .java to .kt * Convert NonNullJsonAdapter * Rename .java to .kt * Convert NullSafeJsonAdapter * Fix missing null-check * Rename .java to .kt * Convert Util (initial pass) * Push to top-level Util * Use extensions and properties where possible * Use knownNotNull * Spotless * Clean up impl type classes a bit * Update code gen * Use extension for resolving * Spotless * Fix master conflicts * Rename to toStringWithAnnotations() * for (t in args.indices) { * for (i in interfaces.indices) { * Template
This commit is contained in:
@@ -78,7 +78,7 @@ final class FallbackEnum {
|
||||
nameStrings = new String[constants.length];
|
||||
for (int i = 0; i < constants.length; i++) {
|
||||
String constantName = constants[i].name();
|
||||
nameStrings[i] = Util.jsonName(constantName, enumType.getField(constantName));
|
||||
nameStrings[i] = Util.jsonName(enumType.getField(constantName), constantName);
|
||||
}
|
||||
options = JsonReader.Options.of(nameStrings);
|
||||
} catch (NoSuchFieldException e) {
|
||||
|
@@ -68,7 +68,7 @@ public final class EnumJsonAdapter<T extends Enum<T>> extends JsonAdapter<T> {
|
||||
nameStrings = new String[constants.length];
|
||||
for (int i = 0; i < constants.length; i++) {
|
||||
String constantName = constants[i].name();
|
||||
nameStrings[i] = jsonName(constantName, enumType.getField(constantName));
|
||||
nameStrings[i] = jsonName(enumType.getField(constantName), constantName);
|
||||
}
|
||||
options = JsonReader.Options.of(nameStrings);
|
||||
} catch (NoSuchFieldException e) {
|
||||
|
@@ -39,7 +39,6 @@ import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonWriter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.internal.Util
|
||||
import com.squareup.moshi.kotlin.codegen.api.FromJsonComponent.ParameterOnly
|
||||
import com.squareup.moshi.kotlin.codegen.api.FromJsonComponent.ParameterProperty
|
||||
import com.squareup.moshi.kotlin.codegen.api.FromJsonComponent.PropertyOnly
|
||||
@@ -47,7 +46,7 @@ import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Type
|
||||
import org.objectweb.asm.Type as AsmType
|
||||
|
||||
private val MOSHI_UTIL = Util::class.asClassName()
|
||||
private const val MOSHI_UTIL_PACKAGE = "com.squareup.moshi.internal"
|
||||
private const val TO_STRING_PREFIX = "GeneratedJsonAdapter("
|
||||
private const val TO_STRING_SIZE_BASE = TO_STRING_PREFIX.length + 1 // 1 is the closing paren
|
||||
|
||||
@@ -61,8 +60,8 @@ public class AdapterGenerator(
|
||||
private companion object {
|
||||
private val INT_TYPE_BLOCK = CodeBlock.of("%T::class.javaPrimitiveType", INT)
|
||||
private val DEFAULT_CONSTRUCTOR_MARKER_TYPE_BLOCK = CodeBlock.of(
|
||||
"%T.DEFAULT_CONSTRUCTOR_MARKER",
|
||||
Util::class
|
||||
"%M",
|
||||
MemberName(MOSHI_UTIL_PACKAGE, "DEFAULT_CONSTRUCTOR_MARKER")
|
||||
)
|
||||
private val CN_MOSHI = Moshi::class.asClassName()
|
||||
private val CN_TYPE = Type::class.asClassName()
|
||||
@@ -656,8 +655,8 @@ public class AdapterGenerator(
|
||||
|
||||
private fun unexpectedNull(property: PropertyGenerator, reader: ParameterSpec): CodeBlock {
|
||||
return CodeBlock.of(
|
||||
"%T.unexpectedNull(%S, %S, %N)",
|
||||
MOSHI_UTIL,
|
||||
"%M(%S, %S, %N)",
|
||||
MemberName(MOSHI_UTIL_PACKAGE, "unexpectedNull"),
|
||||
property.localName,
|
||||
property.jsonName,
|
||||
reader
|
||||
@@ -699,8 +698,8 @@ public class AdapterGenerator(
|
||||
private fun FunSpec.Builder.addMissingPropertyCheck(property: PropertyGenerator, readerParam: ParameterSpec) {
|
||||
val missingPropertyBlock =
|
||||
CodeBlock.of(
|
||||
"%T.missingProperty(%S, %S, %N)",
|
||||
MOSHI_UTIL,
|
||||
"%M(%S, %S, %N)",
|
||||
MemberName(MOSHI_UTIL_PACKAGE, "missingProperty"),
|
||||
property.localName,
|
||||
property.jsonName,
|
||||
readerParam
|
||||
|
@@ -1270,7 +1270,7 @@ class GeneratedAdaptersTest {
|
||||
@Test fun customGenerator_withClassPresent() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val adapter = moshi.adapter<CustomGeneratedClass>()
|
||||
val unwrapped = (adapter as NullSafeJsonAdapter<CustomGeneratedClass>).delegate()
|
||||
val unwrapped = (adapter as NullSafeJsonAdapter<CustomGeneratedClass>).delegate
|
||||
assertThat(unwrapped).isInstanceOf(
|
||||
GeneratedAdaptersTest_CustomGeneratedClassJsonAdapter::class.java
|
||||
)
|
||||
|
@@ -22,9 +22,12 @@ import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonWriter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.squareup.moshi.internal.Util
|
||||
import com.squareup.moshi.internal.Util.generatedAdapter
|
||||
import com.squareup.moshi.internal.Util.resolve
|
||||
import com.squareup.moshi.internal.generatedAdapter
|
||||
import com.squareup.moshi.internal.isPlatformType
|
||||
import com.squareup.moshi.internal.jsonAnnotations
|
||||
import com.squareup.moshi.internal.missingProperty
|
||||
import com.squareup.moshi.internal.resolve
|
||||
import com.squareup.moshi.internal.unexpectedNull
|
||||
import com.squareup.moshi.rawType
|
||||
import java.lang.reflect.Modifier
|
||||
import java.lang.reflect.Type
|
||||
@@ -86,7 +89,7 @@ internal class KotlinJsonAdapter<T>(
|
||||
values[propertyIndex] = binding.adapter.fromJson(reader)
|
||||
|
||||
if (values[propertyIndex] == null && !binding.property.returnType.isMarkedNullable) {
|
||||
throw Util.unexpectedNull(
|
||||
throw unexpectedNull(
|
||||
binding.property.name,
|
||||
binding.jsonName,
|
||||
reader
|
||||
@@ -102,7 +105,7 @@ internal class KotlinJsonAdapter<T>(
|
||||
when {
|
||||
constructor.parameters[i].isOptional -> isFullInitialized = false
|
||||
constructor.parameters[i].type.isMarkedNullable -> values[i] = null // Replace absent with null.
|
||||
else -> throw Util.missingProperty(
|
||||
else -> throw missingProperty(
|
||||
constructor.parameters[i].name,
|
||||
allBindings[i]?.jsonName,
|
||||
reader
|
||||
@@ -195,9 +198,9 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {
|
||||
if (rawType.isInterface) return null
|
||||
if (rawType.isEnum) return null
|
||||
if (!rawType.isAnnotationPresent(KOTLIN_METADATA)) return null
|
||||
if (Util.isPlatformType(rawType)) return null
|
||||
if (rawType.isPlatformType) return null
|
||||
try {
|
||||
val generatedAdapter = generatedAdapter(moshi, type, rawType)
|
||||
val generatedAdapter = moshi.generatedAdapter(type, rawType)
|
||||
if (generatedAdapter != null) {
|
||||
return generatedAdapter
|
||||
}
|
||||
@@ -288,10 +291,10 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {
|
||||
}
|
||||
else -> error("Not possible!")
|
||||
}
|
||||
val resolvedPropertyType = resolve(type, rawType, propertyType)
|
||||
val resolvedPropertyType = propertyType.resolve(type, rawType)
|
||||
val adapter = moshi.adapter<Any?>(
|
||||
resolvedPropertyType,
|
||||
Util.jsonAnnotations(allAnnotations.toTypedArray()),
|
||||
allAnnotations.toTypedArray().jsonAnnotations,
|
||||
property.name
|
||||
)
|
||||
|
||||
|
@@ -52,12 +52,12 @@ tasks.withType<Test>().configureEach {
|
||||
tasks.withType<KotlinCompile>()
|
||||
.configureEach {
|
||||
kotlinOptions {
|
||||
val args = mutableListOf("-Xopt-in=kotlin.contracts.ExperimentalContracts")
|
||||
val toAdd = mutableListOf("-Xopt-in=kotlin.RequiresOptIn", "-Xopt-in=kotlin.contracts.ExperimentalContracts")
|
||||
if (name.contains("test", true)) {
|
||||
args += "-Xopt-in=kotlin.ExperimentalStdlibApi"
|
||||
toAdd += "-Xopt-in=kotlin.ExperimentalStdlibApi"
|
||||
}
|
||||
@Suppress("SuspiciousCollectionReassignment") // It's not suspicious
|
||||
freeCompilerArgs += args
|
||||
freeCompilerArgs += toAdd
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.squareup.moshi
|
||||
|
||||
import com.squareup.moshi.internal.Util
|
||||
import com.squareup.moshi.internal.boxIfPrimitive
|
||||
import java.lang.reflect.GenericArrayType
|
||||
import java.lang.reflect.Type
|
||||
import java.lang.reflect.WildcardType
|
||||
@@ -42,7 +42,7 @@ public inline fun <reified T : Annotation> Set<Annotation>.nextAnnotations(): Se
|
||||
public inline fun <reified T> subtypeOf(): WildcardType {
|
||||
var type = typeOf<T>().javaType
|
||||
if (type is Class<*>) {
|
||||
type = Util.boxIfPrimitive(type)
|
||||
type = type.boxIfPrimitive()
|
||||
}
|
||||
return Types.subtypeOf(type)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public inline fun <reified T> subtypeOf(): WildcardType {
|
||||
public inline fun <reified T> supertypeOf(): WildcardType {
|
||||
var type = typeOf<T>().javaType
|
||||
if (type is Class<*>) {
|
||||
type = Util.boxIfPrimitive(type)
|
||||
type = type.boxIfPrimitive()
|
||||
}
|
||||
return Types.supertypeOf(type)
|
||||
}
|
||||
|
@@ -16,8 +16,8 @@
|
||||
package com.squareup.moshi;
|
||||
|
||||
import static com.squareup.moshi.internal.Util.canonicalize;
|
||||
import static com.squareup.moshi.internal.Util.jsonAnnotations;
|
||||
import static com.squareup.moshi.internal.Util.typeAnnotatedWithAnnotations;
|
||||
import static com.squareup.moshi.internal.Util.getJsonAnnotations;
|
||||
import static com.squareup.moshi.internal.Util.toStringWithAnnotations;
|
||||
|
||||
import com.squareup.moshi.internal.Util;
|
||||
import java.io.IOException;
|
||||
@@ -57,7 +57,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
||||
"No "
|
||||
+ missingAnnotation
|
||||
+ " adapter for "
|
||||
+ typeAnnotatedWithAnnotations(type, annotations),
|
||||
+ toStringWithAnnotations(type, annotations),
|
||||
e);
|
||||
}
|
||||
} else {
|
||||
@@ -172,7 +172,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
||||
&& parametersAreJsonAdapters(2, parameterTypes)) {
|
||||
// void pointToJson(JsonWriter jsonWriter, Point point) {
|
||||
// void pointToJson(JsonWriter jsonWriter, Point point, JsonAdapter<?> adapter, ...) {
|
||||
Set<? extends Annotation> qualifierAnnotations = jsonAnnotations(parameterAnnotations[1]);
|
||||
Set<? extends Annotation> qualifierAnnotations = getJsonAnnotations(parameterAnnotations[1]);
|
||||
return new AdapterMethod(
|
||||
parameterTypes[1],
|
||||
qualifierAnnotations,
|
||||
@@ -190,10 +190,10 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
||||
|
||||
} else if (parameterTypes.length == 1 && returnType != void.class) {
|
||||
// List<Integer> pointToJson(Point point) {
|
||||
final Set<? extends Annotation> returnTypeAnnotations = jsonAnnotations(method);
|
||||
final Set<? extends Annotation> returnTypeAnnotations = Util.getJsonAnnotations(method);
|
||||
final Set<? extends Annotation> qualifierAnnotations =
|
||||
jsonAnnotations(parameterAnnotations[0]);
|
||||
boolean nullable = Util.hasNullable(parameterAnnotations[0]);
|
||||
getJsonAnnotations(parameterAnnotations[0]);
|
||||
boolean nullable = Util.getHasNullable(parameterAnnotations[0]);
|
||||
return new AdapterMethod(
|
||||
parameterTypes[0],
|
||||
qualifierAnnotations,
|
||||
@@ -251,7 +251,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
||||
static AdapterMethod fromAdapter(Object adapter, Method method) {
|
||||
method.setAccessible(true);
|
||||
final Type returnType = method.getGenericReturnType();
|
||||
final Set<? extends Annotation> returnTypeAnnotations = jsonAnnotations(method);
|
||||
final Set<? extends Annotation> returnTypeAnnotations = Util.getJsonAnnotations(method);
|
||||
final Type[] parameterTypes = method.getGenericParameterTypes();
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
|
||||
@@ -273,8 +273,8 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
||||
} else if (parameterTypes.length == 1 && returnType != void.class) {
|
||||
// Point pointFromJson(List<Integer> o) {
|
||||
final Set<? extends Annotation> qualifierAnnotations =
|
||||
jsonAnnotations(parameterAnnotations[0]);
|
||||
boolean nullable = Util.hasNullable(parameterAnnotations[0]);
|
||||
getJsonAnnotations(parameterAnnotations[0]);
|
||||
boolean nullable = Util.getHasNullable(parameterAnnotations[0]);
|
||||
return new AdapterMethod(
|
||||
returnType, returnTypeAnnotations, adapter, method, parameterTypes.length, 1, nullable) {
|
||||
JsonAdapter<Object> delegate;
|
||||
@@ -354,7 +354,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
for (int i = adaptersOffset, size = parameterTypes.length; i < size; i++) {
|
||||
Type type = ((ParameterizedType) parameterTypes[i]).getActualTypeArguments()[0];
|
||||
Set<? extends Annotation> jsonAnnotations = jsonAnnotations(parameterAnnotations[i]);
|
||||
Set<? extends Annotation> jsonAnnotations = getJsonAnnotations(parameterAnnotations[i]);
|
||||
jsonAdapters[i - adaptersOffset] =
|
||||
Types.equals(this.type, type) && annotations.equals(jsonAnnotations)
|
||||
? moshi.nextAdapter(factory, type, jsonAnnotations)
|
||||
|
@@ -139,8 +139,8 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
|
||||
if (jsonAnnotation != null && jsonAnnotation.ignore()) continue;
|
||||
|
||||
// Look up a type adapter for this type.
|
||||
Type fieldType = resolve(type, rawType, field.getGenericType());
|
||||
Set<? extends Annotation> annotations = Util.jsonAnnotations(field);
|
||||
Type fieldType = resolve(field.getGenericType(), type, rawType);
|
||||
Set<? extends Annotation> annotations = Util.getJsonAnnotations(field);
|
||||
String fieldName = field.getName();
|
||||
JsonAdapter<Object> adapter = moshi.adapter(fieldType, annotations, fieldName);
|
||||
|
||||
@@ -148,7 +148,7 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
|
||||
field.setAccessible(true);
|
||||
|
||||
// Store it using the field's name. If there was already a field with this name, fail!
|
||||
String jsonName = jsonName(fieldName, jsonAnnotation);
|
||||
String jsonName = jsonName(jsonAnnotation, fieldName);
|
||||
FieldBinding<Object> fieldBinding = new FieldBinding<>(jsonName, field, adapter);
|
||||
FieldBinding<?> replaced = fieldBindings.put(jsonName, fieldBinding);
|
||||
if (replaced != null) {
|
||||
|
@@ -16,12 +16,12 @@
|
||||
package com.squareup.moshi
|
||||
|
||||
import com.squareup.moshi.Types.createJsonQualifierImplementation
|
||||
import com.squareup.moshi.internal.Util
|
||||
import com.squareup.moshi.internal.Util.canonicalize
|
||||
import com.squareup.moshi.internal.Util.isAnnotationPresent
|
||||
import com.squareup.moshi.internal.Util.removeSubtypeWildcard
|
||||
import com.squareup.moshi.internal.Util.typeAnnotatedWithAnnotations
|
||||
import com.squareup.moshi.internal.Util.typesMatch
|
||||
import com.squareup.moshi.internal.NO_ANNOTATIONS
|
||||
import com.squareup.moshi.internal.canonicalize
|
||||
import com.squareup.moshi.internal.isAnnotationPresent
|
||||
import com.squareup.moshi.internal.removeSubtypeWildcard
|
||||
import com.squareup.moshi.internal.toStringWithAnnotations
|
||||
import com.squareup.moshi.internal.typesMatch
|
||||
import java.lang.reflect.Type
|
||||
import javax.annotation.CheckReturnValue
|
||||
|
||||
@@ -42,10 +42,10 @@ public class Moshi internal constructor(builder: Builder) {
|
||||
|
||||
/** Returns a JSON adapter for `type`, creating it if necessary. */
|
||||
@CheckReturnValue
|
||||
public fun <T> adapter(type: Type): JsonAdapter<T> = adapter(type, Util.NO_ANNOTATIONS)
|
||||
public fun <T> adapter(type: Type): JsonAdapter<T> = adapter(type, NO_ANNOTATIONS)
|
||||
|
||||
@CheckReturnValue
|
||||
public fun <T> adapter(type: Class<T>): JsonAdapter<T> = adapter(type, Util.NO_ANNOTATIONS)
|
||||
public fun <T> adapter(type: Class<T>): JsonAdapter<T> = adapter(type, NO_ANNOTATIONS)
|
||||
|
||||
@CheckReturnValue
|
||||
public fun <T> adapter(type: Type, annotationType: Class<out Annotation>): JsonAdapter<T> =
|
||||
@@ -78,7 +78,7 @@ public class Moshi internal constructor(builder: Builder) {
|
||||
annotations: Set<Annotation>,
|
||||
fieldName: String?
|
||||
): JsonAdapter<T> {
|
||||
val cleanedType = removeSubtypeWildcard(canonicalize(type))
|
||||
val cleanedType = type.canonicalize().removeSubtypeWildcard()
|
||||
|
||||
// If there's an equivalent adapter in the cache, we're done!
|
||||
val cacheKey = cacheKey(cleanedType, annotations)
|
||||
@@ -107,7 +107,7 @@ public class Moshi internal constructor(builder: Builder) {
|
||||
success = true
|
||||
return result
|
||||
}
|
||||
throw IllegalArgumentException("No JsonAdapter for ${typeAnnotatedWithAnnotations(type, annotations)}")
|
||||
throw IllegalArgumentException("No JsonAdapter for ${type.toStringWithAnnotations(annotations)}")
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw lookupChain.exceptionWithLookupStack(e)
|
||||
} finally {
|
||||
@@ -121,7 +121,7 @@ public class Moshi internal constructor(builder: Builder) {
|
||||
type: Type,
|
||||
annotations: Set<Annotation>
|
||||
): JsonAdapter<T> {
|
||||
val cleanedType = removeSubtypeWildcard(canonicalize(type))
|
||||
val cleanedType = type.canonicalize().removeSubtypeWildcard()
|
||||
val skipPastIndex = factories.indexOf(skipPast)
|
||||
require(skipPastIndex != -1) { "Unable to skip past unknown factory $skipPast" }
|
||||
for (i in (skipPastIndex + 1) until factories.size) {
|
||||
@@ -129,7 +129,7 @@ public class Moshi internal constructor(builder: Builder) {
|
||||
val result = factories[i].create(cleanedType, annotations, this) as JsonAdapter<T>?
|
||||
if (result != null) return result
|
||||
}
|
||||
throw IllegalArgumentException("No next JsonAdapter for ${typeAnnotatedWithAnnotations(cleanedType, annotations)}")
|
||||
throw IllegalArgumentException("No next JsonAdapter for ${cleanedType.toStringWithAnnotations(annotations)}")
|
||||
}
|
||||
|
||||
/** Returns a new builder containing all custom factories used by the current instance. */
|
||||
@@ -354,7 +354,7 @@ public class Moshi internal constructor(builder: Builder) {
|
||||
require(annotation.isAnnotationPresent(JsonQualifier::class.java)) { "$annotation does not have @JsonQualifier" }
|
||||
require(annotation.declaredMethods.isEmpty()) { "Use JsonAdapter.Factory for annotations with elements" }
|
||||
return JsonAdapter.Factory { targetType, annotations, _ ->
|
||||
if (typesMatch(type, targetType) && annotations.size == 1 && isAnnotationPresent(annotations, annotation)) {
|
||||
if (typesMatch(type, targetType) && annotations.size == 1 && annotations.isAnnotationPresent(annotation)) {
|
||||
jsonAdapter
|
||||
} else {
|
||||
null
|
||||
|
@@ -275,7 +275,7 @@ final class StandardJsonAdapters {
|
||||
for (int i = 0; i < constants.length; i++) {
|
||||
T constant = constants[i];
|
||||
String constantName = constant.name();
|
||||
nameStrings[i] = Util.jsonName(constantName, enumType.getField(constantName));
|
||||
nameStrings[i] = Util.jsonName(enumType.getField(constantName), constantName);
|
||||
}
|
||||
options = JsonReader.Options.of(nameStrings);
|
||||
} catch (NoSuchFieldException e) {
|
||||
|
@@ -19,9 +19,9 @@ import static com.squareup.moshi.internal.Util.EMPTY_TYPE_ARRAY;
|
||||
import static com.squareup.moshi.internal.Util.getGenericSupertype;
|
||||
import static com.squareup.moshi.internal.Util.resolve;
|
||||
|
||||
import com.squareup.moshi.internal.Util.GenericArrayTypeImpl;
|
||||
import com.squareup.moshi.internal.Util.ParameterizedTypeImpl;
|
||||
import com.squareup.moshi.internal.Util.WildcardTypeImpl;
|
||||
import com.squareup.moshi.internal.GenericArrayTypeImpl;
|
||||
import com.squareup.moshi.internal.ParameterizedTypeImpl;
|
||||
import com.squareup.moshi.internal.WildcardTypeImpl;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -110,7 +110,7 @@ public final class Types {
|
||||
if (typeArguments.length == 0) {
|
||||
throw new IllegalArgumentException("Missing type arguments for " + rawType);
|
||||
}
|
||||
return new ParameterizedTypeImpl(null, rawType, typeArguments);
|
||||
return ParameterizedTypeImpl.create(null, rawType, typeArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,12 +122,12 @@ public final class Types {
|
||||
if (typeArguments.length == 0) {
|
||||
throw new IllegalArgumentException("Missing type arguments for " + rawType);
|
||||
}
|
||||
return new ParameterizedTypeImpl(ownerType, rawType, typeArguments);
|
||||
return ParameterizedTypeImpl.create(ownerType, rawType, typeArguments);
|
||||
}
|
||||
|
||||
/** Returns an array type whose elements are all instances of {@code componentType}. */
|
||||
public static GenericArrayType arrayOf(Type componentType) {
|
||||
return new GenericArrayTypeImpl(componentType);
|
||||
return GenericArrayTypeImpl.create(componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,7 +143,7 @@ public final class Types {
|
||||
} else {
|
||||
upperBounds = new Type[] {bound};
|
||||
}
|
||||
return new WildcardTypeImpl(upperBounds, EMPTY_TYPE_ARRAY);
|
||||
return WildcardTypeImpl.create(upperBounds, EMPTY_TYPE_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +157,7 @@ public final class Types {
|
||||
} else {
|
||||
lowerBounds = new Type[] {bound};
|
||||
}
|
||||
return new WildcardTypeImpl(new Type[] {Object.class}, lowerBounds);
|
||||
return WildcardTypeImpl.create(new Type[] {Object.class}, lowerBounds);
|
||||
}
|
||||
|
||||
public static Class<?> getRawType(Type type) {
|
||||
@@ -362,12 +362,12 @@ public final class Types {
|
||||
static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype) {
|
||||
if (!supertype.isAssignableFrom(contextRawType)) throw new IllegalArgumentException();
|
||||
return resolve(
|
||||
context, contextRawType, getGenericSupertype(context, contextRawType, supertype));
|
||||
getGenericSupertype(context, contextRawType, supertype), context, contextRawType);
|
||||
}
|
||||
|
||||
static Type getGenericSuperclass(Type type) {
|
||||
Class<?> rawType = Types.getRawType(type);
|
||||
return resolve(type, rawType, rawType.getGenericSuperclass());
|
||||
return resolve(rawType.getGenericSuperclass(), type, rawType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.internal;
|
||||
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.JsonDataException;
|
||||
import com.squareup.moshi.JsonReader;
|
||||
import com.squareup.moshi.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class NonNullJsonAdapter<T> extends JsonAdapter<T> {
|
||||
|
||||
private final JsonAdapter<T> delegate;
|
||||
|
||||
public NonNullJsonAdapter(JsonAdapter<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public JsonAdapter<T> delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T fromJson(JsonReader reader) throws IOException {
|
||||
if (reader.peek() == JsonReader.Token.NULL) {
|
||||
throw new JsonDataException("Unexpected null at " + reader.getPath());
|
||||
} else {
|
||||
return delegate.fromJson(reader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
|
||||
if (value == null) {
|
||||
throw new JsonDataException("Unexpected null at " + writer.getPath());
|
||||
} else {
|
||||
delegate.toJson(writer, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate + ".nonNull()";
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.internal
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonDataException
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonWriter
|
||||
|
||||
public class NonNullJsonAdapter<T>(public val delegate: JsonAdapter<T>) : JsonAdapter<T>() {
|
||||
override fun fromJson(reader: JsonReader): T {
|
||||
return if (reader.peek() == JsonReader.Token.NULL) {
|
||||
throw JsonDataException("Unexpected null at " + reader.path)
|
||||
} else {
|
||||
val result = delegate.fromJson(reader)
|
||||
knownNotNull(result)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
override fun toJson(writer: JsonWriter, value: T?) {
|
||||
if (value == null) {
|
||||
throw JsonDataException("Unexpected null at " + writer.path)
|
||||
} else {
|
||||
delegate.toJson(writer, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = "$delegate.nonNull()"
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.internal;
|
||||
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.JsonReader;
|
||||
import com.squareup.moshi.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class NullSafeJsonAdapter<T> extends JsonAdapter<T> {
|
||||
|
||||
private final JsonAdapter<T> delegate;
|
||||
|
||||
public NullSafeJsonAdapter(JsonAdapter<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public JsonAdapter<T> delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable T fromJson(JsonReader reader) throws IOException {
|
||||
if (reader.peek() == JsonReader.Token.NULL) {
|
||||
return reader.nextNull();
|
||||
} else {
|
||||
return delegate.fromJson(reader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
|
||||
if (value == null) {
|
||||
writer.nullValue();
|
||||
} else {
|
||||
delegate.toJson(writer, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate + ".nullSafe()";
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.internal
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonWriter
|
||||
|
||||
public class NullSafeJsonAdapter<T>(public val delegate: JsonAdapter<T>) : JsonAdapter<T>() {
|
||||
override fun fromJson(reader: JsonReader): T? {
|
||||
return if (reader.peek() == JsonReader.Token.NULL) {
|
||||
reader.nextNull()
|
||||
} else {
|
||||
delegate.fromJson(reader)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toJson(writer: JsonWriter, value: T?) {
|
||||
if (value == null) {
|
||||
writer.nullValue()
|
||||
} else {
|
||||
delegate.toJson(writer, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = "$delegate.nullSafe()"
|
||||
}
|
@@ -1,684 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.internal;
|
||||
|
||||
import static com.squareup.moshi.Types.arrayOf;
|
||||
import static com.squareup.moshi.Types.subtypeOf;
|
||||
import static com.squareup.moshi.Types.supertypeOf;
|
||||
|
||||
import com.squareup.moshi.Json;
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.JsonClass;
|
||||
import com.squareup.moshi.JsonDataException;
|
||||
import com.squareup.moshi.JsonQualifier;
|
||||
import com.squareup.moshi.JsonReader;
|
||||
import com.squareup.moshi.Moshi;
|
||||
import com.squareup.moshi.Types;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.GenericDeclaration;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class Util {
|
||||
public static final Set<Annotation> NO_ANNOTATIONS = Collections.emptySet();
|
||||
public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
|
||||
@Nullable public static final Class<?> DEFAULT_CONSTRUCTOR_MARKER;
|
||||
@Nullable private static final Class<? extends Annotation> METADATA;
|
||||
|
||||
/** A map from primitive types to their corresponding wrapper types. */
|
||||
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE;
|
||||
|
||||
static {
|
||||
Class<? extends Annotation> metadata = null;
|
||||
try {
|
||||
//noinspection unchecked
|
||||
metadata = (Class<? extends Annotation>) Class.forName(getKotlinMetadataClassName());
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
METADATA = metadata;
|
||||
|
||||
// We look up the constructor marker separately because Metadata might be (justifiably)
|
||||
// stripped by R8/Proguard but the DefaultConstructorMarker is still present.
|
||||
Class<?> defaultConstructorMarker = null;
|
||||
try {
|
||||
defaultConstructorMarker = Class.forName("kotlin.jvm.internal.DefaultConstructorMarker");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
DEFAULT_CONSTRUCTOR_MARKER = defaultConstructorMarker;
|
||||
|
||||
Map<Class<?>, Class<?>> primToWrap = new LinkedHashMap<>(16);
|
||||
|
||||
primToWrap.put(boolean.class, Boolean.class);
|
||||
primToWrap.put(byte.class, Byte.class);
|
||||
primToWrap.put(char.class, Character.class);
|
||||
primToWrap.put(double.class, Double.class);
|
||||
primToWrap.put(float.class, Float.class);
|
||||
primToWrap.put(int.class, Integer.class);
|
||||
primToWrap.put(long.class, Long.class);
|
||||
primToWrap.put(short.class, Short.class);
|
||||
primToWrap.put(void.class, Void.class);
|
||||
|
||||
PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap);
|
||||
}
|
||||
|
||||
// Extracted as a method with a keep rule to prevent R8 from keeping Kotlin Metada
|
||||
private static String getKotlinMetadataClassName() {
|
||||
return "kotlin.Metadata";
|
||||
}
|
||||
|
||||
private Util() {}
|
||||
|
||||
public static String jsonName(String declaredName, AnnotatedElement element) {
|
||||
return jsonName(declaredName, element.getAnnotation(Json.class));
|
||||
}
|
||||
|
||||
public static String jsonName(String declaredName, @Nullable Json annotation) {
|
||||
if (annotation == null) return declaredName;
|
||||
String annotationName = annotation.name();
|
||||
return Json.UNSET_NAME.equals(annotationName) ? declaredName : annotationName;
|
||||
}
|
||||
|
||||
public static boolean typesMatch(Type pattern, Type candidate) {
|
||||
// TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
|
||||
return Types.equals(pattern, candidate);
|
||||
}
|
||||
|
||||
public static Set<? extends Annotation> jsonAnnotations(AnnotatedElement annotatedElement) {
|
||||
return jsonAnnotations(annotatedElement.getAnnotations());
|
||||
}
|
||||
|
||||
public static Set<? extends Annotation> jsonAnnotations(Annotation[] annotations) {
|
||||
Set<Annotation> result = null;
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation.annotationType().isAnnotationPresent(JsonQualifier.class)) {
|
||||
if (result == null) result = new LinkedHashSet<>();
|
||||
result.add(annotation);
|
||||
}
|
||||
}
|
||||
return result != null ? Collections.unmodifiableSet(result) : Util.NO_ANNOTATIONS;
|
||||
}
|
||||
|
||||
public static boolean isAnnotationPresent(
|
||||
Set<? extends Annotation> annotations, Class<? extends Annotation> annotationClass) {
|
||||
if (annotations.isEmpty()) return false; // Save an iterator in the common case.
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation.annotationType() == annotationClass) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if {@code annotations} has any annotation whose simple name is Nullable. */
|
||||
public static boolean hasNullable(Annotation[] annotations) {
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation.annotationType().getSimpleName().equals("Nullable")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if {@code rawType} is built in. We don't reflect on private fields of platform
|
||||
* types because they're unspecified and likely to be different on Java vs. Android.
|
||||
*/
|
||||
public static boolean isPlatformType(Class<?> rawType) {
|
||||
String name = rawType.getName();
|
||||
return name.startsWith("android.")
|
||||
|| name.startsWith("androidx.")
|
||||
|| name.startsWith("java.")
|
||||
|| name.startsWith("javax.")
|
||||
|| name.startsWith("kotlin.")
|
||||
|| name.startsWith("kotlinx.")
|
||||
|| name.startsWith("scala.");
|
||||
}
|
||||
|
||||
/** Throws the cause of {@code e}, wrapping it if it is checked. */
|
||||
public static RuntimeException rethrowCause(InvocationTargetException e) {
|
||||
Throwable cause = e.getTargetException();
|
||||
if (cause instanceof RuntimeException) throw (RuntimeException) cause;
|
||||
if (cause instanceof Error) throw (Error) cause;
|
||||
throw new RuntimeException(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a type that is functionally equal but not necessarily equal according to {@link
|
||||
* Object#equals(Object) Object.equals()}.
|
||||
*/
|
||||
public static Type canonicalize(Type type) {
|
||||
if (type instanceof Class) {
|
||||
Class<?> c = (Class<?>) type;
|
||||
return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
|
||||
|
||||
} else if (type instanceof ParameterizedType) {
|
||||
if (type instanceof ParameterizedTypeImpl) return type;
|
||||
ParameterizedType p = (ParameterizedType) type;
|
||||
return new ParameterizedTypeImpl(
|
||||
p.getOwnerType(), p.getRawType(), p.getActualTypeArguments());
|
||||
|
||||
} else if (type instanceof GenericArrayType) {
|
||||
if (type instanceof GenericArrayTypeImpl) return type;
|
||||
GenericArrayType g = (GenericArrayType) type;
|
||||
return new GenericArrayTypeImpl(g.getGenericComponentType());
|
||||
|
||||
} else if (type instanceof WildcardType) {
|
||||
if (type instanceof WildcardTypeImpl) return type;
|
||||
WildcardType w = (WildcardType) type;
|
||||
return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
|
||||
|
||||
} else {
|
||||
return type; // This type is unsupported!
|
||||
}
|
||||
}
|
||||
|
||||
/** If type is a "? extends X" wildcard, returns X; otherwise returns type unchanged. */
|
||||
public static Type removeSubtypeWildcard(Type type) {
|
||||
if (!(type instanceof WildcardType)) return type;
|
||||
|
||||
Type[] lowerBounds = ((WildcardType) type).getLowerBounds();
|
||||
if (lowerBounds.length != 0) return type;
|
||||
|
||||
Type[] upperBounds = ((WildcardType) type).getUpperBounds();
|
||||
if (upperBounds.length != 1) throw new IllegalArgumentException();
|
||||
|
||||
return upperBounds[0];
|
||||
}
|
||||
|
||||
public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
|
||||
return resolve(context, contextRawType, toResolve, new LinkedHashSet<TypeVariable<?>>());
|
||||
}
|
||||
|
||||
private static Type resolve(
|
||||
Type context,
|
||||
Class<?> contextRawType,
|
||||
Type toResolve,
|
||||
Collection<TypeVariable<?>> visitedTypeVariables) {
|
||||
// This implementation is made a little more complicated in an attempt to avoid object-creation.
|
||||
while (true) {
|
||||
if (toResolve instanceof TypeVariable) {
|
||||
TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
|
||||
if (visitedTypeVariables.contains(typeVariable)) {
|
||||
// cannot reduce due to infinite recursion
|
||||
return toResolve;
|
||||
} else {
|
||||
visitedTypeVariables.add(typeVariable);
|
||||
}
|
||||
toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
|
||||
if (toResolve == typeVariable) return toResolve;
|
||||
|
||||
} else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) {
|
||||
Class<?> original = (Class<?>) toResolve;
|
||||
Type componentType = original.getComponentType();
|
||||
Type newComponentType =
|
||||
resolve(context, contextRawType, componentType, visitedTypeVariables);
|
||||
return componentType == newComponentType ? original : arrayOf(newComponentType);
|
||||
|
||||
} else if (toResolve instanceof GenericArrayType) {
|
||||
GenericArrayType original = (GenericArrayType) toResolve;
|
||||
Type componentType = original.getGenericComponentType();
|
||||
Type newComponentType =
|
||||
resolve(context, contextRawType, componentType, visitedTypeVariables);
|
||||
return componentType == newComponentType ? original : arrayOf(newComponentType);
|
||||
|
||||
} else if (toResolve instanceof ParameterizedType) {
|
||||
ParameterizedType original = (ParameterizedType) toResolve;
|
||||
Type ownerType = original.getOwnerType();
|
||||
Type newOwnerType = resolve(context, contextRawType, ownerType, visitedTypeVariables);
|
||||
boolean changed = newOwnerType != ownerType;
|
||||
|
||||
Type[] args = original.getActualTypeArguments();
|
||||
for (int t = 0, length = args.length; t < length; t++) {
|
||||
Type resolvedTypeArgument =
|
||||
resolve(context, contextRawType, args[t], visitedTypeVariables);
|
||||
if (resolvedTypeArgument != args[t]) {
|
||||
if (!changed) {
|
||||
args = args.clone();
|
||||
changed = true;
|
||||
}
|
||||
args[t] = resolvedTypeArgument;
|
||||
}
|
||||
}
|
||||
|
||||
return changed
|
||||
? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args)
|
||||
: original;
|
||||
|
||||
} else if (toResolve instanceof WildcardType) {
|
||||
WildcardType original = (WildcardType) toResolve;
|
||||
Type[] originalLowerBound = original.getLowerBounds();
|
||||
Type[] originalUpperBound = original.getUpperBounds();
|
||||
|
||||
if (originalLowerBound.length == 1) {
|
||||
Type lowerBound =
|
||||
resolve(context, contextRawType, originalLowerBound[0], visitedTypeVariables);
|
||||
if (lowerBound != originalLowerBound[0]) {
|
||||
return supertypeOf(lowerBound);
|
||||
}
|
||||
} else if (originalUpperBound.length == 1) {
|
||||
Type upperBound =
|
||||
resolve(context, contextRawType, originalUpperBound[0], visitedTypeVariables);
|
||||
if (upperBound != originalUpperBound[0]) {
|
||||
return subtypeOf(upperBound);
|
||||
}
|
||||
}
|
||||
return original;
|
||||
|
||||
} else {
|
||||
return toResolve;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
|
||||
Class<?> declaredByRaw = declaringClassOf(unknown);
|
||||
|
||||
// We can't reduce this further.
|
||||
if (declaredByRaw == null) return unknown;
|
||||
|
||||
Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw);
|
||||
if (declaredBy instanceof ParameterizedType) {
|
||||
int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
|
||||
return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
|
||||
}
|
||||
|
||||
return unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the generic supertype for {@code supertype}. For example, given a class {@code
|
||||
* IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the
|
||||
* result when the supertype is {@code Collection.class} is {@code Collection<Integer>}.
|
||||
*/
|
||||
public static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve) {
|
||||
if (toResolve == rawType) {
|
||||
return context;
|
||||
}
|
||||
|
||||
// we skip searching through interfaces if unknown is an interface
|
||||
if (toResolve.isInterface()) {
|
||||
Class<?>[] interfaces = rawType.getInterfaces();
|
||||
for (int i = 0, length = interfaces.length; i < length; i++) {
|
||||
if (interfaces[i] == toResolve) {
|
||||
return rawType.getGenericInterfaces()[i];
|
||||
} else if (toResolve.isAssignableFrom(interfaces[i])) {
|
||||
return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check our supertypes
|
||||
if (!rawType.isInterface()) {
|
||||
while (rawType != Object.class) {
|
||||
Class<?> rawSupertype = rawType.getSuperclass();
|
||||
if (rawSupertype == toResolve) {
|
||||
return rawType.getGenericSuperclass();
|
||||
} else if (toResolve.isAssignableFrom(rawSupertype)) {
|
||||
return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
|
||||
}
|
||||
rawType = rawSupertype;
|
||||
}
|
||||
}
|
||||
|
||||
// we can't resolve this further
|
||||
return toResolve;
|
||||
}
|
||||
|
||||
static int hashCodeOrZero(@Nullable Object o) {
|
||||
return o != null ? o.hashCode() : 0;
|
||||
}
|
||||
|
||||
static String typeToString(Type type) {
|
||||
return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
|
||||
}
|
||||
|
||||
static int indexOf(Object[] array, Object toFind) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (toFind.equals(array[i])) return i;
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
|
||||
* a class.
|
||||
*/
|
||||
static @Nullable Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
|
||||
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
|
||||
return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
|
||||
}
|
||||
|
||||
static void checkNotPrimitive(Type type) {
|
||||
if ((type instanceof Class<?>) && ((Class<?>) type).isPrimitive()) {
|
||||
throw new IllegalArgumentException("Unexpected primitive " + type + ". Use the boxed type.");
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ParameterizedTypeImpl implements ParameterizedType {
|
||||
private final @Nullable Type ownerType;
|
||||
private final Type rawType;
|
||||
public final Type[] typeArguments;
|
||||
|
||||
public ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type... typeArguments) {
|
||||
// Require an owner type if the raw type needs it.
|
||||
if (rawType instanceof Class<?>) {
|
||||
Class<?> enclosingClass = ((Class<?>) rawType).getEnclosingClass();
|
||||
if (ownerType != null) {
|
||||
if (enclosingClass == null || Types.getRawType(ownerType) != enclosingClass) {
|
||||
throw new IllegalArgumentException(
|
||||
"unexpected owner type for " + rawType + ": " + ownerType);
|
||||
}
|
||||
} else if (enclosingClass != null) {
|
||||
throw new IllegalArgumentException("unexpected owner type for " + rawType + ": null");
|
||||
}
|
||||
}
|
||||
|
||||
this.ownerType = ownerType == null ? null : canonicalize(ownerType);
|
||||
this.rawType = canonicalize(rawType);
|
||||
this.typeArguments = typeArguments.clone();
|
||||
for (int t = 0; t < this.typeArguments.length; t++) {
|
||||
if (this.typeArguments[t] == null) throw new NullPointerException();
|
||||
checkNotPrimitive(this.typeArguments[t]);
|
||||
this.typeArguments[t] = canonicalize(this.typeArguments[t]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getActualTypeArguments() {
|
||||
return typeArguments.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getRawType() {
|
||||
return rawType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Type getOwnerType() {
|
||||
return ownerType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof ParameterizedType && Types.equals(this, (ParameterizedType) other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1));
|
||||
result.append(typeToString(rawType));
|
||||
|
||||
if (typeArguments.length == 0) {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
result.append("<").append(typeToString(typeArguments[0]));
|
||||
for (int i = 1; i < typeArguments.length; i++) {
|
||||
result.append(", ").append(typeToString(typeArguments[i]));
|
||||
}
|
||||
return result.append(">").toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class GenericArrayTypeImpl implements GenericArrayType {
|
||||
private final Type componentType;
|
||||
|
||||
public GenericArrayTypeImpl(Type componentType) {
|
||||
this.componentType = canonicalize(componentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getGenericComponentType() {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof GenericArrayType && Types.equals(this, (GenericArrayType) o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return componentType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return typeToString(componentType) + "[]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only
|
||||
* support what the Java 6 language needs - at most one bound. If a lower bound is set, the upper
|
||||
* bound must be Object.class.
|
||||
*/
|
||||
public static final class WildcardTypeImpl implements WildcardType {
|
||||
private final Type upperBound;
|
||||
private final @Nullable Type lowerBound;
|
||||
|
||||
public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
|
||||
if (lowerBounds.length > 1) throw new IllegalArgumentException();
|
||||
if (upperBounds.length != 1) throw new IllegalArgumentException();
|
||||
|
||||
if (lowerBounds.length == 1) {
|
||||
if (lowerBounds[0] == null) throw new NullPointerException();
|
||||
checkNotPrimitive(lowerBounds[0]);
|
||||
if (upperBounds[0] != Object.class) throw new IllegalArgumentException();
|
||||
this.lowerBound = canonicalize(lowerBounds[0]);
|
||||
this.upperBound = Object.class;
|
||||
|
||||
} else {
|
||||
if (upperBounds[0] == null) throw new NullPointerException();
|
||||
checkNotPrimitive(upperBounds[0]);
|
||||
this.lowerBound = null;
|
||||
this.upperBound = canonicalize(upperBounds[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getUpperBounds() {
|
||||
return new Type[] {upperBound};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getLowerBounds() {
|
||||
return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof WildcardType && Types.equals(this, (WildcardType) other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// This equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()).
|
||||
return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (lowerBound != null) {
|
||||
return "? super " + typeToString(lowerBound);
|
||||
} else if (upperBound == Object.class) {
|
||||
return "?";
|
||||
} else {
|
||||
return "? extends " + typeToString(upperBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String typeAnnotatedWithAnnotations(
|
||||
Type type, Set<? extends Annotation> annotations) {
|
||||
return type + (annotations.isEmpty() ? " (with no annotations)" : " annotated " + annotations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the generated JsonAdapter for classes annotated {@link JsonClass}. This works because it
|
||||
* uses the same naming conventions as {@code JsonClassCodeGenProcessor}.
|
||||
*/
|
||||
public static @Nullable JsonAdapter<?> generatedAdapter(
|
||||
Moshi moshi, Type type, Class<?> rawType) {
|
||||
JsonClass jsonClass = rawType.getAnnotation(JsonClass.class);
|
||||
if (jsonClass == null || !jsonClass.generateAdapter()) {
|
||||
return null;
|
||||
}
|
||||
String adapterClassName = Types.generatedJsonAdapterName(rawType.getName());
|
||||
Class<? extends JsonAdapter<?>> adapterClass = null;
|
||||
try {
|
||||
//noinspection unchecked - We generate types to match.
|
||||
adapterClass =
|
||||
(Class<? extends JsonAdapter<?>>)
|
||||
Class.forName(adapterClassName, true, rawType.getClassLoader());
|
||||
Constructor<? extends JsonAdapter<?>> constructor;
|
||||
Object[] args;
|
||||
if (type instanceof ParameterizedType) {
|
||||
Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments();
|
||||
try {
|
||||
// Common case first
|
||||
constructor = adapterClass.getDeclaredConstructor(Moshi.class, Type[].class);
|
||||
args = new Object[] {moshi, typeArgs};
|
||||
} catch (NoSuchMethodException e) {
|
||||
constructor = adapterClass.getDeclaredConstructor(Type[].class);
|
||||
args = new Object[] {typeArgs};
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
// Common case first
|
||||
constructor = adapterClass.getDeclaredConstructor(Moshi.class);
|
||||
args = new Object[] {moshi};
|
||||
} catch (NoSuchMethodException e) {
|
||||
constructor = adapterClass.getDeclaredConstructor();
|
||||
args = new Object[0];
|
||||
}
|
||||
}
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance(args).nullSafe();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Failed to find the generated JsonAdapter class for " + type, e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
if (!(type instanceof ParameterizedType) && adapterClass.getTypeParameters().length != 0) {
|
||||
throw new RuntimeException(
|
||||
"Failed to find the generated JsonAdapter constructor for '"
|
||||
+ type
|
||||
+ "'. Suspiciously, the type was not parameterized but the target class '"
|
||||
+ adapterClass.getCanonicalName()
|
||||
+ "' is generic. Consider using "
|
||||
+ "Types#newParameterizedType() to define these missing type variables.",
|
||||
e);
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Failed to find the generated JsonAdapter constructor for " + type, e);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Failed to access the generated JsonAdapter for " + type, e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException("Failed to instantiate the generated JsonAdapter for " + type, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw rethrowCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isKotlin(Class<?> targetClass) {
|
||||
return METADATA != null && targetClass.isAnnotationPresent(METADATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflectively looks up the defaults constructor of a kotlin class.
|
||||
*
|
||||
* @param targetClass the target kotlin class to instantiate.
|
||||
* @param <T> the type of {@code targetClass}.
|
||||
* @return the instantiated {@code targetClass} instance.
|
||||
*/
|
||||
public static <T> Constructor<T> lookupDefaultsConstructor(Class<T> targetClass) {
|
||||
if (DEFAULT_CONSTRUCTOR_MARKER == null) {
|
||||
throw new IllegalStateException(
|
||||
"DefaultConstructorMarker not on classpath. Make sure the "
|
||||
+ "Kotlin stdlib is on the classpath.");
|
||||
}
|
||||
Constructor<T> defaultConstructor = findConstructor(targetClass);
|
||||
defaultConstructor.setAccessible(true);
|
||||
return defaultConstructor;
|
||||
}
|
||||
|
||||
private static <T> Constructor<T> findConstructor(Class<T> targetClass) {
|
||||
for (Constructor<?> constructor : targetClass.getDeclaredConstructors()) {
|
||||
Class<?>[] paramTypes = constructor.getParameterTypes();
|
||||
if (paramTypes.length != 0
|
||||
&& paramTypes[paramTypes.length - 1].equals(DEFAULT_CONSTRUCTOR_MARKER)) {
|
||||
//noinspection unchecked
|
||||
return (Constructor<T>) constructor;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("No defaults constructor found for " + targetClass);
|
||||
}
|
||||
|
||||
public static JsonDataException missingProperty(
|
||||
String propertyName, String jsonName, JsonReader reader) {
|
||||
String path = reader.getPath();
|
||||
String message;
|
||||
if (jsonName.equals(propertyName)) {
|
||||
message = String.format("Required value '%s' missing at %s", propertyName, path);
|
||||
} else {
|
||||
message =
|
||||
String.format(
|
||||
"Required value '%s' (JSON name '%s') missing at %s", propertyName, jsonName, path);
|
||||
}
|
||||
return new JsonDataException(message);
|
||||
}
|
||||
|
||||
public static JsonDataException unexpectedNull(
|
||||
String propertyName, String jsonName, JsonReader reader) {
|
||||
String path = reader.getPath();
|
||||
String message;
|
||||
if (jsonName.equals(propertyName)) {
|
||||
message = String.format("Non-null value '%s' was null at %s", propertyName, path);
|
||||
} else {
|
||||
message =
|
||||
String.format(
|
||||
"Non-null value '%s' (JSON name '%s') was null at %s", propertyName, jsonName, path);
|
||||
}
|
||||
return new JsonDataException(message);
|
||||
}
|
||||
|
||||
// Public due to inline access in MoshiKotlinTypesExtensions
|
||||
public static <T> Class<T> boxIfPrimitive(Class<T> type) {
|
||||
// cast is safe: long.class and Long.class are both of type Class<Long>
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<T> wrapped = (Class<T>) PRIMITIVE_TO_WRAPPER_TYPE.get(type);
|
||||
return (wrapped == null) ? type : wrapped;
|
||||
}
|
||||
}
|
632
moshi/src/main/java/com/squareup/moshi/internal/Util.kt
Normal file
632
moshi/src/main/java/com/squareup/moshi/internal/Util.kt
Normal file
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@file:JvmName("Util")
|
||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
package com.squareup.moshi.internal
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.JsonDataException
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.squareup.moshi.asArrayType
|
||||
import com.squareup.moshi.rawType
|
||||
import java.lang.ClassNotFoundException
|
||||
import java.lang.Error
|
||||
import java.lang.IllegalAccessException
|
||||
import java.lang.IllegalStateException
|
||||
import java.lang.InstantiationException
|
||||
import java.lang.NoSuchMethodException
|
||||
import java.lang.RuntimeException
|
||||
import java.lang.StringBuilder
|
||||
import java.lang.Void
|
||||
import java.lang.reflect.AnnotatedElement
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.GenericArrayType
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
import java.lang.reflect.TypeVariable
|
||||
import java.lang.reflect.WildcardType
|
||||
import java.util.Collections
|
||||
import java.util.LinkedHashSet
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@JvmField public val NO_ANNOTATIONS: Set<Annotation> = emptySet()
|
||||
@JvmField public val EMPTY_TYPE_ARRAY: Array<Type> = arrayOf()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val METADATA: Class<out Annotation>? = try {
|
||||
Class.forName(kotlinMetadataClassName) as Class<out Annotation>
|
||||
} catch (ignored: ClassNotFoundException) {
|
||||
null
|
||||
}
|
||||
|
||||
// We look up the constructor marker separately because Metadata might be (justifiably)
|
||||
// stripped by R8/Proguard but the DefaultConstructorMarker is still present.
|
||||
public val DEFAULT_CONSTRUCTOR_MARKER: Class<*>? = try {
|
||||
Class.forName("kotlin.jvm.internal.DefaultConstructorMarker")
|
||||
} catch (ignored: ClassNotFoundException) {
|
||||
null
|
||||
}
|
||||
|
||||
/** A map from primitive types to their corresponding wrapper types. */
|
||||
private val PRIMITIVE_TO_WRAPPER_TYPE: Map<Class<*>, Class<*>> = buildMap(16) {
|
||||
put(Boolean::class.javaPrimitiveType!!, Boolean::class.java)
|
||||
put(Byte::class.javaPrimitiveType!!, Byte::class.java)
|
||||
put(Char::class.javaPrimitiveType!!, Char::class.java)
|
||||
put(Double::class.javaPrimitiveType!!, Double::class.java)
|
||||
put(Float::class.javaPrimitiveType!!, Float::class.java)
|
||||
put(Int::class.javaPrimitiveType!!, Int::class.java)
|
||||
put(Long::class.javaPrimitiveType!!, Long::class.java)
|
||||
put(Short::class.javaPrimitiveType!!, Short::class.java)
|
||||
put(Void.TYPE, Void::class.java)
|
||||
}
|
||||
|
||||
// Extracted as a method with a keep rule to prevent R8 from keeping Kotlin Metadata
|
||||
private val kotlinMetadataClassName: String
|
||||
get() = "kotlin.Metadata"
|
||||
|
||||
public fun AnnotatedElement.jsonName(declaredName: String): String {
|
||||
return getAnnotation(Json::class.java).jsonName(declaredName)
|
||||
}
|
||||
|
||||
public fun Json?.jsonName(declaredName: String): String {
|
||||
if (this == null) return declaredName
|
||||
val annotationName: String = name
|
||||
return if (Json.UNSET_NAME == annotationName) declaredName else annotationName
|
||||
}
|
||||
|
||||
public fun typesMatch(pattern: Type, candidate: Type): Boolean {
|
||||
// TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
|
||||
return Types.equals(pattern, candidate)
|
||||
}
|
||||
|
||||
public val AnnotatedElement.jsonAnnotations: Set<Annotation>
|
||||
get() = annotations.jsonAnnotations
|
||||
|
||||
public val Array<Annotation>.jsonAnnotations: Set<Annotation>
|
||||
get() {
|
||||
var result: MutableSet<Annotation>? = null
|
||||
for (annotation in this) {
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
if ((annotation as java.lang.annotation.Annotation).annotationType()
|
||||
.isAnnotationPresent(JsonQualifier::class.java)
|
||||
) {
|
||||
if (result == null) result = LinkedHashSet()
|
||||
result.add(annotation)
|
||||
}
|
||||
}
|
||||
return if (result != null) Collections.unmodifiableSet(result) else NO_ANNOTATIONS
|
||||
}
|
||||
|
||||
public fun Set<Annotation>.isAnnotationPresent(
|
||||
annotationClass: Class<out Annotation>
|
||||
): Boolean {
|
||||
if (isEmpty()) return false // Save an iterator in the common case.
|
||||
for (annotation in this) {
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
if ((annotation as java.lang.annotation.Annotation).annotationType() == annotationClass) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** Returns true if `annotations` has any annotation whose simple name is Nullable. */
|
||||
public val Array<Annotation>.hasNullable: Boolean
|
||||
get() {
|
||||
for (annotation in this) {
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
if ((annotation as java.lang.annotation.Annotation).annotationType().simpleName == "Nullable") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if `rawType` is built in. We don't reflect on private fields of platform
|
||||
* types because they're unspecified and likely to be different on Java vs. Android.
|
||||
*/
|
||||
public val Class<*>.isPlatformType: Boolean
|
||||
get() {
|
||||
val name = name
|
||||
return (
|
||||
name.startsWith("android.") ||
|
||||
name.startsWith("androidx.") ||
|
||||
name.startsWith("java.") ||
|
||||
name.startsWith("javax.") ||
|
||||
name.startsWith("kotlin.") ||
|
||||
name.startsWith("kotlinx.") ||
|
||||
name.startsWith("scala.")
|
||||
)
|
||||
}
|
||||
|
||||
/** Throws the cause of `e`, wrapping it if it is checked. */
|
||||
public fun InvocationTargetException.rethrowCause(): RuntimeException {
|
||||
val cause = targetException
|
||||
if (cause is RuntimeException) throw cause
|
||||
if (cause is Error) throw cause
|
||||
throw RuntimeException(cause)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a type that is functionally equal but not necessarily equal according to [[Object.equals()]][Object.equals].
|
||||
*/
|
||||
public fun Type.canonicalize(): Type {
|
||||
return when (this) {
|
||||
is Class<*> -> {
|
||||
if (isArray) GenericArrayTypeImpl(this@canonicalize.componentType.canonicalize()) else this
|
||||
}
|
||||
is ParameterizedType -> {
|
||||
if (this is ParameterizedTypeImpl) return this
|
||||
ParameterizedTypeImpl(ownerType, rawType, *actualTypeArguments)
|
||||
}
|
||||
is GenericArrayType -> {
|
||||
if (this is GenericArrayTypeImpl) return this
|
||||
GenericArrayTypeImpl(genericComponentType)
|
||||
}
|
||||
is WildcardType -> {
|
||||
if (this is WildcardTypeImpl) return this
|
||||
WildcardTypeImpl(upperBounds, lowerBounds)
|
||||
}
|
||||
else -> this // This type is unsupported!
|
||||
}
|
||||
}
|
||||
|
||||
/** If type is a "? extends X" wildcard, returns X; otherwise returns type unchanged. */
|
||||
public fun Type.removeSubtypeWildcard(): Type {
|
||||
if (this !is WildcardType) return this
|
||||
val lowerBounds = lowerBounds
|
||||
if (lowerBounds.isNotEmpty()) return this
|
||||
val upperBounds = upperBounds
|
||||
require(upperBounds.size == 1)
|
||||
return upperBounds[0]
|
||||
}
|
||||
|
||||
public fun Type.resolve(context: Type, contextRawType: Class<*>): Type {
|
||||
return this.resolve(context, contextRawType, LinkedHashSet())
|
||||
}
|
||||
|
||||
private fun Type.resolve(
|
||||
context: Type,
|
||||
contextRawType: Class<*>,
|
||||
visitedTypeVariables: MutableCollection<TypeVariable<*>>
|
||||
): Type {
|
||||
// This implementation is made a little more complicated in an attempt to avoid object-creation.
|
||||
var toResolve = this
|
||||
while (true) {
|
||||
when {
|
||||
toResolve is TypeVariable<*> -> {
|
||||
val typeVariable = toResolve
|
||||
if (typeVariable in visitedTypeVariables) {
|
||||
// cannot reduce due to infinite recursion
|
||||
return toResolve
|
||||
} else {
|
||||
visitedTypeVariables += typeVariable
|
||||
}
|
||||
toResolve = resolveTypeVariable(context, contextRawType, typeVariable)
|
||||
if (toResolve === typeVariable) return toResolve
|
||||
}
|
||||
toResolve is Class<*> && toResolve.isArray -> {
|
||||
val original = toResolve
|
||||
val componentType: Type = original.componentType
|
||||
val newComponentType = componentType.resolve(context, contextRawType, visitedTypeVariables)
|
||||
return if (componentType === newComponentType) original else newComponentType.asArrayType()
|
||||
}
|
||||
toResolve is GenericArrayType -> {
|
||||
val original = toResolve
|
||||
val componentType = original.genericComponentType
|
||||
val newComponentType = componentType.resolve(context, contextRawType, visitedTypeVariables)
|
||||
return if (componentType === newComponentType) original else newComponentType.asArrayType()
|
||||
}
|
||||
toResolve is ParameterizedType -> {
|
||||
val original = toResolve
|
||||
val ownerType: Type? = original.ownerType
|
||||
val newOwnerType = ownerType?.let {
|
||||
ownerType.resolve(context, contextRawType, visitedTypeVariables)
|
||||
}
|
||||
var changed = newOwnerType !== ownerType
|
||||
var args = original.actualTypeArguments
|
||||
for (t in args.indices) {
|
||||
val resolvedTypeArgument = args[t].resolve(context, contextRawType, visitedTypeVariables)
|
||||
if (resolvedTypeArgument !== args[t]) {
|
||||
if (!changed) {
|
||||
args = args.clone()
|
||||
changed = true
|
||||
}
|
||||
args[t] = resolvedTypeArgument
|
||||
}
|
||||
}
|
||||
return if (changed) ParameterizedTypeImpl(newOwnerType, original.rawType, *args) else original
|
||||
}
|
||||
toResolve is WildcardType -> {
|
||||
val original = toResolve
|
||||
val originalLowerBound = original.lowerBounds
|
||||
val originalUpperBound = original.upperBounds
|
||||
if (originalLowerBound.size == 1) {
|
||||
val lowerBound = originalLowerBound[0].resolve(context, contextRawType, visitedTypeVariables)
|
||||
if (lowerBound !== originalLowerBound[0]) {
|
||||
return Types.supertypeOf(lowerBound)
|
||||
}
|
||||
} else if (originalUpperBound.size == 1) {
|
||||
val upperBound = originalUpperBound[0].resolve(context, contextRawType, visitedTypeVariables)
|
||||
if (upperBound !== originalUpperBound[0]) {
|
||||
return Types.subtypeOf(upperBound)
|
||||
}
|
||||
}
|
||||
return original
|
||||
}
|
||||
else -> return toResolve
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun resolveTypeVariable(context: Type, contextRawType: Class<*>, unknown: TypeVariable<*>): Type {
|
||||
val declaredByRaw = declaringClassOf(unknown) ?: return unknown
|
||||
|
||||
// We can't reduce this further.
|
||||
val declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw)
|
||||
if (declaredBy is ParameterizedType) {
|
||||
val index = declaredByRaw.typeParameters.indexOf(unknown)
|
||||
return declaredBy.actualTypeArguments[index]
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the generic supertype for `supertype`. For example, given a class `IntegerSet`, the result for when supertype is `Set.class` is `Set<Integer>` and the
|
||||
* result when the supertype is `Collection.class` is `Collection<Integer>`.
|
||||
*/
|
||||
public fun getGenericSupertype(context: Type, rawTypeInitial: Class<*>, toResolve: Class<*>): Type {
|
||||
var rawType = rawTypeInitial
|
||||
if (toResolve == rawType) {
|
||||
return context
|
||||
}
|
||||
|
||||
// we skip searching through interfaces if unknown is an interface
|
||||
if (toResolve.isInterface) {
|
||||
val interfaces = rawType.interfaces
|
||||
for (i in interfaces.indices) {
|
||||
if (interfaces[i] == toResolve) {
|
||||
return rawType.genericInterfaces[i]
|
||||
} else if (toResolve.isAssignableFrom(interfaces[i])) {
|
||||
return getGenericSupertype(rawType.genericInterfaces[i], interfaces[i], toResolve)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check our supertypes
|
||||
if (!rawType.isInterface) {
|
||||
while (rawType != Any::class.java) {
|
||||
val rawSupertype = rawType.superclass
|
||||
if (rawSupertype == toResolve) {
|
||||
return rawType.genericSuperclass
|
||||
} else if (toResolve.isAssignableFrom(rawSupertype)) {
|
||||
return getGenericSupertype(rawType.genericSuperclass, rawSupertype, toResolve)
|
||||
}
|
||||
rawType = rawSupertype
|
||||
}
|
||||
}
|
||||
|
||||
// we can't resolve this further
|
||||
return toResolve
|
||||
}
|
||||
|
||||
public val Any?.hashCodeOrZero: Int
|
||||
get() {
|
||||
return this?.hashCode() ?: 0
|
||||
}
|
||||
|
||||
public fun Type.typeToString(): String {
|
||||
return if (this is Class<*>) name else toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the declaring class of `typeVariable`, or `null` if it was not declared by
|
||||
* a class.
|
||||
*/
|
||||
public fun declaringClassOf(typeVariable: TypeVariable<*>): Class<*>? {
|
||||
val genericDeclaration = typeVariable.genericDeclaration
|
||||
return if (genericDeclaration is Class<*>) genericDeclaration else null
|
||||
}
|
||||
|
||||
public fun Type.checkNotPrimitive() {
|
||||
require(!(this is Class<*> && isPrimitive)) { "Unexpected primitive $this. Use the boxed type." }
|
||||
}
|
||||
|
||||
public fun Type.toStringWithAnnotations(annotations: Set<Annotation>): String {
|
||||
return toString() + if (annotations.isEmpty()) " (with no annotations)" else " annotated $annotations"
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the generated JsonAdapter for classes annotated [JsonClass]. This works because it
|
||||
* uses the same naming conventions as `JsonClassCodeGenProcessor`.
|
||||
*/
|
||||
public fun Moshi.generatedAdapter(
|
||||
type: Type,
|
||||
rawType: Class<*>
|
||||
): JsonAdapter<*>? {
|
||||
val jsonClass = rawType.getAnnotation(JsonClass::class.java)
|
||||
if (jsonClass == null || !jsonClass.generateAdapter) {
|
||||
return null
|
||||
}
|
||||
val adapterClassName = Types.generatedJsonAdapterName(rawType.name)
|
||||
var possiblyFoundAdapter: Class<out JsonAdapter<*>>? = null
|
||||
return try {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val adapterClass = Class.forName(adapterClassName, true, rawType.classLoader) as Class<out JsonAdapter<*>>
|
||||
possiblyFoundAdapter = adapterClass
|
||||
var constructor: Constructor<out JsonAdapter<*>>
|
||||
var args: Array<Any>
|
||||
if (type is ParameterizedType) {
|
||||
val typeArgs = type.actualTypeArguments
|
||||
try {
|
||||
// Common case first
|
||||
constructor = adapterClass.getDeclaredConstructor(Moshi::class.java, Array<Type>::class.java)
|
||||
args = arrayOf(this, typeArgs)
|
||||
} catch (e: NoSuchMethodException) {
|
||||
constructor = adapterClass.getDeclaredConstructor(Array<Type>::class.java)
|
||||
args = arrayOf(typeArgs)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
// Common case first
|
||||
constructor = adapterClass.getDeclaredConstructor(Moshi::class.java)
|
||||
args = arrayOf(this)
|
||||
} catch (e: NoSuchMethodException) {
|
||||
constructor = adapterClass.getDeclaredConstructor()
|
||||
args = emptyArray()
|
||||
}
|
||||
}
|
||||
constructor.isAccessible = true
|
||||
constructor.newInstance(*args).nullSafe()
|
||||
} catch (e: ClassNotFoundException) {
|
||||
throw RuntimeException("Failed to find the generated JsonAdapter class for $type", e)
|
||||
} catch (e: NoSuchMethodException) {
|
||||
if (possiblyFoundAdapter != null && type !is ParameterizedType && possiblyFoundAdapter.typeParameters.isNotEmpty()) {
|
||||
throw RuntimeException(
|
||||
"Failed to find the generated JsonAdapter constructor for '$type'. Suspiciously, the type was not parameterized but the target class '${possiblyFoundAdapter.canonicalName}' is generic. Consider using Types#newParameterizedType() to define these missing type variables.",
|
||||
e
|
||||
)
|
||||
} else {
|
||||
throw RuntimeException(
|
||||
"Failed to find the generated JsonAdapter constructor for $type", e
|
||||
)
|
||||
}
|
||||
} catch (e: IllegalAccessException) {
|
||||
throw RuntimeException("Failed to access the generated JsonAdapter for $type", e)
|
||||
} catch (e: InstantiationException) {
|
||||
throw RuntimeException("Failed to instantiate the generated JsonAdapter for $type", e)
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.rethrowCause()
|
||||
}
|
||||
}
|
||||
|
||||
public val Class<*>.isKotlin: Boolean
|
||||
get() = METADATA != null && isAnnotationPresent(METADATA)
|
||||
|
||||
/**
|
||||
* Reflectively looks up the defaults constructor of a kotlin class.
|
||||
*
|
||||
* @receiver the target kotlin class to instantiate.
|
||||
* @param T the type of `targetClass`.
|
||||
* @return the instantiated `targetClass` instance.
|
||||
*/
|
||||
public fun <T> Class<T>.lookupDefaultsConstructor(): Constructor<T> {
|
||||
checkNotNull(DEFAULT_CONSTRUCTOR_MARKER) {
|
||||
"DefaultConstructorMarker not on classpath. Make sure the Kotlin stdlib is on the classpath."
|
||||
}
|
||||
val defaultConstructor = findConstructor()
|
||||
defaultConstructor.isAccessible = true
|
||||
return defaultConstructor
|
||||
}
|
||||
|
||||
private fun <T> Class<T>.findConstructor(): Constructor<T> {
|
||||
for (constructor in declaredConstructors) {
|
||||
val paramTypes = constructor.parameterTypes
|
||||
if (paramTypes.isNotEmpty() && paramTypes[paramTypes.size - 1] == DEFAULT_CONSTRUCTOR_MARKER) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return constructor as Constructor<T>
|
||||
}
|
||||
}
|
||||
throw IllegalStateException("No defaults constructor found for $this")
|
||||
}
|
||||
|
||||
public fun missingProperty(
|
||||
propertyName: String?,
|
||||
jsonName: String?,
|
||||
reader: JsonReader
|
||||
): JsonDataException {
|
||||
val path = reader.path
|
||||
val message = if (jsonName == propertyName) {
|
||||
"Required value '$propertyName' missing at $path"
|
||||
} else {
|
||||
"Required value '$propertyName' (JSON name '$jsonName') missing at $path"
|
||||
}
|
||||
return JsonDataException(message)
|
||||
}
|
||||
|
||||
public fun unexpectedNull(
|
||||
propertyName: String,
|
||||
jsonName: String,
|
||||
reader: JsonReader
|
||||
): JsonDataException {
|
||||
val path = reader.path
|
||||
val message: String = if (jsonName == propertyName) {
|
||||
"Non-null value '$propertyName' was null at $path"
|
||||
} else {
|
||||
"Non-null value '$propertyName' (JSON name '$jsonName') was null at $path"
|
||||
}
|
||||
return JsonDataException(message)
|
||||
}
|
||||
|
||||
// A sneaky way to mark value as known to be not null, allowing smart casts and skipping the null-check intrinsic
|
||||
// Safe to use here because it's already compiled and not being used externally
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
public inline fun <T : Any> knownNotNull(value: T?) {
|
||||
contract {
|
||||
returns() implies (value != null)
|
||||
}
|
||||
}
|
||||
|
||||
// Public due to inline access in MoshiKotlinTypesExtensions
|
||||
public fun <T> Class<T>.boxIfPrimitive(): Class<T> {
|
||||
// cast is safe: long.class and Long.class are both of type Class<Long>
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val wrapped = PRIMITIVE_TO_WRAPPER_TYPE[this] as Class<T>?
|
||||
return wrapped ?: this
|
||||
}
|
||||
|
||||
internal class ParameterizedTypeImpl private constructor(
|
||||
private val ownerType: Type?,
|
||||
private val rawType: Type,
|
||||
@JvmField
|
||||
val typeArguments: Array<Type>
|
||||
) : ParameterizedType {
|
||||
override fun getActualTypeArguments() = typeArguments.clone()
|
||||
|
||||
override fun getRawType() = rawType
|
||||
|
||||
override fun getOwnerType() = ownerType
|
||||
|
||||
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||
override fun equals(other: Any?) =
|
||||
other is ParameterizedType && Types.equals(this, other as ParameterizedType?)
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return typeArguments.contentHashCode() xor rawType.hashCode() xor ownerType.hashCodeOrZero
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val result = StringBuilder(30 * (typeArguments.size + 1))
|
||||
result.append(rawType.typeToString())
|
||||
if (typeArguments.isEmpty()) {
|
||||
return result.toString()
|
||||
}
|
||||
result.append("<").append(typeArguments[0].typeToString())
|
||||
for (i in 1 until typeArguments.size) {
|
||||
result.append(", ").append(typeArguments[i].typeToString())
|
||||
}
|
||||
return result.append(">").toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmName("create")
|
||||
@JvmStatic
|
||||
operator fun invoke(
|
||||
ownerType: Type?,
|
||||
rawType: Type,
|
||||
vararg typeArguments: Type
|
||||
): ParameterizedTypeImpl {
|
||||
// Require an owner type if the raw type needs it.
|
||||
if (rawType is Class<*>) {
|
||||
val enclosingClass = rawType.enclosingClass
|
||||
if (ownerType != null) {
|
||||
require(!(enclosingClass == null || ownerType.rawType != enclosingClass)) { "unexpected owner type for $rawType: $ownerType" }
|
||||
} else require(enclosingClass == null) { "unexpected owner type for $rawType: null" }
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val finalTypeArgs = typeArguments.clone() as Array<Type>
|
||||
for (t in finalTypeArgs.indices) {
|
||||
finalTypeArgs[t].checkNotPrimitive()
|
||||
finalTypeArgs[t] = finalTypeArgs[t].canonicalize()
|
||||
}
|
||||
return ParameterizedTypeImpl(ownerType?.canonicalize(), rawType.canonicalize(), finalTypeArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class GenericArrayTypeImpl private constructor(private val componentType: Type) : GenericArrayType {
|
||||
override fun getGenericComponentType() = componentType
|
||||
|
||||
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||
override fun equals(other: Any?) =
|
||||
other is GenericArrayType && Types.equals(this, other as GenericArrayType?)
|
||||
|
||||
override fun hashCode() = componentType.hashCode()
|
||||
|
||||
override fun toString() = componentType.typeToString() + "[]"
|
||||
|
||||
companion object {
|
||||
@JvmName("create")
|
||||
@JvmStatic
|
||||
operator fun invoke(componentType: Type): GenericArrayTypeImpl {
|
||||
return GenericArrayTypeImpl(componentType.canonicalize())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only
|
||||
* support what the Java 6 language needs - at most one bound. If a lower bound is set, the upper
|
||||
* bound must be Object.class.
|
||||
*/
|
||||
internal class WildcardTypeImpl private constructor(
|
||||
private val upperBound: Type,
|
||||
private val lowerBound: Type?
|
||||
) : WildcardType {
|
||||
|
||||
override fun getUpperBounds() = arrayOf(upperBound)
|
||||
|
||||
override fun getLowerBounds() = lowerBound?.let { arrayOf(it) } ?: EMPTY_TYPE_ARRAY
|
||||
|
||||
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||
override fun equals(other: Any?) = other is WildcardType && Types.equals(this, other as WildcardType?)
|
||||
|
||||
override fun hashCode(): Int {
|
||||
// This equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()).
|
||||
return (if (lowerBound != null) 31 + lowerBound.hashCode() else 1) xor 31 + upperBound.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return when {
|
||||
lowerBound != null -> "? super ${lowerBound.typeToString()}"
|
||||
upperBound === Any::class.java -> "?"
|
||||
else -> "? extends ${upperBound.typeToString()}"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
operator fun invoke(
|
||||
upperBounds: Array<Type>,
|
||||
lowerBounds: Array<Type>
|
||||
): WildcardTypeImpl {
|
||||
require(lowerBounds.size <= 1)
|
||||
require(upperBounds.size == 1)
|
||||
return if (lowerBounds.size == 1) {
|
||||
lowerBounds[0].checkNotPrimitive()
|
||||
require(!(upperBounds[0] !== Any::class.java))
|
||||
WildcardTypeImpl(
|
||||
lowerBound = lowerBounds[0].canonicalize(),
|
||||
upperBound = Any::class.java
|
||||
)
|
||||
} else {
|
||||
upperBounds[0].checkNotPrimitive()
|
||||
WildcardTypeImpl(
|
||||
lowerBound = null,
|
||||
upperBound = upperBounds[0].canonicalize()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -95,10 +95,10 @@ final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
||||
MethodHandles.Lookup lookup,
|
||||
RecordComponent component) {
|
||||
var componentName = component.getName();
|
||||
var jsonName = Util.jsonName(componentName, component);
|
||||
var jsonName = Util.jsonName(component, componentName);
|
||||
|
||||
var componentType = Util.resolve(type, rawType, component.getGenericType());
|
||||
Set<? extends Annotation> qualifiers = Util.jsonAnnotations(component);
|
||||
var componentType = Util.resolve(component.getGenericType(), type, rawType);
|
||||
Set<? extends Annotation> qualifiers = Util.getJsonAnnotations(component);
|
||||
var adapter = moshi.adapter(componentType, qualifiers);
|
||||
|
||||
MethodHandle accessor;
|
||||
|
@@ -819,7 +819,7 @@ public final class MoshiTest {
|
||||
Moshi moshi = new Moshi.Builder().add(new UppercaseAdapterFactory()).build();
|
||||
|
||||
Field uppercaseString = MoshiTest.class.getDeclaredField("uppercaseString");
|
||||
Set<? extends Annotation> annotations = Util.jsonAnnotations(uppercaseString);
|
||||
Set<? extends Annotation> annotations = Util.getJsonAnnotations(uppercaseString);
|
||||
JsonAdapter<String> adapter = moshi.<String>adapter(String.class, annotations).lenient();
|
||||
assertThat(adapter.toJson("a")).isEqualTo("\"A\"");
|
||||
assertThat(adapter.fromJson("\"b\"")).isEqualTo("B");
|
||||
@@ -869,7 +869,7 @@ public final class MoshiTest {
|
||||
Field uppercaseStringsField = MoshiTest.class.getDeclaredField("uppercaseStrings");
|
||||
try {
|
||||
moshi.adapter(
|
||||
uppercaseStringsField.getGenericType(), Util.jsonAnnotations(uppercaseStringsField));
|
||||
uppercaseStringsField.getGenericType(), Util.getJsonAnnotations(uppercaseStringsField));
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected)
|
||||
@@ -886,7 +886,7 @@ public final class MoshiTest {
|
||||
Field uppercaseStringField = MoshiTest.class.getDeclaredField("uppercaseString");
|
||||
try {
|
||||
moshi.adapter(
|
||||
uppercaseStringField.getGenericType(), Util.jsonAnnotations(uppercaseStringField));
|
||||
uppercaseStringField.getGenericType(), Util.getJsonAnnotations(uppercaseStringField));
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected)
|
||||
|
@@ -23,9 +23,9 @@ import com.squareup.moshi.internal.Util;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test fixes for infinite recursion on {@link Util#resolve(java.lang.reflect.Type, Class,
|
||||
* java.lang.reflect.Type)}, described at <a href="https://github.com/google/gson/issues/440">Issue
|
||||
* #440</a> and similar issues.
|
||||
* Test fixes for infinite recursion on {@link Util#resolve(java.lang.reflect.Type,
|
||||
* java.lang.reflect.Type, Class)}, described at <a
|
||||
* href="https://github.com/google/gson/issues/440">Issue #440</a> and similar issues.
|
||||
*
|
||||
* <p>These tests originally caused {@link StackOverflowError} because of infinite recursion on
|
||||
* attempts to resolve generics on types, with an intermediate types like 'Foo2<? extends ? super
|
||||
|
Reference in New Issue
Block a user