Refactored TaskBuilder to be more predictable

This commit is contained in:
Alexander Nozik 2019-09-04 09:50:56 +03:00
parent 7045f34c2c
commit 418e1c2134
6 changed files with 51 additions and 42 deletions

View File

@ -227,4 +227,6 @@ fun <T : Any> DataNode<T>.first(): Data<T>? = 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 <T : Any> DataNode<*>.checkType(type: KClass<out T>)
//expect fun <T : Any, R : Any> DataNode<T>.cast(type: KClass<out R>): DataNode<R>

View File

@ -1,10 +0,0 @@
package hep.dataforge.data
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<*>) {
//Not supported in js yet
}

View File

@ -0,0 +1,18 @@
package hep.dataforge.data
import kotlin.reflect.KClass
/**
* Check that node is compatible with given type meaning that each element could be cast to the type
*/
actual fun <T : Any> DataNode<*>.checkType(type: KClass<out T>) {
//Not supported in js yet
}
///**
// * Performing
// */
//@Suppress("UNCHECKED_CAST")
//actual fun <T : Any, R : Any> DataNode<T>.cast(type: KClass<out R>): DataNode<R>{
// return this as DataNode<R>
//}

View File

@ -8,7 +8,7 @@ import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
@Suppress("UNCHECKED_CAST")
fun <T : Any, R : Any> Data<T>.safeCast(type: KClass<out R>): Data<R>? {
private fun <T : Any, R : Any> Data<T>.safeCast(type: KClass<out R>): Data<R>? {
return if (this.type.isSubclassOf(type)) {
return object : Data<R> {
override val meta: Meta get() = this@safeCast.meta
@ -23,20 +23,6 @@ fun <T : Any, R : Any> Data<T>.safeCast(type: KClass<out R>): Data<R>? {
}
}
/**
* 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 <T : Any, R : Any> DataNode<T>.cast(type: KClass<out R>): DataNode<R> {
return if (this is CastDataNode) {
origin.cast(type)
} else {
CastDataNode(this, type)
}
}
inline fun <T : Any, reified R : Any> DataNode<T>.cast(): DataNode<R> = cast(R::class)
class CastDataNode<out T : Any>(val origin: DataNode<Any>, override val type: KClass<out T>) : DataNode<T> {
override val items: Map<NameToken, DataItem<T>> by lazy {
origin.items.mapNotNull { (key, item) ->

View File

@ -12,8 +12,22 @@ fun <T : Any> Data<T>.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 <T: Any> DataNode<*>.checkType(type: KClass<out T>) {
if (!type.isSuperclassOf(type)) {
error("$type expected, but $type received")
}
}
}
/**
* 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 <T : Any, R : Any> DataNode<T>.cast(type: KClass<out R>): DataNode<R> {
return if (this is CastDataNode) {
origin.cast(type)
} else {
CastDataNode(this, type)
}
}
inline fun <T : Any, reified R : Any> DataNode<T>.cast(): DataNode<R> = cast(R::class)

View File

@ -6,6 +6,7 @@ import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.string
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import kotlin.reflect.KClass
@ -14,6 +15,7 @@ import kotlin.reflect.KClass
class TaskBuilder(val name: String) {
private var modelTransform: TaskModelBuilder.(Meta) -> Unit = { data("*") }
var descriptor: NodeDescriptor? = null
private val dataTransforms: MutableList<DataTransformation> = ArrayList()
/**
* TODO will look better as extension class
@ -33,8 +35,6 @@ class TaskBuilder(val name: String) {
}
}
private val dataTransforms: MutableList<DataTransformation> = ArrayList();
fun model(modelTransform: TaskModelBuilder.(Meta) -> Unit) {
this.modelTransform = modelTransform
}
@ -43,17 +43,19 @@ class TaskBuilder(val name: String) {
inputType: KClass<T>,
from: String = "",
to: String = "",
block: TaskModel.(Context, DataNode<T>) -> DataNode<Any>
block: TaskEnv.(DataNode<T>) -> DataNode<Any>
) {
dataTransforms += DataTransformation(from, to) { context, model, data ->
block(model, context, data.cast(inputType))
data.checkType(inputType)
val env = TaskEnv(EmptyName, model.meta, context)
env.block(data.cast(inputType))
}
}
inline fun <reified T : Any> transform(
from: String = "",
to: String = "",
noinline block: TaskModel.(Context, DataNode<T>) -> DataNode<Any>
noinline block: TaskEnv.(DataNode<T>) -> DataNode<Any>
) {
transform(T::class, from, to, block)
}
@ -64,10 +66,10 @@ class TaskBuilder(val name: String) {
inline fun <reified T : Any, reified R : Any> action(
from: String = "",
to: String = "",
crossinline block: Context.() -> Action<T, R>
crossinline block: TaskEnv.() -> Action<T, R>
) {
transform(from, to) { context, data: DataNode<T> ->
block(context).invoke(data, meta)
transform(from, to) { data: DataNode<T> ->
block().invoke(data, meta)
}
}
@ -82,7 +84,6 @@ class TaskBuilder(val name: String) {
crossinline block: PipeBuilder<T, R>.(Context) -> Unit
) {
action(from, to) {
val context = this
PipeAction(
inputType = T::class,
outputType = R::class
@ -99,7 +100,6 @@ class TaskBuilder(val name: String) {
crossinline block: suspend TaskEnv.(T) -> R
) {
action(from, to) {
val context = this
PipeAction(
inputType = T::class,
outputType = R::class
@ -124,7 +124,7 @@ class TaskBuilder(val name: String) {
JoinAction(
inputType = T::class,
outputType = R::class
) { block(this@action) }
) { block(context) }
}
}
@ -137,7 +137,6 @@ class TaskBuilder(val name: String) {
crossinline block: suspend TaskEnv.(Map<Name, T>) -> R
) {
action(from, to) {
val context = this
JoinAction(
inputType = T::class,
outputType = R::class,
@ -164,7 +163,7 @@ class TaskBuilder(val name: String) {
SplitAction(
inputType = T::class,
outputType = R::class
) { block(this@action) }
) { block(context) }
}
}
@ -187,7 +186,7 @@ class TaskBuilder(val name: String) {
val model = this
if (dataTransforms.isEmpty()) {
//return data node as is
logger.warn("No transformation present, returning input data")
logger.warn { "No transformation present, returning input data" }
data
} else {
val builder = DataTreeBuilder(Any::class)