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

commonMain.io.realm.RealmList.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:0.4.1' instead.

There is a newer version: 0.5.0
Show newest version
/*
 * Copyright 2021 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

import io.realm.internal.Mediator
import io.realm.internal.RealmReference
import io.realm.interop.Link
import io.realm.interop.NativePointer
import io.realm.interop.RealmInterop
import kotlin.reflect.KClass

/**
 * RealmList is used to model one-to-many relationships in a [RealmObject].
 *
 * A RealmList has two modes: managed and unmanaged. In `managed` mode all objects are persisted
 * inside a Realm whereas in `unmanaged` mode it works as a normal [MutableList].
 *
 * Only Realm can create managed RealmLists. Managed RealmLists will automatically update their
 * content whenever the underlying Realm is updated. Said content can only be accessed using the
 * getter of a [RealmObject].
 *
 * Unmanaged RealmLists can be created by the user and can contain both managed and unmanaged
 * [RealmObject]s. This is useful when dealing with JSON deserializers like GSON or other frameworks
 * that inject values into a class. Unmanaged elements in a list can be added to a Realm using the
 * [MutableRealm.copyToRealm] method.
 */
class RealmList private constructor(
    delegate: MutableList
) : MutableList by delegate {

    /**
     * Constructs a RealmList in unmanaged mode.
     */
    constructor() : this(UnmanagedListDelegate())

    /**
     * Constructs a RealmList in managed mode. For internal use only.
     */
    constructor(
        listPtr: NativePointer,
        metadata: OperatorMetadata
    ) : this(ManagedListDelegate(listPtr, metadata))

    /**
     * Metadata needed to correctly instantiate a list operator. For internal use only.
     */
    data class OperatorMetadata(
        val clazz: KClass<*>,
        val isRealmObject: Boolean,
        val mediator: Mediator,
        val realm: RealmReference
    )

    /**
     * Facilitates conversion between Realm Core types and Kotlin types and other Realm-related
     * checks.
     */
    internal class Operator(
        private val metadata: OperatorMetadata
    ) {

        /**
         * Converts the underlying Core type to the correct type expressed in the RealmList.
         */
        @Suppress("UNCHECKED_CAST")
        fun convert(value: Any?): E {
            if (value == null) {
                return null as E
            }
            return with(metadata) {
                when (clazz) {
                    Byte::class -> (value as Long).toByte()
                    Char::class -> (value as Long).toChar()
                    Short::class -> (value as Long).toShort()
                    Int::class -> (value as Long).toInt()
                    Long::class,
                    Boolean::class,
                    Float::class,
                    Double::class,
                    String::class -> value
                    else -> when {
                        isRealmObject -> (value as Link).toRealmObject(
                            clazz as KClass,
                            mediator,
                            realm
                        )
                        else -> throw IllegalArgumentException("Unsupported type '$clazz'.")
                    }
                } as E
            }
        }

        /**
         * Checks if the Realm associated to this RealmList is still accessible, throwing an
         * [IllegalStateException] if not.
         */
        fun checkRealmClosed() {
            metadata.realm.checkClosed()
        }
    }
}

/**
 * Represents an unmanaged [RealmList] backed by a [MutableList] via class delegation.
 */
private class UnmanagedListDelegate(
    list: MutableList = mutableListOf()
) : MutableList by list

/**
 * Represents a managed [RealmList]. Its data will be persisted in Realm.
 *
 * [AbstractMutableList] provides enough default implementations on which we can rely. It can also
 * be used as a delegate since it implements [MutableList].
 */
private class ManagedListDelegate(
    private val listPtr: NativePointer,
    metadata: RealmList.OperatorMetadata
) : AbstractMutableList() {

    private val operator = RealmList.Operator(metadata)

    override val size: Int
        get() {
            operator.checkRealmClosed()
            return RealmInterop.realm_list_size(listPtr).toInt()
        }

    override fun get(index: Int): E {
        operator.checkRealmClosed()
        return operator.convert(RealmInterop.realm_list_get(listPtr, index.toLong()))
    }

    override fun add(index: Int, element: E) {
        operator.checkRealmClosed()
        RealmInterop.realm_list_add(listPtr, index.toLong(), element)
    }

    // FIXME bug in AbstractMutableList.addAll native implementation:
    //  https://youtrack.jetbrains.com/issue/KT-47211
    //  Remove this method once the native implementation has a check for valid index
    override fun addAll(index: Int, elements: Collection): Boolean {
        operator.checkRealmClosed()
        rangeCheckForAdd(index)
        return super.addAll(index, elements)
    }

    override fun clear() {
        operator.checkRealmClosed()
        RealmInterop.realm_list_clear(listPtr)
    }

    override fun removeAt(index: Int): E = get(index).also {
        operator.checkRealmClosed()
        RealmInterop.realm_list_erase(listPtr, index.toLong())
    }

    override fun set(index: Int, element: E): E {
        operator.checkRealmClosed()
        return operator.convert(RealmInterop.realm_list_set(listPtr, index.toLong(), element))
    }

    private fun rangeCheckForAdd(index: Int) {
        if (index < 0 || index > size) {
            throw IndexOutOfBoundsException("Index: '$index', Size: '$size'")
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy