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