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

commonMain.io.realm.kotlin.internal.RealmObjectReference.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.internal.interop.Callback
import io.realm.kotlin.internal.interop.PropertyKey
import io.realm.kotlin.internal.interop.RealmChangesPointer
import io.realm.kotlin.internal.interop.RealmInterop
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.RealmObjectPointer
import io.realm.kotlin.internal.schema.ClassMetadata
import io.realm.kotlin.internal.schema.PropertyMetadata
import io.realm.kotlin.notifications.ObjectChange
import io.realm.kotlin.notifications.internal.DeletedObjectImpl
import io.realm.kotlin.notifications.internal.InitialObjectImpl
import io.realm.kotlin.notifications.internal.UpdatedObjectImpl
import io.realm.kotlin.types.BaseRealmObject
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.flow.Flow
import kotlin.reflect.KClass

/**
 * A RealmObjectReference that links a specific Kotlin RealmObjectInternal instance with an underlying C++
 * Realm Object.
 *
 * It contains a pointer to the object and it is the main entry point to the Realm object features.
 */
// TODO Public due to being a transitive dependency of RealmObjectInternal
public class RealmObjectReference(
    public val className: String,
    public val type: KClass,
    public val owner: RealmReference,
    public val mediator: Mediator,
    public override val objectPointer: RealmObjectPointer,
) :
    RealmStateHolder,
    RealmObjectInterop,
    InternalDeleteable,
    CoreNotifiable, ObjectChange> {

    public val metadata: ClassMetadata = owner.schemaMetadata[className]!!

    // Any methods added to this interface, needs to be fake overridden on the user classes by
    // the compiler plugin, see "RealmObjectInternal overrides" in RealmModelLowering.lower
    public fun propertyInfoOrThrow(
        propertyName: String
    ): PropertyMetadata = this.metadata.getOrThrow(propertyName)

    override fun realmState(): RealmState {
        return owner
    }

    private fun newObjectReference(
        owner: RealmReference,
        pointer: RealmObjectPointer,
        clazz: KClass = type
    ): RealmObjectReference = RealmObjectReference(
        type = clazz,
        owner = owner,
        mediator = mediator,
        className = className,
        objectPointer = pointer
    )

    @Suppress("unchecked_cast")
    override fun freeze(
        frozenRealm: RealmReference
    ): RealmObjectReference? {
        return RealmInterop.realm_object_resolve_in(
            objectPointer,
            frozenRealm.dbPointer
        )?.let { pointer: RealmObjectPointer ->
            newObjectReference(frozenRealm, pointer)
        } as RealmObjectReference?
    }

    override fun thaw(liveRealm: RealmReference): RealmObjectReference? {
        return thaw(liveRealm, type)
    }

    @Suppress("unchecked_cast")
    public fun thaw(
        liveRealm: RealmReference,
        clazz: KClass
    ): RealmObjectReference? {
        val dbPointer = liveRealm.dbPointer
        return RealmInterop.realm_object_resolve_in(objectPointer, dbPointer)
            ?.let { pointer: RealmObjectPointer ->
                newObjectReference(liveRealm, pointer, clazz)
            } as RealmObjectReference?
    }

    override fun registerForNotification(
        keyPaths: RealmKeyPathArrayPointer?,
        callback: Callback
    ): RealmNotificationTokenPointer {
        // We should never get here unless it is a managed object as unmanaged doesn't support observing
        return RealmInterop.realm_object_add_notification_callback(
            this.objectPointer,
            keyPaths,
            callback
        )
    }

    override fun changeFlow(scope: ProducerScope>): ChangeFlow, ObjectChange> =
        ObjectChangeFlow(scope)

    internal fun getChangedFieldNames(
        change: RealmChangesPointer
    ): Array {
        return RealmInterop.realm_object_changes_get_modified_properties(
            change
        ).map { propertyKey: PropertyKey ->
            metadata[propertyKey]?.name ?: ""
        }.toTypedArray()
    }

    override fun asFlow(keyPaths: List?): Flow> {
        val keyPathInfo = keyPaths?.let {
            Pair(metadata.classKey, it)
        }
        return this.owner.owner.registerObserver(this, keyPathInfo)
    }

    override fun delete() {
        if (isFrozen()) {
            throw IllegalArgumentException(
                "Frozen objects cannot be deleted. They must be converted to live objects first " +
                    "by using `MutableRealm/DynamicMutableRealm.findLatest(frozenObject)`."
            )
        }
        if (!isValid()) {
            throw IllegalArgumentException(INVALID_OBJECT_MSG)
        }
        objectPointer.let { RealmInterop.realm_object_delete(it) }
    }

    override fun isValid(): Boolean =
        objectPointer.let { ptr ->
            !ptr.isReleased() && RealmInterop.realm_object_is_valid(ptr)
        }

    internal fun checkValid() {
        if (!this.isValid()) {
            throw IllegalStateException(INVALID_OBJECT_MSG)
        }
    }

    public companion object {
        public const val INVALID_OBJECT_MSG: String = "Cannot perform this operation on an invalid/deleted object"
    }
}

internal fun  RealmObjectReference.checkNotificationsAvailable() {
    if (RealmInterop.realm_is_closed(owner.dbPointer)) {
        throw IllegalStateException("Changes cannot be observed when the Realm has been closed.")
    }
    if (!isValid()) {
        throw IllegalStateException("Changes cannot be observed on objects that have been deleted from the Realm.")
    }
}

internal class ObjectChangeFlow(scope: ProducerScope>) :
    ChangeFlow, ObjectChange>(scope) {

    override fun initial(frozenRef: RealmObjectReference): ObjectChange {
        val obj: E = frozenRef.toRealmObject()
        return InitialObjectImpl(obj)
    }

    override fun update(
        frozenRef: RealmObjectReference,
        change: RealmChangesPointer
    ): ObjectChange {
        val obj: E = frozenRef.toRealmObject()
        val changedFieldNames = frozenRef.getChangedFieldNames(change)
        return UpdatedObjectImpl(obj, changedFieldNames)
    }

    override fun delete(): ObjectChange = DeletedObjectImpl()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy