From 6ea4d7a6abec216f366b6aa4f3b9664d6470984e Mon Sep 17 00:00:00 2001
From: DBotThePony <dbotthepony@yandex.ru>
Date: Sun, 22 Jan 2023 23:24:06 +0700
Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=BD=D0=BE=D1=81=D1=82?=
 =?UTF-8?q?=D1=8C=D1=8E=20=D0=BF=D0=B5=D1=80=D0=B5=D1=88=D0=BB=D0=B8=20?=
 =?UTF-8?q?=D0=BD=D0=B0=20TypeAdapterFactory,=20=D0=BD=D0=B0=D0=B2=D0=B5?=
 =?UTF-8?q?=D1=80=D0=BD=D0=BE=D0=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../kstarbound/StarboundJsonAdapters.kt       |  6 ++-
 .../defs/item/ArmorItemPrototype.kt           |  9 ++--
 .../defs/item/CurrencyItemPrototype.kt        |  7 +--
 .../kstarbound/defs/item/ItemPrototype.kt     | 10 ++---
 .../io/json/builder/BuilderAdapter.kt         |  2 +-
 .../json/factory/ArrayListAdapterFactory.kt   | 17 +++++++
 .../io/json/factory/ArrayListTypeAdapter.kt   | 45 +++++++++++++++++++
 7 files changed, 82 insertions(+), 14 deletions(-)
 create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/io/json/factory/ArrayListAdapterFactory.kt
 create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/io/json/factory/ArrayListTypeAdapter.kt

diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/StarboundJsonAdapters.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/StarboundJsonAdapters.kt
index fd392719..c47b199e 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/StarboundJsonAdapters.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/StarboundJsonAdapters.kt
@@ -52,14 +52,18 @@ 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.builder.EnumAdapter
+import ru.dbotthepony.kstarbound.io.json.factory.ArrayListAdapterFactory
 import ru.dbotthepony.kstarbound.io.json.factory.ImmutableCollectionAdapterFactory
 import ru.dbotthepony.kstarbound.math.PolyTypeAdapter
 
 fun addStarboundJsonAdapters(builder: GsonBuilder) {
 	with(builder) {
-		// ImmutableList, и прочее
+		// ImmutableList, ImmutableSet, ImmutableMap
 		registerTypeAdapterFactory(ImmutableCollectionAdapterFactory)
 
+		// ArrayList
+		registerTypeAdapterFactory(ArrayListAdapterFactory)
+
 		registerTypeAdapter(ColorTypeAdapter.nullSafe())
 
 		// математические классы
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ArmorItemPrototype.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ArmorItemPrototype.kt
index 4141bdb4..d0e9fb7c 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ArmorItemPrototype.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ArmorItemPrototype.kt
@@ -1,5 +1,6 @@
 package ru.dbotthepony.kstarbound.defs.item
 
+import com.google.common.collect.ImmutableList
 import ru.dbotthepony.kstarbound.Starbound
 import ru.dbotthepony.kstarbound.defs.util.enrollMap
 import ru.dbotthepony.kstarbound.io.json.builder.BuilderAdapter
@@ -9,13 +10,13 @@ import ru.dbotthepony.kstarbound.io.json.neverNull
 import ru.dbotthepony.kstarbound.util.NotNullVar
 
 class ArmorItemPrototype : ItemPrototype(), IArmorItemDefinition {
-	override var colorOptions: List<Map<String, String>> = listOf()
+	override var colorOptions: ImmutableList<Map<String, String>> = ImmutableList.of()
 	override var maleFrames: IArmorItemDefinition.ArmorFrames by NotNullVar()
 	override var femaleFrames: IArmorItemDefinition.ArmorFrames by NotNullVar()
 	override var level: Double = 1.0
-	override var leveledStatusEffects: List<LeveledStatusEffect> = listOf()
+	override var leveledStatusEffects: ImmutableList<LeveledStatusEffect> = ImmutableList.of()
 
-	override var scripts: List<String> = listOf()
+	override var scripts: ImmutableList<String> = ImmutableList.of()
 	override var scriptDelta: Int = 1
 
 	override var armorType: ArmorPieceType by NotNullVar()
@@ -60,7 +61,7 @@ class ArmorItemPrototype : ItemPrototype(), IArmorItemDefinition {
 	companion object {
 		val ADAPTER = BuilderAdapter.Builder(::ArmorItemPrototype)
 			.also { addFields(it as BuilderAdapter.Builder<ItemPrototype>) } // безопасность: свойства родительского класса объявлены как final
-			.add(ArmorItemPrototype::colorOptions, Starbound.NULLABLE_STRING_ADAPTER.neverNull().asJsonObject().asList())
+			.auto(ArmorItemPrototype::colorOptions)
 			.auto(ArmorItemPrototype::maleFrames)
 			.auto(ArmorItemPrototype::femaleFrames)
 			.auto(ArmorItemPrototype::level)
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/CurrencyItemPrototype.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/CurrencyItemPrototype.kt
index 6bf524a3..1a5b1b00 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/CurrencyItemPrototype.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/CurrencyItemPrototype.kt
@@ -1,13 +1,14 @@
 package ru.dbotthepony.kstarbound.defs.item
 
+import com.google.common.collect.ImmutableList
 import ru.dbotthepony.kstarbound.defs.util.enrollMap
 import ru.dbotthepony.kstarbound.io.json.builder.BuilderAdapter
 import ru.dbotthepony.kstarbound.util.NotNullVar
 
 class CurrencyItemPrototype : ItemPrototype(), ICurrencyItemDefinition {
-	override var pickupSoundsSmall: List<String> = listOf()
-	override var pickupSoundsMedium: List<String> = listOf()
-	override var pickupSoundsLarge: List<String> = listOf()
+	override var pickupSoundsSmall: ImmutableList<String> = ImmutableList.of()
+	override var pickupSoundsMedium: ImmutableList<String> = ImmutableList.of()
+	override var pickupSoundsLarge: ImmutableList<String> = ImmutableList.of()
 	override var smallStackLimit: Long by NotNullVar()
 	override var mediumStackLimit: Long by NotNullVar()
 	override var currency: String by NotNullVar()
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemPrototype.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemPrototype.kt
index 014f8225..1bbb75c0 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemPrototype.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemPrototype.kt
@@ -14,16 +14,16 @@ open class ItemPrototype : IItemDefinition, INativeJsonHolder {
 	final override var price: Long = 0L
 	final override var rarity: ItemRarity = ItemRarity.COMMON
 	final override var category: String? = null
-	final override var inventoryIcon: ArrayList<IItemDefinition.InventoryIcon>? = null
-	final override var itemTags: ArrayList<String> = ArrayList()
-	final override var learnBlueprintsOnPickup: ArrayList<String> = ArrayList()
+	final override var inventoryIcon: ImmutableList<IItemDefinition.InventoryIcon>? = null
+	final override var itemTags: ImmutableList<String> = ImmutableList.of()
+	final override var learnBlueprintsOnPickup: ImmutableList<String> = ImmutableList.of()
 	final override var maxStack: Long = 9999L
 	final override var eventCategory: String? = null
 	final override var consumeOnPickup: Boolean = false
-	final override var pickupQuestTemplates: ArrayList<String> = ArrayList()
+	final override var pickupQuestTemplates: ImmutableList<String> = ImmutableList.of()
 	final override var tooltipKind: ItemTooltipKind = ItemTooltipKind.NORMAL
 	final override var twoHanded: Boolean = false
-	final override var radioMessagesOnPickup: ArrayList<String> = ArrayList()
+	final override var radioMessagesOnPickup: ImmutableList<String> = ImmutableList.of()
 	final override var fuelAmount: Long? = null
 
 	var descriptionData: ThingDescription by NotNullVar()
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/builder/BuilderAdapter.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/builder/BuilderAdapter.kt
index 34589ff1..43b24d8c 100644
--- a/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/builder/BuilderAdapter.kt
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/builder/BuilderAdapter.kt
@@ -218,7 +218,7 @@ class BuilderAdapter<T : Any> private constructor(
 
 		@OptIn(ExperimentalStdlibApi::class)
 		override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
-			if (type.isAssignableFrom(factoryReturnType)) {
+			if (type.rawType == factoryReturnType) {
 				return build(gson) as TypeAdapter<T>
 			}
 
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/factory/ArrayListAdapterFactory.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/factory/ArrayListAdapterFactory.kt
new file mode 100644
index 00000000..bd1a825d
--- /dev/null
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/factory/ArrayListAdapterFactory.kt
@@ -0,0 +1,17 @@
+package ru.dbotthepony.kstarbound.io.json.factory
+
+import com.google.gson.Gson
+import com.google.gson.TypeAdapter
+import com.google.gson.TypeAdapterFactory
+import com.google.gson.reflect.TypeToken
+import java.lang.reflect.ParameterizedType
+
+object ArrayListAdapterFactory : TypeAdapterFactory {
+	override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
+		if (ArrayList::class.java.isAssignableFrom(type.rawType) && type.type is ParameterizedType) {
+			return ArrayListTypeAdapter(gson.getAdapter(TypeToken.get((type.type as ParameterizedType).actualTypeArguments[0]))).nullSafe() as TypeAdapter<T>
+		}
+
+		return null
+	}
+}
diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/factory/ArrayListTypeAdapter.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/factory/ArrayListTypeAdapter.kt
new file mode 100644
index 00000000..8a1d6064
--- /dev/null
+++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/factory/ArrayListTypeAdapter.kt
@@ -0,0 +1,45 @@
+package ru.dbotthepony.kstarbound.io.json.factory
+
+import com.google.gson.TypeAdapter
+import com.google.gson.stream.JsonReader
+import com.google.gson.stream.JsonToken
+import com.google.gson.stream.JsonWriter
+import java.util.ArrayList
+
+class ArrayListTypeAdapter<E>(val elementAdapter: TypeAdapter<E>) : TypeAdapter<java.util.ArrayList<E>>() {
+	override fun write(out: JsonWriter, value: ArrayList<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): ArrayList<E> {
+		if (reader.peek() != JsonToken.BEGIN_ARRAY) {
+			// не массив, возможно упрощение структуры "a": [value] -> "a": value
+			val list = ArrayList<E>(1)
+			list.add(elementAdapter.read(reader))
+			return list
+		}
+
+		reader.beginArray()
+
+		val list = ArrayList<E>()
+
+		while (reader.peek() != JsonToken.END_ARRAY) {
+			list.add(elementAdapter.read(reader))
+		}
+
+		reader.endArray()
+
+		return list
+	}
+}