Сохранение Json структуры в KConcreteTypeAdapter
This commit is contained in:
parent
e5728e5ec9
commit
d016aa807c
@ -163,7 +163,7 @@ fun main() {
|
|||||||
//client.world!!.parallax = Starbound.parallaxAccess["garden"]
|
//client.world!!.parallax = Starbound.parallaxAccess["garden"]
|
||||||
|
|
||||||
for (i in 0 .. 16) {
|
for (i in 0 .. 16) {
|
||||||
val item = ItemEntity(client.world!!, Starbound.itemAccess["brain"]!!)
|
val item = ItemEntity(client.world!!, Starbound.itemAccess["money"]!!)
|
||||||
|
|
||||||
item.position = Vector2d(600.0 + 16.0 + i, 721.0 + 48.0)
|
item.position = Vector2d(600.0 + 16.0 + i, 721.0 + 48.0)
|
||||||
item.spawn()
|
item.spawn()
|
||||||
|
@ -458,7 +458,5 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items.values.stream().filter { it.currency != null }.forEach(::println)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.google.common.collect.ImmutableMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает глубокую неизменяемую копию [input] примитивов/List'ов/Map'ов
|
||||||
|
*/
|
||||||
|
fun enrollList(input: List<Any>, interner: (String) -> String = String::intern): ImmutableList<Any> {
|
||||||
|
val builder = ImmutableList.builder<Any>()
|
||||||
|
|
||||||
|
for (v in input) {
|
||||||
|
when (v) {
|
||||||
|
is Map<*, *> -> builder.add(enrollMap(v as Map<String, Any>, interner))
|
||||||
|
is List<*> -> builder.add(enrollList(v as List<Any>, interner))
|
||||||
|
else -> builder.add((v as? String)?.let(interner) ?: v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает глубокую неизменяемую копию [input] примитивов/List'ов/Map'ов
|
||||||
|
*/
|
||||||
|
fun enrollMap(input: Map<String, Any>, interner: (String) -> String = String::intern): ImmutableMap<String, Any> {
|
||||||
|
val builder = ImmutableMap.builder<String, Any>()
|
||||||
|
|
||||||
|
for ((k, v) in input) {
|
||||||
|
when (v) {
|
||||||
|
is Map<*, *> -> builder.put(interner(k), enrollMap(v as Map<String, Any>))
|
||||||
|
is List<*> -> builder.put(interner(k), enrollList(v as List<Any>))
|
||||||
|
else -> builder.put(interner(k), (v as? String)?.let(interner) ?: v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.google.gson.JsonNull
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonPrimitive
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||||
|
|
||||||
|
private fun flattenJsonPrimitive(input: JsonPrimitive): Any {
|
||||||
|
if (input.isNumber) {
|
||||||
|
return input.asNumber
|
||||||
|
} else if (input.isString) {
|
||||||
|
return input.asString.intern()
|
||||||
|
} else {
|
||||||
|
return input.asBoolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun flattenJsonArray(input: JsonArray): ArrayList<Any> {
|
||||||
|
val flattened = ArrayList<Any>(input.size())
|
||||||
|
|
||||||
|
for (v in input) {
|
||||||
|
when (v) {
|
||||||
|
is JsonObject -> flattened.add(flattenJsonObject(v))
|
||||||
|
is JsonArray -> flattened.add(flattenJsonArray(v))
|
||||||
|
is JsonPrimitive -> flattened.add(flattenJsonPrimitive(v))
|
||||||
|
// is JsonNull -> baked.add(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flattened
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun flattenJsonObject(input: JsonObject): Object2ObjectArrayMap<String, Any> {
|
||||||
|
val flattened = Object2ObjectArrayMap<String, Any>()
|
||||||
|
|
||||||
|
for ((k, v) in input.entrySet()) {
|
||||||
|
when (v) {
|
||||||
|
is JsonObject -> flattened[k] = flattenJsonObject(v)
|
||||||
|
is JsonArray -> flattened[k] = flattenJsonArray(v)
|
||||||
|
is JsonPrimitive -> flattened[k] = flattenJsonPrimitive(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flattened
|
||||||
|
}
|
||||||
|
|
||||||
|
fun flattenJsonElement(input: JsonElement): Any? {
|
||||||
|
return when (input) {
|
||||||
|
is JsonObject -> flattenJsonObject(input)
|
||||||
|
is JsonArray -> flattenJsonArray(input)
|
||||||
|
is JsonPrimitive -> flattenJsonPrimitive(input)
|
||||||
|
is JsonNull -> null
|
||||||
|
else -> throw IllegalArgumentException("Unknown argument $input")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает глубокую изменяемую копию [input] примитивов/List'ов/Map'ов
|
||||||
|
*/
|
||||||
|
fun flattenList(input: List<Any>): ArrayList<Any> {
|
||||||
|
val list = ArrayList<Any>(input.size)
|
||||||
|
|
||||||
|
for (v in input) {
|
||||||
|
when (v) {
|
||||||
|
is Map<*, *> -> list.add(flattenMap(v as Map<String, Any>))
|
||||||
|
is List<*> -> list.add(flattenList(v as List<Any>))
|
||||||
|
else -> list.add(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
fun flattenMap(input: Map<String, Any>): Object2ObjectArrayMap<String, Any> {
|
||||||
|
val map = Object2ObjectArrayMap<String, Any>()
|
||||||
|
|
||||||
|
for ((k, v) in input) {
|
||||||
|
when (v) {
|
||||||
|
is Map<*, *> -> map[k] = flattenMap(v as Map<String, Any>)
|
||||||
|
is List<*> -> map[k] = flattenList(v as List<Any>)
|
||||||
|
else -> map[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map
|
||||||
|
}
|
@ -1,124 +1,8 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs
|
package ru.dbotthepony.kstarbound.defs
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
import com.google.gson.*
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||||
|
|
||||||
private fun flattenJsonPrimitive(input: JsonPrimitive): Any {
|
|
||||||
if (input.isNumber) {
|
|
||||||
return input.asNumber
|
|
||||||
} else if (input.isString) {
|
|
||||||
return input.asString.intern()
|
|
||||||
} else {
|
|
||||||
return input.asBoolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenJsonArray(input: JsonArray): ArrayList<Any> {
|
|
||||||
val flattened = ArrayList<Any>(input.size())
|
|
||||||
|
|
||||||
for (v in input) {
|
|
||||||
when (v) {
|
|
||||||
is JsonObject -> flattened.add(flattenJsonObject(v))
|
|
||||||
is JsonArray -> flattened.add(flattenJsonArray(v))
|
|
||||||
is JsonPrimitive -> flattened.add(flattenJsonPrimitive(v))
|
|
||||||
// is JsonNull -> baked.add(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return flattened
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenJsonObject(input: JsonObject): Object2ObjectArrayMap<String, Any> {
|
|
||||||
val flattened = Object2ObjectArrayMap<String, Any>()
|
|
||||||
|
|
||||||
for ((k, v) in input.entrySet()) {
|
|
||||||
when (v) {
|
|
||||||
is JsonObject -> flattened[k] = flattenJsonObject(v)
|
|
||||||
is JsonArray -> flattened[k] = flattenJsonArray(v)
|
|
||||||
is JsonPrimitive -> flattened[k] = flattenJsonPrimitive(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return flattened
|
|
||||||
}
|
|
||||||
|
|
||||||
fun flattenJsonElement(input: JsonElement): Any? {
|
|
||||||
return when (input) {
|
|
||||||
is JsonObject -> flattenJsonObject(input)
|
|
||||||
is JsonArray -> flattenJsonArray(input)
|
|
||||||
is JsonPrimitive -> flattenJsonPrimitive(input)
|
|
||||||
is JsonNull -> null
|
|
||||||
else -> throw IllegalArgumentException("Unknown argument $input")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Возвращает глубокую неизменяемую копию [input] примитивов/List'ов/Map'ов
|
|
||||||
*/
|
|
||||||
fun enrollList(input: List<Any>): ImmutableList<Any> {
|
|
||||||
val builder = ImmutableList.builder<Any>()
|
|
||||||
|
|
||||||
for (v in input) {
|
|
||||||
when (v) {
|
|
||||||
is Map<*, *> -> builder.add(enrollMap(v as Map<String, Any>))
|
|
||||||
is List<*> -> builder.add(enrollList(v as List<Any>))
|
|
||||||
else -> builder.add((v as? String)?.intern() ?: v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Возвращает глубокую неизменяемую копию [input] примитивов/List'ов/Map'ов
|
|
||||||
*/
|
|
||||||
fun enrollMap(input: Map<String, Any>): ImmutableMap<String, Any> {
|
|
||||||
val builder = ImmutableMap.builder<String, Any>()
|
|
||||||
|
|
||||||
for ((k, v) in input) {
|
|
||||||
when (v) {
|
|
||||||
is Map<*, *> -> builder.put(k.intern(), enrollMap(v as Map<String, Any>))
|
|
||||||
is List<*> -> builder.put(k.intern(), enrollList(v as List<Any>))
|
|
||||||
else -> builder.put(k.intern(), (v as? String)?.intern() ?: v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Возвращает глубокую изменяемую копию [input] примитивов/List'ов/Map'ов
|
|
||||||
*/
|
|
||||||
fun flattenList(input: List<Any>): ArrayList<Any> {
|
|
||||||
val list = ArrayList<Any>(input.size)
|
|
||||||
|
|
||||||
for (v in input) {
|
|
||||||
when (v) {
|
|
||||||
is Map<*, *> -> list.add(flattenMap(v as Map<String, Any>))
|
|
||||||
is List<*> -> list.add(flattenList(v as List<Any>))
|
|
||||||
else -> list.add(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
fun flattenMap(input: Map<String, Any>): Object2ObjectArrayMap<String, Any> {
|
|
||||||
val map = Object2ObjectArrayMap<String, Any>()
|
|
||||||
|
|
||||||
for ((k, v) in input) {
|
|
||||||
when (v) {
|
|
||||||
is Map<*, *> -> map[k] = flattenMap(v as Map<String, Any>)
|
|
||||||
is List<*> -> map[k] = flattenList(v as List<Any>)
|
|
||||||
else -> map[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Базовый класс описания прототипа игрового объекта
|
* Базовый класс описания прототипа игрового объекта
|
||||||
*
|
*
|
||||||
|
@ -172,6 +172,13 @@ data class ItemDefinition(
|
|||||||
* Lua скрипты для выполнения
|
* Lua скрипты для выполнения
|
||||||
*/
|
*/
|
||||||
val scripts: List<String> = listOf(),
|
val scripts: List<String> = listOf(),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Прототип данного предмета, как JSON структура
|
||||||
|
*
|
||||||
|
* Имеет смысл только для Lua скриптов
|
||||||
|
*/
|
||||||
|
val json: Map<String, Any>,
|
||||||
) {
|
) {
|
||||||
data class FossilSetDescription(
|
data class FossilSetDescription(
|
||||||
val price: Long = 0L,
|
val price: Long = 0L,
|
||||||
@ -221,6 +228,8 @@ data class ItemDefinition(
|
|||||||
|
|
||||||
.list(ItemDefinition::scripts, transformer = Starbound::readingFolderListTransformer)
|
.list(ItemDefinition::scripts, transformer = Starbound::readingFolderListTransformer)
|
||||||
|
|
||||||
|
.storesJson()
|
||||||
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val FOSSIL_ADAPTER = KConcreteTypeAdapter.Builder(FossilSetDescription::class)
|
val FOSSIL_ADAPTER = KConcreteTypeAdapter.Builder(FossilSetDescription::class)
|
||||||
|
@ -218,12 +218,14 @@ data class RenderMatch(
|
|||||||
val PIECE_ADAPTER = KConcreteTypeAdapter.Builder(Piece::class)
|
val PIECE_ADAPTER = KConcreteTypeAdapter.Builder(Piece::class)
|
||||||
.plain(Piece::name)
|
.plain(Piece::name)
|
||||||
.plain(Piece::offset)
|
.plain(Piece::offset)
|
||||||
.build(asList = true)
|
.inputAsList()
|
||||||
|
.build()
|
||||||
|
|
||||||
val MATCHER_ADAPTER = KConcreteTypeAdapter.Builder(Matcher::class)
|
val MATCHER_ADAPTER = KConcreteTypeAdapter.Builder(Matcher::class)
|
||||||
.plain(Matcher::offset)
|
.plain(Matcher::offset)
|
||||||
.plain(Matcher::ruleName)
|
.plain(Matcher::ruleName)
|
||||||
.build(asList = true)
|
.inputAsList()
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +243,8 @@ data class RenderMatchList(
|
|||||||
val ADAPTER = KConcreteTypeAdapter.Builder(RenderMatchList::class)
|
val ADAPTER = KConcreteTypeAdapter.Builder(RenderMatchList::class)
|
||||||
.plain(RenderMatchList::name)
|
.plain(RenderMatchList::name)
|
||||||
.list(RenderMatchList::list)
|
.list(RenderMatchList::list)
|
||||||
.build(asList = true)
|
.inputAsList()
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,12 @@ import com.google.common.collect.ImmutableList
|
|||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
import com.google.common.collect.Interner
|
import com.google.common.collect.Interner
|
||||||
import com.google.common.collect.Interners
|
import com.google.common.collect.Interners
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonParseException
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.internal.bind.JsonTreeReader
|
||||||
import com.google.gson.internal.bind.TypeAdapters
|
import com.google.gson.internal.bind.TypeAdapters
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonToken
|
import com.google.gson.stream.JsonToken
|
||||||
@ -14,11 +18,15 @@ import it.unimi.dsi.fastutil.objects.Object2IntArrayMap
|
|||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.defs.enrollList
|
||||||
|
import ru.dbotthepony.kstarbound.defs.enrollMap
|
||||||
|
import ru.dbotthepony.kstarbound.defs.flattenJsonElement
|
||||||
import ru.dbotthepony.kstarbound.getValue
|
import ru.dbotthepony.kstarbound.getValue
|
||||||
import ru.dbotthepony.kstarbound.setValue
|
import ru.dbotthepony.kstarbound.setValue
|
||||||
import java.lang.reflect.Constructor
|
import java.lang.reflect.Constructor
|
||||||
import kotlin.jvm.internal.DefaultConstructorMarker
|
import kotlin.jvm.internal.DefaultConstructorMarker
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.*
|
||||||
|
import kotlin.reflect.full.isSubclassOf
|
||||||
import kotlin.reflect.full.isSuperclassOf
|
import kotlin.reflect.full.isSuperclassOf
|
||||||
import kotlin.reflect.full.isSupertypeOf
|
import kotlin.reflect.full.isSupertypeOf
|
||||||
import kotlin.reflect.full.memberProperties
|
import kotlin.reflect.full.memberProperties
|
||||||
@ -173,8 +181,9 @@ private data class PackedProperty<Clazz : Any, T>(
|
|||||||
class KConcreteTypeAdapter<T : Any> private constructor(
|
class KConcreteTypeAdapter<T : Any> private constructor(
|
||||||
val bound: KClass<T>,
|
val bound: KClass<T>,
|
||||||
private val types: ImmutableList<PackedProperty<T, *>>,
|
private val types: ImmutableList<PackedProperty<T, *>>,
|
||||||
private val asJsonArray: Boolean = false,
|
val asJsonArray: Boolean,
|
||||||
val stringInterner: Interner<String>
|
val stringInterner: Interner<String>,
|
||||||
|
val storesJson: Boolean
|
||||||
) : TypeAdapter<T>() {
|
) : TypeAdapter<T>() {
|
||||||
private val mapped = Object2IntArrayMap<String>()
|
private val mapped = Object2IntArrayMap<String>()
|
||||||
private val loggedMisses = ObjectArraySet<String>()
|
private val loggedMisses = ObjectArraySet<String>()
|
||||||
@ -193,10 +202,17 @@ class KConcreteTypeAdapter<T : Any> private constructor(
|
|||||||
* Обычный конструктор класса (без флагов "значения по умолчанию")
|
* Обычный конструктор класса (без флагов "значения по умолчанию")
|
||||||
*/
|
*/
|
||||||
private val regularFactory: KFunction<T> = bound.constructors.firstOrNull first@{
|
private val regularFactory: KFunction<T> = bound.constructors.firstOrNull first@{
|
||||||
if (it.parameters.size == types.size) {
|
var requiredSize = types.size
|
||||||
val iterator = types.iterator()
|
|
||||||
|
|
||||||
for (param in it.parameters) {
|
if (storesJson)
|
||||||
|
requiredSize++
|
||||||
|
|
||||||
|
if (it.parameters.size == requiredSize) {
|
||||||
|
val iterator = types.iterator()
|
||||||
|
val factoryIterator = it.parameters.iterator()
|
||||||
|
|
||||||
|
while (factoryIterator.hasNext() && iterator.hasNext()) {
|
||||||
|
val param = factoryIterator.next()
|
||||||
val nextParam = iterator.next()
|
val nextParam = iterator.next()
|
||||||
|
|
||||||
val a = param.type
|
val a = param.type
|
||||||
@ -207,6 +223,20 @@ class KConcreteTypeAdapter<T : Any> private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (storesJson) {
|
||||||
|
val nextParam = factoryIterator.next()
|
||||||
|
|
||||||
|
if (asJsonArray) {
|
||||||
|
if (!(nextParam.type.classifier as KClass<*>).isSubclassOf(List::class)) {
|
||||||
|
return@first false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!(nextParam.type.classifier as KClass<*>).isSubclassOf(Map::class)) {
|
||||||
|
return@first false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return@first true
|
return@first true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,14 +247,20 @@ class KConcreteTypeAdapter<T : Any> private constructor(
|
|||||||
* Синтетический конструктор класса, который создаётся Kotlin'ном, для создания классов со значениями по умолчанию
|
* Синтетический конструктор класса, который создаётся Kotlin'ном, для создания классов со значениями по умолчанию
|
||||||
*/
|
*/
|
||||||
private val syntheticFactory: Constructor<T>? = try {
|
private val syntheticFactory: Constructor<T>? = try {
|
||||||
bound.java.getDeclaredConstructor(*types.map { (it.returnType.classifier as KClass<*>).java }.also {
|
val typelist = types.map { (it.returnType.classifier as KClass<*>).java }.toMutableList()
|
||||||
it as MutableList
|
|
||||||
|
|
||||||
for (i in 0 until (if (types.size % 31 == 0) types.size / 31 else types.size / 31 + 1))
|
if (storesJson)
|
||||||
it.add(Int::class.java)
|
if (asJsonArray)
|
||||||
|
typelist.add(List::class.java)
|
||||||
|
else
|
||||||
|
typelist.add(Map::class.java)
|
||||||
|
|
||||||
it.add(DefaultConstructorMarker::class.java)
|
for (i in 0 until (if (types.size % 31 == 0) types.size / 31 else types.size / 31 + 1))
|
||||||
}.toTypedArray())
|
typelist.add(Int::class.java)
|
||||||
|
|
||||||
|
typelist.add(DefaultConstructorMarker::class.java)
|
||||||
|
|
||||||
|
bound.java.getDeclaredConstructor(*typelist.toTypedArray())
|
||||||
} catch(_: NoSuchMethodException) {
|
} catch(_: NoSuchMethodException) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -270,30 +306,51 @@ class KConcreteTypeAdapter<T : Any> private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun read(reader: JsonReader): T {
|
override fun read(reader: JsonReader): T {
|
||||||
if (asJsonArray) {
|
|
||||||
reader.beginArray()
|
|
||||||
} else {
|
|
||||||
reader.beginObject()
|
|
||||||
}
|
|
||||||
|
|
||||||
// таблица присутствия значений (если значение true то на i было значение внутри json)
|
// таблица присутствия значений (если значение true то на i было значение внутри json)
|
||||||
val presentValues = BooleanArray(types.size)
|
val presentValues = BooleanArray(types.size + (if (storesJson) 1 else 0))
|
||||||
val readValues = arrayOfNulls<Any>(types.size)
|
val readValues = arrayOfNulls<Any>(types.size + (if (storesJson) 1 else 0))
|
||||||
|
|
||||||
|
if (storesJson)
|
||||||
|
presentValues[presentValues.size - 1] = true
|
||||||
|
|
||||||
|
@Suppress("name_shadowing")
|
||||||
|
var reader = reader
|
||||||
|
|
||||||
// Если нам необходимо читать объект как набор данных массива, то давай
|
// Если нам необходимо читать объект как набор данных массива, то давай
|
||||||
if (asJsonArray) {
|
if (asJsonArray) {
|
||||||
val iterator = types.iterator()
|
val iterator = types.iterator()
|
||||||
var fieldId = 0
|
var fieldId = 0
|
||||||
|
|
||||||
|
if (storesJson) {
|
||||||
|
val readArray = TypeAdapters.JSON_ELEMENT.read(reader)
|
||||||
|
|
||||||
|
if (readArray !is JsonArray) {
|
||||||
|
throw JsonParseException("Expected JSON element to be an Array, ${readArray::class.qualifiedName} given")
|
||||||
|
}
|
||||||
|
|
||||||
|
reader = JsonTreeReader(readArray)
|
||||||
|
readValues[readValues.size - 1] = enrollList(flattenJsonElement(readArray) as List<Any>, stringInterner::intern)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.beginArray()
|
||||||
|
|
||||||
while (reader.peek() != JsonToken.END_ARRAY) {
|
while (reader.peek() != JsonToken.END_ARRAY) {
|
||||||
if (!iterator.hasNext()) {
|
if (!iterator.hasNext()) {
|
||||||
val name = fieldId.toString()
|
val name = fieldId.toString()
|
||||||
|
|
||||||
if (loggedMisses.add(name)) {
|
if (loggedMisses.add(name)) {
|
||||||
if (currentSymbolicName == null) {
|
if (storesJson) {
|
||||||
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field")
|
if (currentSymbolicName == null) {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field, it will be only visible to Lua scripts")
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field, it will be only visible to Lua scripts (reading: $currentSymbolicName)")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field (reading: $currentSymbolicName)")
|
if (currentSymbolicName == null) {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field")
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field (reading: $currentSymbolicName)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,16 +374,37 @@ class KConcreteTypeAdapter<T : Any> private constructor(
|
|||||||
}
|
}
|
||||||
// иначе - читаем как json object
|
// иначе - читаем как json object
|
||||||
} else {
|
} else {
|
||||||
|
if (storesJson) {
|
||||||
|
val readMap = TypeAdapters.JSON_ELEMENT.read(reader)
|
||||||
|
|
||||||
|
if (readMap !is JsonObject) {
|
||||||
|
throw JsonParseException("Expected JSON element to be a Map, ${readMap::class.qualifiedName} given")
|
||||||
|
}
|
||||||
|
|
||||||
|
reader = JsonTreeReader(readMap)
|
||||||
|
readValues[readValues.size - 1] = enrollMap(flattenJsonElement(readMap) as Map<String, Any>, stringInterner::intern)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.beginObject()
|
||||||
|
|
||||||
while (reader.peek() != JsonToken.END_OBJECT) {
|
while (reader.peek() != JsonToken.END_OBJECT) {
|
||||||
val name = reader.nextName()
|
val name = reader.nextName()
|
||||||
val fieldId = mapped.getInt(name)
|
val fieldId = mapped.getInt(name)
|
||||||
|
|
||||||
if (fieldId == -1) {
|
if (fieldId == -1) {
|
||||||
if (loggedMisses.add(name)) {
|
if (loggedMisses.add(name)) {
|
||||||
if (currentSymbolicName == null) {
|
if (storesJson) {
|
||||||
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field")
|
if (currentSymbolicName == null) {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field, it will be only visible to Lua scripts")
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field, it will be only visible to Lua scripts (reading: $currentSymbolicName)")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field (reading: $currentSymbolicName)")
|
if (currentSymbolicName == null) {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field")
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.simpleName} has no such field (reading: $currentSymbolicName)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,6 +517,21 @@ class KConcreteTypeAdapter<T : Any> private constructor(
|
|||||||
private val types = ArrayList<PackedProperty<T, *>>()
|
private val types = ArrayList<PackedProperty<T, *>>()
|
||||||
var stringInterner: Interner<String> = Interners.newWeakInterner()
|
var stringInterner: Interner<String> = Interners.newWeakInterner()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Принимает ли класс *последним* аргументом JSON объект
|
||||||
|
*
|
||||||
|
* На самом деле, JSON "заворачивается" в [ImmutableMap], или [ImmutableList] если указано [asList]/[inputAsList]
|
||||||
|
*
|
||||||
|
* Поэтому, конструктор класса ОБЯЗАН принимать [Map]/[ImmutableMap] или [List]/[ImmutableList] первым аргументом,
|
||||||
|
* иначе поиск конструктора завершится неудчаей
|
||||||
|
*/
|
||||||
|
var storesJson = false
|
||||||
|
|
||||||
|
fun storesJson(): Builder<T> {
|
||||||
|
storesJson = true
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
fun specifyStringInterner(interner: Interner<String>): Builder<T> {
|
fun specifyStringInterner(interner: Interner<String>): Builder<T> {
|
||||||
stringInterner = interner
|
stringInterner = interner
|
||||||
return this
|
return this
|
||||||
@ -573,12 +666,25 @@ class KConcreteTypeAdapter<T : Any> private constructor(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(asList: Boolean = false): KConcreteTypeAdapter<T> {
|
var asList = false
|
||||||
|
|
||||||
|
fun inputAsMap(): Builder<T> {
|
||||||
|
asList = false
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun inputAsList(): Builder<T> {
|
||||||
|
asList = true
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): KConcreteTypeAdapter<T> {
|
||||||
return KConcreteTypeAdapter(
|
return KConcreteTypeAdapter(
|
||||||
bound = clazz,
|
bound = clazz,
|
||||||
types = ImmutableList.copyOf(types),
|
types = ImmutableList.copyOf(types),
|
||||||
asJsonArray = asList,
|
asJsonArray = asList,
|
||||||
stringInterner = stringInterner
|
stringInterner = stringInterner,
|
||||||
|
storesJson = storesJson
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user