Refactor Meta delegates

This commit is contained in:
Alexander Nozik 2020-01-10 11:14:18 +03:00
parent b83821af51
commit fe6760eee6
11 changed files with 241 additions and 487 deletions

View File

@ -61,7 +61,7 @@ class NodeDescriptor : ItemDescriptor() {
* *
* @return * @return
*/ */
var default: Config? by nullableConfig() var default: Config? by config()
/** /**
* The map of children node descriptors * The map of children node descriptors

View File

@ -1,5 +1,9 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.descriptors.Described
import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.descriptors.defaultItem
import hep.dataforge.descriptors.get
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
@ -9,7 +13,7 @@ import hep.dataforge.names.toName
* It is not possible to know if some property is declared by provider just by looking on [Configurable], * It is not possible to know if some property is declared by provider just by looking on [Configurable],
* this information should be provided externally. * this information should be provided externally.
*/ */
interface Configurable { interface Configurable : Described {
/** /**
* Backing config * Backing config
*/ */
@ -19,12 +23,15 @@ interface Configurable {
* Default meta item provider * Default meta item provider
*/ */
fun getDefaultItem(name: Name): MetaItem<*>? = null fun getDefaultItem(name: Name): MetaItem<*>? = null
override val descriptor: NodeDescriptor? get() = null
} }
/** /**
* Get a property with default * Get a property with default
*/ */
fun Configurable.getProperty(name: Name): MetaItem<*>? = config[name] ?: getDefaultItem(name) fun Configurable.getProperty(name: Name): MetaItem<*>? =
config[name] ?: getDefaultItem(name) ?: descriptor?.get(name)?.defaultItem()
fun Configurable.getProperty(key: String) = getProperty(key.toName()) fun Configurable.getProperty(key: String) = getProperty(key.toName())

View File

@ -28,19 +28,6 @@ open class ConfigurableDelegate(
val name = key ?: property.name.asName() val name = key ?: property.name.asName()
owner.setProperty(name, value) owner.setProperty(name, value)
} }
fun <T> transform(
writer: (T) -> MetaItem<*>? = { MetaItem.of(it) },
reader: (MetaItem<*>?) -> T
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return reader(this@ConfigurableDelegate.getValue(thisRef, property))
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this@ConfigurableDelegate.setValue(thisRef, property, writer(value))
}
}
} }
class LazyConfigurableDelegate( class LazyConfigurableDelegate(
@ -55,7 +42,7 @@ class LazyConfigurableDelegate(
* A property delegate that uses custom key * A property delegate that uses custom key
*/ */
fun Configurable.item(default: Any?, key: Name? = null): ConfigurableDelegate = fun Configurable.item(default: Any?, key: Name? = null): ConfigurableDelegate =
ConfigurableDelegate(this, key, MetaItem.of(default)) ConfigurableDelegate(this, key, default?.let { MetaItem.of(it) })
/** /**
* Generation of item delegate with lazy default. * Generation of item delegate with lazy default.
@ -70,7 +57,7 @@ fun <T> Configurable.item(
writer: (T) -> MetaItem<*>? = { MetaItem.of(it) }, writer: (T) -> MetaItem<*>? = { MetaItem.of(it) },
reader: (MetaItem<*>?) -> T reader: (MetaItem<*>?) -> T
): ReadWriteProperty<Any?, T> = ): ReadWriteProperty<Any?, T> =
ConfigurableDelegate(this, key, default?.let { MetaItem.of(it) }).transform(reader = reader, writer = writer) ConfigurableDelegate(this, key, default?.let { MetaItem.of(it) }).map(reader = reader, writer = writer)
fun Configurable.value(default: Any? = null, key: Name? = null): ReadWriteProperty<Any?, Value?> = fun Configurable.value(default: Any? = null, key: Name? = null): ReadWriteProperty<Any?, Value?> =
item(default, key).transform { it.value } item(default, key).transform { it.value }
@ -81,7 +68,7 @@ fun <T> Configurable.value(
writer: (T) -> Value? = { Value.of(it) }, writer: (T) -> Value? = { Value.of(it) },
reader: (Value?) -> T reader: (Value?) -> T
): ReadWriteProperty<Any?, T> = ): ReadWriteProperty<Any?, T> =
ConfigurableDelegate(this, key, default?.let { MetaItem.of(it) }).transform( ConfigurableDelegate(this, key, default?.let { MetaItem.of(it) }).map(
reader = { reader(it.value) }, reader = { reader(it.value) },
writer = { writer(it)?.let { MetaItem.ValueItem(it) } } writer = { writer(it)?.let { MetaItem.ValueItem(it) } }
) )
@ -194,31 +181,9 @@ fun Configurable.doubleArray(vararg doubles: Double, key: Name? = null): ReadWri
/* Node delegates */ /* Node delegates */
fun Configurable.nullableConfig(key: Name? = null): ReadWriteProperty<Any?, Config?> = fun Configurable.config(key: Name? = null): ReadWriteProperty<Any?, Config?> =
object : ReadWriteProperty<Any?, Config?> { config.node(key)
override fun getValue(thisRef: Any?, property: KProperty<*>): Config? {
val name = key ?: property.name.asName()
return config[name].node
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Config?) {
val name = key ?: property.name.asName()
config[name] = value
}
}
fun Configurable.config(key: Name? = null, default: Config.() -> Unit = {}): ReadWriteProperty<Any?, Config> =
object : ReadWriteProperty<Any?, Config> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Config {
val name = key ?: property.name.asName()
return config[name].node ?: Config().apply(default).also { config[name] = it }
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Config) {
val name = key ?: property.name.asName()
config[name] = value
}
}
fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: Name? = null): ReadWriteProperty<Any?, T?> = fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: Name? = null): ReadWriteProperty<Any?, T?> =
object : ReadWriteProperty<Any?, T?> { object : ReadWriteProperty<Any?, T?> {

View File

@ -5,6 +5,7 @@ import hep.dataforge.names.NameToken
/** /**
* A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [Scheme]. * A meta laminate consisting of multiple immutable meta layers. For mutable front layer, use [Scheme].
* If [layers] list contains a [Laminate] it is flat-mapped.
*/ */
class Laminate(layers: List<Meta>) : MetaBase() { class Laminate(layers: List<Meta>) : MetaBase() {

View File

@ -0,0 +1,98 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.Value
import kotlin.jvm.JvmName
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/* Meta delegates */
open class MetaDelegate(
open val owner: Meta,
val key: Name? = null,
open val default: MetaItem<*>? = null
) : ReadOnlyProperty<Any?, MetaItem<*>?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
return owner[key ?: property.name.asName()] ?: default
}
}
class LazyMetaDelegate(
owner: Meta,
key: Name? = null,
defaultProvider: () -> MetaItem<*>? = { null }
) : MetaDelegate(owner, key) {
override val default by lazy(defaultProvider)
}
class DelegateWrapper<T, R>(
val delegate: ReadOnlyProperty<Any?, T>,
val reader: (T) -> R
) : ReadOnlyProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R {
return reader(delegate.getValue(thisRef, property))
}
}
fun <T, R> ReadOnlyProperty<Any?, T>.map(reader: (T) -> R): DelegateWrapper<T, R> =
DelegateWrapper(this, reader)
fun Meta.item(default: Any? = null, key: Name? = null): MetaDelegate =
MetaDelegate(this, key, default?.let { MetaItem.of(it) })
fun Meta.lazyItem(key: Name? = null, defaultProvider: () -> Any?): LazyMetaDelegate =
LazyMetaDelegate(this, key) { defaultProvider()?.let { MetaItem.of(it) } }
//TODO add caching for sealed nodes
//Read-only delegates for Metas
/**
* A property delegate that uses custom key
*/
fun Meta.value(default: Value? = null, key: Name? = null) =
item(default, key).map { it.value }
fun Meta.string(default: String? = null, key: Name? = null) =
item(default, key).map { it.string }
fun Meta.boolean(default: Boolean? = null, key: Name? = null) =
item(default, key).map { it.boolean }
fun Meta.number(default: Number? = null, key: Name? = null) =
item(default, key).map { it.number }
fun Meta.node(key: Name? = null) =
item(key).map { it.node }
@JvmName("safeString")
fun Meta.string(default: String, key: Name? = null) =
item(default, key).map { it.string!! }
@JvmName("safeBoolean")
fun Meta.boolean(default: Boolean, key: Name? = null) =
item(default, key).map { it.boolean!! }
@JvmName("safeNumber")
fun Meta.number(default: Number, key: Name? = null) =
item(default, key).map { it.number!! }
@JvmName("lazyString")
fun Meta.string(key: Name? = null, default: () -> String) =
lazyItem(key, default).map { it.string!! }
@JvmName("lazyBoolean")
fun Meta.boolean(key: Name? = null, default: () -> Boolean) =
lazyItem(key, default).map { it.boolean!! }
@JvmName("lazyNumber")
fun Meta.number(key: Name? = null, default: () -> Number) =
lazyItem(key, default).map { it.number!! }
inline fun <reified E : Enum<E>> Meta.enum(default: E, key: Name? = null) =
item(default, key).map { it.enum<E>()!! }

View File

@ -0,0 +1,108 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.Value
import kotlin.jvm.JvmName
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/* Read-write delegates */
open class MutableMetaDelegate<M : MutableMeta<M>>(
override val owner: M,
key: Name? = null,
default: MetaItem<*>? = null
) : MetaDelegate(owner, key, default), ReadWriteProperty<Any?, MetaItem<*>?> {
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) {
val name = key ?: property.name.asName()
owner.setItem(name, value)
}
}
class LazyMutableMetaDelegate<M : MutableMeta<M>>(
owner: M,
key: Name? = null,
defaultProvider: () -> MetaItem<*>? = { null }
) : MutableMetaDelegate<M>(owner, key) {
override val default by lazy(defaultProvider)
}
class ReadWriteDelegateWrapper<T, R>(
val delegate: ReadWriteProperty<Any?, T>,
val reader: (T) -> R,
val writer: (R) -> T
) : ReadWriteProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R {
return reader(delegate.getValue(thisRef, property))
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
delegate.setValue(thisRef, property, writer(value))
}
}
fun <T, R> ReadWriteProperty<Any?, T>.map(reader: (T) -> R, writer: (R) -> T): ReadWriteDelegateWrapper<T, R> =
ReadWriteDelegateWrapper(this, reader, writer)
fun <R> ReadWriteProperty<Any?, MetaItem<*>?>.transform(reader: (MetaItem<*>?) -> R): ReadWriteProperty<Any?, R> =
map(reader = reader, writer = { MetaItem.of(it) })
fun <R> ReadWriteProperty<Any?, Value?>.transform(reader: (Value?) -> R) =
map(reader = reader, writer = { Value.of(it) })
fun <M : MutableMeta<M>> M.item(default: Any? = null, key: Name? = null): MutableMetaDelegate<M> =
MutableMetaDelegate(this, key, default?.let { MetaItem.of(it) })
fun <M : MutableMeta<M>> M.lazyItem(key: Name? = null, defaultProvider: () -> Any?): LazyMutableMetaDelegate<M> =
LazyMutableMetaDelegate(this, key) { defaultProvider()?.let { MetaItem.of(it) } }
//Read-write delegates
/**
* A property delegate that uses custom key
*/
fun <M : MutableMeta<M>> M.value(default: Value? = null, key: Name? = null): ReadWriteProperty<Any?, Value?> =
item(default, key).transform { it.value }
fun <M : MutableMeta<M>> M.string(default: String? = null, key: Name? = null): ReadWriteProperty<Any?, String?> =
item(default, key).transform { it.string }
fun <M : MutableMeta<M>> M.boolean(default: Boolean? = null, key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
item(default, key).transform { it.boolean }
fun <M : MutableMeta<M>> M.number(default: Number? = null, key: Name? = null): ReadWriteProperty<Any?, Number?> =
item(default, key).transform { it.number }
inline fun <reified M : MutableMeta<M>> M.node(key: Name? = null) =
item(this, key).transform { it.node as? M }
@JvmName("safeString")
fun <M : MutableMeta<M>> M.string(default: String, key: Name? = null) =
item(default, key).transform { it.string!! }
@JvmName("safeBoolean")
fun <M : MutableMeta<M>> M.boolean(default: Boolean, key: Name? = null) =
item(default, key).transform { it.boolean!! }
@JvmName("safeNumber")
fun <M : MutableMeta<M>> M.number(default: Number, key: Name? = null) =
item(default, key).transform { it.number!! }
@JvmName("lazyString")
fun <M : MutableMeta<M>> M.string(key: Name? = null, default: () -> String) =
lazyItem(key, default).transform { it.string!! }
@JvmName("safeBoolean")
fun <M : MutableMeta<M>> M.boolean(key: Name? = null, default: () -> Boolean) =
lazyItem(key, default).transform { it.boolean!! }
@JvmName("safeNumber")
fun <M : MutableMeta<M>> M.number(key: Name? = null, default: () -> Number) =
lazyItem(key, default).transform { it.number!! }
inline fun <M : MutableMeta<M>, reified E : Enum<E>> M.enum(default: E, key: Name? = null) =
item(default, key).transform { it.enum<E>()!! }

View File

@ -5,6 +5,9 @@ import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.plus import hep.dataforge.names.plus
/**
* A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification].
*/
open class Scheme() : Configurable, Described { open class Scheme() : Configurable, Described {
constructor(config: Config, defaultProvider: (Name) -> MetaItem<*>?) : this() { constructor(config: Config, defaultProvider: (Name) -> MetaItem<*>?) : this() {
this.config = config this.config = config
@ -14,13 +17,14 @@ open class Scheme() : Configurable, Described {
//constructor(config: Config, default: Meta) : this(config, { default[it] }) //constructor(config: Config, default: Meta) : this(config, { default[it] })
constructor(config: Config) : this(config, { null }) constructor(config: Config) : this(config, { null })
final override lateinit var config: Config final override var config: Config = Config()
internal set internal set
lateinit var defaultProvider: (Name) -> MetaItem<*>? lateinit var defaultProvider: (Name) -> MetaItem<*>?
internal set internal set
override val descriptor: NodeDescriptor? = null final override var descriptor: NodeDescriptor? = null
internal set
override fun getDefaultItem(name: Name): MetaItem<*>? { override fun getDefaultItem(name: Name): MetaItem<*>? {
return defaultProvider(name) ?: descriptor?.get(name)?.defaultItem() return defaultProvider(name) ?: descriptor?.get(name)?.defaultItem()
@ -60,15 +64,21 @@ open class SchemeSpec<T : Scheme>(val builder: () -> T) : Specification<T> {
} }
} }
/**
* A scheme that uses [Meta] as a default layer
*/
open class MetaScheme( open class MetaScheme(
val meta: Meta, val meta: Meta,
override val descriptor: NodeDescriptor? = null, descriptor: NodeDescriptor? = null,
config: Config = Config() config: Config = Config()
) : Scheme(config, meta::get) { ) : Scheme(config, meta::get) {
override val defaultLayer: Meta get() = meta init {
this.descriptor = descriptor
}
override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node)
} }
fun Meta.toScheme() = MetaScheme(this) fun Meta.asScheme() = MetaScheme(this)
fun <T : Configurable> Meta.toScheme(spec: Specification<T>, block: T.() -> Unit) = spec.wrap(this).apply(block) fun <T : Configurable> Meta.toScheme(spec: Specification<T>, block: T.() -> Unit) = spec.wrap(this).apply(block)

View File

@ -3,14 +3,6 @@ package hep.dataforge.meta
import hep.dataforge.descriptors.NodeDescriptor import hep.dataforge.descriptors.NodeDescriptor
import hep.dataforge.values.Value import hep.dataforge.values.Value
///**
// * Find all elements with given body
// */
//private fun Meta.byBody(body: String): Map<String, MetaItem<*>> =
// items.filter { it.key.body == body }.mapKeys { it.key.index }
//
//private fun Meta.distinctNames() = items.keys.map { it.body }.distinct()
/** /**
* Convert meta to map of maps * Convert meta to map of maps
*/ */

View File

@ -1,414 +0,0 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.Null
import hep.dataforge.values.Value
import hep.dataforge.values.asValue
import kotlin.jvm.JvmName
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/* Meta delegates */
//TODO add caching for sealed nodes
class ValueDelegate(val meta: Meta, private val key: String? = null, private val default: Value? = null) :
ReadOnlyProperty<Any?, Value?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Value? {
return meta[key ?: property.name]?.value ?: default
}
}
class StringDelegate(val meta: Meta, private val key: String? = null, private val default: String? = null) :
ReadOnlyProperty<Any?, String?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String? {
return meta[key ?: property.name]?.string ?: default
}
}
class BooleanDelegate(
val meta: Meta,
private val key: String? = null,
private val default: Boolean? = null
) : ReadOnlyProperty<Any?, Boolean?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean? {
return meta[key ?: property.name]?.boolean ?: default
}
}
class NumberDelegate(
val meta: Meta,
private val key: String? = null,
private val default: Number? = null
) : ReadOnlyProperty<Any?, Number?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Number? {
return meta[key ?: property.name]?.number ?: default
}
//delegates for number transformation
val double get() = DelegateWrapper(this) { it?.toDouble() }
val int get() = DelegateWrapper(this) { it?.toInt() }
val short get() = DelegateWrapper(this) { it?.toShort() }
val long get() = DelegateWrapper(this) { it?.toLong() }
}
class DelegateWrapper<T, R>(val delegate: ReadOnlyProperty<Any?, T>, val reader: (T) -> R) :
ReadOnlyProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R {
return reader(delegate.getValue(thisRef, property))
}
}
//Delegates with non-null values
class SafeStringDelegate(
val meta: Meta,
private val key: String? = null,
default: () -> String
) : ReadOnlyProperty<Any?, String> {
private val default: String by lazy(default)
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
return meta[key ?: property.name]?.string ?: default
}
}
class SafeBooleanDelegate(
val meta: Meta,
private val key: String? = null,
default: () -> Boolean
) : ReadOnlyProperty<Any?, Boolean> {
private val default: Boolean by lazy(default)
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
return meta[key ?: property.name]?.boolean ?: default
}
}
class SafeNumberDelegate(
val meta: Meta,
private val key: String? = null,
default: () -> Number
) : ReadOnlyProperty<Any?, Number> {
private val default: Number by lazy(default)
override fun getValue(thisRef: Any?, property: KProperty<*>): Number {
return meta[key ?: property.name]?.number ?: default
}
val double get() = DelegateWrapper(this) { it.toDouble() }
val int get() = DelegateWrapper(this) { it.toInt() }
val short get() = DelegateWrapper(this) { it.toShort() }
val long get() = DelegateWrapper(this) { it.toLong() }
}
class SafeEnumDelegate<E : Enum<E>>(
val meta: Meta,
private val key: String? = null,
private val default: E,
private val resolver: (String) -> E
) : ReadOnlyProperty<Any?, E> {
override fun getValue(thisRef: Any?, property: KProperty<*>): E {
return (meta[key ?: property.name]?.string)?.let { resolver(it) } ?: default
}
}
//Child node delegate
class ChildDelegate<T>(
val meta: Meta,
private val key: String? = null,
private val converter: (Meta) -> T
) : ReadOnlyProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
return meta[key ?: property.name]?.node?.let { converter(it) }
}
}
//Read-only delegates for Metas
/**
* A property delegate that uses custom key
*/
fun Meta.value(default: Value = Null, key: String? = null) = ValueDelegate(this, key, default)
fun Meta.string(default: String? = null, key: String? = null) = StringDelegate(this, key, default)
fun Meta.boolean(default: Boolean? = null, key: String? = null) = BooleanDelegate(this, key, default)
fun Meta.number(default: Number? = null, key: String? = null) = NumberDelegate(this, key, default)
fun Meta.child(key: String? = null) = ChildDelegate(this, key) { it }
@JvmName("safeString")
fun Meta.string(default: String, key: String? = null) =
SafeStringDelegate(this, key) { default }
@JvmName("safeBoolean")
fun Meta.boolean(default: Boolean, key: String? = null) =
SafeBooleanDelegate(this, key) { default }
@JvmName("safeNumber")
fun Meta.number(default: Number, key: String? = null) =
SafeNumberDelegate(this, key) { default }
@JvmName("safeString")
fun Meta.string(key: String? = null, default: () -> String) =
SafeStringDelegate(this, key, default)
@JvmName("safeBoolean")
fun Meta.boolean(key: String? = null, default: () -> Boolean) =
SafeBooleanDelegate(this, key, default)
@JvmName("safeNumber")
fun Meta.number(key: String? = null, default: () -> Number) =
SafeNumberDelegate(this, key, default)
inline fun <reified E : Enum<E>> Meta.enum(default: E, key: String? = null) =
SafeEnumDelegate(this, key, default) { enumValueOf(it) }
/* Read-write delegates */
class MutableValueDelegate<M : MutableMeta<M>>(
val meta: M,
private val key: Name? = null,
private val default: Value? = null
) : ReadWriteProperty<Any?, Value?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Value? {
return meta[key ?: property.name.asName()]?.value ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) {
val name = key ?: property.name.asName()
if (value == null) {
meta.remove(name)
} else {
meta.setValue(name, value)
}
}
fun <T> transform(writer: (T) -> Value? = { Value.of(it) }, reader: (Value?) -> T) =
ReadWriteDelegateWrapper(this, reader, writer)
}
class MutableStringDelegate<M : MutableMeta<M>>(
val meta: M,
private val key: Name? = null,
private val default: String? = null
) : ReadWriteProperty<Any?, String?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String? {
return meta[key ?: property.name.asName()]?.string ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) {
val name = key ?: property.name.asName()
if (value == null) {
meta.remove(name)
} else {
meta.setValue(name, value.asValue())
}
}
}
class MutableBooleanDelegate<M : MutableMeta<M>>(
val meta: M,
private val key: Name? = null,
private val default: Boolean? = null
) : ReadWriteProperty<Any?, Boolean?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean? {
return meta[key ?: property.name.asName()]?.boolean ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean?) {
val name = key ?: property.name.asName()
if (value == null) {
meta.remove(name)
} else {
meta.setValue(name, value.asValue())
}
}
}
class MutableNumberDelegate<M : MutableMeta<M>>(
val meta: M,
private val key: Name? = null,
private val default: Number? = null
) : ReadWriteProperty<Any?, Number?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Number? {
return meta[key ?: property.name.asName()]?.number ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Number?) {
val name = key ?: property.name.asName()
if (value == null) {
meta.remove(name)
} else {
meta.setValue(name, value.asValue())
}
}
val double get() = ReadWriteDelegateWrapper(this, reader = { it?.toDouble() }, writer = { it })
val float get() = ReadWriteDelegateWrapper(this, reader = { it?.toFloat() }, writer = { it })
val int get() = ReadWriteDelegateWrapper(this, reader = { it?.toInt() }, writer = { it })
val short get() = ReadWriteDelegateWrapper(this, reader = { it?.toShort() }, writer = { it })
val long get() = ReadWriteDelegateWrapper(this, reader = { it?.toLong() }, writer = { it })
}
//Delegates with non-null values
class MutableSafeStringDelegate<M : MutableMeta<M>>(
val meta: M,
private val key: Name? = null,
default: () -> String
) : ReadWriteProperty<Any?, String> {
private val default: String by lazy(default)
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
return meta[key ?: property.name.asName()]?.string ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
meta.setValue(key ?: property.name.asName(), value.asValue())
}
}
class MutableSafeBooleanDelegate<M : MutableMeta<M>>(
val meta: M,
private val key: Name? = null,
default: () -> Boolean
) : ReadWriteProperty<Any?, Boolean> {
private val default: Boolean by lazy(default)
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
return meta[key ?: property.name.asName()]?.boolean ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
meta.setValue(key ?: property.name.asName(), value.asValue())
}
}
class MutableSafeNumberDelegate<M : MutableMeta<M>>(
val meta: M,
private val key: Name? = null,
default: () -> Number
) : ReadWriteProperty<Any?, Number> {
private val default: Number by lazy(default)
override fun getValue(thisRef: Any?, property: KProperty<*>): Number {
return meta[key ?: property.name.asName()]?.number ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Number) {
meta.setValue(key ?: property.name.asName(), value.asValue())
}
val double get() = ReadWriteDelegateWrapper(this, reader = { it.toDouble() }, writer = { it })
val float get() = ReadWriteDelegateWrapper(this, reader = { it.toFloat() }, writer = { it })
val int get() = ReadWriteDelegateWrapper(this, reader = { it.toInt() }, writer = { it })
val short get() = ReadWriteDelegateWrapper(this, reader = { it.toShort() }, writer = { it })
val long get() = ReadWriteDelegateWrapper(this, reader = { it.toLong() }, writer = { it })
}
class MutableSafeEnumvDelegate<M : MutableMeta<M>, E : Enum<E>>(
val meta: M,
private val key: Name? = null,
private val default: E,
private val resolver: (String) -> E
) : ReadWriteProperty<Any?, E> {
override fun getValue(thisRef: Any?, property: KProperty<*>): E {
return (meta[key ?: property.name.asName()]?.string)?.let { resolver(it) } ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: E) {
meta.setValue(key ?: property.name.asName(), value.name.asValue())
}
}
//Child node delegate
class MutableNodeDelegate<M : MutableMeta<M>>(
val meta: M,
private val key: Name? = null
) : ReadWriteProperty<Any?, M?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): M? {
return meta[key ?: property.name.asName()]?.node
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: M?) {
meta[key ?: property.name.asName()] = value
}
}
class ReadWriteDelegateWrapper<T, R>(
val delegate: ReadWriteProperty<Any?, T>,
val reader: (T) -> R,
val writer: (R) -> T
) : ReadWriteProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R {
return reader(delegate.getValue(thisRef, property))
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
delegate.setValue(thisRef, property, writer(value))
}
}
//Read-write delegates
/**
* A property delegate that uses custom key
*/
fun <M : MutableMeta<M>> M.value(default: Value = Null, key: Name? = null) =
MutableValueDelegate(this, key, default)
fun <M : MutableMeta<M>> M.string(default: String? = null, key: Name? = null) =
MutableStringDelegate(this, key, default)
fun <M : MutableMeta<M>> M.boolean(default: Boolean? = null, key: Name? = null) =
MutableBooleanDelegate(this, key, default)
fun <M : MutableMeta<M>> M.number(default: Number? = null, key: Name? = null) =
MutableNumberDelegate(this, key, default)
fun <M : MutableMeta<M>> M.child(key: Name? = null) =
MutableNodeDelegate(this, key)
@JvmName("safeString")
fun <M : MutableMeta<M>> M.string(default: String, key: Name? = null) =
MutableSafeStringDelegate(this, key) { default }
@JvmName("safeBoolean")
fun <M : MutableMeta<M>> M.boolean(default: Boolean, key: Name? = null) =
MutableSafeBooleanDelegate(this, key) { default }
@JvmName("safeNumber")
fun <M : MutableMeta<M>> M.number(default: Number, key: Name? = null) =
MutableSafeNumberDelegate(this, key) { default }
@JvmName("safeString")
fun <M : MutableMeta<M>> M.string(key: Name? = null, default: () -> String) =
MutableSafeStringDelegate(this, key, default)
@JvmName("safeBoolean")
fun <M : MutableMeta<M>> M.boolean(key: Name? = null, default: () -> Boolean) =
MutableSafeBooleanDelegate(this, key, default)
@JvmName("safeNumber")
fun <M : MutableMeta<M>> M.number(key: Name? = null, default: () -> Number) =
MutableSafeNumberDelegate(this, key, default)
inline fun <M : MutableMeta<M>, reified E : Enum<E>> M.enum(default: E, key: Name? = null) =
MutableSafeEnumvDelegate(this, key, default) { enumValueOf(it) }

View File

@ -26,26 +26,13 @@ fun Meta.getIndexed(name: Name): Map<String, MetaItem<*>> {
@DFExperimental @DFExperimental
fun Meta.getIndexed(name: String): Map<String, MetaItem<*>> = this@getIndexed.getIndexed(name.toName()) fun Meta.getIndexed(name: String): Map<String, MetaItem<*>> = this@getIndexed.getIndexed(name.toName())
/** /**
* Get all items matching given name. * Get all items matching given name.
*/ */
@Suppress("UNCHECKED_CAST")
@DFExperimental @DFExperimental
fun <M : MetaNode<M>> M.getIndexed(name: Name): Map<String, MetaItem<M>> { fun <M : MetaNode<M>> M.getIndexed(name: Name): Map<String, MetaItem<M>> =
val root: MetaNode<M>? = when (name.length) { (this as Meta).getIndexed(name) as Map<String, MetaItem<M>>
0 -> error("Can't use empty name for that")
1 -> this
else -> (this[name.cutLast()] as? MetaItem.NodeItem<M>)?.node
}
val (body, index) = name.last()!!
val regex = index.toRegex()
return root?.items
?.filter { it.key.body == body && (index.isEmpty() || regex.matches(it.key.index)) }
?.mapKeys { it.key.index }
?: emptyMap()
}
@DFExperimental @DFExperimental
fun <M : MetaNode<M>> M.getIndexed(name: String): Map<String, MetaItem<M>> = getIndexed(name.toName()) fun <M : MetaNode<M>> M.getIndexed(name: String): Map<String, MetaItem<M>> = getIndexed(name.toName())

View File

@ -13,7 +13,7 @@ class SchemeTest{
"d" put it "d" put it
} }
} }
}.toScheme() }.asScheme()
val meta = styled.toMeta() val meta = styled.toMeta()