Ещё больше рефакторинга десериализера

This commit is contained in:
DBotThePony 2022-12-30 16:59:21 +07:00
parent b939373298
commit 53c4c3fa11
Signed by: DBot
GPG Key ID: DCC23B5715498507
7 changed files with 201 additions and 187 deletions

View File

@ -1,7 +1,12 @@
package ru.dbotthepony.kstarbound
import com.google.common.collect.ImmutableList
import com.google.common.collect.Interner
import com.google.common.collect.Interners
import com.google.gson.*
import com.google.gson.internal.bind.TypeAdapters
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import org.apache.logging.log4j.LogManager
@ -104,6 +109,18 @@ object Starbound {
val functionsAccess: Map<String, JsonFunction> = Collections.unmodifiableMap(functions)
val itemAccess: Map<String, ItemDefinition> = Collections.unmodifiableMap(items)
val assetStringInterner: Interner<String> = Interners.newStrongInterner()
val stringTypeAdapter: TypeAdapter<String?> = object : TypeAdapter<String>() {
override fun write(out: JsonWriter, value: String) {
out.value(value)
}
override fun read(`in`: JsonReader): String {
return assetStringInterner.intern(TypeAdapters.STRING.read(`in`))
}
}.nullSafe()
val gson: Gson = GsonBuilder()
.enableComplexMapKeySerialization()
.serializeNulls()
@ -112,6 +129,9 @@ object Starbound {
.setPrettyPrinting()
.registerTypeAdapter(Color::class.java, ColorTypeAdapter.nullSafe())
// чтоб строки всегда intern'ились
.registerTypeAdapter(stringTypeAdapter)
// math
.registerTypeAdapter(AABB::class.java, AABBTypeAdapter)
.registerTypeAdapter(AABBi::class.java, AABBiTypeAdapter)
@ -137,6 +157,19 @@ object Starbound {
.create()
@Suppress("unchecked_cast")
fun <T> getTypeAdapter(type: Class<T>): TypeAdapter<T> {
return when (type) {
Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T>
Double::class.java -> TypeAdapters.DOUBLE as TypeAdapter<T>
String::class.java -> stringTypeAdapter as TypeAdapter<T>
Int::class.java -> TypeAdapters.INTEGER as TypeAdapter<T>
Long::class.java -> TypeAdapters.LONG as TypeAdapter<T>
Boolean::class.java -> TypeAdapters.BOOLEAN as TypeAdapter<T>
else -> gson.getAdapter(type)
}
}
var initializing = false
private set
var initialized = false
@ -442,7 +475,8 @@ object Starbound {
}
private fun loadItemDefinitions(callback: (String) -> Unit) {
val files = listOf(".item", ".currency", ".head", ".chest", ".legs")
//val files = listOf(".item", ".currency", ".head", ".chest", ".legs")
val files = listOf(".item", ".currency")
for (fs in fileSystems) {
for (listedFile in fs.explore().filter { it.isFile }.filter { f -> files.any { f.name.endsWith(it) } }) {

View File

@ -313,7 +313,6 @@ data class ItemDefinition(
.auto(FossilSetDescription::price)
.auto(FossilSetDescription::shortdescription)
.auto(FossilSetDescription::description)
.specifyStringInterner(ADAPTER.stringInterner)
.build()
val ARMOR_FRAMES_ADAPTER = KConcreteTypeAdapter.Builder(ArmorFrames::class)

View File

@ -2,8 +2,6 @@ package ru.dbotthepony.kstarbound.io.json
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.common.collect.Interner
import com.google.common.collect.Interners
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
@ -27,145 +25,8 @@ import java.lang.reflect.Constructor
import kotlin.jvm.internal.DefaultConstructorMarker
import kotlin.reflect.*
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.isSuperclassOf
import kotlin.reflect.full.isSupertypeOf
@Suppress("unchecked_cast")
private fun <T> resolveBound(bound: Class<T>, stringAdapter: TypeAdapter<String>): TypeAdapter<T> {
return when (bound) {
Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T>
Double::class.java -> TypeAdapters.DOUBLE as TypeAdapter<T>
String::class.java -> stringAdapter as TypeAdapter<T>
Int::class.java -> TypeAdapters.INTEGER as TypeAdapter<T>
Long::class.java -> TypeAdapters.LONG as TypeAdapter<T>
Boolean::class.java -> TypeAdapters.BOOLEAN as TypeAdapter<T>
else -> Starbound.gson.getAdapter(bound)
}
}
private class ListAdapter<T>(private val type: Class<T>, private val stringAdapter: TypeAdapter<String>) : TypeAdapter<List<T>?>() {
private val resolvedBound by lazy { resolveBound(type, stringAdapter) }
override fun write(out: JsonWriter, value: List<T>?) {
out.beginArray()
if (value != null) {
val resolvedBound = resolvedBound
for (v in value) {
resolvedBound.write(out, v)
}
}
out.endArray()
}
override fun read(reader: JsonReader): List<T> {
reader.beginArray()
val builder = ImmutableList.builder<T>()
val resolvedBound = resolvedBound
while (reader.peek() != JsonToken.END_ARRAY) {
val readObject = resolvedBound.read(reader) ?: throw JsonSyntaxException("List does not accept nulls")
builder.add(readObject as T)
}
reader.endArray()
return builder.build()
}
}
private class MapAdapter<K, V>(private val keyType: Class<K>, private val valueType: Class<V>, private val stringAdapter: TypeAdapter<String>) : TypeAdapter<Map<K, V>?>() {
private val resolvedKey by lazy { resolveBound(keyType, stringAdapter) }
private val resolvedValue by lazy { resolveBound(valueType, stringAdapter) }
override fun write(out: JsonWriter, value: Map<K, V>?) {
out.beginArray()
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()
}
}
out.endArray()
}
override fun read(reader: JsonReader): Map<K, V> {
reader.beginArray()
val builder = ImmutableMap.builder<K, V>()
val resolvedKey = resolvedKey
val resolvedValue = resolvedValue
while (reader.peek() != JsonToken.END_ARRAY) {
reader.beginArray()
builder.put(resolvedKey.read(reader), resolvedValue.read(reader))
reader.endArray()
}
reader.endArray()
return builder.build()
}
}
private class StringMapAdapter<V>(private val type: Class<V>, private val stringAdapter: TypeAdapter<String>, private val interner: Interner<String>) : TypeAdapter<Map<String, V>?>() {
private val resolvedBound by lazy { resolveBound(type, stringAdapter) }
override fun write(out: JsonWriter, value: Map<String, V>?) {
val resolvedBound = resolvedBound
out.beginObject()
if (value != null) {
for ((k, v) in value) {
out.name(k)
resolvedBound.write(out, v)
}
}
out.endObject()
}
override fun read(reader: JsonReader): Map<String, V> {
val builder = ImmutableMap.builder<String, V>()
reader.beginObject()
val resolvedBound = resolvedBound
while (reader.peek() != JsonToken.END_OBJECT) {
builder.put(interner.intern(reader.nextName()), resolvedBound.read(reader))
}
reader.endObject()
return builder.build()
}
}
private class LazyTypeProvider<T : Any?>(private val bound: Class<T>) : TypeAdapter<T>() {
private val resolved by lazy { resolveBound(bound, TypeAdapters.STRING) }
override fun write(out: JsonWriter, value: T) {
resolved.write(out, value)
}
override fun read(`in`: JsonReader): T {
return resolved.read(`in`)
}
}
private data class PackedProperty<Clazz : Any, T>(
val property: KProperty1<Clazz, T>,
val adapter: TypeAdapter<T>,
@ -181,19 +42,18 @@ class KConcreteTypeAdapter<T : Any> private constructor(
val bound: KClass<T>,
private val types: ImmutableList<PackedProperty<T, *>>,
val asJsonArray: Boolean,
val stringInterner: Interner<String>,
val storesJson: Boolean
) : TypeAdapter<T>() {
private val mapped = Object2IntArrayMap<String>()
private val name2index = Object2IntArrayMap<String>()
private val loggedMisses = ObjectArraySet<String>()
var currentSymbolicName by ThreadLocal<String>()
init {
mapped.defaultReturnValue(-1)
name2index.defaultReturnValue(-1)
for ((i, pair) in types.withIndex()) {
mapped[pair.property.name] = i
name2index[pair.property.name] = i
}
}
@ -328,7 +188,7 @@ class KConcreteTypeAdapter<T : Any> private constructor(
}
reader = JsonTreeReader(readArray)
readValues[readValues.size - 1] = enrollList(flattenJsonElement(readArray) as List<Any>, stringInterner::intern)
readValues[readValues.size - 1] = enrollList(flattenJsonElement(readArray) as List<Any>, Starbound.assetStringInterner::intern)
}
reader.beginArray()
@ -373,14 +233,14 @@ class KConcreteTypeAdapter<T : Any> private constructor(
}
reader = JsonTreeReader(readMap)
readValues[readValues.size - 1] = enrollMap(flattenJsonElement(readMap) as Map<String, Any>, stringInterner::intern)
readValues[readValues.size - 1] = enrollMap(flattenJsonElement(readMap) as Map<String, Any>, Starbound.assetStringInterner::intern)
}
reader.beginObject()
while (reader.peek() != JsonToken.END_OBJECT) {
val name = reader.nextName()
val fieldId = mapped.getInt(name)
val fieldId = name2index.getInt(name)
if (fieldId == -1) {
if (!storesJson && loggedMisses.add(name)) {
@ -498,7 +358,6 @@ class KConcreteTypeAdapter<T : Any> private constructor(
*/
class Builder<T : Any>(val clazz: KClass<T>) {
private val types = ArrayList<PackedProperty<T, *>>()
var stringInterner: Interner<String> = Interners.newWeakInterner()
/**
* Принимает ли класс *последним* аргументом JSON объект
@ -515,21 +374,6 @@ class KConcreteTypeAdapter<T : Any> private constructor(
return this
}
fun specifyStringInterner(interner: Interner<String>): Builder<T> {
stringInterner = interner
return this
}
private val internedStringAdapter: TypeAdapter<String> = object : TypeAdapter<String>() {
override fun write(out: JsonWriter, value: String) {
return TypeAdapters.STRING.write(out, value)
}
override fun read(`in`: JsonReader): String {
return stringInterner.intern(TypeAdapters.STRING.read(`in`))
}
}
/**
* Добавляет поле с определённым адаптером
*/
@ -556,23 +400,7 @@ class KConcreteTypeAdapter<T : Any> private constructor(
fun <In> auto(field: KProperty1<T, In>, transformer: (In) -> In = { it }): Builder<T> {
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(PackedProperty(field as KProperty1<T, Float>, TypeAdapters.FLOAT, transformer = transformer as (Number) -> Number))
} else if (classifier.isSuperclassOf(Double::class)) {
types.add(PackedProperty(field as KProperty1<T, Double>, TypeAdapters.DOUBLE, transformer = transformer as (Number) -> Number))
} else if (classifier.isSuperclassOf(Int::class)) {
types.add(PackedProperty(field as KProperty1<T, Int>, TypeAdapters.INTEGER, transformer = transformer as (Number) -> Number))
} else if (classifier.isSuperclassOf(Long::class)) {
types.add(PackedProperty(field as KProperty1<T, Long>, TypeAdapters.LONG, transformer = transformer as (Number) -> Number))
} else if (classifier.isSuperclassOf(String::class)) {
types.add(PackedProperty(field as KProperty1<T, String>, internedStringAdapter, transformer = transformer as (String) -> String))
} else if (classifier.isSuperclassOf(Boolean::class)) {
types.add(PackedProperty(field as KProperty1<T, Boolean>, TypeAdapters.BOOLEAN, transformer = transformer as (Boolean) -> Boolean))
} else {
types.add(PackedProperty(field, LazyTypeProvider(classifier.java) as TypeAdapter<Any?>, transformer = transformer as (Any?) -> Any?))
}
types.add(PackedProperty(field, LazyTypeProvider(classifier.java) as TypeAdapter<Any?>, transformer = transformer as (Any?) -> Any?))
return this
}
@ -582,7 +410,7 @@ class KConcreteTypeAdapter<T : Any> private constructor(
* Список неизменяем (создаётся объект [ImmutableList])
*/
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>, transformer: (List<V>?) -> List<V>? = { it }): Builder<T> {
types.add(PackedProperty(field, ListAdapter(type, internedStringAdapter), transformer = transformer))
types.add(PackedProperty(field, ListAdapter(type), transformer = transformer))
return this
}
@ -601,7 +429,7 @@ class KConcreteTypeAdapter<T : Any> private constructor(
* Таблица неизменяема (создаётся объект [ImmutableMap])
*/
fun <K, V> map(field: KProperty1<T, Map<K, V>>, keyType: Class<K>, valueType: Class<V>): Builder<T> {
types.add(PackedProperty(field, MapAdapter(keyType, valueType, internedStringAdapter)))
types.add(PackedProperty(field, MapAdapter(keyType, valueType)))
return this
}
@ -625,7 +453,7 @@ class KConcreteTypeAdapter<T : Any> private constructor(
* Таблица неизменяема (создаётся объект [ImmutableMap])
*/
fun <K : Any, V : Any> map(field: KProperty1<T, Map<K, V>?>, keyType: KClass<K>, valueType: KClass<V>): Builder<T> {
types.add(PackedProperty(field, MapAdapter(keyType.java, valueType.java, internedStringAdapter)))
types.add(PackedProperty(field, MapAdapter(keyType.java, valueType.java)))
return this
}
@ -635,7 +463,7 @@ class KConcreteTypeAdapter<T : Any> private constructor(
* Таблица неизменяема (создаётся объект [ImmutableMap])
*/
fun <V> map(field: KProperty1<T, Map<String, V>?>, valueType: Class<V>): Builder<T> {
types.add(PackedProperty(field, StringMapAdapter(valueType, internedStringAdapter, stringInterner)))
types.add(PackedProperty(field, StringMapAdapter(valueType)))
return this
}
@ -645,7 +473,7 @@ class KConcreteTypeAdapter<T : Any> private constructor(
* Таблица неизменяема (создаётся объект [ImmutableMap])
*/
fun <V : Any> map(field: KProperty1<T, Map<String, V>?>, valueType: KClass<V>): Builder<T> {
types.add(PackedProperty(field, StringMapAdapter(valueType.java, internedStringAdapter, stringInterner)))
types.add(PackedProperty(field, StringMapAdapter(valueType.java)))
return this
}
@ -666,7 +494,6 @@ class KConcreteTypeAdapter<T : Any> private constructor(
bound = clazz,
types = ImmutableList.copyOf(types),
asJsonArray = asList,
stringInterner = stringInterner,
storesJson = storesJson
)
}

View File

@ -0,0 +1,18 @@
package ru.dbotthepony.kstarbound.io.json
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.Starbound
class LazyTypeProvider<T : Any?>(private val bound: Class<T>) : TypeAdapter<T>() {
private val resolved by lazy { Starbound.getTypeAdapter(bound) }
override fun write(out: JsonWriter, value: T) {
resolved.write(out, value)
}
override fun read(`in`: JsonReader): T {
return resolved.read(`in`)
}
}

View File

@ -0,0 +1,43 @@
package ru.dbotthepony.kstarbound.io.json
import com.google.common.collect.ImmutableList
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.Starbound
class ListAdapter<T>(private val type: Class<T>) : TypeAdapter<List<T>?>() {
private val resolvedBound by lazy { Starbound.getTypeAdapter(type) }
override fun write(out: JsonWriter, value: List<T>?) {
out.beginArray()
if (value != null) {
val resolvedBound = resolvedBound
for (v in value) {
resolvedBound.write(out, v)
}
}
out.endArray()
}
override fun read(reader: JsonReader): List<T> {
reader.beginArray()
val builder = ImmutableList.builder<T>()
val resolvedBound = resolvedBound
while (reader.peek() != JsonToken.END_ARRAY) {
val readObject = resolvedBound.read(reader) ?: throw JsonSyntaxException("List does not accept nulls")
builder.add(readObject as T)
}
reader.endArray()
return builder.build()
}
}

View File

@ -0,0 +1,50 @@
package ru.dbotthepony.kstarbound.io.json
import com.google.common.collect.ImmutableMap
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.Starbound
class MapAdapter<K, V>(private val keyType: Class<K>, private val valueType: Class<V>) : TypeAdapter<Map<K, V>?>() {
private val resolvedKey by lazy { Starbound.getTypeAdapter(keyType) }
private val resolvedValue by lazy { Starbound.getTypeAdapter(valueType) }
override fun write(out: JsonWriter, value: Map<K, V>?) {
out.beginArray()
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()
}
}
out.endArray()
}
override fun read(reader: JsonReader): Map<K, V> {
reader.beginArray()
val builder = ImmutableMap.builder<K, V>()
val resolvedKey = resolvedKey
val resolvedValue = resolvedValue
while (reader.peek() != JsonToken.END_ARRAY) {
reader.beginArray()
builder.put(resolvedKey.read(reader), resolvedValue.read(reader))
reader.endArray()
}
reader.endArray()
return builder.build()
}
}

View File

@ -0,0 +1,43 @@
package ru.dbotthepony.kstarbound.io.json
import com.google.common.collect.ImmutableMap
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.Starbound
class StringMapAdapter<V>(private val type: Class<V>) : TypeAdapter<Map<String, V>?>() {
private val resolvedBound by lazy { Starbound.getTypeAdapter(type) }
override fun write(out: JsonWriter, value: Map<String, V>?) {
val resolvedBound = resolvedBound
out.beginObject()
if (value != null) {
for ((k, v) in value) {
out.name(k)
resolvedBound.write(out, v)
}
}
out.endObject()
}
override fun read(reader: JsonReader): Map<String, V> {
val builder = ImmutableMap.builder<String, V>()
reader.beginObject()
val resolvedBound = resolvedBound
while (reader.peek() != JsonToken.END_OBJECT) {
builder.put(Starbound.assetStringInterner.intern(reader.nextName()), resolvedBound.read(reader))
}
reader.endObject()
return builder.build()
}
}