Более функциональный подход к typeadapter

This commit is contained in:
DBotThePony 2022-12-30 17:24:13 +07:00
parent 53c4c3fa11
commit 366e59cf14
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 112 additions and 83 deletions

View File

@ -33,7 +33,7 @@ data class MaterialModifier(
.auto(MaterialModifier::grass)
.list(MaterialModifier::miningSounds)
.auto(MaterialModifier::miningParticle)
.plain(MaterialModifier::renderTemplate, RenderTemplate.CACHE)
.add(MaterialModifier::renderTemplate, RenderTemplate.CACHE)
.auto(MaterialModifier::renderParameters)
.build()

View File

@ -38,7 +38,7 @@ data class TileDefinition(
TileDefinition::health,
TileDefinition::category
)
.plain(TileDefinition::renderTemplate, RenderTemplate.CACHE)
.add(TileDefinition::renderTemplate, RenderTemplate.CACHE)
.auto(
TileDefinition::renderParameters,
)

View File

@ -0,0 +1,41 @@
package ru.dbotthepony.kstarbound.io.json
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
fun <T> TypeAdapter<T>.transformRead(transformer: (T) -> T): TypeAdapter<T> {
return object : TypeAdapter<T>() {
override fun write(out: JsonWriter, value: T) {
return this@transformRead.write(out, value)
}
override fun read(`in`: JsonReader): T {
return transformer(this@transformRead.read(`in`))
}
}
}
fun <T> TypeAdapter<T>.transformWrite(transformer: (T) -> T): TypeAdapter<T> {
return object : TypeAdapter<T>() {
override fun write(out: JsonWriter, value: T) {
return this@transformWrite.write(out, transformer(value))
}
override fun read(`in`: JsonReader): T {
return this@transformWrite.read(`in`)
}
}
}
fun <T> TypeAdapter<T>.transform(transformRead: (T) -> T, transformWrite: (T) -> T): TypeAdapter<T> {
return object : TypeAdapter<T>() {
override fun write(out: JsonWriter, value: T) {
return this@transform.write(out, transformWrite(value))
}
override fun read(`in`: JsonReader): T {
return transformRead(this@transform.read(`in`))
}
}
}

View File

@ -377,7 +377,7 @@ class KConcreteTypeAdapter<T : Any> private constructor(
/**
* Добавляет поле с определённым адаптером
*/
fun <V> plain(field: KProperty1<T, V>, adapter: TypeAdapter<V>): Builder<T> {
fun <V> add(field: KProperty1<T, V>, adapter: TypeAdapter<V>): Builder<T> {
types.add(PackedProperty(field, adapter))
return this
}
@ -410,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), transformer = transformer))
types.add(PackedProperty(field, ListAdapter(type).nullSafe(), transformer = transformer))
return this
}
@ -420,7 +420,7 @@ class KConcreteTypeAdapter<T : Any> private constructor(
* Список неизменяем (создаётся объект [ImmutableList])
*/
inline fun <reified V : Any> list(field: KProperty1<T, List<V>?>, noinline transformer: (List<V>?) -> List<V>? = { it }): Builder<T> {
return this.list(field, V::class.java, transformer)
return add(field, ListAdapter(V::class.java).nullSafe())
}
/**
@ -453,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)))
types.add(PackedProperty(field, MapAdapter(keyType.java, valueType.java).nullSafe()))
return this
}
@ -463,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)))
types.add(PackedProperty(field, String2ObjectAdapter(valueType).nullSafe()))
return this
}
@ -473,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)))
types.add(PackedProperty(field, String2ObjectAdapter(valueType.java).nullSafe()))
return this
}

View File

@ -8,18 +8,18 @@ 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) }
fun <T : Any> TypeAdapter<T>.asList(): TypeAdapter<List<T>> {
return ListAdapter(this)
}
override fun write(out: JsonWriter, value: List<T>?) {
class ListAdapter<T : Any>(val elementAdapter: TypeAdapter<T>, val valueTransformer: (T) -> T = { it }) : TypeAdapter<List<T>>() {
constructor(type: Class<T>) : this(LazyTypeProvider(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)
}
for (v in value) {
elementAdapter.write(out, v)
}
out.endArray()
@ -29,15 +29,14 @@ class ListAdapter<T>(private val type: Class<T>) : TypeAdapter<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)
val readObject = elementAdapter.read(reader) ?: throw JsonSyntaxException("List does not accept nulls")
builder.add(valueTransformer(readObject))
}
reader.endArray()
return builder.build()
}
}
}

View File

@ -7,23 +7,17 @@ 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) }
class MapAdapter<K, V>(val keyAdapter: TypeAdapter<K>, val valueAdapter: TypeAdapter<V>) : TypeAdapter<Map<K, V>>() {
constructor(keyType: Class<K>, valueType: Class<V>) : this(LazyTypeProvider(keyType), LazyTypeProvider(valueType))
override fun write(out: JsonWriter, value: Map<K, V>?) {
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()
}
for ((k, v) in value) {
out.beginArray()
keyAdapter.write(out, k)
valueAdapter.write(out, v)
out.endArray()
}
out.endArray()
@ -34,12 +28,9 @@ class MapAdapter<K, V>(private val keyType: Class<K>, private val valueType: Cla
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))
builder.put(keyAdapter.read(reader), valueAdapter.read(reader))
reader.endArray()
}
@ -47,4 +38,4 @@ class MapAdapter<K, V>(private val keyType: Class<K>, private val valueType: Cla
return builder.build()
}
}
}

View File

@ -0,0 +1,41 @@
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
fun <T> TypeAdapter<T>.asJsonObject(): TypeAdapter<Map<String, T>> {
return String2ObjectAdapter(this)
}
class String2ObjectAdapter<T>(val adapter: TypeAdapter<T>) : TypeAdapter<Map<String, T>>() {
constructor(type: Class<T>) : this(LazyTypeProvider(type))
override fun write(out: JsonWriter, value: Map<String, T>) {
out.beginObject()
for ((k, v) in value) {
out.name(k)
adapter.write(out, v)
}
out.endObject()
}
override fun read(reader: JsonReader): Map<String, T> {
val builder = ImmutableMap.builder<String, T>()
reader.beginObject()
while (reader.peek() != JsonToken.END_OBJECT) {
builder.put(Starbound.assetStringInterner.intern(reader.nextName()), adapter.read(reader))
}
reader.endObject()
return builder.build()
}
}

View File

@ -1,43 +0,0 @@
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()
}
}