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

kotlin.script.experimental.util.propertiesCollection.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package kotlin.script.experimental.util

import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.KType
import kotlin.script.experimental.api.KotlinType

open class PropertiesCollection(protected var properties: Map, Any?> = emptyMap()) : Serializable {

    open class Key(
        val name: String,
        @Transient val getDefaultValue: PropertiesCollection.() -> T?
    ) : Serializable {

        constructor(name: String, defaultValue: T? = null) : this(name, { defaultValue })

        override fun equals(other: Any?): Boolean = if (other is Key<*>) name == other.name else false
        override fun hashCode(): Int = name.hashCode()
        override fun toString(): String = "Key($name)"

        companion object {
            @JvmStatic
            private val serialVersionUID = 0L
        }
    }

    class TransientKey(
        name: String,
        getDefaultValue: PropertiesCollection.() -> T?
    ) : Key(name, getDefaultValue)

    class CopiedKey(
        source: Key,
        getSourceProperties: PropertiesCollection.() -> PropertiesCollection?
    ) : Key(
        source.name,
        {
            val sourceProperties = getSourceProperties()
            if (sourceProperties == null) source.getDefaultValue(this)
            else sourceProperties.get(source)
        }
    )

    class PropertyKeyDelegate(private val getDefaultValue: PropertiesCollection.() -> T?, val isTransient: Boolean = false) {
        constructor(defaultValue: T?, isTransient: Boolean = false) : this({ defaultValue }, isTransient)

        operator fun getValue(thisRef: Any?, property: KProperty<*>): Key =
            if (isTransient) TransientKey(property.name, getDefaultValue)
            else Key(property.name, getDefaultValue)
    }

    class PropertyKeyCopyDelegate(
        val source: Key, val getSourceProperties: PropertiesCollection.() -> PropertiesCollection? = { null }
    ) {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): Key = CopiedKey(source, getSourceProperties)
    }

    @Suppress("UNCHECKED_CAST")
    operator fun  get(key: Key): T? =
        (properties[key] ?: if (properties.containsKey(key)) null else key.getDefaultValue(this)) as? T

    @Suppress("UNCHECKED_CAST")
    fun  getNoDefault(key: Key): T? =
        properties[key]?.let { it as T }

    fun  containsKey(key: Key): Boolean =
        properties.containsKey(key)

    fun entries(): Set, Any?>> = properties.entries

    val notTransientData: Map, Any?>
        get() = properties.filter { (key, value) -> key !is TransientKey<*> && value is Serializable }

    fun isEmpty(): Boolean = properties.isEmpty()

    override fun equals(other: Any?): Boolean =
        (other as? PropertiesCollection)?.let { it.properties == properties } == true

    override fun hashCode(): Int = properties.hashCode()

    private fun writeObject(outputStream: ObjectOutputStream) {
        outputStream.writeObject(notTransientData)
    }

    private fun readObject(inputStream: ObjectInputStream) {
        @Suppress("UNCHECKED_CAST")
        properties = inputStream.readObject() as Map, Any?>
    }

    companion object {
        fun  key(defaultValue: T? = null, isTransient: Boolean = false): PropertyKeyDelegate =
            PropertyKeyDelegate(defaultValue, isTransient)

        fun  key(getDefaultValue: PropertiesCollection.() -> T?, isTransient: Boolean = false): PropertyKeyDelegate =
            PropertyKeyDelegate(getDefaultValue, isTransient)

//        fun  keyCopy(source: Key): PropertyKeyCopyDelegate = PropertyKeyCopyDelegate(source)

        fun  keyCopy(
            source: Key, getSourceProperties: PropertiesCollection.() -> PropertiesCollection? = { null }
        ): PropertyKeyCopyDelegate =
            PropertyKeyCopyDelegate(source, getSourceProperties)

        @JvmStatic
        private val serialVersionUID = 1L
    }

    // properties builder base class (DSL for building properties collection)

    open class Builder(baseProperties: Iterable = emptyList()) {

        val data: MutableMap, Any?> = LinkedHashMap, Any?>().apply {
            baseProperties.forEach { putAll(it.properties) }
        }

        // generic for all properties

        operator fun  Key.invoke(v: T) {
            data[this] = v
        }

        fun  Key.put(v: T) {
            data[this] = v
        }

        fun  Key.putIfNotNull(v: T?) {
            if (v != null) {
                data[this] = v
            }
        }

        fun  Key.replaceOnlyDefault(v: T?) {
            if (!data.containsKey(this) || data[this] == this.getDefaultValue(PropertiesCollection(data))) {
                data[this] = v
            }
        }

        fun  Key.update(body: (T?) -> T?) {
            putIfNotNull(body(data[this]?.let {
                @Suppress("UNCHECKED_CAST")
                it as T
            }))
        }

        // generic for lists

        fun  Key>.putIfAny(vals: Iterable?) {
            if (vals?.any() == true) {
                data[this] = if (vals is List) vals else vals.toList()
            }
        }

        operator fun  Key>.invoke(vararg vals: T) {
            append(vals.asIterable())
        }

        // generic for maps:

        @JvmName("putIfAny_map")
        fun  Key>.putIfAny(vals: Iterable>?) {
            if (vals?.any() == true) {
                data[this] = vals.toMap()
            }
        }

        fun  Key>.putIfAny(vals: Map?) {
            if (vals?.isNotEmpty() == true) {
                data[this] = vals
            }
        }

        operator fun  Key>.invoke(vararg vs: Pair) {
            append(vs.asIterable())
        }

        // for strings and list of strings that could be converted from other types

        @JvmName("invoke_string_fqn_from_reflected_class")
        operator fun Key.invoke(kclass: KClass<*>) {
            data[this] = kclass.java.name
        }

        @JvmName("invoke_string_list_fqn_from_reflected_class")
        operator fun Key>.invoke(vararg kclasses: KClass<*>) {
            append(kclasses.map { it.java.name })
        }

        // for KotlinType:

        operator fun Key.invoke(kclass: KClass<*>) {
            data[this] = KotlinType(kclass)
        }

        operator fun Key.invoke(ktype: KType) {
            data[this] = KotlinType(ktype)
        }

        operator fun Key.invoke(fqname: String) {
            data[this] = KotlinType(fqname)
        }

        // for list of KotlinTypes

        operator fun Key>.invoke(vararg classes: KClass<*>) {
            append(classes.map { KotlinType(it) })
        }

        operator fun Key>.invoke(vararg types: KType) {
            append(types.map { KotlinType(it) })
        }

        operator fun Key>.invoke(vararg fqnames: String) {
            append(fqnames.map { KotlinType(it) })
        }

        // for map of generic keys to KotlinTypes:

        @JvmName("invoke_kotlintype_map_from_kclass")
        operator fun  Key>.invoke(vararg classes: Pair>) {
            append(classes.map { (k, v) -> k to KotlinType(v) })
        }

        @JvmName("invoke_kotlintype_map_from_ktype")
        operator fun  Key>.invoke(vararg types: Pair) {
            append(types.map { (k, v) -> k to KotlinType(v) })
        }

        @JvmName("invoke_kotlintype_map_from_fqname")
        operator fun  Key>.invoke(vararg fqnames: Pair) {
            append(fqnames.map { (k, v) -> k to KotlinType(v) })
        }

        // direct manipulation - public - for usage in inline dsl methods and for extending dsl

        operator fun  set(key: Key, value: T) {
            data[key] = value
        }

        fun  reset(key: Key) {
            data.remove(key)
        }

        @Suppress("UNCHECKED_CAST")
        operator fun  get(key: Key): T? = data[key]?.let { it as T }

        operator fun  Key.invoke(): T? = get(this)

        // appenders to list and map properties

        @JvmName("appendToList")
        fun  Key>.append(values: Iterable) {
            val newValues = get(this)?.let { it + values } ?: values.toList()
            data[this] = newValues
        }

        fun  Key>.append(vararg values: V) {
            val newValues = get(this)?.let { it + values } ?: values.toList()
            data[this] = newValues
        }

        fun  Key>.append(values: Map) {
            val newValues = get(this)?.let { it + values } ?: values
            data[this] = newValues
        }

        @JvmName("appendToMap")
        fun  Key>.append(values: Iterable>) {
            val newValues = get(this)?.let { it + values } ?: values.toMap()
            data[this] = newValues
        }

        // include another builder
        operator fun  T.invoke(body: T.() -> Unit) {
            this.body()
            [email protected](this.data)
        }
    }
}

fun  PropertiesCollection.getOrError(key: PropertiesCollection.Key): T =
    get(key) ?: throw IllegalArgumentException("Unknown key $key")





© 2015 - 2024 Weber Informatics LLC | Privacy Policy