From c239abe6e8cc26e0414dea58fa5a826e319b7826 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 11 Sep 2019 23:03:56 +0300 Subject: [PATCH] Added unsafe data casts for all platforms and safeCast for JVM --- .../kotlin/hep/dataforge/data/Data.kt | 29 -------- .../kotlin/hep/dataforge/data/DataNode.kt | 9 +-- .../kotlin/hep/dataforge/data/JoinAction.kt | 2 +- .../kotlin/hep/dataforge/data/PipeAction.kt | 2 +- .../kotlin/hep/dataforge/data/SplitAction.kt | 2 +- .../kotlin/hep/dataforge/data/dataCast.kt | 72 +++++++++++++++++++ .../kotlin/hep/dataforge/data/dataJS.kt | 14 ++-- .../kotlin/hep/dataforge/data/CastDataNode.kt | 10 --- .../kotlin/hep/dataforge/data/dataJVM.kt | 21 +++--- .../hep/dataforge/workspace/TaskBuilder.kt | 2 +- 10 files changed, 96 insertions(+), 67 deletions(-) create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt 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 098c5c4c..fb56ff19 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -4,7 +4,6 @@ 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 @@ -66,34 +65,6 @@ interface Data : Goal, MetaRepr { } -fun Data.upcast(type: KClass): Data { - return object : Data by this { - override val type: KClass = type - } -} - -/** - * Upcast a [Data] to a supertype - */ -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, override val meta: Meta = EmptyMeta, 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 f894c24c..011bbf79 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt @@ -224,11 +224,4 @@ fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNod } } -fun DataNode.first(): Data? = dataSequence().first().second - -/** - * Check that node is compatible with given type meaning that each element could be cast to the type - */ -expect fun DataNode<*>.checkType(type: KClass) - -//expect fun DataNode.cast(type: KClass): DataNode \ No newline at end of file +fun DataNode.first(): Data? = dataSequence().first().second \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/JoinAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/JoinAction.kt index 4acae87f..31a54a74 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/JoinAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/JoinAction.kt @@ -80,7 +80,7 @@ class JoinAction( ) : Action { override fun invoke(node: DataNode, meta: Meta): DataNode { - node.checkType(inputType) + node.ensureType(inputType) return DataNode.build(outputType) { JoinGroupBuilder(meta).apply(action).buildGroups(node).forEach { group -> diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/PipeAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/PipeAction.kt index c84e5a13..59162179 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/PipeAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/PipeAction.kt @@ -29,7 +29,7 @@ class PipeAction( ) : Action { override fun invoke(node: DataNode, meta: Meta): DataNode { - node.checkType(inputType) + node.ensureType(inputType) return DataNode.build(outputType) { node.dataSequence().forEach { (name, data) -> diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt index be9764a6..1e7e6c7c 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt @@ -39,7 +39,7 @@ class SplitAction( ) : Action { override fun invoke(node: DataNode, meta: Meta): DataNode { - node.checkType(inputType) + node.ensureType(inputType) return DataNode.build(outputType) { node.dataSequence().forEach { (name, data) -> diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt new file mode 100644 index 00000000..556b77fc --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt @@ -0,0 +1,72 @@ +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 + +fun Data.upcast(type: KClass): Data { + return object : Data by this { + override val type: KClass = type + } +} + +/** + * Safe upcast a [Data] to a supertype + */ +inline fun Data.upcast(): Data = upcast(R::class) + +/** + * Check if node could be safely cast to given class + */ +expect fun DataNode<*>.canCast(type: KClass): Boolean + +/** + * Check if data could be safely cast to given class + */ +expect fun Data<*>.canCast(type: KClass): Boolean + +fun DataItem<*>.canCast(type: KClass): Boolean = when (this) { + is DataItem.Node -> value.canCast(type) + is DataItem.Leaf -> value.canCast(type) +} + +/** + * 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) + +@Suppress("UNCHECKED_CAST") +fun DataNode<*>.cast(type: KClass): DataNode { + return object : DataNode { + override val type: KClass = type + override val items: Map> get() = this@cast.items as Map> + } +} + +inline fun DataNode<*>.cast(): DataNode = cast(R::class) + +/** + * Check that node is compatible with given type meaning that each element could be cast to the type + */ +fun DataNode<*>.ensureType(type: KClass) { + if (!canCast(type)) { + error("$type expected, but $type received") + } +} + + +//expect fun DataNode.cast(type: KClass): DataNode \ No newline at end of file diff --git a/dataforge-data/src/jsMain/kotlin/hep/dataforge/data/dataJS.kt b/dataforge-data/src/jsMain/kotlin/hep/dataforge/data/dataJS.kt index 7706efad..54148bc5 100644 --- a/dataforge-data/src/jsMain/kotlin/hep/dataforge/data/dataJS.kt +++ b/dataforge-data/src/jsMain/kotlin/hep/dataforge/data/dataJS.kt @@ -5,14 +5,12 @@ import kotlin.reflect.KClass /** * 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<*>.canCast(type: KClass): Boolean { //Not supported in js yet + return true } -///** -// * Performing -// */ -//@Suppress("UNCHECKED_CAST") -//actual fun DataNode.cast(type: KClass): DataNode{ -// return this as DataNode -//} \ No newline at end of file +actual fun Data<*>.canCast(type: KClass): Boolean { + //Not supported in js yet + return true +} 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 2ba3319f..79a00075 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/CastDataNode.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/CastDataNode.kt @@ -2,18 +2,8 @@ package hep.dataforge.data import hep.dataforge.names.NameToken import kotlin.reflect.KClass -import kotlin.reflect.full.isSubclassOf -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 { origin.items.mapNotNull { (key, item) -> 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 bf803cf0..b2ce7bb6 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt @@ -2,6 +2,7 @@ package hep.dataforge.data import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass +import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSuperclassOf /** @@ -12,11 +13,15 @@ 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) { - if (!type.isSuperclassOf(type)) { - error("$type expected, but $type received") - } -} +actual fun DataNode<*>.canCast(type: KClass): Boolean = + type.isSuperclassOf(type) + +actual fun Data<*>.canCast(type: KClass): Boolean = + this.type.isSubclassOf(type) + + +fun Data<*>.safeCast(type: KClass): Data? = + if (canCast(type)) cast(type) else null /** * Filter a node by data and node type. Resulting node and its subnodes is guaranteed to have border type [type], @@ -30,12 +35,12 @@ fun DataNode<*>.safeCast(type: KClass): DataNode { } } -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 +} + +inline fun DataItem<*>?.safeCast(): DataItem? = safeCast(R::class) \ No newline at end of file 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 b029079c..ac068a56 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -47,7 +47,7 @@ class TaskBuilder(val name: String) { block: TaskEnv.(DataNode) -> DataNode ) { dataTransforms += DataTransformation(from, to) { context, model, data -> - data.checkType(inputType) + data.ensureType(inputType) val env = TaskEnv(EmptyName, model.meta, context) env.block(data.safeCast(inputType)) }