aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt')
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/remote/RemoteAgent.kt307
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
}
}
}