Ещё больше рефакторинга десериализера
This commit is contained in:
parent
b939373298
commit
53c4c3fa11
@ -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) } }) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
@ -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`)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user