mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 16:09:21 +08:00
Convert Collection adapter to Kotlin (#1485)
* Consolidate contract utils * Rename .java to .kt * Convert CollectionJsonAdapter to Kotlin * Lift factory into companion object * Update moshi/src/main/java/com/squareup/moshi/internal/Util.kt
This commit is contained in:
@@ -30,6 +30,9 @@ val japicmp = tasks.register<JapicmpTask>("japicmp") {
|
|||||||
"com.squareup.moshi.internal.NullSafeJsonAdapter", // Internal.
|
"com.squareup.moshi.internal.NullSafeJsonAdapter", // Internal.
|
||||||
"com.squareup.moshi.internal.Util" // Internal.
|
"com.squareup.moshi.internal.Util" // Internal.
|
||||||
)
|
)
|
||||||
|
fieldExcludes = listOf(
|
||||||
|
"com.squareup.moshi.CollectionJsonAdapter#FACTORY" // False-positive, class is not public anyway
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named("check").configure {
|
tasks.named("check").configure {
|
||||||
|
@@ -1,100 +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;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/** Converts collection types to JSON arrays containing their converted contents. */
|
|
||||||
abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAdapter<C> {
|
|
||||||
public static final JsonAdapter.Factory FACTORY =
|
|
||||||
new JsonAdapter.Factory() {
|
|
||||||
@Override
|
|
||||||
public @Nullable JsonAdapter<?> create(
|
|
||||||
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
|
|
||||||
Class<?> rawType = Types.getRawType(type);
|
|
||||||
if (!annotations.isEmpty()) return null;
|
|
||||||
if (rawType == List.class || rawType == Collection.class) {
|
|
||||||
return newArrayListAdapter(type, moshi).nullSafe();
|
|
||||||
} else if (rawType == Set.class) {
|
|
||||||
return newLinkedHashSetAdapter(type, moshi).nullSafe();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final JsonAdapter<T> elementAdapter;
|
|
||||||
|
|
||||||
private CollectionJsonAdapter(JsonAdapter<T> elementAdapter) {
|
|
||||||
this.elementAdapter = elementAdapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> JsonAdapter<Collection<T>> newArrayListAdapter(Type type, Moshi moshi) {
|
|
||||||
Type elementType = Types.collectionElementType(type, Collection.class);
|
|
||||||
JsonAdapter<T> elementAdapter = moshi.adapter(elementType);
|
|
||||||
return new CollectionJsonAdapter<Collection<T>, T>(elementAdapter) {
|
|
||||||
@Override
|
|
||||||
Collection<T> newCollection() {
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> JsonAdapter<Set<T>> newLinkedHashSetAdapter(Type type, Moshi moshi) {
|
|
||||||
Type elementType = Types.collectionElementType(type, Collection.class);
|
|
||||||
JsonAdapter<T> elementAdapter = moshi.adapter(elementType);
|
|
||||||
return new CollectionJsonAdapter<Set<T>, T>(elementAdapter) {
|
|
||||||
@Override
|
|
||||||
Set<T> newCollection() {
|
|
||||||
return new LinkedHashSet<>();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract C newCollection();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public C fromJson(JsonReader reader) throws IOException {
|
|
||||||
C result = newCollection();
|
|
||||||
reader.beginArray();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
result.add(elementAdapter.fromJson(reader));
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(JsonWriter writer, C value) throws IOException {
|
|
||||||
writer.beginArray();
|
|
||||||
for (T element : value) {
|
|
||||||
elementAdapter.toJson(writer, element);
|
|
||||||
}
|
|
||||||
writer.endArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return elementAdapter + ".collection()";
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.squareup.moshi.internal.markNotNull
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
/** Converts collection types to JSON arrays containing their converted contents. */
|
||||||
|
internal abstract class CollectionJsonAdapter<C : MutableCollection<T?>, T> private constructor(
|
||||||
|
private val elementAdapter: JsonAdapter<T>
|
||||||
|
) : JsonAdapter<C>() {
|
||||||
|
|
||||||
|
abstract fun newCollection(): C
|
||||||
|
|
||||||
|
override fun fromJson(reader: JsonReader): C {
|
||||||
|
val result = newCollection()
|
||||||
|
reader.beginArray()
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
result.add(elementAdapter.fromJson(reader))
|
||||||
|
}
|
||||||
|
reader.endArray()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toJson(writer: JsonWriter, value: C?) {
|
||||||
|
markNotNull(value) // Always wrapped in nullSafe()
|
||||||
|
writer.beginArray()
|
||||||
|
for (element in value) {
|
||||||
|
elementAdapter.toJson(writer, element)
|
||||||
|
}
|
||||||
|
writer.endArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString() = "$elementAdapter.collection()"
|
||||||
|
|
||||||
|
companion object Factory : JsonAdapter.Factory {
|
||||||
|
override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
|
||||||
|
if (annotations.isNotEmpty()) return null
|
||||||
|
return when (type.rawType) {
|
||||||
|
List::class.java, Collection::class.java -> {
|
||||||
|
newArrayListAdapter<Any>(type, moshi).nullSafe()
|
||||||
|
}
|
||||||
|
Set::class.java -> {
|
||||||
|
newLinkedHashSetAdapter<Any>(type, moshi).nullSafe()
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> newArrayListAdapter(type: Type, moshi: Moshi): JsonAdapter<MutableCollection<T?>> {
|
||||||
|
val elementType = Types.collectionElementType(type, Collection::class.java)
|
||||||
|
val elementAdapter = moshi.adapter<T>(elementType)
|
||||||
|
return object : CollectionJsonAdapter<MutableCollection<T?>, T>(elementAdapter) {
|
||||||
|
override fun newCollection(): MutableCollection<T?> = ArrayList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> newLinkedHashSetAdapter(type: Type, moshi: Moshi): JsonAdapter<MutableSet<T?>> {
|
||||||
|
val elementType = Types.collectionElementType(type, Collection::class.java)
|
||||||
|
val elementAdapter = moshi.adapter<T>(elementType)
|
||||||
|
return object : CollectionJsonAdapter<MutableSet<T?>, T>(elementAdapter) {
|
||||||
|
override fun newCollection(): MutableSet<T?> = LinkedHashSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.squareup.moshi
|
package com.squareup.moshi
|
||||||
|
|
||||||
|
import com.squareup.moshi.internal.knownNotNull
|
||||||
import okio.Buffer
|
import okio.Buffer
|
||||||
import okio.BufferedSource
|
import okio.BufferedSource
|
||||||
import okio.ByteString
|
import okio.ByteString
|
||||||
@@ -23,7 +24,6 @@ import okio.EOFException
|
|||||||
import okio.IOException
|
import okio.IOException
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import kotlin.contracts.contract
|
|
||||||
|
|
||||||
internal class JsonUtf8Reader : JsonReader {
|
internal class JsonUtf8Reader : JsonReader {
|
||||||
/** The input JSON. */
|
/** The input JSON. */
|
||||||
@@ -1098,19 +1098,3 @@ internal class JsonUtf8Reader : JsonReader {
|
|||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun Byte.asChar(): Char = toInt().toChar()
|
private inline fun Byte.asChar(): Char = toInt().toChar()
|
||||||
|
|
||||||
// Sneaky backdoor way of marking a value as non-null to the compiler and skip the null-check intrinsic.
|
|
||||||
// Safe to use (unstable) contracts since they're gone in the final bytecode
|
|
||||||
// TODO move this to Util.kt after it's migrated to kotlin
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline fun <T> markNotNull(value: T?) {
|
|
||||||
contract {
|
|
||||||
returns() implies (value != null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline fun <T> knownNotNull(value: T?): T {
|
|
||||||
markNotNull(value)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
@@ -330,7 +330,7 @@ public class Moshi internal constructor(builder: Builder) {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val BUILT_IN_FACTORIES: List<JsonAdapter.Factory> = buildList(6) {
|
val BUILT_IN_FACTORIES: List<JsonAdapter.Factory> = buildList(6) {
|
||||||
add(StandardJsonAdapters.FACTORY)
|
add(StandardJsonAdapters.FACTORY)
|
||||||
add(CollectionJsonAdapter.FACTORY)
|
add(CollectionJsonAdapter.Factory)
|
||||||
add(MapJsonAdapter.FACTORY)
|
add(MapJsonAdapter.FACTORY)
|
||||||
add(ArrayJsonAdapter.FACTORY)
|
add(ArrayJsonAdapter.FACTORY)
|
||||||
add(RecordJsonAdapter.FACTORY)
|
add(RecordJsonAdapter.FACTORY)
|
||||||
|
@@ -25,9 +25,7 @@ public class NonNullJsonAdapter<T>(public val delegate: JsonAdapter<T>) : JsonAd
|
|||||||
return if (reader.peek() == JsonReader.Token.NULL) {
|
return if (reader.peek() == JsonReader.Token.NULL) {
|
||||||
throw JsonDataException("Unexpected null at " + reader.path)
|
throw JsonDataException("Unexpected null at " + reader.path)
|
||||||
} else {
|
} else {
|
||||||
val result = delegate.fromJson(reader)
|
knownNotNull(delegate.fromJson(reader))
|
||||||
knownNotNull(result)
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -46,7 +46,6 @@ import java.lang.reflect.TypeVariable
|
|||||||
import java.lang.reflect.WildcardType
|
import java.lang.reflect.WildcardType
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.LinkedHashSet
|
import java.util.LinkedHashSet
|
||||||
import kotlin.contracts.ExperimentalContracts
|
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
@JvmField public val NO_ANNOTATIONS: Set<Annotation> = emptySet()
|
@JvmField public val NO_ANNOTATIONS: Set<Annotation> = emptySet()
|
||||||
@@ -477,16 +476,21 @@ public fun unexpectedNull(
|
|||||||
return JsonDataException(message)
|
return JsonDataException(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A sneaky way to mark value as known to be not null, allowing smart casts and skipping the null-check intrinsic
|
// Sneaky backdoor way of marking a value as non-null to the compiler and skip the null-check intrinsic.
|
||||||
// Safe to use here because it's already compiled and not being used externally
|
// Safe to use (unstable) contracts since they're gone in the final bytecode
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
@OptIn(ExperimentalContracts::class)
|
internal inline fun <T> markNotNull(value: T?) {
|
||||||
public inline fun <T : Any> knownNotNull(value: T?) {
|
|
||||||
contract {
|
contract {
|
||||||
returns() implies (value != null)
|
returns() implies (value != null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
internal inline fun <T> knownNotNull(value: T?): T {
|
||||||
|
markNotNull(value)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
// Public due to inline access in MoshiKotlinTypesExtensions
|
// Public due to inline access in MoshiKotlinTypesExtensions
|
||||||
public fun <T> Class<T>.boxIfPrimitive(): Class<T> {
|
public fun <T> Class<T>.boxIfPrimitive(): Class<T> {
|
||||||
// cast is safe: long.class and Long.class are both of type Class<Long>
|
// cast is safe: long.class and Long.class are both of type Class<Long>
|
||||||
|
Reference in New Issue
Block a user