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

godot.core.bridge.Dictionary.kt Maven / Gradle / Ivy

There is a newer version: 0.10.0-4.3.0
Show newest version
@file:Suppress("unused", "PackageDirectoryMismatch")

package godot.core

import godot.annotation.CoreTypeHelper
import godot.core.memory.MemoryManager
import godot.core.memory.TransferContext
import godot.util.MapIterator
import godot.util.VoidPtr
import godot.util.isNullable
import kotlincompile.definitions.GodotJvmBuildConfig
import kotlin.jvm.internal.Reflection
import kotlin.reflect.KClass

class Dictionary : NativeCoreType, MutableMap {

    internal var keyVariantConverter: VariantConverter = VariantParser.NIL
    internal var valueVariantConverter: VariantConverter = VariantParser.NIL

    @PublishedApi
    internal constructor(handle: VoidPtr) {
        keyVariantConverter = VariantCaster.ANY
        valueVariantConverter = VariantCaster.ANY
        _handle = handle
        MemoryManager.registerNativeCoreType(this, VariantParser.DICTIONARY)
    }

    constructor(keyClass: Class<*>, valueClass: Class<*>) : this(Reflection.getOrCreateKotlinClass(keyClass), Reflection.getOrCreateKotlinClass(valueClass))

    @PublishedApi
    internal constructor(keyClass: KClass<*>, valueClass: KClass<*>) {
        val keyVariantConverter = variantMapper[keyClass]
        val valueVariantConverter  = variantMapper[valueClass]

        if (GodotJvmBuildConfig.DEBUG) {
            checkNotNull(keyVariantConverter) {
                "Can't create a Dictionary with generic key ${keyClass}."
            }

            checkNotNull(valueVariantConverter) {
                "Can't create a Dictionary with generic key ${valueClass}."
            }
        }
        
        this.keyVariantConverter = keyVariantConverter!!
        this.valueVariantConverter = valueVariantConverter!!
        _handle = Bridge.engine_call_constructor()
        MemoryManager.registerNativeCoreType(this, VariantParser.DICTIONARY)
    }

    //########################PUBLIC###############################
    //PROPERTIES
    override val size: Int
        get() {
            Bridge.engine_call_size(_handle)
            return TransferContext.readReturnValue(VariantCaster.INT) as Int
        }

    override val keys: MutableSet
        get() = object : AbstractMutableSet() {
            override fun add(element: K): Boolean = throw UnsupportedOperationException("Add is not supported on keys")
            override fun clear() {
                [email protected]()
            }

            override operator fun contains(element: K): Boolean = containsKey(element)

            override operator fun iterator(): MutableIterator {
                val entryIterator = keys().iterator()
                return object : MutableIterator {
                    override fun hasNext(): Boolean = entryIterator.hasNext()
                    override fun next(): K = entryIterator.next()
                    override fun remove() = throw UnsupportedOperationException("Remove is not supported on keys")
                }
            }

            override fun remove(element: K): Boolean {
                if (containsKey(element)) {
                    [email protected](element)
                    return true
                }
                return false
            }

            override val size: Int get() = [email protected]
        }

    override val values: MutableCollection
        get() = object : AbstractMutableCollection() {
            override fun add(element: V): Boolean =
                throw UnsupportedOperationException("Add is not supported on values")

            override fun clear() = [email protected]()

            override operator fun contains(element: V): Boolean = containsValue(element)

            override operator fun iterator(): MutableIterator {
                val entryIterator = values().iterator()
                return object : MutableIterator {
                    override fun hasNext(): Boolean = entryIterator.hasNext()
                    override fun next(): V = entryIterator.next()
                    override fun remove() = throw UnsupportedOperationException("Remove is not supported on values")
                }
            }

            override val size: Int get() = [email protected]
        }

    override val entries: MutableSet>
        get() = object : AbstractMutableSet>() {

            override fun add(element: MutableMap.MutableEntry): Boolean {
                val ret = has(element.key)
                this@Dictionary[element.key] = element.value
                return ret
            }

            override fun clear() {
                [email protected]()
            }

            override operator fun contains(element: MutableMap.MutableEntry): Boolean {
                val value = get(element.key, null)
                if (value == element.value) {
                    return true
                }
                return false
            }

            override operator fun iterator(): MapIterator {
                return MapIterator(
                    [email protected](),
                    this@Dictionary::get,
                    this@Dictionary::set,
                    this@Dictionary::erase
                )
            }

            override fun remove(element: MutableMap.MutableEntry): Boolean {
                val value = get(element.key, null)
                if (value == element.value) {
                    [email protected](element.key)
                    return true
                }
                return false
            }

            override val size: Int get() = [email protected]
        }

//CONSTRUCTOR
    /**
     * Create a shallow copy of the Dictionary
     */
    constructor(other: Dictionary) {
        keyVariantConverter = other.keyVariantConverter
        valueVariantConverter = other.valueVariantConverter
        _handle = other._handle
        MemoryManager.registerNativeCoreType(this, VariantParser.DICTIONARY)
    }


//API
    /**
     * Clear the dictionary, removing all key/value pairs.
     */
    override fun clear() {
        Bridge.engine_call_clear(_handle)
    }

    override fun containsKey(key: K) = contains(key)

    override fun containsValue(value: V): Boolean {
        values.forEach {
            if (it == value)
                return true
        }
        return false
    }

    /**
     * Creates a copy of the dictionary, and returns it.
     * The deep parameter causes inner dictionaries and arrays to be copied recursively, but does not apply to objects.
     */

    fun duplicate(deep: Boolean): Dictionary {
        TransferContext.writeArguments(VariantParser.BOOL to deep)
        Bridge.engine_call_duplicate(_handle)
        @Suppress("UNCHECKED_CAST")
        return (TransferContext.readReturnValue(VariantParser.DICTIONARY) as Dictionary).also {
            it.keyVariantConverter = keyVariantConverter
            it.valueVariantConverter = valueVariantConverter
        }
    }

    /**
     * Erase a dictionary key/value pair by key. Doesn't return a Boolean like the GDScript version because the GDNative function doesn't return anything
     */
    fun erase(key: K) {
        TransferContext.writeArguments(keyVariantConverter to key)
        Bridge.engine_call_erase(_handle)
    }

    fun findKey(value: V): K {
        TransferContext.writeArguments(valueVariantConverter to value)
        Bridge.engine_call_find_key(_handle)
        @Suppress("UNCHECKED_CAST")
        return TransferContext.readReturnValue(keyVariantConverter) as K
    }

    /**
     * Returns the current value for the specified key in the Dictionary.
     * If the key does not exist, the method returns the value of the optional default argument, or null if it is omitted.
     */
    fun get(key: K, default: V?): V? {
        TransferContext.writeArguments(keyVariantConverter to key, valueVariantConverter to default)
        Bridge.engine_call_get(_handle)
        @Suppress("UNCHECKED_CAST")
        return TransferContext.readReturnValue(valueVariantConverter) as V?
    }

    /**
     * Returns true if the dictionary has a given key.
     * Note: This is equivalent to using the in operator as follows:
     */
    fun has(key: K): Boolean {
        TransferContext.writeArguments(keyVariantConverter to key)
        Bridge.engine_call_has(_handle)
        return TransferContext.readReturnValue(VariantParser.BOOL) as Boolean
    }


    /**
     * Returns true if the dictionary has all of the keys in the given array.
     */
    fun hasAll(keys: VariantArray): Boolean {
        TransferContext.writeArguments(VariantParser.ARRAY to keys)
        Bridge.engine_call_hasAll(_handle)
        return TransferContext.readReturnValue(VariantParser.BOOL) as Boolean
    }


    /**
     * Returns a hashed integer value representing the dictionary contents. This can be used to compare dictionaries by value
     */
    fun hash(): Int {
        Bridge.engine_call_hash(_handle)
        return TransferContext.readReturnValue(VariantCaster.INT) as Int
    }

    /**
     * Returns true if the dictionary is read-only. See [makeReadOnly]
     */
    fun isReadOnly(): Boolean {
        Bridge.engine_call_is_read_only(_handle)
        return TransferContext.readReturnValue(VariantParser.BOOL) as Boolean
    }

    /**
     * Returns true if the dictionary is empty.
     */
    override fun isEmpty(): Boolean {
        Bridge.engine_call_is_empty(_handle)
        return TransferContext.readReturnValue(VariantParser.BOOL) as Boolean
    }

    /**
     * Returns the list of keys in the Dictionary.
     */
    fun keys(): VariantArray {
        Bridge.engine_call_keys(_handle)
        @Suppress("UNCHECKED_CAST")
        return (TransferContext.readReturnValue(VariantParser.ARRAY) as VariantArray).also {
            it.variantConverter = keyVariantConverter
        }
    }

    fun makeReadOnly() {
        Bridge.engine_call_make_read_only(_handle)
    }

    fun merge(dictionary: Dictionary, overwrite: Boolean = false) {
        TransferContext.writeArguments(VariantParser.DICTIONARY to dictionary, VariantParser.BOOL to overwrite)
        Bridge.engine_call_merge(_handle)
    }

    override fun put(key: K, value: V): V? {
        val ret = get(key, null)
        set(key, value)
        return ret
    }

    override fun putAll(from: Map) {
        from.forEach {
            set(it.key, it.value)
        }
    }

    override fun remove(key: K): V? {
        val ret = get(key, null)
        erase(key)
        return ret
    }

    /**
     * Returns the list of values in the Dictionary.
     */
    fun values(): VariantArray {
        Bridge.engine_call_values(_handle)
        @Suppress("UNCHECKED_CAST")
        return (TransferContext.readReturnValue(VariantParser.ARRAY) as VariantArray).also {
            it.variantConverter = valueVariantConverter
        }
    }


    //UTILITIES
    override operator fun get(key: K): V {
        TransferContext.writeArguments(keyVariantConverter to key)
        Bridge.engine_call_operator_get(_handle)
        @Suppress("UNCHECKED_CAST")
        return TransferContext.readReturnValue(valueVariantConverter) as V
    }

    @CoreTypeHelper
    inline fun  get(key: K, block: V.() -> R): R {
        val localCopy = this[key]
        val ret = localCopy.block()
        this[key] = localCopy
        return ret
    }

    operator fun set(key: K, value: V) {
        TransferContext.writeArguments(keyVariantConverter to key, valueVariantConverter to value)
        Bridge.engine_call_operator_set(_handle)
    }

    operator fun contains(key: K): Boolean = has(key)

    override fun equals(other: Any?): Boolean {
        if (other == null || other !is Dictionary<*, *>) {
            return false
        }
        TransferContext.writeArguments(VariantParser.DICTIONARY to this, VariantParser.DICTIONARY to other)
        Bridge.engine_call_equals(_handle)
        return TransferContext.readReturnValue(VariantParser.BOOL) as Boolean
    }

    override fun hashCode(): Int {
        return _handle.hashCode()
    }

    override fun toString(): String {
        return "Dictionary($size)"
    }

    @Suppress("FunctionName")
    private object Bridge {
        external fun engine_call_constructor(): VoidPtr

        external fun engine_call_clear(_handle: VoidPtr)
        external fun engine_call_duplicate(_handle: VoidPtr)
        external fun engine_call_erase(_handle: VoidPtr)
        external fun engine_call_find_key(_handle: VoidPtr)
        external fun engine_call_get(_handle: VoidPtr)
        external fun engine_call_has(_handle: VoidPtr)
        external fun engine_call_hasAll(_handle: VoidPtr)
        external fun engine_call_hash(_handle: VoidPtr)
        external fun engine_call_is_empty(_handle: VoidPtr)
        external fun engine_call_is_read_only(_handle: VoidPtr)
        external fun engine_call_keys(_handle: VoidPtr)
        external fun engine_call_make_read_only(_handle: VoidPtr)
        external fun engine_call_merge(_handle: VoidPtr)
        external fun engine_call_size(_handle: VoidPtr)
        external fun engine_call_values(_handle: VoidPtr)
        external fun engine_call_operator_get(_handle: VoidPtr)
        external fun engine_call_operator_set(_handle: VoidPtr)
        external fun engine_call_equals(_handle: VoidPtr)
    }


    companion object {
        inline operator fun  invoke(): Dictionary {
            
            // The nullable check can't be inside the regular constructor because of Java
            if (GodotJvmBuildConfig.DEBUG) {
                if(isNullable() && K::class in notNullableVariantSet){
                    error("Can't create a Dictionary with generic key ${K::class} as nullable.")
                }

                if(isNullable() && V::class in notNullableVariantSet){
                    error("Can't create a Dictionary with generic value ${V::class} as nullable.")
                }
            }
            return Dictionary(K::class, V::class)
        }
    }
}

inline fun  dictionaryOf(vararg args: Pair) = Dictionary().also {
    it.putAll(args)
}

/**
 * Convert a Map into a Dictionary
 */
inline fun  Map.toDictionary() = Dictionary().also {
    it.putAll(this)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy