SDPTeam15/PolyEvents

View on GitHub
app/src/main/java/com/github/sdpteam15/polyevents/model/observable/ObservableMap.kt

Summary

Maintainability
A
0 mins
Test Coverage
A
90%
package com.github.sdpteam15.polyevents.model.observable

import androidx.lifecycle.LifecycleOwner
import com.github.sdpteam15.polyevents.helper.HelperFunctions

/**
 * Observable live map of type T
 */
@Suppress("UNCHECKED_CAST")
class ObservableMap<K, T>(val creator: Any? = null) : MutableMap<K, T> {

    private val observersPut = mutableSetOf<(UpdateKeyedValue<K, T>) -> Boolean>()
    private val observersRemove = mutableSetOf<(UpdateKeyedValue<K, T>) -> Boolean>()
    private val observersItemUpdate = mutableSetOf<(UpdateKeyedValue<K, T>) -> Boolean>()
    private val observers = mutableSetOf<(ObserversInfo<K, T>) -> Boolean>()

    private val mapValues: MutableMap<K, Observable<T>> = mutableMapOf()
    private val removeItemObserver: MutableMap<Observable<T>, () -> Boolean> = mutableMapOf()

    /**
     * Use an UpdateKeyedValue object each time we want to set a new value for the data.
     * It contains the new value and eventually the object that set the new value.
     * @property value the value
     * @property key the key
     * @property sender object that modified the data
     */
    open class UpdateKeyedValue<K, T>(value: T, val key: K, sender: Any?) :
        Observable.UpdateValue<T>(value, sender) {
        override fun toString() = "value:'$value', key:'$key', sender:'$sender'"
    }

    /**
     * Use an ObserversInfo object each time we want notify an update of the map
     */
    class ObserversInfo<K, T>(
        value: Map<K, T>,
        val info: Info,
        val args: Any?,
        sender: Any?
    ) : Observable.UpdateValue<Map<K, T>>(value, sender) {
        override fun toString() = "value:'$value', info:$info, args:'$args', sender:'$sender'"
    }

    /**
     * Update type
     */
    enum class Info {
        put,
        putAll,
        remove,
        updateAll,
        clear,
        itemUpdated,
    }

    override val size get() = mapValues.size

    override fun containsKey(key: K) = mapValues.containsKey(key)

    override operator fun get(key: K): T? = mapValues[key]?.value

    /**
     * Returns the observable element at the specified index in the list.
     * @return the observable element at the specified index in the list.
     */
    fun getObservable(key: K): Observable<T>? = mapValues[key]

    override fun containsValue(value: T): Boolean {
        for (e in mapValues.values)
            if (value?.equals(e.value) == true)
                return true
        return false
    }

    /**
     * Returns if the map maps one or more keys to the specified value.
     * @return if the map maps one or more keys to the specified value.
     */
    fun containsObservable(value: Observable<T>) = mapValues.containsValue(value)

    override fun isEmpty() = mapValues.isEmpty()

    private fun put(
        key: K,
        observable: Observable<T>,
        sender: Any? = null,
        notify: Boolean
    ): Observable<T>? {
        if (observable.value != null) {
            var r: (() -> Boolean)? = null
            val isNull = mapValues[key] == null

            if (!isNull) {
                val v = observable.map(true, mapValues[key]!!) { it }
                removeItemObserver[v.then]!!()
                r = v.remove
            }
            mapValues[key] = observable
            removeItemObserver[observable] =
                observable.observe(false) {
                    itemUpdated(
                        UpdateKeyedValue(
                            it.value,
                            key,
                            it.sender
                        )
                    )
                }.remove
            if (r != null)
                removeItemObserver[observable] = { r() && removeItemObserver[observable]!!() }
            itemPut(UpdateKeyedValue(observable.value!!, key, sender))
            if (notify) {
                notifyUpdate(sender, Info.put, Triple(key, observable, isNull))
            }
            return observable
        }
        return null
    }

    /**
     * Add an item.
     * @param key Key to add.
     * @param item Item to add.
     * @param sender The source of the event.
     * @return Observable added.
     */
    fun put(key: K, item: T, sender: Any?) = put(key, item, sender, true)


    /**
     * Add an observable.
     * @param key key of add.
     * @param observable Observable to add.
     * @param sender The source of the event.
     * @return Observable added.
     */
    fun put(key: K, observable: Observable<T>, sender: Any? = null): Observable<T>? =
        put(key, observable, sender, true)

    private fun put(key: K, value: T, sender: Any? = null, notify: Boolean) =
        if (mapValues[key] != null) {
            mapValues[key]!!.postValue(value, sender)
            mapValues[key]!!
        } else
            put(key, Observable(value), sender, notify)

    override fun put(key: K, value: T): T? = put(key, value, null)?.value

    /**
     * Add all items in the map.
     * @param from map to add.
     * @param sender The source of the event.
     */
    fun putAll(from: Map<out K, T>, sender: Any? = null): Boolean {
        var res = true
        val added = mutableListOf<Observable<T>>()
        for (key: K in from.keys) {
            val isNull = mapValues[key] == null
            val ads = put(key, from[key]!!, sender, false)
            if (ads != null) {
                if (isNull)
                    added.add(ads)
            } else
                res = false
        }
        notifyUpdate(sender, Info.putAll, Pair(from, added))
        return res
    }

    override fun putAll(from: Map<out K, T>) {
        putAll(from, null)
    }

    /**
     * Remove an item.
     * @param key key of the item to remove.
     * @param sender The source of the event.
     * @return observable removed.
     */
    fun remove(key: K, sender: Any? = null) = remove(key, sender, true)
    private fun remove(
        key: K,
        sender: Any?,
        notify: Boolean
    ): Observable<T>? {
        val observable = mapValues.remove(key) ?: return null
        removeItemObserver[observable]!!()
        removeItemObserver.remove(observable)
        itemRemoved(UpdateKeyedValue(observable.value!!, key, sender))
        if (notify) {
            notifyUpdate(sender, Info.remove, Pair(key, observable))
        }
        return observable
    }

    override fun remove(key: K): T? = remove(key, null)?.value

    /**
     * Update the elements in this collection to that are contained in the specified collection.
     * @param items items map.
     * @param sender The source of the event.
     */
    fun updateAll(items: Map<K, T>, sender: Any? = null) {
        val toremove = mutableMapOf<K, Observable<T>>()
        val added = mutableMapOf<K, Observable<T>>()
        val toadd = mutableMapOf<K, T>()
        toadd.putAll(items)
        for (key in mapValues.keys) {
            if (mapValues[key]!!.value != items[key])
                toremove[key] = mapValues[key]!!
            else
                toadd.remove(key)
        }
        for (key in toremove.keys)
            remove(key, sender, false)
        for (key in toadd.keys)
            added[key] = put(key, toadd[key]!!, sender, false)!!
        notifyUpdate(sender, Info.updateAll, Triple(items, toremove, added))
    }

    override fun clear() = clear(null)

    /**
     * Clear all items.
     * @param sender The source of the event.
     */
    fun clear(sender: Any?) {
        for (key in mapValues.keys.toList()) {
            remove(key, sender, false)
        }
        notifyUpdate(sender, Info.clear)
    }

    override val entries: MutableSet<MutableMap.MutableEntry<K, T>>
        get() {
            val result = mutableSetOf<MutableMap.MutableEntry<K, T>>()
            for (e in mapValues.entries)
                result.add(object : MutableMap.MutableEntry<K, T> {
                    override val key: K
                        get() = e.key
                    override val value: T
                        get() = e.value.value!!

                    override fun setValue(newValue: T): T {
                        val v = value
                        e.value.postValue(newValue, this)
                        return v
                    }
                })
            return result
        }
    override val keys: MutableSet<K>
        get() = mapValues.keys
    override val values: MutableCollection<T>
        get() = valuesWhileTrue { true }.then

    /**
     *  Add an observer for the live data additions while it return true
     *  @param observer observer for the live data additions
     *  @return a method to remove the observer
     */
    fun observePutWhileTrue(
        observer: (UpdateKeyedValue<K, T>) -> Boolean
    ): Observable.ThenOrRemove<ObservableMap<K, T>> {
        observersPut.add(observer)
        return Observable.ThenOrRemove(this, creator, { leavePut(observer) })
    }

    /**
     *  Add an observer for the live data additions while it return true
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data additions
     *  @return a method to remove the observer
     */
    fun observePutWhileTrue(
        lifecycle: LifecycleOwner,
        observer: (UpdateKeyedValue<K, T>) -> Boolean
    ) =
        Observable.observeOnDestroy(lifecycle, observePutWhileTrue(observer))

    /**
     *  Add an observer for the live data additions
     *  @param observer observer for the live data additions
     *  @return a method to remove the observer
     */
    fun observePut(observer: (UpdateKeyedValue<K, T>) -> Unit) =
        observePutWhileTrue {
            observer(it)
            true
        }

    /**
     *  Add an observer for the live data additions
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data additions
     *  @return a method to remove the observer
     */
    fun observePut(
        lifecycle: LifecycleOwner,
        observer: (UpdateKeyedValue<K, T>) -> Unit
    ) =
        Observable.observeOnDestroy(lifecycle, observePut(observer))

    /**
     *  Add an observer for the live data additions once
     *  @param observer observer for the live data additions
     *  @return a method to remove the observer
     */
    fun observePutOnce(observer: (UpdateKeyedValue<K, T>) -> Unit) =
        observePutWhileTrue {
            observer(it)
            false
        }

    /**
     *  Add an observer for the live data additions once
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data additions
     *  @return a method to remove the observer
     */
    fun observePutOnce(
        lifecycle: LifecycleOwner,
        observer: (UpdateKeyedValue<K, T>) -> Unit
    ) =
        Observable.observeOnDestroy(lifecycle, observePutOnce(observer))

    /**
     *  remove an observer for the live data
     *  @param observer observer for the live data
     *  @return if the observer have been remove
     */
    fun leavePut(observer: (UpdateKeyedValue<K, T>) -> Boolean) = observersPut.remove(observer)

    /**
     *  Add an observer for the live data removals while it return true
     *  @param observer observer for the live data removals
     *  @return a method to remove the observer
     */
    fun observeRemoveWhileTrue(observer: (UpdateKeyedValue<K, T>) -> Boolean): Observable.ThenOrRemove<ObservableMap<K, T>> {
        observersRemove.add(observer)
        return Observable.ThenOrRemove(this, creator, { leaveRemove(observer) })
    }

    /**
     *  Add an observer for the live data removals while it return true
     *  @param observer lifecycle of the observer to automatically remove it from the observers when stopped
     *  @return a method to remove the observer
     */
    fun observeRemoveWhileTrue(
        lifecycle: LifecycleOwner,
        observer: (UpdateKeyedValue<K, T>) -> Boolean
    ) =
        Observable.observeOnDestroy(lifecycle, observeRemoveWhileTrue(observer))

    /**
     *  Add an observer for the live data removals
     *  @param observer observer for the live data removals
     *  @return a method to remove the observer
     */
    fun observeRemove(observer: (UpdateKeyedValue<K, T>) -> Unit) = observeRemoveWhileTrue {
        observer(it)
        true
    }

    /**
     *  Add an observer for the live data removals
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data removals
     *  @return a method to remove the observer
     */
    fun observeRemove(lifecycle: LifecycleOwner, observer: (UpdateKeyedValue<K, T>) -> Unit) =
        Observable.observeOnDestroy(lifecycle, observeRemove(observer))

    /**
     *  Add an observer for the live data removals once
     *  @param observer observer for the live data removals
     *  @return a method to remove the observer
     */
    fun observeRemoveOnce(observer: (UpdateKeyedValue<K, T>) -> Unit) = observeRemoveWhileTrue {
        observer(it)
        false
    }

    /**
     *  Add an observer for the live data removals once
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data removals
     *  @return a method to remove the observer
     */
    fun observeRemoveOnce(
        lifecycle: LifecycleOwner,
        observer: (UpdateKeyedValue<K, T>) -> Unit
    ) = Observable.observeOnDestroy(lifecycle, observeRemoveOnce(observer))

    /**
     *  remove an observer for the live data
     *  @param observer observer for the live data
     *  @return if the observer have been remove
     */
    fun leaveRemove(observer: (UpdateKeyedValue<K, T>) -> Boolean) =
        observersRemove.remove(observer)

    /**
     *  Add an observer for the live data updating while it return tru
     *  @param observer observer for the live data updating
     *  @return a method to remove the observer
     */
    fun observeUpdateWhileTrue(
        observer: (UpdateKeyedValue<K, T>) -> Boolean
    ): Observable.ThenOrRemove<ObservableMap<K, T>> {
        observersItemUpdate.add(observer)
        return Observable.ThenOrRemove(this, creator, { leaveUpdate(observer) })
    }

    /**
     *  Add an observer for the live data updating while it return tru
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data updating
     *  @return a method to remove the observer
     */
    fun observeUpdateWhileTrue(
        lifecycle: LifecycleOwner,
        observer: (UpdateKeyedValue<K, T>) -> Boolean
    ) = Observable.observeOnDestroy(lifecycle, observeUpdateWhileTrue(observer))

    /**
     *  Add an observer for the live data updating
     *  @param observer observer for the live data updating
     *  @return a method to remove the observer
     */
    fun observeUpdate(observer: (UpdateKeyedValue<K, T>) -> Unit) = observeUpdateWhileTrue {
        observer(it)
        true
    }

    /**
     *  Add an observer for the live data updating
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data updating
     *  @return a method to remove the observer
     */
    fun observeUpdate(
        lifecycle: LifecycleOwner,
        observer: (UpdateKeyedValue<K, T>) -> Unit
    ) = Observable.observeOnDestroy(lifecycle, observeUpdate(observer))

    /**
     *  Add an observer for the live data updating once
     *  @param observer observer for the live data updating
     *  @return a method to remove the observer
     */
    fun observeUpdateOnce(observer: (UpdateKeyedValue<K, T>) -> Unit) =
        observeUpdateWhileTrue {
            observer(it)
            false
        }

    /**
     *  Add an observer for the live data updating once
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data updating
     *  @return a method to remove the observer
     */
    fun observeUpdateOnce(
        lifecycle: LifecycleOwner,
        observer: (UpdateKeyedValue<K, T>) -> Unit
    ) = Observable.observeOnDestroy(lifecycle, observeUpdateOnce(observer))

    /**
     *  remove an observer for the live data
     *  @param observer observer for the live data
     *  @return if the observer have been remove
     */
    fun leaveUpdate(observer: (UpdateKeyedValue<K, T>) -> Boolean) =
        observersItemUpdate.remove(observer)

    /**
     *  Add an observer for the live data while it return true
     *  @param observer observer for the live data
     *  @return a method to remove the observer
     */
    fun observeWhileTrue(observer: (Observable.UpdateValue<Map<K, T>>) -> Boolean): Observable.ThenOrRemove<ObservableMap<K, T>> {
        observers.add(observer)
        return Observable.ThenOrRemove(this, creator, { leave(observer) })
    }

    /**
     *  Add an observer for the live data while it return true
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data
     *  @return a method to remove the observer
     */
    fun observeWhileTrue(
        lifecycle: LifecycleOwner,
        observer: (Observable.UpdateValue<Map<K, T>>) -> Boolean
    ) =
        Observable.observeOnDestroy(lifecycle, observeWhileTrue(observer))

    /**
     *  Add an observer for the live data
     *  @param observer observer for the live data
     *  @return a method to remove the observer
     */
    fun observe(observer: (Observable.UpdateValue<Map<K, T>>) -> Unit) = observeWhileTrue {
        observer(it)
        true
    }

    /**
     *  Add an observer for the live data
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data
     *  @return a method to remove the observer
     */
    fun observe(lifecycle: LifecycleOwner, observer: (Observable.UpdateValue<Map<K, T>>) -> Unit) =
        Observable.observeOnDestroy(lifecycle, observe(observer))

    /**
     *  Add an observer for the live data once
     *  @param observer observer for the live data
     *  @return a method to remove the observer
     */
    fun observeOnce(observer: (Observable.UpdateValue<Map<K, T>>) -> Unit) = observeWhileTrue {
        observer(it)
        false
    }

    /**
     *  Add an observer for the live data once
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observer observer for the live data
     *  @return a method to remove the observer
     */
    fun observeOnce(
        lifecycle: LifecycleOwner,
        observer: (Observable.UpdateValue<Map<K, T>>) -> Unit
    ) =
        Observable.observeOnDestroy(lifecycle, observeOnce(observer))

    /**
     *  remove an observer for the live data
     *  @param observer observer for the live data
     *  @return if the observer have been remove
     */
    fun leave(observer: (ObserversInfo<K, T>) -> Boolean) = observers.remove(observer)

    /**
     *  map to an other observable while it return true
     *  @param observableMap observer for the live data
     *  @param condition condition to continue to observe
     *  @param mapper mapper from the live data to the new one
     *  @return if the observer have been remove
     */
    fun <U> mapWhileTrue(
        observableMap: ObservableMap<K, U> = ObservableMap(creator = creator),
        condition: () -> Boolean,
        mapper: (T) -> U
    ): Observable.ThenOrRemove<ObservableMap<K, U>> {
        observableMap.clear()
        val tempMap = mutableMapOf<K, U>()
        for (key in mapValues.keys)
            tempMap[key] = mapper(mapValues[key]!!.value!!)
        observableMap.putAll(tempMap, this)
        val result: (ObserversInfo<K, T>) -> Boolean =
            {
                when (it.info) {
                    Info.put -> {
                        val (key, observable, _) = it.args as Triple<K, Observable<T>, Boolean>
                        observableMap.put(key, mapper(observable.value!!), it.sender)
                    }
                    Info.putAll -> {
                        val (from, _) = it.args as Pair<Map<out K, T>, MutableList<Observable<T>>>
                        val tempMap2 = mutableMapOf<K, U>()
                        for (key in from.keys)
                            tempMap2[key] = mapper(from[key]!!)
                        observableMap.putAll(tempMap2, it.sender)
                    }
                    Info.remove -> {
                        val (key, _) = it.args as Pair<K, Observable<T>>
                        observableMap.remove(key, it.sender)
                    }
                    Info.updateAll -> {
                        val (items, _, _) = it.args as Triple<Map<K, T>, Map<K, Observable<T>>, Map<K, Observable<T>>>
                        val tempMap2 = mutableMapOf<K, U>()
                        for (key in items.keys)
                            tempMap2[key] = mapper(items[key]!!)
                        observableMap.updateAll(tempMap2)
                    }
                    Info.clear -> {
                        observableMap.clear(it.sender)
                    }
                    Info.itemUpdated -> {
                        val value = it.args as UpdateKeyedValue<K, T>
                        observableMap.getObservable(value.key)!!
                            .postValue(mapper(value.value), it.sender)
                    }
                }
                condition()
            }
        observers.add(result)
        return Observable.ThenOrRemove(observableMap, creator, { observers.remove(result) })
    }

    /**
     *  map to an other observable while it return true
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observableMap observer for the live data
     *  @param condition condition to continue to observe
     *  @param mapper mapper from the live data to the new one
     *  @return if the observer have been remove
     */
    fun <U> mapWhileTrue(
        lifecycle: LifecycleOwner,
        observableMap: ObservableMap<K, U> = ObservableMap(creator = creator),
        condition: () -> Boolean,
        mapper: (T) -> U
    ) = Observable.observeOnDestroy(lifecycle, mapWhileTrue(observableMap, condition, mapper))

    /**
     *  map to an other observable
     *  @param observableMap observer for the live data
     *  @param mapper mapper from the live data to the new one
     *  @return if the observer have been remove
     */
    fun <U> map(
        observableMap: ObservableMap<K, U> = ObservableMap(),
        mapper: (T) -> U
    ) = mapWhileTrue(observableMap, { true }, mapper)

    /**
     *  map to an other observable
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observableMap observer for the live data
     *  @param mapper mapper from the live data to the new one
     *  @return if the observer have been remove
     */
    fun <U> map(
        lifecycle: LifecycleOwner,
        observableMap: ObservableMap<K, U> = ObservableMap(creator = creator),
        mapper: (T) -> U
    ) = Observable.observeOnDestroy(lifecycle, map(observableMap, mapper))

    /**
     *  map to an other observable once
     *  @param observableMap observer for the live data
     *  @param mapper mapper from the live data to the new one
     *  @return if the observer have been remove
     */
    fun <U> mapOnce(
        observableMap: ObservableMap<K, U> = ObservableMap(creator = creator),
        mapper: (T) -> U
    ) = mapWhileTrue(observableMap, { false }, mapper)

    /**
     *  map to an other observable once
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observableMap observer for the live data
     *  @param mapper mapper from the live data to the new one
     *  @return if the observer have been remove
     */
    fun <U> mapOnce(
        lifecycle: LifecycleOwner,
        observableMap: ObservableMap<K, U> = ObservableMap(creator = creator),
        mapper: (T) -> U
    ) = Observable.observeOnDestroy(lifecycle, mapOnce(observableMap, mapper))

    /**
     *  map to an other observable while it return true
     *  @param observableList observer for the live data
     *  @param condition condition to continue to observe
     *  @return if the observer have been remove
     */
    fun valuesWhileTrue(
        observableList: ObservableList<T> = ObservableList(creator = creator),
        condition: () -> Boolean
    ): Observable.ThenOrRemove<ObservableList<T>> {
        observableList.clear()
        for (observable in mapValues.values)
            observableList.add(observable, creator)
        val result: (ObserversInfo<K, T>) -> Boolean =
            {
                when (it.info) {
                    Info.put -> {
                        val (_, observable, isNull) = it.args as Triple<K, Observable<T>, Boolean>
                        if (isNull)
                            observableList.add(observable, it.sender)
                    }
                    Info.putAll -> {
                        val (_, observables) = it.args as Pair<Map<out K, T>, MutableList<Observable<T>>>
                        for (observable in observables)
                            observableList.add(observable, it.sender)
                    }
                    Info.remove -> {
                        val (_, observable) = it.args as Pair<K, Observable<T>>
                        observableList.remove(observable)
                    }
                    Info.updateAll -> {
                        val (items, _, _) = it.args as Triple<Map<K, T>, Map<K, Observable<T>>, Map<K, Observable<T>>>
                        observableList.updateAll(items.map { it.value })
                    }
                    Info.clear -> {
                        observableList.clear(it.sender)
                    }
                    Info.itemUpdated -> { }
                }
                condition()
            }
        observers.add(result)
        return Observable.ThenOrRemove(observableList, this, { leave(result) })
    }

    /**
     *  map to an other observable while it return true
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observableList observer for the live data
     *  @param condition condition to continue to observe
     *  @return if the observer have been remove
     */
    fun valuesWhileTrue(
        lifecycle: LifecycleOwner,
        observableList: ObservableList<T> = ObservableList(creator = creator),
        condition: () -> Boolean
    ) = Observable.observeOnDestroy(lifecycle, valuesWhileTrue(observableList, condition))

    /**
     *  map to an other observable
     *  @param observableList observer for the live data
     *  @return if the observer have been remove
     */
    fun values(
        observableList: ObservableList<T> = ObservableList(creator = creator)
    ) = valuesWhileTrue(observableList) { true }

    /**
     *  map to an other observable
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observableList observer for the live data
     *  @return if the observer have been remove
     */
    fun values(
        lifecycle: LifecycleOwner,
        observableList: ObservableList<T> = ObservableList(creator = creator),
    ) = Observable.observeOnDestroy(lifecycle, values(observableList))

    /**
     *  map to an other observable once
     *  @param observableList observer for the live data
     *  @return if the observer have been remove
     */
    fun valuesOnce(
        observableList: ObservableList<T> = ObservableList(creator = creator)
    ) = valuesWhileTrue(observableList) { false }

    /**
     *  map to an other observable once
     *  @param lifecycle lifecycle of the observer to automatically remove it from the observers when stopped
     *  @param observableList observer for the live data
     *  @return if the observer have been remove
     */
    fun valueOnce(
        lifecycle: LifecycleOwner,
        observableList: ObservableList<T> = ObservableList(creator = creator),
    ) = Observable.observeOnDestroy(lifecycle, valuesOnce(observableList))

    private fun itemPut(value: UpdateKeyedValue<K, T>) {
        HelperFunctions.run(Runnable {
            for (obs in observersPut.toList())
                if (!obs(value))
                    leavePut(obs)
        })
    }

    private fun itemRemoved(value: UpdateKeyedValue<K, T>) {
        HelperFunctions.run(Runnable {
            for (obs in observersRemove.toList())
                if (!obs(value))
                    leaveRemove(obs)
        })
    }

    private fun itemUpdated(value: UpdateKeyedValue<K, T>) {
        HelperFunctions.run(Runnable {
            for (obs in observersItemUpdate.toList())
                if (!obs(value))
                    leaveUpdate(obs)
            notifyUpdate(value.sender, Info.itemUpdated, value)
        })
    }

    private fun notifyUpdate(sender: Any? = null, info: Info, args: Any? = null) {
        if (observers.isNotEmpty()) {
            val valueList =
                ObserversInfo(
                    this as MutableMap<K, T>,
                    info,
                    args,
                    sender
                )
            for (obs in observers.toList())
                if (!obs(valueList))
                    leave(obs)
        }
    }

    override fun toString(): String = "ObservableMap$mapValues"
}