KConcreteTypeAdapter functional
This commit is contained in:
parent
e2b17f5761
commit
d3396ddb7c
@ -1,31 +1,17 @@
|
|||||||
package ru.dbotthepony.kstarbound
|
package ru.dbotthepony.kstarbound
|
||||||
|
|
||||||
import com.google.gson.JsonElement
|
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.lwjgl.Version
|
import org.lwjgl.Version
|
||||||
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
||||||
import ru.dbotthepony.kbox2d.api.*
|
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.defs.MaterialModifier
|
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||||
import ru.dbotthepony.kstarbound.defs.TileDefinition
|
|
||||||
import ru.dbotthepony.kstarbound.defs.projectile.ProjectilePhysics
|
|
||||||
import ru.dbotthepony.kstarbound.defs.world.dungeon.DungeonWorldDef
|
|
||||||
import ru.dbotthepony.kstarbound.io.*
|
import ru.dbotthepony.kstarbound.io.*
|
||||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
|
|
||||||
import ru.dbotthepony.kstarbound.world.Chunk
|
|
||||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Move
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
|
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.*
|
|
||||||
import java.util.zip.Inflater
|
import java.util.zip.Inflater
|
||||||
|
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
@ -41,34 +27,6 @@ fun main() {
|
|||||||
//return
|
//return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true) {
|
|
||||||
val input = "{\n" +
|
|
||||||
" \"modId\" : 26,\n" +
|
|
||||||
" \"modName\" : \"aegisalt\",\n" +
|
|
||||||
" \"itemDrop\" : \"aegisaltore\",\n" +
|
|
||||||
" \"description\" : \"Aegisalt.\",\n" +
|
|
||||||
" \"health\" : 5,\n" +
|
|
||||||
" \"harvestLevel\" : 5,\n" +
|
|
||||||
" \"breaksWithTile\" : true,\n" +
|
|
||||||
"\n" +
|
|
||||||
" \"miningSounds\" : [ \"/sfx/tools/pickaxe_ore.ogg\", \"/sfx/tools/pickaxe_ore2.ogg\" ],\n" +
|
|
||||||
" \"miningParticle\" : \"orespark\",\n" +
|
|
||||||
"\n" +
|
|
||||||
" \"renderTemplate\" : \"/tiles/classicmaterialtemplate.config\",\n" +
|
|
||||||
" \"renderParameters\" : {\n" +
|
|
||||||
" \"texture\" : \"aegisalt.png\",\n" +
|
|
||||||
" \"variants\" : 8,\n" +
|
|
||||||
" \"multiColored\" : false,\n" +
|
|
||||||
" \"zLevel\" : 0\n" +
|
|
||||||
" }\n" +
|
|
||||||
"}\n"
|
|
||||||
|
|
||||||
val json = Starbound.gson.fromJson(input, MaterialModifier::class.java)
|
|
||||||
|
|
||||||
println(json)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val db = BTreeDB(File("F:\\SteamLibrary\\steamapps\\common\\Starbound - Unstable\\storage\\universe\\389760395_938904237_-238610574_5.world"))
|
val db = BTreeDB(File("F:\\SteamLibrary\\steamapps\\common\\Starbound - Unstable\\storage\\universe\\389760395_938904237_-238610574_5.world"))
|
||||||
//val db = BTreeDB(File("world.world"))
|
//val db = BTreeDB(File("world.world"))
|
||||||
|
|
||||||
@ -133,6 +91,34 @@ fun main() {
|
|||||||
val ent = PlayerEntity(client.world!!)
|
val ent = PlayerEntity(client.world!!)
|
||||||
|
|
||||||
Starbound.onInitialize {
|
Starbound.onInitialize {
|
||||||
|
if (true) {
|
||||||
|
val input = "{\n" +
|
||||||
|
" \"modId\" : 26,\n" +
|
||||||
|
" \"modName\" : \"aegisalt\",\n" +
|
||||||
|
" \"itemDrop\" : \"aegisaltore\",\n" +
|
||||||
|
" \"description\" : \"Aegisalt.\",\n" +
|
||||||
|
" \"health\" : 5,\n" +
|
||||||
|
" \"harvestLevel\" : 5,\n" +
|
||||||
|
" \"breaksWithTile\" : true,\n" +
|
||||||
|
"\n" +
|
||||||
|
" \"miningSounds\" : [ \"/sfx/tools/pickaxe_ore.ogg\", \"/sfx/tools/pickaxe_ore2.ogg\" ],\n" +
|
||||||
|
" \"miningParticle\" : \"orespark\",\n" +
|
||||||
|
"\n" +
|
||||||
|
" \"renderTemplate\" : \"/tiles/classicmaterialtemplate.config\",\n" +
|
||||||
|
" \"renderParameters\" : {\n" +
|
||||||
|
" \"texture\" : \"aegisalt.png\",\n" +
|
||||||
|
" \"variants\" : 8,\n" +
|
||||||
|
" \"multiColored\" : false,\n" +
|
||||||
|
" \"zLevel\" : 0\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n"
|
||||||
|
|
||||||
|
val json = Starbound.gson.fromJson(input, MaterialModifier::class.java)
|
||||||
|
|
||||||
|
println(json)
|
||||||
|
return@onInitialize
|
||||||
|
}
|
||||||
|
|
||||||
var find = 0L
|
var find = 0L
|
||||||
var set = 0L
|
var set = 0L
|
||||||
var parse = 0L
|
var parse = 0L
|
||||||
|
@ -9,6 +9,9 @@ import ru.dbotthepony.kstarbound.api.getPathFilename
|
|||||||
import ru.dbotthepony.kstarbound.api.getPathFolder
|
import ru.dbotthepony.kstarbound.api.getPathFolder
|
||||||
import ru.dbotthepony.kstarbound.defs.*
|
import ru.dbotthepony.kstarbound.defs.*
|
||||||
import ru.dbotthepony.kstarbound.defs.projectile.*
|
import ru.dbotthepony.kstarbound.defs.projectile.*
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.RenderParameters
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.RenderTemplate
|
||||||
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
|
import ru.dbotthepony.kstarbound.defs.world.SkyParameters
|
||||||
import ru.dbotthepony.kstarbound.defs.world.dungeon.DungeonWorldDef
|
import ru.dbotthepony.kstarbound.defs.world.dungeon.DungeonWorldDef
|
||||||
import ru.dbotthepony.kstarbound.io.*
|
import ru.dbotthepony.kstarbound.io.*
|
||||||
@ -72,6 +75,8 @@ object Starbound : IVFS {
|
|||||||
.also(ParallaxPrototype::registerGson)
|
.also(ParallaxPrototype::registerGson)
|
||||||
.also(JsonFunction::registerGson)
|
.also(JsonFunction::registerGson)
|
||||||
.also(MaterialModifier::registerGson)
|
.also(MaterialModifier::registerGson)
|
||||||
|
.also(RenderParameters::registerGson)
|
||||||
|
.also(RenderTemplate::registerGson)
|
||||||
|
|
||||||
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs
|
package ru.dbotthepony.kstarbound.defs.tile
|
||||||
|
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
|
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
|
||||||
@ -11,9 +11,10 @@ data class MaterialModifier(
|
|||||||
val health: Int,
|
val health: Int,
|
||||||
val harvestLevel: Int,
|
val harvestLevel: Int,
|
||||||
val breaksWithTile: Boolean,
|
val breaksWithTile: Boolean,
|
||||||
val miningSounds: List<String>,
|
val miningSounds: List<String> = listOf(),
|
||||||
val miningParticle: String,
|
val miningParticle: String? = null,
|
||||||
val renderTemplate: String,
|
val renderTemplate: RenderTemplate,
|
||||||
|
val renderParameters: RenderParameters
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
val ADAPTER = KConcreteTypeAdapter.Builder(MaterialModifier::class)
|
val ADAPTER = KConcreteTypeAdapter.Builder(MaterialModifier::class)
|
||||||
@ -26,7 +27,8 @@ data class MaterialModifier(
|
|||||||
.plain(MaterialModifier::breaksWithTile)
|
.plain(MaterialModifier::breaksWithTile)
|
||||||
.list(MaterialModifier::miningSounds, String::class.java)
|
.list(MaterialModifier::miningSounds, String::class.java)
|
||||||
.plain(MaterialModifier::miningParticle)
|
.plain(MaterialModifier::miningParticle)
|
||||||
.plain(MaterialModifier::renderTemplate)
|
.plain(MaterialModifier::renderTemplate, RenderTemplate.CACHE)
|
||||||
|
.plain(MaterialModifier::renderParameters)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun registerGson(gsonBuilder: GsonBuilder) {
|
fun registerGson(gsonBuilder: GsonBuilder) {
|
@ -0,0 +1,26 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs.tile
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
|
||||||
|
|
||||||
|
data class RenderParameters(
|
||||||
|
val texture: String,
|
||||||
|
val variants: Int,
|
||||||
|
val multiColored: Boolean,
|
||||||
|
val zLevel: Int,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val ADAPTER = KConcreteTypeAdapter.Builder(RenderParameters::class)
|
||||||
|
.plain(
|
||||||
|
RenderParameters::texture,
|
||||||
|
RenderParameters::variants,
|
||||||
|
RenderParameters::multiColored,
|
||||||
|
RenderParameters::zLevel,
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||||
|
gsonBuilder.registerTypeAdapter(RenderParameters::class.java, ADAPTER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,186 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs.tile
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
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
|
||||||
|
import ru.dbotthepony.kstarbound.defs.TileDefinition
|
||||||
|
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
|
||||||
|
import ru.dbotthepony.kstarbound.world.ITileGetter
|
||||||
|
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
data class RenderPiece(
|
||||||
|
val texture: String? = null,
|
||||||
|
val textureSize: Vector2i,
|
||||||
|
val texturePosition: Vector2i,
|
||||||
|
val colorStride: Vector2i? = null,
|
||||||
|
val variantStride: Vector2i? = null,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val ADAPTER = KConcreteTypeAdapter.Builder(RenderPiece::class)
|
||||||
|
.plain(
|
||||||
|
RenderPiece::texture,
|
||||||
|
RenderPiece::textureSize,
|
||||||
|
RenderPiece::texturePosition,
|
||||||
|
RenderPiece::colorStride,
|
||||||
|
RenderPiece::variantStride,
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RenderRule(
|
||||||
|
val type: String,
|
||||||
|
val matchHue: Boolean = false,
|
||||||
|
val inverse: Boolean = false,
|
||||||
|
) {
|
||||||
|
private fun doTest(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean {
|
||||||
|
return when (type) {
|
||||||
|
"EqualsSelf" -> getter[thisPos + offsetPos]?.def == thisRef
|
||||||
|
"Connects" -> getter[thisPos + offsetPos] != null
|
||||||
|
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean {
|
||||||
|
if (inverse) {
|
||||||
|
return !doTest(getter, thisRef, thisPos, offsetPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doTest(getter, thisRef, thisPos, offsetPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val ADAPTER = KConcreteTypeAdapter.Builder(RenderRule::class)
|
||||||
|
.plain(
|
||||||
|
RenderRule::type,
|
||||||
|
RenderRule::matchHue,
|
||||||
|
RenderRule::inverse,
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RenderRuleList(
|
||||||
|
val entries: List<RenderRule>
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val ADAPTER = KConcreteTypeAdapter.Builder(RenderRuleList::class)
|
||||||
|
.list(RenderRuleList::entries, RenderRule::class)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RenderMatch(
|
||||||
|
val pieces: List<Piece>? = null,
|
||||||
|
val matchAllPoints: List<Matcher>? = null,
|
||||||
|
val subMatches: List<RenderMatch>? = null,
|
||||||
|
val haltOnMatch: Boolean = false
|
||||||
|
) {
|
||||||
|
data class Piece(
|
||||||
|
val name: String,
|
||||||
|
val point: Vector2i
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Matcher(
|
||||||
|
val point: Vector2i,
|
||||||
|
val ruleName: String
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val ADAPTER = KConcreteTypeAdapter.Builder(RenderMatch::class)
|
||||||
|
.list(RenderMatch::pieces, Piece::class)
|
||||||
|
.list(RenderMatch::matchAllPoints, Matcher::class)
|
||||||
|
.list(RenderMatch::subMatches, RenderMatch::class)
|
||||||
|
.plain(RenderMatch::haltOnMatch)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val PIECE_ADAPTER = KConcreteTypeAdapter.Builder(Piece::class)
|
||||||
|
.plain(Piece::name)
|
||||||
|
.plain(Piece::point)
|
||||||
|
.build(asList = true)
|
||||||
|
|
||||||
|
val MATCHER_ADAPTER = KConcreteTypeAdapter.Builder(Matcher::class)
|
||||||
|
.plain(Matcher::point)
|
||||||
|
.plain(Matcher::ruleName)
|
||||||
|
.build(asList = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RenderMatchList(
|
||||||
|
val name: String,
|
||||||
|
val list: List<RenderMatch>
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val ADAPTER = KConcreteTypeAdapter.Builder(RenderMatchList::class)
|
||||||
|
.plain(RenderMatchList::name)
|
||||||
|
.list(RenderMatchList::list, RenderMatch::class)
|
||||||
|
.build(asList = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RenderTemplate(
|
||||||
|
val pieces: Map<String, RenderPiece>,
|
||||||
|
val representativePiece: String,
|
||||||
|
val matches: List<RenderMatchList>
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val ADAPTER = KConcreteTypeAdapter.Builder(RenderTemplate::class)
|
||||||
|
.map(RenderTemplate::pieces, RenderPiece::class.java)
|
||||||
|
.plain(RenderTemplate::representativePiece)
|
||||||
|
.list(RenderTemplate::matches, RenderMatchList::class.java)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||||
|
RenderPiece.ADAPTER.register(gsonBuilder)
|
||||||
|
RenderRule.ADAPTER.register(gsonBuilder)
|
||||||
|
RenderRuleList.ADAPTER.register(gsonBuilder)
|
||||||
|
RenderMatch.ADAPTER.register(gsonBuilder)
|
||||||
|
RenderMatch.PIECE_ADAPTER.register(gsonBuilder)
|
||||||
|
RenderMatch.MATCHER_ADAPTER.register(gsonBuilder)
|
||||||
|
RenderMatchList.ADAPTER.register(gsonBuilder)
|
||||||
|
|
||||||
|
ADAPTER.register(gsonBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val cache = ConcurrentHashMap<String, RenderTemplate>()
|
||||||
|
private val _readingFolder = ThreadLocal<String>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Случит переменной для указания из какой папки происходит чтение в данном потоке
|
||||||
|
*/
|
||||||
|
var readingFolder: String?
|
||||||
|
get() = _readingFolder.get()
|
||||||
|
set(value) { _readingFolder.set(value) }
|
||||||
|
|
||||||
|
val CACHE = object : TypeAdapter<RenderTemplate>() {
|
||||||
|
override fun write(out: JsonWriter, value: RenderTemplate) {
|
||||||
|
ADAPTER.write(out, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(reader: JsonReader): RenderTemplate {
|
||||||
|
if (reader.peek() != JsonToken.STRING) {
|
||||||
|
throw JsonSyntaxException("Expected string as input for render template cache retriever")
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = reader.nextString()
|
||||||
|
|
||||||
|
if (path[0] != '/') {
|
||||||
|
// относительный путь
|
||||||
|
|
||||||
|
val readingFolder = readingFolder ?: throw NullPointerException("Currently read folder is not specified")
|
||||||
|
path = "$readingFolder/$path"
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache.computeIfAbsent(path) {
|
||||||
|
return@computeIfAbsent Starbound.gson.fromJson(Starbound.getReader(it), RenderTemplate::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ package ru.dbotthepony.kstarbound.io
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.GsonBuilder
|
||||||
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.TypeAdapters
|
import com.google.gson.internal.bind.TypeAdapters
|
||||||
@ -14,24 +14,15 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
|||||||
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 kotlin.reflect.KClass
|
import java.lang.reflect.Constructor
|
||||||
import kotlin.reflect.KFunction
|
import kotlin.jvm.internal.DefaultConstructorMarker
|
||||||
import kotlin.reflect.KProperty1
|
import kotlin.reflect.*
|
||||||
import kotlin.reflect.KType
|
|
||||||
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
|
||||||
|
|
||||||
private class PassthroughAdapter<T : Any?>(private val bound: Class<T>) : TypeAdapter<T?>() {
|
@Suppress("unchecked_cast")
|
||||||
override fun write(out: JsonWriter, value: T?) {
|
private fun <T> resolveBound(bound: Class<T>): TypeAdapter<T> {
|
||||||
Starbound.gson.toJson(Starbound.gson.toJsonTree(value, bound) as JsonElement, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(reader: JsonReader): T? {
|
|
||||||
return Starbound.gson.fromJson(reader, bound)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> resolveBound(bound: Class<T>): TypeAdapter<T>? {
|
|
||||||
return when (bound) {
|
return when (bound) {
|
||||||
Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T>
|
Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T>
|
||||||
Double::class.java -> TypeAdapters.DOUBLE as TypeAdapter<T>
|
Double::class.java -> TypeAdapters.DOUBLE as TypeAdapter<T>
|
||||||
@ -53,14 +44,8 @@ class ListAdapter<T>(private val bound: Class<T>) : TypeAdapter<List<T>>() {
|
|||||||
|
|
||||||
val resolvedBound = resolvedBound
|
val resolvedBound = resolvedBound
|
||||||
|
|
||||||
if (resolvedBound != null) {
|
for (v in value) {
|
||||||
for (v in value) {
|
resolvedBound.write(out, v)
|
||||||
resolvedBound.write(out, v)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (v in value) {
|
|
||||||
Starbound.gson.toJson(Starbound.gson.toJsonTree(v, bound) as JsonElement, out)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.endArray()
|
out.endArray()
|
||||||
@ -72,16 +57,9 @@ class ListAdapter<T>(private val bound: Class<T>) : TypeAdapter<List<T>>() {
|
|||||||
val builder = ImmutableList.builder<T>()
|
val builder = ImmutableList.builder<T>()
|
||||||
val resolvedBound = resolvedBound
|
val resolvedBound = resolvedBound
|
||||||
|
|
||||||
if (resolvedBound != null) {
|
while (reader.peek() != JsonToken.END_ARRAY) {
|
||||||
while (reader.peek() != JsonToken.END_ARRAY) {
|
val readObject = resolvedBound.read(reader) ?: throw JsonSyntaxException("List does not accept nulls")
|
||||||
val readObject = resolvedBound.read(reader) ?: throw JsonSyntaxException("List does not accept nulls")
|
builder.add(readObject as T)
|
||||||
builder.add(readObject as T)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (reader.peek() != JsonToken.END_ARRAY) {
|
|
||||||
val readObject = Starbound.gson.fromJson<T>(reader, bound) ?: throw JsonSyntaxException("List does not accept nulls")
|
|
||||||
builder.add(readObject)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.endArray()
|
reader.endArray()
|
||||||
@ -105,34 +83,11 @@ class MapAdapter<K, V>(private val boundKey: Class<K>, private val boundValue: C
|
|||||||
val resolvedKey = resolvedKey
|
val resolvedKey = resolvedKey
|
||||||
val resolvedValue = resolvedValue
|
val resolvedValue = resolvedValue
|
||||||
|
|
||||||
if (resolvedKey != null && resolvedValue != null) {
|
for ((k, v) in value) {
|
||||||
for ((k, v) in value) {
|
out.beginArray()
|
||||||
out.beginArray()
|
resolvedKey.write(out, k)
|
||||||
resolvedKey.write(out, k)
|
resolvedValue.write(out, v)
|
||||||
resolvedValue.write(out, v)
|
out.endArray()
|
||||||
out.endArray()
|
|
||||||
}
|
|
||||||
} else if (resolvedKey != null) {
|
|
||||||
for ((k, v) in value) {
|
|
||||||
out.beginArray()
|
|
||||||
resolvedKey.write(out, k)
|
|
||||||
Starbound.gson.toJson(Starbound.gson.toJsonTree(v, boundValue) as JsonElement, out)
|
|
||||||
out.endArray()
|
|
||||||
}
|
|
||||||
} else if (resolvedValue != null) {
|
|
||||||
for ((k, v) in value) {
|
|
||||||
out.beginArray()
|
|
||||||
Starbound.gson.toJson(Starbound.gson.toJsonTree(k, boundKey) as JsonElement, out)
|
|
||||||
resolvedValue.write(out, v)
|
|
||||||
out.endArray()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for ((k, v) in value) {
|
|
||||||
out.beginArray()
|
|
||||||
Starbound.gson.toJson(Starbound.gson.toJsonTree(k, boundKey) as JsonElement, out)
|
|
||||||
Starbound.gson.toJson(Starbound.gson.toJsonTree(v, boundValue) as JsonElement, out)
|
|
||||||
out.endArray()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.endArray()
|
out.endArray()
|
||||||
@ -146,30 +101,10 @@ class MapAdapter<K, V>(private val boundKey: Class<K>, private val boundValue: C
|
|||||||
val resolvedKey = resolvedKey
|
val resolvedKey = resolvedKey
|
||||||
val resolvedValue = resolvedValue
|
val resolvedValue = resolvedValue
|
||||||
|
|
||||||
if (resolvedKey != null && resolvedValue != null) {
|
while (reader.peek() != JsonToken.END_ARRAY) {
|
||||||
while (reader.peek() != JsonToken.END_ARRAY) {
|
reader.beginArray()
|
||||||
reader.beginArray()
|
builder.put(resolvedKey.read(reader), resolvedValue.read(reader))
|
||||||
builder.put(resolvedKey.read(reader), resolvedValue.read(reader))
|
reader.endArray()
|
||||||
reader.endArray()
|
|
||||||
}
|
|
||||||
} else if (resolvedKey != null) {
|
|
||||||
while (reader.peek() != JsonToken.END_ARRAY) {
|
|
||||||
reader.beginArray()
|
|
||||||
builder.put(resolvedKey.read(reader), Starbound.gson.fromJson(reader, boundValue))
|
|
||||||
reader.endArray()
|
|
||||||
}
|
|
||||||
} else if (resolvedValue != null) {
|
|
||||||
while (reader.peek() != JsonToken.END_ARRAY) {
|
|
||||||
reader.beginArray()
|
|
||||||
builder.put(Starbound.gson.fromJson(reader, boundKey), resolvedValue.read(reader))
|
|
||||||
reader.endArray()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (reader.peek() != JsonToken.END_ARRAY) {
|
|
||||||
reader.beginArray()
|
|
||||||
builder.put(Starbound.gson.fromJson(reader, boundKey), Starbound.gson.fromJson(reader, boundValue))
|
|
||||||
reader.endArray()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.endArray()
|
reader.endArray()
|
||||||
@ -188,16 +123,9 @@ class StringMapAdapter<V>(private val bound: Class<V>) : TypeAdapter<Map<String,
|
|||||||
|
|
||||||
out.beginObject()
|
out.beginObject()
|
||||||
|
|
||||||
if (resolvedBound != null) {
|
for ((k, v) in value) {
|
||||||
for ((k, v) in value) {
|
out.name(k)
|
||||||
out.name(k)
|
resolvedBound.write(out, v)
|
||||||
resolvedBound.write(out, v)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for ((k, v) in value) {
|
|
||||||
out.name(k)
|
|
||||||
Starbound.gson.toJson(Starbound.gson.toJsonTree(v, bound) as JsonElement, out)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.endObject()
|
out.endObject()
|
||||||
@ -210,14 +138,8 @@ class StringMapAdapter<V>(private val bound: Class<V>) : TypeAdapter<Map<String,
|
|||||||
|
|
||||||
val resolvedBound = resolvedBound
|
val resolvedBound = resolvedBound
|
||||||
|
|
||||||
if (resolvedBound != null) {
|
while (reader.peek() != JsonToken.END_OBJECT) {
|
||||||
while (reader.peek() != JsonToken.END_OBJECT) {
|
builder.put(reader.nextName(), resolvedBound.read(reader))
|
||||||
builder.put(reader.nextName(), resolvedBound.read(reader))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (reader.peek() != JsonToken.END_OBJECT) {
|
|
||||||
builder.put(reader.nextName(), Starbound.gson.fromJson(reader, bound))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.endObject()
|
reader.endObject()
|
||||||
@ -226,13 +148,30 @@ class StringMapAdapter<V>(private val bound: Class<V>) : TypeAdapter<Map<String,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class LazyTypeProvider<T : Any?>(private val bound: Class<T>) : TypeAdapter<T>() {
|
||||||
|
private val resolved by lazy { resolveBound(bound) }
|
||||||
|
|
||||||
|
override fun write(out: JsonWriter, value: T) {
|
||||||
|
resolved.write(out, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(`in`: JsonReader): T {
|
||||||
|
return resolved.read(`in`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TypeAdapter для классов которые создаются единожды и более не меняются ("бетонных классов").
|
* TypeAdapter для классов которые создаются единожды и более не меняются ("бетонных классов").
|
||||||
*/
|
*/
|
||||||
class KConcreteTypeAdapter<T : Any>(
|
class KConcreteTypeAdapter<T : Any>(
|
||||||
val bound: KClass<T>,
|
val bound: KClass<T>,
|
||||||
val types: ImmutableList<Pair<KProperty1<T, *>, TypeAdapter<*>>>
|
val types: ImmutableList<Pair<KProperty1<T, *>, TypeAdapter<*>>>,
|
||||||
|
val asList: Boolean = false
|
||||||
) : TypeAdapter<T>() {
|
) : TypeAdapter<T>() {
|
||||||
|
fun register(gsonBuilder: GsonBuilder) {
|
||||||
|
gsonBuilder.registerTypeAdapter(bound.java, this)
|
||||||
|
}
|
||||||
|
|
||||||
private val returnTypeCache = Object2ObjectArrayMap<KProperty1<T, *>, KType>()
|
private val returnTypeCache = Object2ObjectArrayMap<KProperty1<T, *>, KType>()
|
||||||
private val mapped = Object2IntArrayMap<String>()
|
private val mapped = Object2IntArrayMap<String>()
|
||||||
|
|
||||||
@ -250,7 +189,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val factory: KFunction<T> = bound.constructors.firstOrNull first@{
|
private val regularFactory: KFunction<T> = bound.constructors.firstOrNull first@{
|
||||||
if (it.parameters.size == types.size) {
|
if (it.parameters.size == types.size) {
|
||||||
val iterator = types.iterator()
|
val iterator = types.iterator()
|
||||||
|
|
||||||
@ -271,11 +210,50 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
return@first false
|
return@first false
|
||||||
} ?: throw NoSuchElementException("Unable to determine constructor for ${bound.qualifiedName} matching (${types.joinToString(", ")})")
|
} ?: throw NoSuchElementException("Unable to determine constructor for ${bound.qualifiedName} matching (${types.joinToString(", ")})")
|
||||||
|
|
||||||
|
private val syntheticFactory: Constructor<T>? = try {
|
||||||
|
bound.java.getDeclaredConstructor(*types.map { (it.first.returnType.classifier as KClass<*>).java }.also {
|
||||||
|
it as MutableList
|
||||||
|
it.add(Int::class.java)
|
||||||
|
it.add(DefaultConstructorMarker::class.java)
|
||||||
|
}.toTypedArray())
|
||||||
|
} catch(_: NoSuchMethodException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val syntheticPrimitives: Array<Any?>?
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (syntheticFactory == null) {
|
||||||
|
syntheticPrimitives = null
|
||||||
|
} else {
|
||||||
|
syntheticPrimitives = arrayOfNulls(syntheticFactory.parameters.size)
|
||||||
|
|
||||||
|
for ((i, param) in syntheticFactory.parameters.withIndex()) {
|
||||||
|
val type = param.parameterizedType as? Class<*> ?: continue
|
||||||
|
|
||||||
|
if (type.isPrimitive) {
|
||||||
|
syntheticPrimitives[i] = when (type.name) {
|
||||||
|
"boolean" -> false
|
||||||
|
"int" -> 0
|
||||||
|
"long" -> 0L
|
||||||
|
"short" -> (0).toShort()
|
||||||
|
"byte" -> (0).toByte()
|
||||||
|
"char" -> (0).toChar()
|
||||||
|
"float" -> 0f
|
||||||
|
"double" -> 0.0
|
||||||
|
else -> throw IllegalArgumentException("mamma mia: ${type.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: T) {
|
override fun write(out: JsonWriter, value: T) {
|
||||||
out.beginObject()
|
out.beginObject()
|
||||||
|
|
||||||
for ((field, adapter) in types) {
|
for ((field, adapter) in types) {
|
||||||
out.name(field.name)
|
out.name(field.name)
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
(adapter as TypeAdapter<Any>).write(out, (field as KProperty1<T, Any>).get(value))
|
(adapter as TypeAdapter<Any>).write(out, (field as KProperty1<T, Any>).get(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,79 +261,211 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun read(reader: JsonReader): T {
|
override fun read(reader: JsonReader): T {
|
||||||
reader.beginObject()
|
if (asList) {
|
||||||
|
reader.beginArray()
|
||||||
|
} else {
|
||||||
|
reader.beginObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
val presentValues = BooleanArray(types.size)
|
||||||
val readValues = arrayOfNulls<Any>(types.size)
|
val readValues = arrayOfNulls<Any>(types.size)
|
||||||
|
|
||||||
while (reader.peek() != JsonToken.END_OBJECT) {
|
if (asList) {
|
||||||
val name = reader.nextName()
|
val iterator = types.iterator()
|
||||||
val fieldId = mapped.getInt(name)
|
var fieldId = 0
|
||||||
|
|
||||||
if (fieldId == -1) {
|
while (reader.peek() != JsonToken.END_ARRAY) {
|
||||||
if (loggedMisses.add(name)) {
|
if (!iterator.hasNext()) {
|
||||||
LOGGER.warn("Skipping JSON field with name $name because ${bound.qualifiedName} has no such field")
|
val name = fieldId.toString()
|
||||||
|
|
||||||
|
if (loggedMisses.add(name)) {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.qualifiedName} has no such field")
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.skipValue()
|
||||||
|
fieldId++
|
||||||
|
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.skipValue()
|
val (field, adapter) = iterator.next()
|
||||||
} else {
|
|
||||||
val (field, adapter) = types[fieldId]
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
readValues[fieldId] = adapter.read(reader)
|
readValues[fieldId] = adapter.read(reader)
|
||||||
|
presentValues[fieldId] = true
|
||||||
} catch(err: Throwable) {
|
} catch(err: Throwable) {
|
||||||
throw JsonSyntaxException("Exception reading field ${field.name}", err)
|
throw JsonSyntaxException("Exception reading field ${field.name}", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldId++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (reader.peek() != JsonToken.END_OBJECT) {
|
||||||
|
val name = reader.nextName()
|
||||||
|
val fieldId = mapped.getInt(name)
|
||||||
|
|
||||||
|
if (fieldId == -1) {
|
||||||
|
if (loggedMisses.add(name)) {
|
||||||
|
LOGGER.warn("Skipping JSON field with name $name because ${bound.qualifiedName} has no such field")
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.skipValue()
|
||||||
|
} else {
|
||||||
|
val (field, adapter) = types[fieldId]
|
||||||
|
|
||||||
|
try {
|
||||||
|
readValues[fieldId] = adapter.read(reader)
|
||||||
|
presentValues[fieldId] = true
|
||||||
|
} catch(err: Throwable) {
|
||||||
|
throw JsonSyntaxException("Exception reading field ${field.name}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((i, pair) in types.withIndex()) {
|
for (i in readValues.indices) {
|
||||||
val (field) = pair
|
if (readValues[i] is String) {
|
||||||
|
readValues[i] = (readValues[i] as String).intern()
|
||||||
if (readValues[i] == null && !returnTypeCache[field]!!.isMarkedNullable) {
|
|
||||||
throw JsonSyntaxException("Field ${field.name} does not accept nulls")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.endObject()
|
if (asList) {
|
||||||
|
reader.endArray()
|
||||||
|
} else {
|
||||||
|
reader.endObject()
|
||||||
|
}
|
||||||
|
|
||||||
return factory.call(*readValues as Array<out Any>)
|
if (syntheticFactory == null || presentValues.all { it }) {
|
||||||
|
for ((i, pair) in types.withIndex()) {
|
||||||
|
val (field) = pair
|
||||||
|
|
||||||
|
if (readValues[i] == null) {
|
||||||
|
if (!returnTypeCache[field]!!.isMarkedNullable) {
|
||||||
|
throw JsonSyntaxException("Field ${field.name} does not accept nulls")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!regularFactory.parameters[i].isOptional && !presentValues[i]) {
|
||||||
|
throw JsonSyntaxException("Field ${field.name} must be defined (even just as null)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
|
return regularFactory.call(*readValues as Array<out Any>)
|
||||||
|
} else {
|
||||||
|
val ints = if (presentValues.size % 31 == 0) presentValues.size / 31 else presentValues.size / 31 + 1
|
||||||
|
val copied = readValues.copyOf(readValues.size + ints + 1)
|
||||||
|
var intIndex = readValues.size
|
||||||
|
var target = 0
|
||||||
|
var targetMove = 0
|
||||||
|
|
||||||
|
for (bool in presentValues) {
|
||||||
|
if (!bool) {
|
||||||
|
target = target.or(1.shl(targetMove))
|
||||||
|
}
|
||||||
|
|
||||||
|
targetMove++
|
||||||
|
|
||||||
|
if (targetMove >= 32) {
|
||||||
|
copied[intIndex++] = target
|
||||||
|
target = 0
|
||||||
|
targetMove = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetMove != 0) {
|
||||||
|
copied[intIndex] = target
|
||||||
|
}
|
||||||
|
|
||||||
|
val syntheticPrimitives = syntheticPrimitives!!
|
||||||
|
|
||||||
|
for ((i, pair) in types.withIndex()) {
|
||||||
|
if (copied[i] != null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val (field) = pair
|
||||||
|
val param = regularFactory.parameters[i]
|
||||||
|
|
||||||
|
if (!param.isOptional && !presentValues[i]) {
|
||||||
|
throw JsonSyntaxException("Field ${field.name} is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnTypeCache[field]!!.isMarkedNullable) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.isOptional && !presentValues[i]) {
|
||||||
|
copied[i] = syntheticPrimitives[i] ?: throw NullPointerException("HOW $i")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
throw JsonSyntaxException("Field ${field.name} does not accept nulls")
|
||||||
|
}
|
||||||
|
|
||||||
|
return syntheticFactory.newInstance(*copied)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Builder<T : Any>(val clazz: KClass<T>) {
|
class Builder<T : Any>(val clazz: KClass<T>) {
|
||||||
private val types = ArrayList<Pair<KProperty1<T, *>, TypeAdapter<*>>>()
|
private val types = ArrayList<Pair<KProperty1<T, *>, TypeAdapter<*>>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Добавляет поле без generic типов
|
* Добавляет поле с определённым адаптером
|
||||||
*/
|
*/
|
||||||
fun plain(field: KProperty1<T, *>): Builder<T> {
|
fun <V> plain(field: KProperty1<T, V>, adapter: TypeAdapter<V>): Builder<T> {
|
||||||
val returnType = field.returnType
|
types.add(field to adapter)
|
||||||
val classifier = returnType.classifier as? KClass<*> ?: throw ClassCastException("Unable to cast ${returnType.classifier} to KClass of property ${field.name}!")
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
if (classifier.isSuperclassOf(Float::class)) {
|
/**
|
||||||
types.add(field to TypeAdapters.FLOAT)
|
* Добавляет поле(я) без generic типов и без преобразователей
|
||||||
} else if (classifier.isSuperclassOf(Double::class)) {
|
*/
|
||||||
types.add(field to TypeAdapters.DOUBLE)
|
fun plain(vararg fields: KProperty1<T, *>): Builder<T> {
|
||||||
} else if (classifier.isSuperclassOf(Int::class)) {
|
for (field in fields) {
|
||||||
types.add(field to TypeAdapters.INTEGER)
|
val returnType = field.returnType
|
||||||
} else if (classifier.isSuperclassOf(Long::class)) {
|
val classifier = returnType.classifier as? KClass<*> ?: throw ClassCastException("Unable to cast ${returnType.classifier} to KClass of property ${field.name}!")
|
||||||
types.add(field to TypeAdapters.LONG)
|
|
||||||
} else if (classifier.isSuperclassOf(String::class)) {
|
if (classifier.isSuperclassOf(Float::class)) {
|
||||||
types.add(field to TypeAdapters.STRING)
|
types.add(field to TypeAdapters.FLOAT)
|
||||||
} else if (classifier.isSuperclassOf(Boolean::class)) {
|
} else if (classifier.isSuperclassOf(Double::class)) {
|
||||||
types.add(field to TypeAdapters.BOOLEAN)
|
types.add(field to TypeAdapters.DOUBLE)
|
||||||
} else {
|
} else if (classifier.isSuperclassOf(Int::class)) {
|
||||||
types.add(field to PassthroughAdapter(classifier.java))
|
types.add(field to TypeAdapters.INTEGER)
|
||||||
|
} else if (classifier.isSuperclassOf(Long::class)) {
|
||||||
|
types.add(field to TypeAdapters.LONG)
|
||||||
|
} else if (classifier.isSuperclassOf(String::class)) {
|
||||||
|
types.add(field to TypeAdapters.STRING)
|
||||||
|
} else if (classifier.isSuperclassOf(Boolean::class)) {
|
||||||
|
types.add(field to TypeAdapters.BOOLEAN)
|
||||||
|
} else {
|
||||||
|
types.add(field to LazyTypeProvider(classifier.java))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Добавляет поле(я) без generic типов.
|
||||||
|
*
|
||||||
|
* Если поле с данным именем не найдено, кидается [NoSuchElementException]
|
||||||
|
*/
|
||||||
|
fun plain(vararg fields: String): Builder<T> {
|
||||||
|
val members = clazz.memberProperties
|
||||||
|
|
||||||
|
return this.plain(*Array(fields.size) {
|
||||||
|
val field = fields[it]
|
||||||
|
return@Array members.firstOrNull { it.name == field } ?: throw NoSuchElementException("Unable to find field $field")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Добавляет поле, которое содержит список значений V (без null).
|
* Добавляет поле, которое содержит список значений V (без null).
|
||||||
*
|
*
|
||||||
* Список неизменяем (создаётся объект [ImmutableList])
|
* Список неизменяем (создаётся объект [ImmutableList])
|
||||||
*/
|
*/
|
||||||
fun <V> list(field: KProperty1<T, List<V>>, type: Class<V>): Builder<T> {
|
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>): Builder<T> {
|
||||||
types.add(field to ListAdapter(type))
|
types.add(field to ListAdapter(type))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
@ -365,7 +475,7 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
*
|
*
|
||||||
* Список неизменяем (создаётся объект [ImmutableList])
|
* Список неизменяем (создаётся объект [ImmutableList])
|
||||||
*/
|
*/
|
||||||
fun <V : Any> list(field: KProperty1<T, List<V>>, type: KClass<V>): Builder<T> {
|
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: KClass<V>): Builder<T> {
|
||||||
return this.list(field, type.java)
|
return this.list(field, type.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,6 +489,16 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Добавляет поле-таблицу, которое кодируется как [[key, value], [key, value], ...]
|
||||||
|
*
|
||||||
|
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
||||||
|
*/
|
||||||
|
fun <K : Any, V : Any> map(field: KProperty1<T, Map<K, V>>, keyType: KClass<K>, valueType: KClass<V>): Builder<T> {
|
||||||
|
types.add(field to MapAdapter(keyType.java, valueType.java))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Добавляет поле-таблицу, которое кодируется как {"a": value, "b": value, ...}
|
* Добавляет поле-таблицу, которое кодируется как {"a": value, "b": value, ...}
|
||||||
*
|
*
|
||||||
@ -389,8 +509,18 @@ class KConcreteTypeAdapter<T : Any>(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(): KConcreteTypeAdapter<T> {
|
/**
|
||||||
return KConcreteTypeAdapter(clazz, ImmutableList.copyOf(types))
|
* Добавляет поле-таблицу, которое кодируется как {"a": value, "b": value, ...}
|
||||||
|
*
|
||||||
|
* Таблица неизменяема (создаётся объект [ImmutableMap])
|
||||||
|
*/
|
||||||
|
fun <V : Any> map(field: KProperty1<T, Map<String, V>>, valueType: KClass<V>): Builder<T> {
|
||||||
|
types.add(field to StringMapAdapter(valueType.java))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(asList: Boolean = false): KConcreteTypeAdapter<T> {
|
||||||
|
return KConcreteTypeAdapter(clazz, ImmutableList.copyOf(types), asList = asList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user