mirror of
https://github.com/fankes/moshi.git
synced 2025-10-20 00:19:21 +08:00
Convert RecordJsonAdapter to Kotlin (#1493)
This commit is contained in:
@@ -2,6 +2,7 @@ import com.vanniktech.maven.publish.JavadocJar.Javadoc
|
|||||||
import com.vanniktech.maven.publish.KotlinJvm
|
import com.vanniktech.maven.publish.KotlinJvm
|
||||||
import com.vanniktech.maven.publish.MavenPublishBaseExtension
|
import com.vanniktech.maven.publish.MavenPublishBaseExtension
|
||||||
import org.gradle.jvm.tasks.Jar
|
import org.gradle.jvm.tasks.Jar
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
@@ -11,20 +12,27 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val mainSourceSet by sourceSets.named("main")
|
val mainSourceSet by sourceSets.named("main")
|
||||||
val java16 by sourceSets.creating {
|
val java16: SourceSet by sourceSets.creating {
|
||||||
java {
|
java {
|
||||||
srcDir("src/main/java16")
|
srcDir("src/main/java16")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named<JavaCompile>("compileJava16Java") {
|
// We use JDK 17 for latest but target 16 for maximum compatibility
|
||||||
// We use JDK 17 for latest but target 16 for maximum compatibility
|
val service = project.extensions.getByType<JavaToolchainService>()
|
||||||
javaCompiler.set(
|
val customLauncher = service.launcherFor {
|
||||||
javaToolchains.compilerFor {
|
languageVersion.set(JavaLanguageVersion.of(17))
|
||||||
languageVersion.set(JavaLanguageVersion.of(17))
|
}
|
||||||
}
|
|
||||||
)
|
tasks.named<KotlinCompile>("compileJava16Kotlin") {
|
||||||
options.release.set(16)
|
kotlinJavaToolchain.toolchain.use(customLauncher)
|
||||||
|
kotlinOptions.jvmTarget = "16"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grant our java16 sources access to internal APIs in the main source set
|
||||||
|
kotlin.target.compilations.run {
|
||||||
|
getByName("java16")
|
||||||
|
.associateWith(getByName(KotlinCompilation.MAIN_COMPILATION_NAME))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package our actual RecordJsonAdapter from java16 sources in and denote it as an MRJAR
|
// Package our actual RecordJsonAdapter from java16 sources in and denote it as an MRJAR
|
||||||
|
@@ -29,7 +29,8 @@ val japicmp = tasks.register<JapicmpTask>("japicmp") {
|
|||||||
"com.squareup.moshi.internal.NonNullJsonAdapter", // Internal.
|
"com.squareup.moshi.internal.NonNullJsonAdapter", // Internal.
|
||||||
"com.squareup.moshi.internal.NullSafeJsonAdapter", // Internal.
|
"com.squareup.moshi.internal.NullSafeJsonAdapter", // Internal.
|
||||||
"com.squareup.moshi.internal.Util", // Internal.
|
"com.squareup.moshi.internal.Util", // Internal.
|
||||||
"com.squareup.moshi.StandardJsonAdapters" // Package-private
|
"com.squareup.moshi.StandardJsonAdapters", // Package-private
|
||||||
|
"com.squareup.moshi.RecordJsonAdapter\$ComponentBinding", // Package-private
|
||||||
)
|
)
|
||||||
methodExcludes = listOf(
|
methodExcludes = listOf(
|
||||||
"com.squareup.moshi.JsonAdapter#indent(java.lang.String)" // Was unintentionally open before
|
"com.squareup.moshi.JsonAdapter#indent(java.lang.String)" // Was unintentionally open before
|
||||||
|
@@ -368,7 +368,7 @@ public class Moshi internal constructor(builder: Builder) {
|
|||||||
add(CollectionJsonAdapter.Factory)
|
add(CollectionJsonAdapter.Factory)
|
||||||
add(MapJsonAdapter.Factory)
|
add(MapJsonAdapter.Factory)
|
||||||
add(ArrayJsonAdapter.FACTORY)
|
add(ArrayJsonAdapter.FACTORY)
|
||||||
add(RecordJsonAdapter.FACTORY)
|
add(RecordJsonAdapter.Factory)
|
||||||
add(ClassJsonAdapter.FACTORY)
|
add(ClassJsonAdapter.FACTORY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2021 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;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is just a simple shim for linking in {@link StandardJsonAdapters} and swapped with a real
|
|
||||||
* implementation in Java 16 via MR Jar.
|
|
||||||
*/
|
|
||||||
final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
|
||||||
|
|
||||||
static final JsonAdapter.Factory FACTORY =
|
|
||||||
new JsonAdapter.Factory() {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public JsonAdapter<?> create(
|
|
||||||
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public T fromJson(JsonReader reader) throws IOException {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
|
32
moshi/src/main/java/com/squareup/moshi/RecordJsonAdapter.kt
Normal file
32
moshi/src/main/java/com/squareup/moshi/RecordJsonAdapter.kt
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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
|
||||||
|
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is just a simple shim for linking in [StandardJsonAdapters] and swapped with a real
|
||||||
|
* implementation in Java 16 via MR Jar.
|
||||||
|
*/
|
||||||
|
internal class RecordJsonAdapter<T> : JsonAdapter<T>() {
|
||||||
|
override fun fromJson(reader: JsonReader) = throw AssertionError()
|
||||||
|
|
||||||
|
override fun toJson(writer: JsonWriter, value: T?) = throw AssertionError()
|
||||||
|
|
||||||
|
companion object Factory : JsonAdapter.Factory {
|
||||||
|
override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? = null
|
||||||
|
}
|
||||||
|
}
|
@@ -1,196 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2021 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;
|
|
||||||
|
|
||||||
import static com.squareup.moshi.internal.Util.rethrowCause;
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
|
||||||
|
|
||||||
import com.squareup.moshi.internal.Util;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
import java.lang.reflect.RecordComponent;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link JsonAdapter} that supports Java {@code record} classes via reflection.
|
|
||||||
*
|
|
||||||
* <p><em>NOTE:</em> Java records require JDK 16 or higher.
|
|
||||||
*/
|
|
||||||
final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
|
||||||
|
|
||||||
static final JsonAdapter.Factory FACTORY =
|
|
||||||
new Factory() {
|
|
||||||
@Override
|
|
||||||
public JsonAdapter<?> create(
|
|
||||||
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
|
|
||||||
if (!annotations.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(type instanceof Class) && !(type instanceof ParameterizedType)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawType = Types.getRawType(type);
|
|
||||||
if (!rawType.isRecord()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var components = rawType.getRecordComponents();
|
|
||||||
var bindings = new LinkedHashMap<String, ComponentBinding<?>>();
|
|
||||||
var componentRawTypes = new Class<?>[components.length];
|
|
||||||
var lookup = MethodHandles.lookup();
|
|
||||||
for (int i = 0, componentsLength = components.length; i < componentsLength; i++) {
|
|
||||||
RecordComponent component = components[i];
|
|
||||||
componentRawTypes[i] = component.getType();
|
|
||||||
ComponentBinding<Object> componentBinding =
|
|
||||||
createComponentBinding(type, rawType, moshi, lookup, component);
|
|
||||||
var replaced = bindings.put(componentBinding.jsonName, componentBinding);
|
|
||||||
if (replaced != null) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Conflicting components:\n"
|
|
||||||
+ " "
|
|
||||||
+ replaced.componentName
|
|
||||||
+ "\n"
|
|
||||||
+ " "
|
|
||||||
+ componentBinding.componentName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodHandle constructor;
|
|
||||||
try {
|
|
||||||
constructor =
|
|
||||||
lookup.findConstructor(rawType, methodType(void.class, componentRawTypes));
|
|
||||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RecordJsonAdapter<>(constructor, rawType.getSimpleName(), bindings).nullSafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ComponentBinding<Object> createComponentBinding(
|
|
||||||
Type type,
|
|
||||||
Class<?> rawType,
|
|
||||||
Moshi moshi,
|
|
||||||
MethodHandles.Lookup lookup,
|
|
||||||
RecordComponent component) {
|
|
||||||
var componentName = component.getName();
|
|
||||||
var jsonName = Util.jsonName(component, componentName);
|
|
||||||
|
|
||||||
var componentType = Util.resolve(component.getGenericType(), type, rawType);
|
|
||||||
Set<? extends Annotation> qualifiers = Util.getJsonAnnotations(component);
|
|
||||||
var adapter = moshi.adapter(componentType, qualifiers);
|
|
||||||
|
|
||||||
MethodHandle accessor;
|
|
||||||
try {
|
|
||||||
accessor = lookup.unreflect(component.getAccessor());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ComponentBinding<>(componentName, jsonName, adapter, accessor);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private record ComponentBinding<T>(
|
|
||||||
String componentName, String jsonName, JsonAdapter<T> adapter, MethodHandle accessor) {}
|
|
||||||
|
|
||||||
private final String targetClass;
|
|
||||||
private final MethodHandle constructor;
|
|
||||||
private final ComponentBinding<Object>[] componentBindingsArray;
|
|
||||||
private final JsonReader.Options options;
|
|
||||||
|
|
||||||
@SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument")
|
|
||||||
public RecordJsonAdapter(
|
|
||||||
MethodHandle constructor,
|
|
||||||
String targetClass,
|
|
||||||
Map<String, ComponentBinding<?>> componentBindings) {
|
|
||||||
this.constructor = constructor;
|
|
||||||
this.targetClass = targetClass;
|
|
||||||
//noinspection unchecked
|
|
||||||
this.componentBindingsArray =
|
|
||||||
componentBindings.values().toArray(new ComponentBinding[componentBindings.size()]);
|
|
||||||
this.options =
|
|
||||||
JsonReader.Options.of(
|
|
||||||
componentBindings.keySet().toArray(new String[componentBindings.size()]));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T fromJson(JsonReader reader) throws IOException {
|
|
||||||
var resultsArray = new Object[componentBindingsArray.length];
|
|
||||||
|
|
||||||
reader.beginObject();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
int index = reader.selectName(options);
|
|
||||||
if (index == -1) {
|
|
||||||
reader.skipName();
|
|
||||||
reader.skipValue();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
resultsArray[index] = componentBindingsArray[index].adapter.fromJson(reader);
|
|
||||||
}
|
|
||||||
reader.endObject();
|
|
||||||
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (T) constructor.invokeWithArguments(resultsArray);
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
throw rethrowCause(e);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
// Don't throw a fatal error if it's just an absent primitive.
|
|
||||||
for (int i = 0, limit = componentBindingsArray.length; i < limit; i++) {
|
|
||||||
if (resultsArray[i] == null
|
|
||||||
&& componentBindingsArray[i].accessor.type().returnType().isPrimitive()) {
|
|
||||||
throw Util.missingProperty(
|
|
||||||
componentBindingsArray[i].componentName, componentBindingsArray[i].jsonName, reader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(JsonWriter writer, T value) throws IOException {
|
|
||||||
writer.beginObject();
|
|
||||||
|
|
||||||
for (var binding : componentBindingsArray) {
|
|
||||||
writer.name(binding.jsonName);
|
|
||||||
Object componentValue;
|
|
||||||
try {
|
|
||||||
componentValue = binding.accessor.invoke(value);
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
throw Util.rethrowCause(e);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
binding.adapter.toJson(writer, componentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.endObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "JsonAdapter(" + targetClass + ")";
|
|
||||||
}
|
|
||||||
}
|
|
176
moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.kt
Normal file
176
moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.kt
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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
|
||||||
|
|
||||||
|
import com.squareup.moshi.internal.jsonAnnotations
|
||||||
|
import com.squareup.moshi.internal.jsonName
|
||||||
|
import com.squareup.moshi.internal.knownNotNull
|
||||||
|
import com.squareup.moshi.internal.missingProperty
|
||||||
|
import com.squareup.moshi.internal.resolve
|
||||||
|
import com.squareup.moshi.internal.rethrowCause
|
||||||
|
import java.lang.invoke.MethodHandle
|
||||||
|
import java.lang.invoke.MethodHandles
|
||||||
|
import java.lang.invoke.MethodType.methodType
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
import java.lang.reflect.RecordComponent
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [JsonAdapter] that supports Java `record` classes via reflection.
|
||||||
|
*
|
||||||
|
* **NOTE:** Java records require JDK 16 or higher.
|
||||||
|
*/
|
||||||
|
internal class RecordJsonAdapter<T>(
|
||||||
|
private val constructor: MethodHandle,
|
||||||
|
private val targetClass: String,
|
||||||
|
componentBindings: Map<String, ComponentBinding<Any>>
|
||||||
|
) : JsonAdapter<T>() {
|
||||||
|
|
||||||
|
data class ComponentBinding<T>(
|
||||||
|
val componentName: String,
|
||||||
|
val jsonName: String,
|
||||||
|
val adapter: JsonAdapter<T>,
|
||||||
|
val accessor: MethodHandle
|
||||||
|
)
|
||||||
|
|
||||||
|
private val componentBindingsArray = componentBindings.values.toTypedArray()
|
||||||
|
private val options = JsonReader.Options.of(*componentBindings.keys.toTypedArray())
|
||||||
|
|
||||||
|
override fun fromJson(reader: JsonReader): T? {
|
||||||
|
val resultsArray = arrayOfNulls<Any>(componentBindingsArray.size)
|
||||||
|
|
||||||
|
reader.beginObject()
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
val index = reader.selectName(options)
|
||||||
|
if (index == -1) {
|
||||||
|
reader.skipName()
|
||||||
|
reader.skipValue()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resultsArray[index] = componentBindingsArray[index].adapter.fromJson(reader)
|
||||||
|
}
|
||||||
|
reader.endObject()
|
||||||
|
|
||||||
|
return try {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
constructor.invokeWithArguments(*resultsArray) as T
|
||||||
|
} catch (e: InvocationTargetException) {
|
||||||
|
throw e.rethrowCause()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
// Don't throw a fatal error if it's just an absent primitive.
|
||||||
|
for (i in componentBindingsArray.indices) {
|
||||||
|
if (resultsArray[i] == null && componentBindingsArray[i].accessor.type().returnType().isPrimitive) {
|
||||||
|
throw missingProperty(
|
||||||
|
propertyName = componentBindingsArray[i].componentName,
|
||||||
|
jsonName = componentBindingsArray[i].jsonName,
|
||||||
|
reader = reader
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw AssertionError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toJson(writer: JsonWriter, value: T?) {
|
||||||
|
writer.beginObject()
|
||||||
|
|
||||||
|
for (binding in componentBindingsArray) {
|
||||||
|
writer.name(binding.jsonName)
|
||||||
|
val componentValue = try {
|
||||||
|
binding.accessor.invoke(value)
|
||||||
|
} catch (e: InvocationTargetException) {
|
||||||
|
throw e.rethrowCause()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
throw AssertionError(e)
|
||||||
|
}
|
||||||
|
binding.adapter.toJson(writer, componentValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.endObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString() = "JsonAdapter($targetClass)"
|
||||||
|
|
||||||
|
companion object Factory : JsonAdapter.Factory {
|
||||||
|
|
||||||
|
private val VOID_CLASS = knownNotNull(Void::class.javaPrimitiveType)
|
||||||
|
|
||||||
|
override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
|
||||||
|
if (annotations.isNotEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type !is Class<*> && type !is ParameterizedType) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val rawType = type.rawType
|
||||||
|
if (!rawType.isRecord) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val components = rawType.recordComponents
|
||||||
|
val bindings = LinkedHashMap<String, ComponentBinding<Any>>()
|
||||||
|
val lookup = MethodHandles.lookup()
|
||||||
|
val componentRawTypes = Array<Class<*>>(components.size) { i ->
|
||||||
|
val component = components[i]
|
||||||
|
val componentBinding =
|
||||||
|
createComponentBinding(type, rawType, moshi, lookup, component)
|
||||||
|
val replaced = bindings.put(componentBinding.jsonName, componentBinding)
|
||||||
|
if (replaced != null) {
|
||||||
|
throw IllegalArgumentException(
|
||||||
|
"Conflicting components:\n ${replaced.componentName}\n ${componentBinding.componentName}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
component.type
|
||||||
|
}
|
||||||
|
|
||||||
|
val constructor = try {
|
||||||
|
lookup.findConstructor(rawType, methodType(VOID_CLASS, componentRawTypes))
|
||||||
|
} catch (e: NoSuchMethodException) {
|
||||||
|
throw AssertionError(e)
|
||||||
|
} catch (e: IllegalAccessException) {
|
||||||
|
throw AssertionError(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecordJsonAdapter<Any>(constructor, rawType.simpleName, bindings).nullSafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createComponentBinding(
|
||||||
|
type: Type,
|
||||||
|
rawType: Class<*>,
|
||||||
|
moshi: Moshi,
|
||||||
|
lookup: MethodHandles.Lookup,
|
||||||
|
component: RecordComponent
|
||||||
|
): ComponentBinding<Any> {
|
||||||
|
val componentName = component.name
|
||||||
|
val jsonName = component.jsonName(componentName)
|
||||||
|
|
||||||
|
val componentType = component.genericType.resolve(type, rawType)
|
||||||
|
val qualifiers = component.jsonAnnotations
|
||||||
|
val adapter = moshi.adapter<Any>(componentType, qualifiers)
|
||||||
|
|
||||||
|
val accessor = try {
|
||||||
|
lookup.unreflect(component.accessor)
|
||||||
|
} catch (e: IllegalAccessException) {
|
||||||
|
throw AssertionError(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ComponentBinding(componentName, jsonName, adapter, accessor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user