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

commonMain.io.realm.kotlin.internal.RealmSetInternal.kt Maven / Gradle / Ivy

Go to download

Library code for Realm Kotlin. This artifact is not supposed to be consumed directly, but through 'io.realm.kotlin:gradle-plugin:1.11.1' instead.

There is a newer version: 3.0.0
Show newest version
/*
 * Copyright 2022 Realm Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.realm.kotlin.internal

import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.Versioned
import io.realm.kotlin.dynamic.DynamicRealmObject
import io.realm.kotlin.ext.asRealmObject
import io.realm.kotlin.ext.isManaged
import io.realm.kotlin.internal.RealmValueArgumentConverter.convertToQueryArgs
import io.realm.kotlin.internal.interop.Callback
import io.realm.kotlin.internal.interop.ClassKey
import io.realm.kotlin.internal.interop.RealmChangesPointer
import io.realm.kotlin.internal.interop.RealmInterop
import io.realm.kotlin.internal.interop.RealmInterop.realm_set_get
import io.realm.kotlin.internal.interop.RealmKeyPathArrayPointer
import io.realm.kotlin.internal.interop.RealmNotificationTokenPointer
import io.realm.kotlin.internal.interop.RealmObjectInterop
import io.realm.kotlin.internal.interop.RealmSetPointer
import io.realm.kotlin.internal.interop.RealmValue
import io.realm.kotlin.internal.interop.ValueType
import io.realm.kotlin.internal.interop.getterScope
import io.realm.kotlin.internal.interop.inputScope
import io.realm.kotlin.internal.query.ObjectBoundQuery
import io.realm.kotlin.internal.query.ObjectQuery
import io.realm.kotlin.internal.util.Validation
import io.realm.kotlin.notifications.SetChange
import io.realm.kotlin.notifications.internal.DeletedSetImpl
import io.realm.kotlin.notifications.internal.InitialSetImpl
import io.realm.kotlin.notifications.internal.UpdatedSetImpl
import io.realm.kotlin.query.RealmQuery
import io.realm.kotlin.types.BaseRealmObject
import io.realm.kotlin.types.RealmAny
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.RealmSet
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.flow.Flow
import kotlin.reflect.KClass

/**
 * Implementation for unmanaged sets, backed by a [MutableSet].
 */
public class UnmanagedRealmSet(
    private val backingSet: MutableSet = mutableSetOf()
) : RealmSet, InternalDeleteable, MutableSet by backingSet {
    override fun asFlow(keyPaths: List?): Flow> {
        throw UnsupportedOperationException("Unmanaged sets cannot be observed.")
    }

    override fun delete() {
        throw UnsupportedOperationException("Unmanaged sets cannot be deleted.")
    }

    override fun toString(): String = "UnmanagedRealmSet{${joinToString()}}"

    override fun equals(other: Any?): Boolean = backingSet == other

    override fun hashCode(): Int = backingSet.hashCode()
}

/**
 * Implementation for managed sets, backed by Realm.
 */
internal class ManagedRealmSet constructor(
    // Rework to allow RealmAny
    internal val parent: RealmObjectReference<*>?,
    internal val nativePointer: RealmSetPointer,
    val operator: SetOperator
) : AbstractMutableSet(), RealmSet, InternalDeleteable, CoreNotifiable, SetChange>, Versioned by operator.realmReference {

    override val size: Int
        get() {
            operator.realmReference.checkClosed()
            return RealmInterop.realm_set_size(nativePointer).toInt()
        }

    override fun add(element: E): Boolean {
        return operator.add(element)
    }

    override fun clear() {
        operator.clear()
    }

    override fun contains(element: E): Boolean {
        return operator.contains(element)
    }

    override fun remove(element: E): Boolean {
        return operator.remove(element)
    }

    override fun removeAll(elements: Collection): Boolean {
        return operator.removeAll(elements)
    }

    override fun iterator(): MutableIterator {
        return object : MutableIterator {

            private var expectedModCount = operator.modCount // Current modifications in the map
            private var cursor = 0 // The position returned by next()
            private var lastReturned = -1 // The last known returned position

            override fun hasNext(): Boolean {
                checkConcurrentModification()

                return cursor < size
            }

            override fun next(): E {
                checkConcurrentModification()

                val position = cursor
                if (position >= size) {
                    throw IndexOutOfBoundsException("Cannot access index $position when size is $size. Remember to check hasNext() before using next().")
                }
                val next = operator.get(position)
                lastReturned = position
                cursor = position + 1
                return next
            }

            override fun remove() {
                checkConcurrentModification()

                if (size == 0) {
                    throw NoSuchElementException("Could not remove last element returned by the iterator: set is empty.")
                }
                if (lastReturned < 0) {
                    throw IllegalStateException("Could not remove last element returned by the iterator: iterator never returned an element.")
                }

                val erased = getterScope {
                    val element = operator.get(lastReturned)
                    operator.remove(element)
                        .also {
                            if (lastReturned < cursor) {
                                cursor -= 1
                            }
                            lastReturned = -1
                        }
                }
                expectedModCount = operator.modCount
                if (!erased) {
                    throw NoSuchElementException("Could not remove last element returned by the iterator: was there an element to remove?")
                }
            }

            private fun checkConcurrentModification() {
                if (operator.modCount != expectedModCount) {
                    throw ConcurrentModificationException("The underlying RealmSet was modified while iterating it.")
                }
            }
        }
    }

    override fun asFlow(keyPaths: List?): Flow> {
        val keyPathInfo = keyPaths?.let {
            Validation.isType>(operator, "Keypaths are only supported for sets of objects.")
            Pair(operator.classKey, keyPaths)
        }
        return operator.realmReference.owner.registerObserver(this, keyPathInfo)
    }

    override fun freeze(frozenRealm: RealmReference): ManagedRealmSet? {
        return RealmInterop.realm_set_resolve_in(nativePointer, frozenRealm.dbPointer)?.let {
            ManagedRealmSet(parent, it, operator.copy(frozenRealm, it))
        }
    }

    override fun thaw(liveRealm: RealmReference): ManagedRealmSet? {
        return RealmInterop.realm_set_resolve_in(nativePointer, liveRealm.dbPointer)?.let {
            ManagedRealmSet(parent, it, operator.copy(liveRealm, it))
        }
    }

    override fun registerForNotification(
        keyPaths: RealmKeyPathArrayPointer?,
        callback: Callback
    ): RealmNotificationTokenPointer {
        return RealmInterop.realm_set_add_notification_callback(nativePointer, keyPaths, callback)
    }

    override fun changeFlow(scope: ProducerScope>): ChangeFlow, SetChange> =
        RealmSetChangeFlow(scope)

    override fun delete() {
        RealmInterop.realm_set_remove_all(nativePointer)
    }

    override fun isValid(): Boolean {
        return !nativePointer.isReleased() && RealmInterop.realm_set_is_valid(nativePointer)
    }
}

internal fun  ManagedRealmSet.query(
    query: String,
    args: Array
): RealmQuery {
    val operator: RealmObjectSetOperator = operator as RealmObjectSetOperator
    val queryPointer = inputScope {
        val queryArgs = convertToQueryArgs(args)
        RealmInterop.realm_query_parse_for_set(
            [email protected],
            query,
            queryArgs
        )
    }
    // parent is only available for lists with an object as an immediate parent (contrary to nested
    // collections).
    // Nested collections are only supported for RealmAny-values and are therefore
    // outside of the BaseRealmObject bound for the generic type parameters, so we should never be
    // able to reach here for nested collections of RealmAny.
    if (parent == null) error("Cannot perform subqueries on non-object sets")
    return ObjectBoundQuery(
        parent,
        ObjectQuery(
            operator.realmReference,
            operator.classKey,
            operator.clazz,
            operator.mediator,
            queryPointer,
        )
    )
}

/**
 * Operator interface abstracting the connection between the API and and the interop layer.
 */
internal interface SetOperator : CollectionOperator {

    // Modification counter used to detect concurrent writes from the iterator, taken from Java's
    // AbstractList implementation
    var modCount: Int
    override val nativePointer: RealmSetPointer

    fun add(
        element: E,
        updatePolicy: UpdatePolicy = UpdatePolicy.ALL,
        cache: UnmanagedToManagedObjectCache = mutableMapOf()
    ): Boolean {
        return addInternal(element, updatePolicy, cache)
            .also { modCount++ }
    }

    fun addInternal(
        element: E,
        updatePolicy: UpdatePolicy = UpdatePolicy.ALL,
        cache: UnmanagedToManagedObjectCache = mutableMapOf()
    ): Boolean

    fun addAll(
        elements: Collection,
        updatePolicy: UpdatePolicy = UpdatePolicy.ALL,
        cache: UnmanagedToManagedObjectCache = mutableMapOf()
    ): Boolean {
        realmReference.checkClosed()
        return addAllInternal(elements, updatePolicy, cache)
            .also { modCount++ }
    }

    fun addAllInternal(
        elements: Collection,
        updatePolicy: UpdatePolicy = UpdatePolicy.ALL,
        cache: UnmanagedToManagedObjectCache = mutableMapOf()
    ): Boolean {
        @Suppress("VariableNaming")
        var changed = false
        for (e in elements) {
            val hasChanged = addInternal(e, updatePolicy, cache)
            if (hasChanged) {
                changed = true
            }
        }
        return changed
    }

    fun clear() {
        realmReference.checkClosed()
        RealmInterop.realm_set_clear(nativePointer)
        modCount++
    }

    fun removeInternal(element: E): Boolean
    fun remove(element: E): Boolean {
        return removeInternal(element).also {
            modCount++
        }
    }

    fun removeAll(elements: Collection): Boolean {
        return elements.fold(false) { accumulator, value ->
            remove(value) or accumulator
        }
    }

    fun get(index: Int): E
    fun contains(element: E): Boolean
    fun copy(realmReference: RealmReference, nativePointer: RealmSetPointer): SetOperator
}

internal fun realmAnySetOperator(
    mediator: Mediator,
    realm: RealmReference,
    nativePointer: RealmSetPointer,
    issueDynamicObject: Boolean = false,
    issueDynamicMutableObject: Boolean = false,
): RealmAnySetOperator = RealmAnySetOperator(
    mediator,
    realm,
    nativePointer,
    issueDynamicObject,
    issueDynamicMutableObject
)

internal class RealmAnySetOperator(
    override val mediator: Mediator,
    override val realmReference: RealmReference,
    override val nativePointer: RealmSetPointer,
    val issueDynamicObject: Boolean,
    val issueDynamicMutableObject: Boolean
) : SetOperator {

    override var modCount: Int = 0

    @Suppress("UNCHECKED_CAST")
    override fun get(index: Int): RealmAny? {
        return getterScope {
            val transport = realm_set_get(nativePointer, index.toLong())
            return realmValueToRealmAny(
                transport, null, mediator, realmReference,
                issueDynamicObject,
                issueDynamicMutableObject,
                { error("Set should never container lists") }
            ) { error("Set should never container dictionaries") }
        }
    }

    override fun addInternal(
        element: RealmAny?,
        updatePolicy: UpdatePolicy,
        cache: UnmanagedToManagedObjectCache
    ): Boolean {
        return inputScope {
            realmAnyHandler(
                value = element,
                primitiveValueAsRealmValueHandler = { realmValue: RealmValue ->
                    RealmInterop.realm_set_insert(nativePointer, realmValue)
                },
                referenceAsRealmAnyHandler = { realmValue ->
                    val obj = when (issueDynamicObject) {
                        true -> realmValue.asRealmObject()
                        false -> realmValue.asRealmObject()
                    }
                    val objRef =
                        realmObjectToRealmReferenceWithImport(obj, mediator, realmReference, updatePolicy, cache)
                    RealmInterop.realm_set_insert(nativePointer, realmObjectTransport(objRef))
                },
                listAsRealmAnyHandler = { realmValue -> throw IllegalArgumentException("Sets cannot contain other collections ") },
                dictionaryAsRealmAnyHandler = { realmValue -> throw IllegalArgumentException("Sets cannot contain other collections ") },
            )
        }
    }

    override fun removeInternal(element: RealmAny?): Boolean {
        if (element?.type == RealmAny.Type.OBJECT) {
            if (!element.asRealmObject().isManaged()) return false
        }
        return inputScope {
            val transport = realmAnyToRealmValueWithoutImport(element)
            RealmInterop.realm_set_erase(nativePointer, transport)
        }
    }

    override fun contains(element: RealmAny?): Boolean {
        // Unmanaged objects are never found in a managed dictionary
        if (element?.type == RealmAny.Type.OBJECT) {
            if (!element.asRealmObject().isManaged()) return false
        }
        return inputScope {
            val transport = realmAnyToRealmValueWithoutImport(element)
            RealmInterop.realm_set_find(nativePointer, transport)
        }
    }

    override fun copy(
        realmReference: RealmReference,
        nativePointer: RealmSetPointer
    ): SetOperator =
        RealmAnySetOperator(mediator, realmReference, nativePointer, issueDynamicObject, issueDynamicMutableObject)
}

internal class PrimitiveSetOperator(
    override val mediator: Mediator,
    override val realmReference: RealmReference,
    val realmValueConverter: RealmValueConverter,
    override val nativePointer: RealmSetPointer
) : SetOperator {

    override var modCount: Int = 0

    @Suppress("UNCHECKED_CAST")
    override fun get(index: Int): E {
        return getterScope {
            with(realmValueConverter) {
                val transport = realm_set_get(nativePointer, index.toLong())
                realmValueToPublic(transport)
            } as E
        }
    }

    override fun addInternal(
        element: E,
        updatePolicy: UpdatePolicy,
        cache: UnmanagedToManagedObjectCache
    ): Boolean {
        return inputScope {
            with(realmValueConverter) {
                val transport = publicToRealmValue(element)
                RealmInterop.realm_set_insert(nativePointer, transport)
            }
        }
    }

    override fun removeInternal(element: E): Boolean {
        return inputScope {
            with(realmValueConverter) {
                val transport = publicToRealmValue(element)
                RealmInterop.realm_set_erase(nativePointer, transport)
            }
        }
    }

    override fun contains(element: E): Boolean {
        return inputScope {
            with(realmValueConverter) {
                val transport = publicToRealmValue(element)
                RealmInterop.realm_set_find(nativePointer, transport)
            }
        }
    }

    override fun copy(
        realmReference: RealmReference,
        nativePointer: RealmSetPointer
    ): SetOperator =
        PrimitiveSetOperator(mediator, realmReference, realmValueConverter, nativePointer)
}

internal class RealmObjectSetOperator : SetOperator {

    override val mediator: Mediator
    override val realmReference: RealmReference
    override val nativePointer: RealmSetPointer
    val clazz: KClass
    val classKey: ClassKey

    constructor(
        mediator: Mediator,
        realmReference: RealmReference,
        nativePointer: RealmSetPointer,
        clazz: KClass,
        classKey: ClassKey
    ) {
        this.mediator = mediator
        this.realmReference = realmReference
        this.nativePointer = nativePointer
        this.clazz = clazz
        this.classKey = classKey
    }

    override var modCount: Int = 0

    override fun addInternal(
        element: E,
        updatePolicy: UpdatePolicy,
        cache: UnmanagedToManagedObjectCache
    ): Boolean {
        return inputScope {
            val objRef = realmObjectToRealmReferenceWithImport(
                element as BaseRealmObject?,
                mediator,
                realmReference,
                updatePolicy,
                cache
            )
            val transport = realmObjectTransport(objRef as RealmObjectInterop)
            RealmInterop.realm_set_insert(nativePointer, transport)
        }
    }

    @Suppress("UNCHECKED_CAST")
    override fun get(index: Int): E {
        return getterScope {
            realm_set_get(nativePointer, index.toLong())
                .let { transport ->
                    when (ValueType.RLM_TYPE_NULL) {
                        transport.getType() -> null
                        else -> realmValueToRealmObject(transport, clazz, mediator, realmReference)
                    }
                } as E
        }
    }

    override fun removeInternal(element: E): Boolean {
        // Unmanaged objects are never found in a managed set
        element?.also {
            if (!(it as RealmObjectInternal).isManaged()) return false
        }
        return inputScope {
            val transport = realmObjectToRealmValue(element as BaseRealmObject?)
            RealmInterop.realm_set_erase(nativePointer, transport)
        }
    }

    override fun contains(element: E): Boolean {
        // Unmanaged objects are never found in a managed set
        element?.also {
            if (!(it as RealmObjectInternal).isManaged()) return false
        }
        return inputScope {
            val objRef = realmObjectToRealmReferenceOrError(element as BaseRealmObject?)
            val transport = realmObjectTransport(objRef as RealmObjectInterop)
            RealmInterop.realm_set_find(nativePointer, transport)
        }
    }

    override fun copy(
        realmReference: RealmReference,
        nativePointer: RealmSetPointer
    ): SetOperator {
        return RealmObjectSetOperator(mediator, realmReference, nativePointer, clazz, classKey)
    }
}

internal class RealmSetChangeFlow(scope: ProducerScope>) :
    ChangeFlow, SetChange>(scope) {
    override fun initial(frozenRef: ManagedRealmSet): SetChange = InitialSetImpl(frozenRef)

    override fun update(frozenRef: ManagedRealmSet, change: RealmChangesPointer): SetChange? {
        val builder = SetChangeSetBuilderImpl(change)
        return UpdatedSetImpl(frozenRef, builder.build())
    }

    override fun delete(): SetChange = DeletedSetImpl(UnmanagedRealmSet())
}

internal fun  Array.asRealmSet(): RealmSet =
    UnmanagedRealmSet().apply { addAll(this@asRealmSet) }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy