All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.space.kscience.dataforge.data.DataTree.kt Maven / Gradle / Ivy

There is a newer version: 0.7.0
Show newest version
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