Менее запутанный KConcreteTypeAdapter
This commit is contained in:
parent
f62c6cab66
commit
b8dc265037
@ -38,16 +38,18 @@ private fun <T> resolveBound(bound: Class<T>, stringAdapter: TypeAdapter<String>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ListAdapter<T>(private val bound: Class<T>, private val stringAdapter: TypeAdapter<String>) : TypeAdapter<List<T>>() {
|
private class ListAdapter<T>(private val bound: Class<T>, private val stringAdapter: TypeAdapter<String>) : TypeAdapter<List<T>?>() {
|
||||||
private val resolvedBound by lazy { resolveBound(bound, stringAdapter) }
|
private val resolvedBound by lazy { resolveBound(bound, stringAdapter) }
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: List<T>) {
|
override fun write(out: JsonWriter, value: List<T>?) {
|
||||||
out.beginArray()
|
out.beginArray()
|
||||||
|
|
||||||
val resolvedBound = resolvedBound
|
if (value != null) {
|
||||||
|
val resolvedBound = resolvedBound
|
||||||
|
|
||||||
for (v in value) {
|
for (v in value) {
|
||||||
resolvedBound.write(out, v)
|
resolvedBound.write(out, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out.endArray()
|
out.endArray()
|
||||||
@ -70,21 +72,23 @@ private class ListAdapter<T>(private val bound: Class<T>, private val stringAdap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MapAdapter<K, V>(private val boundKey: Class<K>, private val boundValue: Class<V>, private val stringAdapter: TypeAdapter<String>) : TypeAdapter<Map<K, V>>() {
|
private class MapAdapter<K, V>(private val boundKey: Class<K>, private val boundValue: Class<V>, private val stringAdapter: TypeAdapter<String>) : TypeAdapter<Map<K, V>?>() {
|
||||||
private val resolvedKey by lazy { resolveBound(boundKey, stringAdapter) }
|
private val resolvedKey by lazy { resolveBound(boundKey, stringAdapter) }
|
||||||
private val resolvedValue by lazy { resolveBound(boundValue, stringAdapter) }
|
private val resolvedValue by lazy { resolveBound(boundValue, stringAdapter) }
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: Map<K, V>) {
|
override fun write(out: JsonWriter, value: Map<K, V>?) {
|
||||||
out.beginArray()
|
out.beginArray()
|
||||||
|
|
||||||
val resolvedKey = resolvedKey
|
if (value != null) {
|
||||||
val resolvedValue = resolvedValue
|
val resolvedKey = resolvedKey
|
||||||
|
val resolvedValue = resolvedValue
|
||||||
|
|
||||||
for ((k, v) in value) {
|
for ((k, v) in value) {
|
||||||
out.beginArray()
|
out.beginArray()
|
||||||
resolvedKey.write(out, k)
|
resolvedKey.write(out, k)
|
||||||
resolvedValue.write(out, v)
|
resolvedValue.write(out, v)
|
||||||
out.endArray()
|
out.endArray()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out.endArray()
|
out.endArray()
|
||||||
@ -110,17 +114,19 @@ private class MapAdapter<K, V>(private val boundKey: Class<K>, private val bound
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StringMapAdapter<V>(private val bound: Class<V>, private val stringAdapter: TypeAdapter<String>, private val interner: () -> Interner<String>) : TypeAdapter<Map<String, V>>() {
|
private class StringMapAdapter<V>(private val bound: Class<V>, private val stringAdapter: TypeAdapter<String>, private val interner: () -> Interner<String>) : TypeAdapter<Map<String, V>?>() {
|
||||||
private val resolvedBound by lazy { resolveBound(bound, stringAdapter) }
|
private val resolvedBound by lazy { resolveBound(bound, stringAdapter) }
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: Map<String, V>) {
|
override fun write(out: JsonWriter, value: Map<String, V>?) {
|
||||||
val resolvedBound = resolvedBound
|
val resolvedBound = resolvedBound
|
||||||
|
|
||||||
out.beginObject()
|
out.beginObject()
|
||||||
|
|
||||||
for ((k, v) in value) {
|
if (value != null) {
|
||||||
out.name(k)
|
for ((k, v) in value) {
|
||||||
resolvedBound.write(out, v)
|
out.name(k)
|
||||||
|
resolvedBound.write(out, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out.endObject()
|
out.endObject()
|
||||||
@ -155,15 +161,22 @@ private class LazyTypeProvider<T : Any?>(private val bound: Class<T>) : TypeAdap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private data class PackedProperty<Clazz : Any, T>(
|
||||||
|
val property: KProperty1<Clazz, T>,
|
||||||
|
val adapter: TypeAdapter<T>,
|
||||||
|
val transformer: (T) -> T = { it }
|
||||||
|
) {
|
||||||
|
val returnType = property.returnType
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TypeAdapter для классов которые создаются единожды и более не меняются ("бетонных классов").
|
* TypeAdapter для классов которые создаются единожды и более не меняются ("бетонных классов").
|
||||||
*/
|
*/
|
||||||
class KConcreteTypeAdapter<T : Any>(
|
class KConcreteTypeAdapter<T : Any> private constructor(
|
||||||
val bound: KClass<T>,
|
val bound: KClass<T>,
|
||||||
val types: ImmutableList<Pair<KProperty1<T, *>, TypeAdapter<*>>>,
|
private val types: ImmutableList<PackedProperty<T, *>>,
|
||||||
val asList: Boolean = false
|
private val asJsonArray: Boolean = false
|
||||||
) : TypeAdapter<T>() {
|
) : TypeAdapter<T>() {
|
||||||
private val returnTypeCache = Object2ObjectArrayMap<KProperty1<T, *>, KType>()
|
|
||||||
private val mapped = Object2IntArrayMap<String>()
|
private val mapped = Object2IntArrayMap<String>()
|
||||||
private val internedStrings = Interners.newWeakInterner<String>()
|
private val internedStrings = Interners.newWeakInterner<String>()
|
||||||
private val loggedMisses = ObjectArraySet<String>()
|
private val loggedMisses = ObjectArraySet<String>()
|
||||||
@ -171,14 +184,10 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
var currentSymbolicName by ThreadLocal<String>()
|
var currentSymbolicName by ThreadLocal<String>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
for ((field) in types) {
|
|
||||||
returnTypeCache[field] = field.returnType
|
|
||||||
}
|
|
||||||
|
|
||||||
mapped.defaultReturnValue(-1)
|
mapped.defaultReturnValue(-1)
|
||||||
|
|
||||||
for ((i, pair) in types.withIndex()) {
|
for ((i, pair) in types.withIndex()) {
|
||||||
mapped[pair.first.name] = i
|
mapped[pair.property.name] = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +202,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
val nextParam = iterator.next()
|
val nextParam = iterator.next()
|
||||||
|
|
||||||
val a = param.type
|
val a = param.type
|
||||||
val b = nextParam.first.returnType
|
val b = nextParam.returnType
|
||||||
|
|
||||||
if (!a.isSupertypeOf(b) || a.isMarkedNullable != b.isMarkedNullable) {
|
if (!a.isSupertypeOf(b) || a.isMarkedNullable != b.isMarkedNullable) {
|
||||||
return@first false
|
return@first false
|
||||||
@ -210,7 +219,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
* Синтетический конструктор класса, который создаётся Kotlin'ном, для создания классов со значениями по умолчанию
|
* Синтетический конструктор класса, который создаётся Kotlin'ном, для создания классов со значениями по умолчанию
|
||||||
*/
|
*/
|
||||||
private val syntheticFactory: Constructor<T>? = try {
|
private val syntheticFactory: Constructor<T>? = try {
|
||||||
bound.java.getDeclaredConstructor(*types.map { (it.first.returnType.classifier as KClass<*>).java }.also {
|
bound.java.getDeclaredConstructor(*types.map { (it.returnType.classifier as KClass<*>).java }.also {
|
||||||
it as MutableList
|
it as MutableList
|
||||||
it.add(Int::class.java)
|
it.add(Int::class.java)
|
||||||
it.add(DefaultConstructorMarker::class.java)
|
it.add(DefaultConstructorMarker::class.java)
|
||||||
@ -260,7 +269,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun read(reader: JsonReader): T {
|
override fun read(reader: JsonReader): T {
|
||||||
if (asList) {
|
if (asJsonArray) {
|
||||||
reader.beginArray()
|
reader.beginArray()
|
||||||
} else {
|
} else {
|
||||||
reader.beginObject()
|
reader.beginObject()
|
||||||
@ -271,7 +280,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
val readValues = arrayOfNulls<Any>(types.size)
|
val readValues = arrayOfNulls<Any>(types.size)
|
||||||
|
|
||||||
// Если нам необходимо читать объект как набор данных массива, то давай
|
// Если нам необходимо читать объект как набор данных массива, то давай
|
||||||
if (asList) {
|
if (asJsonArray) {
|
||||||
val iterator = types.iterator()
|
val iterator = types.iterator()
|
||||||
var fieldId = 0
|
var fieldId = 0
|
||||||
|
|
||||||
@ -344,7 +353,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asList) {
|
if (asJsonArray) {
|
||||||
reader.endArray()
|
reader.endArray()
|
||||||
} else {
|
} else {
|
||||||
reader.endObject()
|
reader.endObject()
|
||||||
@ -353,11 +362,11 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
// если у нас есть все значения для конструктора (все значения присутствуют в исходном json объекте)
|
// если у нас есть все значения для конструктора (все значения присутствуют в исходном json объекте)
|
||||||
// или у нас нет синтетического конструктора, то делаем вызов "обычного" конструктора
|
// или у нас нет синтетического конструктора, то делаем вызов "обычного" конструктора
|
||||||
if (syntheticFactory == null || presentValues.all { it }) {
|
if (syntheticFactory == null || presentValues.all { it }) {
|
||||||
for ((i, pair) in types.withIndex()) {
|
for ((i, tuple) in types.withIndex()) {
|
||||||
val (field) = pair
|
val (field) = tuple
|
||||||
|
|
||||||
if (readValues[i] == null) {
|
if (readValues[i] == null) {
|
||||||
if (!returnTypeCache[field]!!.isMarkedNullable) {
|
if (!tuple.returnType.isMarkedNullable) {
|
||||||
throw JsonSyntaxException("Field ${field.name} of ${bound.qualifiedName} does not accept nulls")
|
throw JsonSyntaxException("Field ${field.name} of ${bound.qualifiedName} does not accept nulls")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,19 +408,19 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
|
|
||||||
val syntheticPrimitives = syntheticPrimitives!!
|
val syntheticPrimitives = syntheticPrimitives!!
|
||||||
|
|
||||||
for ((i, pair) in types.withIndex()) {
|
for ((i, tuple) in types.withIndex()) {
|
||||||
if (copied[i] != null) {
|
if (copied[i] != null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val (field) = pair
|
val (field) = tuple
|
||||||
val param = regularFactory.parameters[i]
|
val param = regularFactory.parameters[i]
|
||||||
|
|
||||||
if (!param.isOptional && !presentValues[i]) {
|
if (!param.isOptional && !presentValues[i]) {
|
||||||
throw JsonSyntaxException("Field ${field.name} of ${bound.qualifiedName} is missing")
|
throw JsonSyntaxException("Field ${field.name} of ${bound.qualifiedName} is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnTypeCache[field]!!.isMarkedNullable) {
|
if (tuple.returnType.isMarkedNullable) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +440,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
* Позволяет построить класс [KConcreteTypeAdapter] на основе заданных параметров
|
* Позволяет построить класс [KConcreteTypeAdapter] на основе заданных параметров
|
||||||
*/
|
*/
|
||||||
class Builder<T : Any>(val clazz: KClass<T>) {
|
class Builder<T : Any>(val clazz: KClass<T>) {
|
||||||
private val types = ArrayList<Pair<KProperty1<T, *>, TypeAdapter<*>>>()
|
private val types = ArrayList<PackedProperty<T, *>>()
|
||||||
private var interner by Delegates.notNull<Interner<String>>()
|
private var interner by Delegates.notNull<Interner<String>>()
|
||||||
|
|
||||||
private val internedStringAdapter: TypeAdapter<String> = object : TypeAdapter<String>() {
|
private val internedStringAdapter: TypeAdapter<String> = object : TypeAdapter<String>() {
|
||||||
@ -448,32 +457,33 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
* Добавляет поле с определённым адаптером
|
* Добавляет поле с определённым адаптером
|
||||||
*/
|
*/
|
||||||
fun <V> plain(field: KProperty1<T, V>, adapter: TypeAdapter<V>): Builder<T> {
|
fun <V> plain(field: KProperty1<T, V>, adapter: TypeAdapter<V>): Builder<T> {
|
||||||
types.add(field to adapter)
|
types.add(PackedProperty(field, adapter))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Добавляет поле(я) без generic типов и без преобразователей
|
* Добавляет поле(я) без generic типов и без преобразователей
|
||||||
*/
|
*/
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
fun plain(vararg fields: KProperty1<T, *>): Builder<T> {
|
fun plain(vararg fields: KProperty1<T, *>): Builder<T> {
|
||||||
for (field in fields) {
|
for (field in fields) {
|
||||||
val returnType = field.returnType
|
val returnType = field.returnType
|
||||||
val classifier = returnType.classifier as? KClass<*> ?: throw ClassCastException("Unable to cast ${returnType.classifier} to KClass of property ${field.name}!")
|
val classifier = returnType.classifier as? KClass<*> ?: throw ClassCastException("Unable to cast ${returnType.classifier} to KClass of property ${field.name}!")
|
||||||
|
|
||||||
if (classifier.isSuperclassOf(Float::class)) {
|
if (classifier.isSuperclassOf(Float::class)) {
|
||||||
types.add(field to TypeAdapters.FLOAT)
|
types.add(PackedProperty(field as KProperty1<T, Float>, TypeAdapters.FLOAT))
|
||||||
} else if (classifier.isSuperclassOf(Double::class)) {
|
} else if (classifier.isSuperclassOf(Double::class)) {
|
||||||
types.add(field to TypeAdapters.DOUBLE)
|
types.add(PackedProperty(field as KProperty1<T, Double>, TypeAdapters.DOUBLE))
|
||||||
} else if (classifier.isSuperclassOf(Int::class)) {
|
} else if (classifier.isSuperclassOf(Int::class)) {
|
||||||
types.add(field to TypeAdapters.INTEGER)
|
types.add(PackedProperty(field as KProperty1<T, Int>, TypeAdapters.INTEGER))
|
||||||
} else if (classifier.isSuperclassOf(Long::class)) {
|
} else if (classifier.isSuperclassOf(Long::class)) {
|
||||||
types.add(field to TypeAdapters.LONG)
|
types.add(PackedProperty(field as KProperty1<T, Long>, TypeAdapters.LONG))
|
||||||
} else if (classifier.isSuperclassOf(String::class)) {
|
} else if (classifier.isSuperclassOf(String::class)) {
|
||||||
types.add(field to internedStringAdapter)
|
types.add(PackedProperty(field as KProperty1<T, String>, internedStringAdapter))
|
||||||
} else if (classifier.isSuperclassOf(Boolean::class)) {
|
} else if (classifier.isSuperclassOf(Boolean::class)) {
|
||||||
types.add(field to TypeAdapters.BOOLEAN)
|
types.add(PackedProperty(field as KProperty1<T, Boolean>, TypeAdapters.BOOLEAN))
|
||||||
} else {
|
} else {
|
||||||
types.add(field to LazyTypeProvider(classifier.java))
|
types.add(PackedProperty(field, LazyTypeProvider(classifier.java) as TypeAdapter<Any?>))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,7 +510,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
* Список неизменяем (создаётся объект [ImmutableList])
|
* Список неизменяем (создаётся объект [ImmutableList])
|
||||||
*/
|
*/
|
||||||
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>): Builder<T> {
|
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>): Builder<T> {
|
||||||
types.add(field to ListAdapter(type, internedStringAdapter))
|
types.add(PackedProperty(field, ListAdapter(type, internedStringAdapter)))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,7 +529,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
||||||
*/
|
*/
|
||||||
fun <K, V> map(field: KProperty1<T, Map<K, V>>, keyType: Class<K>, valueType: Class<V>): Builder<T> {
|
fun <K, V> map(field: KProperty1<T, Map<K, V>>, keyType: Class<K>, valueType: Class<V>): Builder<T> {
|
||||||
types.add(field to MapAdapter(keyType, valueType, internedStringAdapter))
|
types.add(PackedProperty(field, MapAdapter(keyType, valueType, internedStringAdapter)))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,7 +539,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
||||||
*/
|
*/
|
||||||
fun <K : Any, V : Any> map(field: KProperty1<T, Map<K, V>?>, keyType: KClass<K>, valueType: KClass<V>): Builder<T> {
|
fun <K : Any, V : Any> map(field: KProperty1<T, Map<K, V>?>, keyType: KClass<K>, valueType: KClass<V>): Builder<T> {
|
||||||
types.add(field to MapAdapter(keyType.java, valueType.java, internedStringAdapter))
|
types.add(PackedProperty(field, MapAdapter(keyType.java, valueType.java, internedStringAdapter)))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,7 +549,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
||||||
*/
|
*/
|
||||||
fun <V> map(field: KProperty1<T, Map<String, V>?>, valueType: Class<V>): Builder<T> {
|
fun <V> map(field: KProperty1<T, Map<String, V>?>, valueType: Class<V>): Builder<T> {
|
||||||
types.add(field to StringMapAdapter(valueType, internedStringAdapter, ::interner))
|
types.add(PackedProperty(field, StringMapAdapter(valueType, internedStringAdapter, ::interner)))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,12 +559,12 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
||||||
*/
|
*/
|
||||||
fun <V : Any> map(field: KProperty1<T, Map<String, V>?>, valueType: KClass<V>): Builder<T> {
|
fun <V : Any> map(field: KProperty1<T, Map<String, V>?>, valueType: KClass<V>): Builder<T> {
|
||||||
types.add(field to StringMapAdapter(valueType.java, internedStringAdapter, ::interner))
|
types.add(PackedProperty(field, StringMapAdapter(valueType.java, internedStringAdapter, ::interner)))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(asList: Boolean = false): KConcreteTypeAdapter<T> {
|
fun build(asList: Boolean = false): KConcreteTypeAdapter<T> {
|
||||||
return KConcreteTypeAdapter(clazz, ImmutableList.copyOf(types), asList = asList).also {
|
return KConcreteTypeAdapter(clazz, ImmutableList.copyOf(types), asJsonArray = asList).also {
|
||||||
interner = it.internedStrings
|
interner = it.internedStrings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user