diff options
Diffstat (limited to 'src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt')
-rw-r--r-- | src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt | 307 |
1 files changed, 241 insertions, 66 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt b/src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt index 25c20445..b4c8561e 100644 --- a/src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt +++ b/src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt @@ -24,25 +24,24 @@ package pl.treksoft.kvision.remote import kotlinx.coroutines.Deferred import kotlinx.coroutines.asDeferred import kotlinx.serialization.KSerializer -import kotlinx.serialization.internal.ArrayListSerializer -import kotlinx.serialization.internal.BooleanSerializer -import kotlinx.serialization.internal.CharSerializer -import kotlinx.serialization.internal.DoubleSerializer -import kotlinx.serialization.internal.LongSerializer -import kotlinx.serialization.internal.StringSerializer -import kotlinx.serialization.json.JSON +import kotlinx.serialization.internal.* import kotlinx.serialization.list import kotlinx.serialization.serializer -import kotlin.js.js +import pl.treksoft.kvision.types.DateSerializer +import pl.treksoft.kvision.types.toStringF +import pl.treksoft.kvision.utils.JSON +import kotlin.js.Date import kotlin.reflect.KClass import kotlin.js.JSON as NativeJSON -internal class NonStandardTypeException(type: String) : Exception("Non standard type: $type!") +internal class NotStandardTypeException(type: String) : Exception("Not a standard type: $type!") + +internal class NotEnumTypeException : Exception("Not the Enum type!") /** * Client side agent for JSON-RPC remote calls. */ -@Suppress("EXPERIMENTAL_FEATURE_WARNING", "LargeClass", "TooManyFunctions") +@Suppress("LargeClass", "TooManyFunctions") open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { val callAgent = CallAgent() @@ -55,9 +54,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, method = method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -72,9 +77,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, method = method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -92,9 +102,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { return callAgent.jsonRpcCall(url, listOf(data), method).then { try { @Suppress("UNCHECKED_CAST") - deserialize<RET>(it, (RET::class as KClass<Any>).js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + deserialize<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -111,9 +126,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -131,9 +151,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2), method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -151,9 +177,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -173,9 +204,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3), method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -195,9 +232,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -224,9 +266,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3, data4), method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -253,9 +301,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3, data4), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -287,9 +340,15 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3, data4, data5), method).then { try { + @Suppress("UNCHECKED_CAST") deserialize<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer(), it) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnum(RET::class as KClass<Any>, it) as RET + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer(), it) + } } }.asDeferred() } @@ -321,9 +380,14 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { serviceManager.getCalls()[function.toString()] ?: throw IllegalStateException("Function not specified!") return callAgent.jsonRpcCall(url, listOf(data1, data2, data3, data4, data5), method).then { try { - deserializeLists<RET>(it, RET::class.js.name) - } catch (t: NonStandardTypeException) { - JSON.nonstrict.parse(RET::class.serializer().list, it) + deserializeList<RET>(it, RET::class.js.name) + } catch (t: NotStandardTypeException) { + try { + @Suppress("UNCHECKED_CAST") + tryDeserializeEnumList(RET::class as KClass<Any>, it) as List<RET> + } catch (t: NotEnumTypeException) { + JSON.nonstrict.parse(RET::class.serializer().list, it) + } } }.asDeferred() } @@ -337,18 +401,81 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { inline fun <reified PAR> serialize(value: PAR, serializer: KSerializer<PAR>?): String? { return value?.let { if (serializer != null) { - JSON.stringify(serializer, it) + JSON.plain.stringify(serializer, it) } else { - if (it is Enum<*>) { - "\"$it\"" - } else { - try { + @Suppress("UNCHECKED_CAST") + trySerialize((PAR::class as KClass<Any>), it as Any) + } + } + } + + /** + * @suppress + * Internal function + */ + @Suppress("ComplexMethod", "TooGenericExceptionCaught", "NestedBlockDepth") + fun trySerialize(kClass: KClass<Any>, value: Any): String { + return if (value is List<*>) { + if (value.size > 0) { + when { + value[0] is String -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(StringSerializer) as KSerializer<Any>, value) + value[0] is Date -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(DateSerializer) as KSerializer<Any>, value) + value[0] is Int -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(IntSerializer) as KSerializer<Any>, value) + value[0] is Long -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(LongSerializer) as KSerializer<Any>, value) + value[0] is Boolean -> @Suppress("UNCHECKED_CAST") - JSON.stringify((PAR::class as KClass<Any>).serializer(), it as Any) + JSON.plain.stringify(ArrayListSerializer(BooleanSerializer) as KSerializer<Any>, value) + value[0] is Float -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(FloatSerializer) as KSerializer<Any>, value) + value[0] is Double -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(DoubleSerializer) as KSerializer<Any>, value) + value[0] is Char -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(CharSerializer) as KSerializer<Any>, value) + value[0] is Short -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(ShortSerializer) as KSerializer<Any>, value) + value[0] is Byte -> + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(ByteSerializer) as KSerializer<Any>, value) + value[0] is Enum<*> -> "[" + value.joinToString(",") { "\"$it\"" } + "]" + else -> try { + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(kClass.serializer()) as KSerializer<Any>, value) } catch (e: Throwable) { - it.toString() + try { + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(ArrayListSerializer(StringSerializer) as KSerializer<Any>, value) + } catch (e: Throwable) { + value.toString() + } } } + } else { + "[]" + } + } else { + when (value) { + is Enum<*> -> "\"$value\"" + is String -> value + is Char -> "\"$value\"" + is Date -> "\"${value.toStringF()}\"" + else -> try { + @Suppress("UNCHECKED_CAST") + JSON.plain.stringify(kClass.serializer(), value) + } catch (e: Throwable) { + value.toString() + } } } } @@ -357,15 +484,37 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { * @suppress * Internal function */ - @Suppress("UNCHECKED_CAST") - fun <RET> deserialize(value: String, type: String): RET { - return when (type) { - "String" -> JSON.parse(StringSerializer, value) as RET - "Number" -> JSON.parse(DoubleSerializer, value) as RET - "Long" -> JSON.parse(LongSerializer, value) as RET - "Boolean" -> JSON.parse(BooleanSerializer, value) as RET - "Char" -> JSON.parse(CharSerializer, value) as RET - else -> throw NonStandardTypeException(type) + @Suppress("UNCHECKED_CAST", "ComplexMethod") + fun <RET> deserialize(value: String, jsType: String): RET { + return when (jsType) { + "String" -> JSON.plain.parse(StringSerializer, value) as RET + "Number" -> JSON.plain.parse(DoubleSerializer, value) as RET + "Long" -> JSON.plain.parse(LongSerializer, value) as RET + "Boolean" -> JSON.plain.parse(BooleanSerializer, value) as RET + "BoxedChar" -> JSON.plain.parse(CharSerializer, value) as RET + "Short" -> JSON.plain.parse(ShortSerializer, value) as RET + "Date" -> JSON.plain.parse(DateSerializer, value) as RET + "Byte" -> JSON.plain.parse(ByteSerializer, value) as RET + else -> throw NotStandardTypeException(jsType) + } + } + + /** + * @suppress + * Internal function + */ + @Suppress("UNCHECKED_CAST", "ComplexMethod") + fun <RET> deserializeList(value: String, jsType: String): List<RET> { + return when (jsType) { + "String" -> JSON.plain.parse(ArrayListSerializer(StringSerializer), value) as List<RET> + "Number" -> JSON.plain.parse(ArrayListSerializer(DoubleSerializer), value) as List<RET> + "Long" -> JSON.plain.parse(ArrayListSerializer(LongSerializer), value) as List<RET> + "Boolean" -> JSON.plain.parse(ArrayListSerializer(BooleanSerializer), value) as List<RET> + "BoxedChar" -> JSON.plain.parse(ArrayListSerializer(CharSerializer), value) as List<RET> + "Short" -> JSON.plain.parse(ArrayListSerializer(ShortSerializer), value) as List<RET> + "Date" -> JSON.plain.parse(ArrayListSerializer(DateSerializer), value) as List<RET> + "Byte" -> JSON.plain.parse(ArrayListSerializer(ByteSerializer), value) as List<RET> + else -> throw NotStandardTypeException(jsType) } } @@ -373,15 +522,41 @@ open class RemoteAgent<out T>(val serviceManager: ServiceManager<T>) { * @suppress * Internal function */ - @Suppress("UNCHECKED_CAST") - fun <RET> deserializeLists(value: String, type: String): List<RET> { - return when (type) { - "String" -> JSON.parse(ArrayListSerializer(StringSerializer), value) as List<RET> - "Number" -> JSON.parse(ArrayListSerializer(DoubleSerializer), value) as List<RET> - "Long" -> JSON.parse(ArrayListSerializer(LongSerializer), value) as List<RET> - "Boolean" -> JSON.parse(ArrayListSerializer(BooleanSerializer), value) as List<RET> - "Char" -> JSON.parse(ArrayListSerializer(CharSerializer), value) as List<RET> - else -> throw NonStandardTypeException(type) + @Suppress("TooGenericExceptionCaught", "ThrowsCount") + fun tryDeserializeEnum(kClass: KClass<Any>, value: String): Any { + return try { + if (kClass.asDynamic().jClass.`$metadata$`.interfaces[0].name == "Enum") { + findEnumValue(kClass, JSON.plain.parse(StringSerializer, value)) ?: throw NotEnumTypeException() + } else { + throw NotEnumTypeException() + } + } catch (e: Throwable) { + throw NotEnumTypeException() + } + } + + /** + * @suppress + * Internal function + */ + @Suppress("TooGenericExceptionCaught", "ThrowsCount") + fun tryDeserializeEnumList(kClass: KClass<Any>, value: String): List<Any> { + return try { + if (kClass.asDynamic().jClass.`$metadata$`.interfaces[0].name == "Enum") { + JSON.plain.parse(ArrayListSerializer(StringSerializer), value).map { + findEnumValue(kClass, JSON.plain.parse(StringSerializer, it)) ?: throw NotEnumTypeException() + } + } else { + throw NotEnumTypeException() + } + } catch (e: Throwable) { + throw NotEnumTypeException() + } + } + + private fun findEnumValue(kClass: KClass<Any>, value: String): Any? { + return (kClass.asDynamic().jClass.values() as Array<Any>).find { + it.asDynamic().name == value } } } |