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

commonMain.maryk.core.values.ValueItems.kt Maven / Gradle / Ivy

Go to download

Maryk is a Kotlin Multiplatform library which helps you to store, query and send data in a structured way over multiple platforms. The data store stores any value with a version, so it is possible to request only the changed data or live listen for updates.

The newest version!

package maryk.core.values

import maryk.core.models.IsDataModel
import maryk.core.models.IsTypedDataModel
import maryk.core.properties.definitions.HasDefaultValueDefinition
import maryk.core.properties.graph.IsPropRefGraph
import maryk.core.properties.graph.PropRefGraph
import maryk.core.properties.types.MutableTypedValue
import maryk.core.properties.types.TypedValue
import kotlin.jvm.JvmInline

interface IsValueItems : Iterable {
    val size: Int

    operator fun get(index: UInt): Any?

    fun getValueItem(index: UInt): ValueItem?

    fun contains(index: UInt): Boolean

    fun copyAdding(toAdd: Iterable): IsValueItems

    fun copySelecting(select: IsPropRefGraph<*>): IsValueItems
    fun toString(dataModel: IsDataModel): String
}

interface IsValueItemsImpl : IsValueItems {
    val list: List

    override val size get() = list.size

    override fun contains(index: UInt) =
        0 <= list.binarySearch { it.index compareTo index }

    override operator fun get(index: UInt): Any? {
        this.list.searchItemByIndex(index).let {
            return when {
                it < 0 -> null
                else -> list[it].value
            }
        }
    }

    override fun getValueItem(index: UInt): ValueItem? {
        this.list.searchItemByIndex(index).let {
            return when {
                it < 0 -> null
                else -> list[it]
            }
        }
    }

    override fun iterator() = object : Iterator {
        var index = 0
        override fun hasNext() = index < list.size
        override fun next() = list[index++]
    }

    override fun copyAdding(toAdd: Iterable) = MutableValueItems(this.list.toMutableList()).also { items ->
        for (addition in toAdd) {
            items += addition
        }
    }

    override fun copySelecting(select: IsPropRefGraph<*>) = MutableValueItems(
        list = this.mapNotNull { valueItem ->
            if (!select.contains(valueItem.index)) {
                null
            } else {
                if (valueItem.value is Values<*>) {
                    (select.selectNodeOrNull(valueItem.index) as? PropRefGraph<*, *>)?.let { subSelect ->
                        ValueItem(valueItem.index, valueItem.value.filterWithSelect(subSelect))
                    } ?: valueItem
                } else valueItem
            }
        }.toMutableList()
    )

    override fun toString(dataModel: IsDataModel): String =
        this.list.joinToString(separator = ", ", prefix = "{", postfix = "}") { valueItem ->
            "${dataModel[valueItem.index]?.name ?: valueItem.index}=${valueItem.value}"
        }
}

internal fun List.searchItemByIndex(index: UInt): Int =
    // Index can never be at a higher spot in list than index itself
    binarySearch(toIndex = minOf(index.toInt(), size)) { it.index compareTo index }

val EmptyValueItems: IsValueItems = MutableValueItems()
@Suppress("FunctionName")
fun ValueItems(vararg item: ValueItem): IsValueItems = MutableValueItems(*item)

@JvmInline
value class MutableValueItems(
    override val list: MutableList
) : IsValueItemsImpl {
    override val size get() = list.size

    constructor() : this(mutableListOf())

    constructor(vararg item: ValueItem) : this(mutableListOf(*item))

    /**
     * Adds ValueItem to ValueItems.
     * If ValueItem contains Values object then it is merging it with existing data.
     */
    operator fun plusAssign(valueItem: ValueItem) {
        this.list.searchItemByIndex(valueItem.index).let {
            when {
                it < 0 -> if (valueItem.value != Unit) {
                    list.add((it * -1) - 1, valueItem)
                } else Unit // Is deleted so do nothing
                else -> {
                    when {
                        valueItem.value is Unit -> {
                            list.removeAt(it)
                        }
                        valueItem.value is TypedValue<*, *> && valueItem.value.value is Unit -> {
                            list.removeAt(it)
                        }
                        else -> {
                            list[it] = when (valueItem.value) {
                                is Values<*> -> {
                                    val newValue = (list[it].value as Values<*>).copy(valueItem.value.values)
                                    ValueItem(valueItem.index, newValue)
                                }
                                else -> valueItem
                            }
                        }
                    }
                }
            }
        }
    }

    operator fun set(index: UInt, value: Any) {
        this.list.searchItemByIndex(index).let {
            val valueItem = ValueItem(index, value)
            when {
                it < 0 -> list.add((it * -1) - 1, valueItem)
                else -> list.set(it, valueItem)
            }
        }
    }

    fun remove(index: UInt) = this.list.searchItemByIndex(index).let {
        when {
            it < 0 -> null
            else -> list.removeAt(it)
        }
    }

    /**
     * Changes valueItem at [referenceIndex] with [valueChanger]
     * If [referenceIndex] is not yet contained in this valueItems then fetch it from [sourceValueItems]
     */
    fun copyFromOriginalAndChange(
        sourceValueItems: IsValueItems?,
        referenceIndex: UInt,
        valueChanger: (Any?, Any?) -> Any?
    ) {
        val index = list.searchItemByIndex(referenceIndex)

        val originalValue = sourceValueItems?.getValueItem(referenceIndex)?.value
        when {
            index < 0 -> {
                val newValue = mutableValueCreator(originalValue)
                list.add((index * -1) - 1, ValueItem(referenceIndex, valueChanger(originalValue, newValue) ?: newValue!!))
            }
            else -> {
                valueChanger(originalValue, list[index].value)?.also {
                    list[index] = ValueItem(referenceIndex, it)
                }
            }
        }
    }

    fun fillWithPairs(dataModel: IsTypedDataModel<*>, pairs: Array, setDefaults: Boolean) {
        for (it in pairs) {
            if (it != null) this += it
        }
        if (setDefaults) {
            for (definition in dataModel.allWithDefaults) {
                val innerDef = definition.definition
                if (this[definition.index] == null) {
                    this[definition.index] = (innerDef as HasDefaultValueDefinition<*>).default!!
                }
            }
        }
    }

    override fun toString() = this.list.joinToString(separator = ", ", prefix = "{", postfix = "}")
}

private fun mutableValueCreator(valueToChange: Any?): Any? = when (valueToChange) {
    null -> null
    is List<*> -> valueToChange.toMutableList()
    is Set<*> -> valueToChange.toMutableSet()
    is Map<*, *> -> valueToChange.toMutableMap()
    is Values<*> ->
        Values(
            valueToChange.dataModel,
            MutableValueItems(mutableListOf()),
            valueToChange.context
        )
    is TypedValue<*, *> -> MutableTypedValue(
        valueToChange.type,
        mutableValueCreator(valueToChange.value) as Any
    )
    else -> valueToChange
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy