diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 9b0d9027..098c5c4c 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.reflect.KClass @@ -34,7 +35,7 @@ interface Data : Goal, MetaRepr { block: suspend CoroutineScope.() -> T ): Data = DynamicData(type, meta, context, dependencies, block) - operator inline fun invoke( + inline operator fun invoke( meta: Meta = EmptyMeta, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), @@ -50,7 +51,7 @@ interface Data : Goal, MetaRepr { block: suspend CoroutineScope.() -> T ): Data = NamedData(name, invoke(type, meta, context, dependencies, block)) - operator inline fun invoke( + inline operator fun invoke( name: String, meta: Meta = EmptyMeta, context: CoroutineContext = EmptyCoroutineContext, @@ -65,7 +66,7 @@ interface Data : Goal, MetaRepr { } -fun Data.cast(type: KClass): Data { +fun Data.upcast(type: KClass): Data { return object : Data by this { override val type: KClass = type } @@ -74,8 +75,24 @@ fun Data.cast(type: KClass): Data { /** * Upcast a [Data] to a supertype */ -inline fun Data.cast(): Data = cast(R::class) +inline fun Data.upcast(): Data = upcast(R::class) +/** + * Unsafe cast of data node + */ +@Suppress("UNCHECKED_CAST") +fun Data<*>.cast(type: KClass): Data{ + return object : Data { + override val meta: Meta get() = this@cast.meta + override val dependencies: Collection> get() = this@cast.dependencies + override val result: Deferred? get() = this@cast.result as Deferred + override fun startAsync(scope: CoroutineScope): Deferred = this@cast.startAsync(scope) as Deferred + override fun reset() = this@cast.reset() + override val type: KClass = type + } +} + +inline fun Data.cast(): Data = cast(R::class) class DynamicData( override val type: KClass, diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt index a66cff88..f894c24c 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt @@ -72,7 +72,7 @@ operator fun DataNode.get(name: Name): DataItem? = when (name.le else -> get(name.first()!!.asName()).node?.get(name.cutFirst()) } -operator fun DataNode.get(name: String): DataItem? = get(name.toString()) +operator fun DataNode.get(name: String): DataItem? = get(name.toName()) /** * Sequence of all children including nodes diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/CastDataNode.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/CastDataNode.kt index c6451518..2ba3319f 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/CastDataNode.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/CastDataNode.kt @@ -1,32 +1,21 @@ package hep.dataforge.data -import hep.dataforge.meta.Meta import hep.dataforge.names.NameToken -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf -@Suppress("UNCHECKED_CAST") -fun Data.safeCast(type: KClass): Data? { - return if (this.type.isSubclassOf(type)) { - return object : Data { - override val meta: Meta get() = this@safeCast.meta - override val dependencies: Collection> get() = this@safeCast.dependencies - override val result: Deferred? get() = this@safeCast.result as Deferred - override fun startAsync(scope: CoroutineScope): Deferred = this@safeCast.startAsync(scope) as Deferred - override fun reset() = this@safeCast.reset() - override val type: KClass = type - } - } else { - null - } -} -inline fun Data<*>.safeCast(): Data? = safeCast(R::class) +fun Data.canCast(type: KClass): Boolean = + this.type.isSubclassOf(type) + +fun Data.safeCast(type: KClass): Data? = + if (canCast(type)) cast(type) else null + + +//inline fun Data<*>.safeCast(): Data? = safeCast(R::class) class CastDataNode(val origin: DataNode, override val type: KClass) : DataNode { - override val items: Map> by lazy { + override val items: Map> by lazy { origin.items.mapNotNull { (key, item) -> when (item) { is DataItem.Leaf -> { @@ -35,7 +24,7 @@ class CastDataNode(val origin: DataNode, override val type: KC } } is DataItem.Node -> { - key to DataItem.Node(item.value.cast(type)) + key to DataItem.Node(item.value.safeCast(type)) } } }.associate { it } diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt index 32bcec5f..bf803cf0 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt @@ -12,7 +12,7 @@ fun Data.get(): T = runBlocking { await() } /** * Check that node is compatible with given type meaning that each element could be cast to the type */ -actual fun DataNode<*>.checkType(type: KClass) { +actual fun DataNode<*>.checkType(type: KClass) { if (!type.isSuperclassOf(type)) { error("$type expected, but $type received") } @@ -22,12 +22,20 @@ actual fun DataNode<*>.checkType(type: KClass) { * Filter a node by data and node type. Resulting node and its subnodes is guaranteed to have border type [type], * but could contain empty nodes */ -fun DataNode.cast(type: KClass): DataNode { +fun DataNode<*>.safeCast(type: KClass): DataNode { return if (this is CastDataNode) { - origin.cast(type) + origin.safeCast(type) } else { CastDataNode(this, type) } } -inline fun DataNode.cast(): DataNode = cast(R::class) \ No newline at end of file +inline fun DataNode<*>.cast(): DataNode = safeCast(R::class) + +fun DataItem<*>?.safeCast(type: KClass): DataItem? = when (this) { + null -> null + is DataItem.Node -> DataItem.Node(this.value.safeCast(type)) + is DataItem.Leaf -> DataItem.Leaf( + this.value.safeCast(type) ?: error("Can't cast data with type ${this.value.type} to $type") + ) +} \ No newline at end of file diff --git a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/tcp/EnvelopeServerTest.kt b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/tcp/EnvelopeServerTest.kt index f00a3754..d48b42ef 100644 --- a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/tcp/EnvelopeServerTest.kt +++ b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/tcp/EnvelopeServerTest.kt @@ -43,10 +43,9 @@ class EnvelopeServerTest { } } - @Test fun doEchoTest() { - val client = EnvelopeClient(Global, host = "localhost", port = 7778) + val request = Envelope.build { type = "test.echo" meta { @@ -56,6 +55,7 @@ class EnvelopeServerTest { writeDouble(22.7) } } + val client = EnvelopeClient(Global, host = "localhost", port = 7778) runBlocking { val response = client.respond(request) diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index afe9ddd1..b029079c 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -49,7 +49,7 @@ class TaskBuilder(val name: String) { dataTransforms += DataTransformation(from, to) { context, model, data -> data.checkType(inputType) val env = TaskEnv(EmptyName, model.meta, context) - env.block(data.cast(inputType)) + env.block(data.safeCast(inputType)) } }