
commonMain.space.kscience.dataforge.data.DataTree.kt Maven / Gradle / Ivy
package space.kscience.dataforge.data
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.*
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.reflect.KType
public sealed class DataTreeItem {
public class Node(public val tree: DataTree) : DataTreeItem()
public class Leaf(public val data: Data) : DataTreeItem()
}
public val DataTreeItem.type: KType
get() = when (this) {
is DataTreeItem.Node -> tree.dataType
is DataTreeItem.Leaf -> data.type
}
/**
* A tree-like [DataSet] grouped into the node. All data inside the node must inherit its type
*/
@Type(DataTree.TYPE)
public interface DataTree : DataSet {
/**
* Children items of this [DataTree] provided asynchronously
*/
public suspend fun items(): Map>
override fun flow(): Flow> = flow {
items().forEach { (token, childItem: DataTreeItem) ->
if(!token.body.startsWith("@")) {
when (childItem) {
is DataTreeItem.Leaf -> emit(childItem.data.named(token.asName()))
is DataTreeItem.Node -> emitAll(childItem.tree.flow().map { it.named(token + it.name) })
}
}
}
}
override suspend fun listTop(prefix: Name): List =
getItem(prefix).tree?.items()?.keys?.map { prefix + it } ?: emptyList()
override suspend fun getData(name: Name): Data? = when (name.length) {
0 -> null
1 -> items()[name.firstOrNull()!!].data
else -> items()[name.firstOrNull()!!].tree?.getData(name.cutFirst())
}
public companion object {
public const val TYPE: String = "dataTree"
}
}
public suspend fun DataSet.getData(name: String): Data? = getData(Name.parse(name))
/**
* Get a [DataTreeItem] with given [name] or null if the item does not exist
*/
public tailrec suspend fun DataTree.getItem(name: Name): DataTreeItem? = when (name.length) {
0 -> DataTreeItem.Node(this)
1 -> items()[name.firstOrNull()]
else -> items()[name.firstOrNull()!!].tree?.getItem(name.cutFirst())
}
public val DataTreeItem?.tree: DataTree? get() = (this as? DataTreeItem.Node)?.tree
public val DataTreeItem?.data: Data? get() = (this as? DataTreeItem.Leaf)?.data
/**
* Flow of all children including nodes
*/
public fun DataTree.itemFlow(): Flow>> = flow {
items().forEach { (head, item) ->
emit(head.asName() to item)
if (item is DataTreeItem.Node) {
val subSequence = item.tree.itemFlow()
.map { (name, data) -> (head.asName() + name) to data }
emitAll(subSequence)
}
}
}
/**
* Get a branch of this [DataTree] with a given [branchName].
* The difference from similar method for [DataSet] is that internal logic is more simple and the return value is a [DataTree]
*/
public fun DataTree.branch(branchName: Name): DataTree = object : DataTree {
override val dataType: KType get() = [email protected]
override suspend fun items(): Map> = getItem(branchName).tree?.items() ?: emptyMap()
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy