This commit is contained in:
DBotThePony 2023-01-22 20:29:48 +07:00
parent 2d7681628d
commit 2d8e3a7ff5
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 234 additions and 5 deletions

View File

@ -1,10 +1,13 @@
package ru.dbotthepony.kstarbound
import com.google.common.collect.ImmutableList
import com.google.gson.GsonBuilder
import org.apache.logging.log4j.LogManager
import org.lwjgl.Version
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.io.*
import ru.dbotthepony.kstarbound.io.json.factory.ImmutableCollectionAdapterFactory
import ru.dbotthepony.kstarbound.world.ChunkPos
import ru.dbotthepony.kstarbound.world.entities.ItemEntity
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity

View File

@ -46,6 +46,7 @@ import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
import ru.dbotthepony.kstarbound.io.json.Vector4iTypeAdapter
import ru.dbotthepony.kstarbound.io.json.factory.ImmutableCollectionAdapterFactory
import ru.dbotthepony.kstarbound.math.*
import ru.dbotthepony.kvector.vector.Color
import java.io.*
@ -145,7 +146,9 @@ object Starbound {
.setDateFormat(DateFormat.LONG)
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
.setPrettyPrinting()
.registerTypeAdapter(Color::class.java, ColorTypeAdapter.nullSafe())
.registerTypeAdapter(ColorTypeAdapter.nullSafe())
.registerTypeAdapterFactory(ImmutableCollectionAdapterFactory)
// чтоб строки всегда intern'ились
.registerTypeAdapter(NULLABLE_STRING_ADAPTER)

View File

@ -2,11 +2,14 @@ package ru.dbotthepony.kstarbound.io.json.builder
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.internal.bind.JsonTreeReader
import com.google.gson.internal.bind.TypeAdapters
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
@ -241,13 +244,17 @@ class BuilderAdapter<T : Any> private constructor(
var mustBePresent: Boolean? = null
}
class Builder<T : Any>(val factory: () -> T, vararg fields: KMutableProperty1<T, *>) {
class Builder<T : Any>(val factory: () -> T, vararg fields: KMutableProperty1<T, *>) : TypeAdapterFactory {
private val properties = ArrayList<WrappedProperty<T, *>>()
private val flatProperties = ArrayList<WrappedProperty<T, *>>()
private val ignoreKeys = ObjectArraySet<String>()
var extraPropertiesAreFatal = false
var logMisses: Boolean? = null
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
return null
}
/**
* Являются ли "лишние" ключи в JSON структуре ошибкой.
*

View File

@ -2,13 +2,16 @@ package ru.dbotthepony.kstarbound.io.json.builder
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.internal.bind.JsonTreeReader
import com.google.gson.internal.bind.TypeAdapters
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
@ -382,7 +385,7 @@ class FactoryAdapter<T : Any> private constructor(
/**
* Позволяет построить класс [FactoryAdapter] на основе заданных параметров
*/
class Builder<T : Any>(val clazz: KClass<T>) {
class Builder<T : Any>(val clazz: KClass<T>) : TypeAdapterFactory {
constructor(clazz: KClass<T>, vararg fields: KProperty1<T, *>) : this(clazz) {
for (field in fields) {
auto(field)
@ -391,6 +394,14 @@ class FactoryAdapter<T : Any> private constructor(
private val types = ArrayList<PackedProperty<T, *>>()
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type.rawType == clazz.java) {
return build() as TypeAdapter<T>
}
return null
}
/**
* Принимает ли класс *последним* аргументом JSON структуру
*

View File

@ -0,0 +1,37 @@
package ru.dbotthepony.kstarbound.io.json.factory
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
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
class ImmutableArrayMapTypeAdapter<K, V>(val keyAdapter: TypeAdapter<K>, val elementAdapter: TypeAdapter<V>) : TypeAdapter<ImmutableMap<K, V>>() {
override fun write(out: JsonWriter, value: ImmutableMap<K, V>) {
out.beginArray()
for ((k, v) in value) {
keyAdapter.write(out, k)
elementAdapter.write(out, v)
}
out.endArray()
}
override fun read(reader: JsonReader): ImmutableMap<K, V> {
reader.beginArray()
val builder = ImmutableMap.Builder<K, V>()
while (reader.peek() != JsonToken.END_OBJECT) {
builder.put(
keyAdapter.read(reader) ?: throw JsonSyntaxException("Nulls as keys are not allowed, near ${reader.path}"),
elementAdapter.read(reader) ?: throw JsonSyntaxException("Nulls as values are not allowed, near ${reader.path}"))
}
reader.endArray()
return builder.build()
}
}

View File

@ -0,0 +1,42 @@
package ru.dbotthepony.kstarbound.io.json.factory
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.internal.`$Gson$Types`
import com.google.gson.reflect.TypeToken
@Suppress("unchecked_cast")
object ImmutableCollectionAdapterFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
when (type.rawType) {
ImmutableList::class.java -> {
val elementType = `$Gson$Types`.getCollectionElementType(type.type, type.rawType)
return ImmutableListTypeAdapter(gson.getAdapter(TypeToken.get(elementType))) as TypeAdapter<T>
}
ImmutableSet::class.java -> {
val elementType = `$Gson$Types`.getCollectionElementType(type.type, type.rawType)
return ImmutableSetTypeAdapter(gson.getAdapter(TypeToken.get(elementType))) as TypeAdapter<T>
}
ImmutableMap::class.java -> {
val (elementType0, elementType1) = `$Gson$Types`.getMapKeyAndValueTypes(type.type, type.rawType)
if (`$Gson$Types`.getRawType(elementType0) == String::class.java) {
return ImmutableMapTypeAdapter(gson.getAdapter(TypeToken.get(elementType1))) as TypeAdapter<T>
}
return ImmutableArrayMapTypeAdapter(
gson.getAdapter(TypeToken.get(elementType0)),
gson.getAdapter(TypeToken.get(elementType1))
) as TypeAdapter<T>
}
else -> return null
}
}
}

View File

@ -0,0 +1,45 @@
package ru.dbotthepony.kstarbound.io.json.factory
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
class ImmutableListTypeAdapter<E>(val elementAdapter: TypeAdapter<E>) : TypeAdapter<ImmutableList<E>>() {
override fun write(out: JsonWriter, value: ImmutableList<E>) {
if (value.size == 1) {
elementAdapter.write(out, value[0])
return
}
out.beginArray()
for (v in value) {
elementAdapter.write(out, v)
}
out.endArray()
}
override fun read(reader: JsonReader): ImmutableList<E> {
if (reader.peek() != JsonToken.BEGIN_ARRAY) {
// не массив, возможно упрощение структуры "a": [value] -> "a": value
return ImmutableList.of(elementAdapter.read(reader) ?: throw JsonSyntaxException("List does not accept nulls"))
}
reader.beginArray()
val builder = ImmutableList.Builder<E>()
while (reader.peek() != JsonToken.END_ARRAY) {
val readObject = elementAdapter.read(reader) ?: throw JsonSyntaxException("List does not accept nulls")
builder.add(readObject)
}
reader.endArray()
return builder.build()
}
}

View File

@ -0,0 +1,35 @@
package ru.dbotthepony.kstarbound.io.json.factory
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
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
class ImmutableMapTypeAdapter<V>(val elementAdapter: TypeAdapter<V>) : TypeAdapter<ImmutableMap<String, V>>() {
override fun write(out: JsonWriter, value: ImmutableMap<String, V>) {
out.beginObject()
for ((k, v) in value) {
out.name(k)
elementAdapter.write(out, v)
}
out.endObject()
}
override fun read(reader: JsonReader): ImmutableMap<String, V> {
reader.beginObject()
val builder = ImmutableMap.Builder<String, V>()
while (reader.peek() != JsonToken.END_OBJECT) {
builder.put(reader.nextName(), elementAdapter.read(reader) ?: throw JsonSyntaxException("Nulls are not allowed, near ${reader.path}"))
}
reader.endObject()
return builder.build()
}
}

View File

@ -0,0 +1,46 @@
package ru.dbotthepony.kstarbound.io.json.factory
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet
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
class ImmutableSetTypeAdapter<E>(val elementAdapter: TypeAdapter<E>) : TypeAdapter<ImmutableSet<E>>() {
override fun write(out: JsonWriter, value: ImmutableSet<E>) {
if (value.size == 1) {
elementAdapter.write(out, value.first())
return
}
out.beginArray()
for (v in value) {
elementAdapter.write(out, v)
}
out.endArray()
}
override fun read(reader: JsonReader): ImmutableSet<E> {
if (reader.peek() != JsonToken.BEGIN_ARRAY) {
// не массив, возможно упрощение структуры "a": [value] -> "a": value
return ImmutableSet.of(elementAdapter.read(reader) ?: throw JsonSyntaxException("List does not accept nulls"))
}
reader.beginArray()
val builder = ImmutableSet.Builder<E>()
while (reader.peek() != JsonToken.END_ARRAY) {
val readObject = elementAdapter.read(reader) ?: throw JsonSyntaxException("List does not accept nulls")
builder.add(readObject)
}
reader.endArray()
return builder.build()
}
}

View File

@ -5,8 +5,8 @@ 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) }
class LazyTypeProvider<T : Any?>(private val bound: Class<T>, private val resolver: (Class<T>) -> TypeAdapter<T> = Starbound::getTypeAdapter) : TypeAdapter<T>() {
private val resolved by lazy { resolver(bound) }
override fun write(out: JsonWriter, value: T) {
resolved.write(out, value)