Yet another refactor of Meta and Configurable delegates (let's hope it is final)

This commit is contained in:
Alexander Nozik 2020-07-05 12:54:27 +03:00
parent faeb737672
commit c280671e61
16 changed files with 490 additions and 538 deletions

View File

@ -7,7 +7,7 @@ plugins {
id("org.jetbrains.dokka") version "0.10.1" id("org.jetbrains.dokka") version "0.10.1"
} }
val dataforgeVersion by extra("0.1.8-dev-6") val dataforgeVersion by extra("0.1.8")
val bintrayRepo by extra("dataforge") val bintrayRepo by extra("dataforge")
val githubProject by extra("dataforge-core") val githubProject by extra("dataforge-core")

View File

@ -1,6 +1,7 @@
package hep.dataforge.io package hep.dataforge.io
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
import kotlinx.io.asBinary import kotlinx.io.asBinary
import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.json import kotlinx.serialization.json.json
@ -74,9 +75,9 @@ class MetaFormatTest {
} }
val meta = json.toMetaItem().node!! val meta = json.toMetaItem().node!!
assertEquals(true, meta["@value[0].@value[1].d"].boolean) assertEquals(true, meta["$JSON_ARRAY_KEY[0].$JSON_ARRAY_KEY[1].d"].boolean)
assertEquals("value", meta["@value[1]"].string) assertEquals("value", meta["$JSON_ARRAY_KEY[1]"].string)
assertEquals(listOf(1.0, 2.0, 3.0), meta["@value[2"].value?.list?.map { it.number.toDouble() }) assertEquals(listOf(1.0, 2.0, 3.0), meta["$JSON_ARRAY_KEY[2"].value?.list?.map { it.number.toDouble() })
} }
} }

View File

@ -2,8 +2,11 @@ package hep.dataforge.meta
import hep.dataforge.meta.descriptors.* import hep.dataforge.meta.descriptors.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.values.Value import hep.dataforge.values.Value
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/** /**
* A container that holds a [Config] and a default item provider. * A container that holds a [Config] and a default item provider.
@ -11,7 +14,7 @@ import hep.dataforge.values.Value
* 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 : Described { interface Configurable : Described, MutableItemProvider {
/** /**
* Backing config * Backing config
*/ */
@ -35,15 +38,15 @@ interface Configurable : Described {
/** /**
* Get a property with default * Get a property with default
*/ */
fun getProperty(name: Name): MetaItem<*>? = override fun getItem(name: Name): MetaItem<*>? =
config[name] ?: getDefaultItem(name) ?: descriptor?.get(name)?.defaultItem() config[name] ?: getDefaultItem(name) ?: descriptor?.get(name)?.defaultItem()
/** /**
* Set a configurable property * Set a configurable property
*/ */
fun setProperty(name: Name, item: MetaItem<*>?) { override fun setItem(name: Name, item: MetaItem<*>?) {
if (validateItem(name, item)) { if (validateItem(name, item)) {
config[name] = item config.setItem(name, item)
} else { } else {
error("Validation failed for property $name with value $item") error("Validation failed for property $name with value $item")
} }
@ -54,22 +57,59 @@ interface Configurable : Described {
* Reset the property to its default value * Reset the property to its default value
*/ */
fun Configurable.resetProperty(name: Name) { fun Configurable.resetProperty(name: Name) {
setProperty(name, null) setItem(name, null)
} }
fun Configurable.getProperty(key: String) = getProperty(key.toName()) fun Configurable.getItem(key: String) = getItem(key.toName())
fun Configurable.setProperty(name: Name, value: Value?) = setProperty(name, value?.let { MetaItem.ValueItem(value) }) fun Configurable.setItem(name: Name, value: Value?) = setItem(name, value?.let { MetaItem.ValueItem(value) })
fun Configurable.setProperty(name: Name, meta: Meta?) = setProperty(name, meta?.let { MetaItem.NodeItem(meta) }) fun Configurable.setItem(name: Name, meta: Meta?) = setItem(name, meta?.let { MetaItem.NodeItem(meta) })
fun Configurable.setProperty(key: String, item: MetaItem<*>?) { fun Configurable.setItem(key: String, item: MetaItem<*>?) = setItem(key.toName(), item)
setProperty(key.toName(), item)
}
fun Configurable.setProperty(key: String, value: Value?) = setProperty(key, value?.let { MetaItem.ValueItem(value) }) fun Configurable.setItem(key: String, value: Value?) = setItem(key, value?.let { MetaItem.ValueItem(value) })
fun Configurable.setProperty(key: String, meta: Meta?) = setProperty(key, meta?.let { MetaItem.NodeItem(meta) }) fun Configurable.setItem(key: String, meta: Meta?) = setItem(key, meta?.let { MetaItem.NodeItem(meta) })
fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) } fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
@DFBuilder @DFBuilder
inline fun <T : Configurable> T.configure(action: Config.() -> Unit): T = apply { config.apply(action) } inline fun <T : Configurable> T.configure(action: Config.() -> Unit): T = apply { config.apply(action) }
/* Node delegates */
fun Configurable.config(key: Name? = null): ReadWriteProperty<Any?, Config?> =
config.node(key)
fun MutableItemProvider.node(key: Name? = null): ReadWriteProperty<Any?, Meta?> = item(key).convert(
reader = { it.node },
writer = { it?.let { MetaItem.NodeItem(it) } }
)
fun <T : Configurable> Configurable.spec(
spec: Specification<T>, key: Name? = null
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
val name = key ?: property.name.asName()
return config[name].node?.let { spec.wrap(it) }
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
val name = key ?: property.name.asName()
config[name] = value?.config
}
}
fun <T : Configurable> Configurable.spec(
spec: Specification<T>, default: T, key: Name? = null
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
val name = key ?: property.name.asName()
return config[name].node?.let { spec.wrap(it) } ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val name = key ?: property.name.asName()
config[name] = value.config
}
}

View File

@ -1,244 +0,0 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.*
import kotlin.jvm.JvmName
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
//delegates
/**
* A delegate that uses a [Configurable] object and delegate read and write operations to its properties
*/
open class ConfigurableDelegate(
val owner: Configurable,
val key: Name? = null,
open val default: MetaItem<*>? = null
) : ReadWriteProperty<Any?, MetaItem<*>?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
val name = key ?: property.name.asName()
return owner.getProperty(name) ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) {
val name = key ?: property.name.asName()
owner.setProperty(name, value)
}
}
class LazyConfigurableDelegate(
configurable: Configurable,
key: Name? = null,
defaultProvider: () -> MetaItem<*>? = { null }
) : ConfigurableDelegate(configurable, key) {
override val default by lazy(defaultProvider)
}
/**
* A property delegate that uses custom key
*/
fun Configurable.item(default: Any? = null, key: Name? = null): ConfigurableDelegate =
ConfigurableDelegate(
this,
key,
default?.let { MetaItem.of(it) })
/**
* Generation of item delegate with lazy default.
* Lazy default could be used also for validation
*/
fun Configurable.lazyItem(key: Name? = null, default: () -> Any?): ConfigurableDelegate =
LazyConfigurableDelegate(this, key) {
default()?.let {
MetaItem.of(it)
}
}
fun <T> Configurable.item(
default: T? = null,
key: Name? = null,
writer: (T) -> MetaItem<*>? = {
MetaItem.of(it)
},
reader: (MetaItem<*>?) -> T
): ReadWriteProperty<Any?, T> =
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?> =
item(default, key).transform { it.value }
fun <T> Configurable.value(
default: T? = null,
key: Name? = null,
writer: (T) -> Value? = { Value.of(it) },
reader: (Value?) -> T
): ReadWriteProperty<Any?, T> =
ConfigurableDelegate(
this,
key,
default?.let { MetaItem.of(it) }
).map(
reader = { reader(it.value) },
writer = { value -> writer(value)?.let { MetaItem.ValueItem(it) } }
)
fun Configurable.string(default: String? = null, key: Name? = null): ReadWriteProperty<Any?, String?> =
item(default, key).transform { it.value?.string }
fun Configurable.boolean(default: Boolean? = null, key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
item(default, key).transform { it.value?.boolean }
fun Configurable.number(default: Number? = null, key: Name? = null): ReadWriteProperty<Any?, Number?> =
item(default, key).transform { it.value?.number }
/* Number delegates*/
fun Configurable.int(default: Int? = null, key: Name? = null): ReadWriteProperty<Any?, Int?> =
item(default, key).transform { it.value?.int }
fun Configurable.double(default: Double? = null, key: Name? = null): ReadWriteProperty<Any?, Double?> =
item(default, key).transform { it.value?.double }
fun Configurable.long(default: Long? = null, key: Name? = null): ReadWriteProperty<Any?, Long?> =
item(default, key).transform { it.value?.long }
fun Configurable.short(default: Short? = null, key: Name? = null): ReadWriteProperty<Any?, Short?> =
item(default, key).transform { it.value?.short }
fun Configurable.float(default: Float? = null, key: Name? = null): ReadWriteProperty<Any?, Float?> =
item(default, key).transform { it.value?.float }
@JvmName("safeString")
fun Configurable.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
item(default, key).transform { it.value!!.string }
@JvmName("safeBoolean")
fun Configurable.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
item(default, key).transform { it.value!!.boolean }
@JvmName("safeNumber")
fun Configurable.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
item(default, key).transform { it.value!!.number }
/* Lazy initializers for values */
@JvmName("lazyString")
fun Configurable.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
lazyItem(key, default).transform { it.value!!.string }
@JvmName("lazyBoolean")
fun Configurable.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
lazyItem(key, default).transform { it.value!!.boolean }
@JvmName("lazyNumber")
fun Configurable.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
lazyItem(key, default).transform { it.value!!.number }
/* Safe number delegates*/
@JvmName("safeInt")
fun Configurable.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
item(default, key).transform { it.value!!.int }
@JvmName("safeDouble")
fun Configurable.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
item(default, key).transform { it.value!!.double }
@JvmName("safeLong")
fun Configurable.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
item(default, key).transform { it.value!!.long }
@JvmName("safeShort")
fun Configurable.short(default: Short, key: Name? = null): ReadWriteProperty<Any?, Short> =
item(default, key).transform { it.value!!.short }
@JvmName("safeFloat")
fun Configurable.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
item(default, key).transform { it.value!!.float }
/**
* Enum delegate
*/
inline fun <reified E : Enum<E>> Configurable.enum(
default: E, key: Name? = null
): ReadWriteProperty<Any?, E> =
item(default, key).transform { item -> item?.string?.let {str->
@Suppress("USELESS_CAST")
enumValueOf<E>(str) as E
} ?: default }
/*
* Extra delegates for special cases
*/
fun Configurable.stringList(vararg strings: String, key: Name? = null): ReadWriteProperty<Any?, List<String>> =
item(listOf(*strings), key) {
it?.value?.stringList ?: emptyList()
}
fun Configurable.stringListOrNull(vararg strings: String, key: Name? = null): ReadWriteProperty<Any?, List<String>?> =
item(listOf(*strings), key) {
it?.value?.stringList
}
fun Configurable.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty<Any?, List<Number>> =
item(listOf(*numbers), key) { item ->
item?.value?.list?.map { it.number } ?: emptyList()
}
/**
* A special delegate for double arrays
*/
fun Configurable.doubleArray(vararg doubles: Double, key: Name? = null): ReadWriteProperty<Any?, DoubleArray> =
item(doubleArrayOf(*doubles), key) {
(it.value as? DoubleArrayValue)?.value
?: it?.value?.list?.map { value -> value.number.toDouble() }?.toDoubleArray()
?: doubleArrayOf()
}
/* Node delegates */
fun Configurable.config(default: Config? = null, key: Name? = null): ReadWriteProperty<Any?, Config?> =
config.node(default,key)
fun Configurable.node(key: Name? = null): ReadWriteProperty<Any?, Meta?> = item(key).map(
reader = { it.node },
writer = { it?.let { MetaItem.NodeItem(it) } }
)
fun <T : Configurable> Configurable.spec(
spec: Specification<T>, key: Name? = null
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
val name = key ?: property.name.asName()
return config[name].node?.let { spec.wrap(it) }
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
val name = key ?: property.name.asName()
config[name] = value?.config
}
}
fun <T : Configurable> Configurable.spec(
spec: Specification<T>, default: T, key: Name? = null
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
val name = key ?: property.name.asName()
return config[name].node?.let { spec.wrap(it) } ?: default
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val name = key ?: property.name.asName()
config[name] = value.config
}
}

View File

@ -0,0 +1,66 @@
package hep.dataforge.meta
import hep.dataforge.meta.transformations.MetaConverter
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.Value
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/* Meta delegates */
open class ItemDelegate(
open val owner: ItemProvider,
val key: Name? = null,
open val default: MetaItem<*>? = null
) : ReadOnlyProperty<Any?, MetaItem<*>?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
return owner.getItem(key ?: property.name.asName()) ?: default
}
}
fun ItemProvider.item(key: Name? = null): ItemDelegate = ItemDelegate(this, key)
//TODO add caching for sealed nodes
//Read-only delegates for Metas
/**
* A property delegate that uses custom key
*/
fun ItemProvider.value(key: Name? = null): ReadOnlyProperty<Any?, Value?> =
item(key).convert(MetaConverter.value)
fun ItemProvider.string(key: Name? = null): ReadOnlyProperty<Any?, String?> =
item(key).convert(MetaConverter.string)
fun ItemProvider.boolean(key: Name? = null): ReadOnlyProperty<Any?, Boolean?> =
item(key).convert(MetaConverter.boolean)
fun ItemProvider.number(key: Name? = null): ReadOnlyProperty<Any?, Number?> =
item(key).convert(MetaConverter.number)
fun ItemProvider.node(key: Name? = null): ReadOnlyProperty<Any?, Meta?> =
item(key).convert(MetaConverter.meta)
fun ItemProvider.string(default: String, key: Name? = null): ReadOnlyProperty<Any?, String> =
item(key).convert(MetaConverter.string) { default }
fun ItemProvider.boolean(default: Boolean, key: Name? = null): ReadOnlyProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean) { default }
fun ItemProvider.number(default: Number, key: Name? = null): ReadOnlyProperty<Any?, Number> =
item(key).convert(MetaConverter.number) { default }
inline fun <reified E : Enum<E>> ItemProvider.enum(default: E, key: Name? = null): ReadOnlyProperty<Any?, E> =
item(key).convert(MetaConverter.enum()) { default }
fun ItemProvider.string(key: Name? = null, default: () -> String): ReadOnlyProperty<Any?, String> =
item(key).convert(MetaConverter.string, default)
fun ItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadOnlyProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean, default)
fun ItemProvider.number(key: Name? = null, default: () -> Number): ReadOnlyProperty<Any?, Number> =
item(key).convert(MetaConverter.number, default)

View File

@ -2,6 +2,7 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
import hep.dataforge.meta.descriptors.ItemDescriptor import hep.dataforge.meta.descriptors.ItemDescriptor
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.ValueDescriptor import hep.dataforge.meta.descriptors.ValueDescriptor
@ -113,17 +114,15 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
val value = if (isEmpty()) { val value = if (isEmpty()) {
Null Null
} else { } else {
ListValue(
map<JsonElement, Value> { map<JsonElement, Value> {
//We already checked that all values are primitives //We already checked that all values are primitives
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor) (it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
} }.asValue()
)
} }
MetaItem.ValueItem(value) MetaItem.ValueItem(value)
} else { } else {
//We can't return multiple items therefore we create top level node //We can't return multiple items therefore we create top level node
json { "@json" to this@toMetaItem }.toMetaItem(descriptor) json { JSON_ARRAY_KEY to this@toMetaItem }.toMetaItem(descriptor)
} }
} }
} }
@ -162,7 +161,8 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
} else value.forEachIndexed { index, jsonElement -> } else value.forEachIndexed { index, jsonElement ->
val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
val indexValue: String = (jsonElement as? JsonObject) val indexValue: String = (jsonElement as? JsonObject)
?.get(indexKey)?.contentOrNull ?: index.toString() //In case index is non-string, the backward transformation will be broken. ?.get(indexKey)?.contentOrNull
?: index.toString() //In case index is non-string, the backward transformation will be broken.
val token = key.withIndex(indexValue) val token = key.withIndex(indexValue)
map[token] = jsonElement.toMetaItem(itemDescriptor) map[token] = jsonElement.toMetaItem(itemDescriptor)
@ -173,4 +173,11 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
} }
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy(::buildItems) override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy(::buildItems)
companion object{
/**
* A key representing top-level json array of nodes, which could not be directly represented by a meta node
*/
const val JSON_ARRAY_KEY = "@jsonArray"
}
} }

View File

@ -85,6 +85,10 @@ interface MetaRepr {
fun toMeta(): Meta fun toMeta(): Meta
} }
interface ItemProvider{
fun getItem(name: Name): MetaItem<*>?
}
/** /**
* Generic meta tree representation. Elements are [MetaItem] objects that could be represented by three different entities: * Generic meta tree representation. Elements are [MetaItem] objects that could be represented by three different entities:
* * [MetaItem.ValueItem] (leaf) * * [MetaItem.ValueItem] (leaf)
@ -92,12 +96,23 @@ interface MetaRepr {
* *
* * Same name siblings are supported via elements with the same [Name] but different queries * * Same name siblings are supported via elements with the same [Name] but different queries
*/ */
interface Meta : MetaRepr { interface Meta : MetaRepr, ItemProvider {
/** /**
* Top level items of meta tree * Top level items of meta tree
*/ */
val items: Map<NameToken, MetaItem<*>> val items: Map<NameToken, MetaItem<*>>
override fun getItem(name: Name): MetaItem<*>? {
if (name.isEmpty()) return NodeItem(this)
return name.first()?.let { token ->
val tail = name.cutFirst()
when (tail.length) {
0 -> items[token]
else -> items[token]?.node?.get(tail)
}
}
}
override fun toMeta(): Meta = seal() override fun toMeta(): Meta = seal()
override fun equals(other: Any?): Boolean override fun equals(other: Any?): Boolean
@ -127,17 +142,7 @@ interface Meta : MetaRepr {
* *
* If [name] is empty return current [Meta] as a [NodeItem] * If [name] is empty return current [Meta] as a [NodeItem]
*/ */
operator fun Meta?.get(name: Name): MetaItem<*>? { operator fun Meta?.get(name: Name): MetaItem<*>? = this?.getItem(name)
if (this == null) return null
if (name.isEmpty()) return NodeItem(this)
return name.first()?.let { token ->
val tail = name.cutFirst()
when (tail.length) {
0 -> items[token]
else -> items[token]?.node?.get(tail)
}
}
}
operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token) operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token)

View File

@ -1,98 +0,0 @@
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,167 @@
package hep.dataforge.meta
import hep.dataforge.meta.transformations.MetaConverter
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.DoubleArrayValue
import hep.dataforge.values.Value
import hep.dataforge.values.stringList
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/* Read-write delegates */
open class MutableItemDelegate(
override val owner: MutableItemProvider,
key: Name? = null,
default: MetaItem<*>? = null
) : ItemDelegate(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)
}
}
fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate =
MutableItemDelegate(this, key)
//Read-write delegates
/**
* A property delegate that uses custom key
*/
fun MutableItemProvider.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
item(key).convert(MetaConverter.value)
fun MutableItemProvider.string(key: Name? = null): ReadWriteProperty<Any?, String?> =
item(key).convert(MetaConverter.string)
fun MutableItemProvider.boolean(key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
item(key).convert(MetaConverter.boolean)
fun MutableItemProvider.number(key: Name? = null): ReadWriteProperty<Any?, Number?> =
item(key).convert(MetaConverter.number)
fun MutableItemProvider.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
item(key).convert(MetaConverter.string) { default }
fun MutableItemProvider.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean) { default }
fun MutableItemProvider.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
item(key).convert(MetaConverter.number) { default }
fun MutableItemProvider.value(key: Name? = null, default: () -> Value): ReadWriteProperty<Any?, Value> =
item(key).convert(MetaConverter.value, default)
fun MutableItemProvider.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
item(key).convert(MetaConverter.string, default)
fun MutableItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
item(key).convert(MetaConverter.boolean, default)
fun MutableItemProvider.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
item(key).convert(MetaConverter.number, default)
inline fun <reified E : Enum<E>> MutableItemProvider.enum(default: E, key: Name? = null): ReadWriteProperty<Any?, E> =
item(key).convert(MetaConverter.enum()) { default }
inline fun <reified M : MutableMeta<M>> M.node(key: Name? = null): ReadWriteProperty<Any?, M?> =
item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItem.NodeItem(it) } })
fun <T> MutableItemProvider.item(
default: T? = null,
key: Name? = null,
writer: (T) -> MetaItem<*>? = { MetaItem.of(it) },
reader: (MetaItem<*>?) -> T
): ReadWriteProperty<Any?, T> = MutableItemDelegate(
this,
key,
default?.let { MetaItem.of(it) }
).convert(reader = reader, writer = writer)
fun Configurable.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
item(key).convert(MetaConverter.value)
fun <T> MutableItemProvider.value(
default: T? = null,
key: Name? = null,
writer: (T) -> Value? = { Value.of(it) },
reader: (Value?) -> T
): ReadWriteProperty<Any?, T> = MutableItemDelegate(
this,
key,
default?.let { MetaItem.of(it) }
).convert(
reader = { reader(it.value) },
writer = { value -> writer(value)?.let { MetaItem.ValueItem(it) } }
)
/* Number delegates*/
fun MutableItemProvider.int(key: Name? = null): ReadWriteProperty<Any?, Int?> =
item(key).convert(MetaConverter.int)
fun MutableItemProvider.double(key: Name? = null): ReadWriteProperty<Any?, Double?> =
item(key).convert(MetaConverter.double)
fun MutableItemProvider.long(key: Name? = null): ReadWriteProperty<Any?, Long?> =
item(key).convert(MetaConverter.long)
fun MutableItemProvider.float(key: Name? = null): ReadWriteProperty<Any?, Float?> =
item(key).convert(MetaConverter.float)
/* Safe number delegates*/
fun MutableItemProvider.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
item(key).convert(MetaConverter.int) { default }
fun MutableItemProvider.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
item(key).convert(MetaConverter.double) { default }
fun MutableItemProvider.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
item(key).convert(MetaConverter.long) { default }
fun MutableItemProvider.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
item(key).convert(MetaConverter.float) { default }
/*
* Extra delegates for special cases
*/
fun MutableItemProvider.stringList(vararg strings: String, key: Name? = null): ReadWriteProperty<Any?, List<String>> =
item(listOf(*strings), key) {
it?.value?.stringList ?: emptyList()
}
fun MutableItemProvider.stringListOrNull(
vararg strings: String,
key: Name? = null
): ReadWriteProperty<Any?, List<String>?> =
item(listOf(*strings), key) {
it?.value?.stringList
}
fun MutableItemProvider.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty<Any?, List<Number>> =
item(listOf(*numbers), key) { item ->
item?.value?.list?.map { it.number } ?: emptyList()
}
/**
* A special delegate for double arrays
*/
fun MutableItemProvider.doubleArray(vararg doubles: Double, key: Name? = null): ReadWriteProperty<Any?, DoubleArray> =
item(doubleArrayOf(*doubles), key) {
(it.value as? DoubleArrayValue)?.value
?: it?.value?.list?.map { value -> value.number.toDouble() }?.toDoubleArray()
?: doubleArrayOf()
}
fun <T> MutableItemProvider.listValue(
key: Name? = null,
writer: (T) -> Value = { Value.of(it) },
reader: (Value) -> T
): ReadWriteProperty<Any?, List<T>?> = item(key).convert(MetaConverter.valueList(writer, reader))

View File

@ -3,9 +3,12 @@ package hep.dataforge.meta
import hep.dataforge.names.* import hep.dataforge.names.*
import hep.dataforge.values.Value import hep.dataforge.values.Value
interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M> { interface MutableItemProvider : ItemProvider {
fun setItem(name: Name, item: MetaItem<*>?)
}
interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M>, MutableItemProvider {
override val items: Map<NameToken, MetaItem<M>> override val items: Map<NameToken, MetaItem<M>>
operator fun set(name: Name, item: MetaItem<*>?)
// fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) // fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
// fun removeListener(owner: Any? = null) // fun removeListener(owner: Any? = null)
} }
@ -49,7 +52,7 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
*/ */
internal abstract fun empty(): M internal abstract fun empty(): M
override operator fun set(name: Name, item: MetaItem<*>?) { override fun setItem(name: Name, item: MetaItem<*>?) {
when (name.length) { when (name.length) {
0 -> error("Can't setValue meta item for empty name") 0 -> error("Can't setValue meta item for empty name")
1 -> { 1 -> {
@ -63,7 +66,7 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
if (items[token] == null) { if (items[token] == null) {
replaceItem(token, null, MetaItem.NodeItem(empty())) replaceItem(token, null, MetaItem.NodeItem(empty()))
} }
items[token]?.node!![name.cutFirst()] = item items[token]?.node!!.setItem(name.cutFirst(), item)
} }
} }
} }
@ -71,27 +74,21 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun MutableMeta<*>.remove(name: Name) = set(name, null) inline fun MutableMeta<*>.remove(name: Name) = setItem(name, null)
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun MutableMeta<*>.remove(name: String) = remove(name.toName()) inline fun MutableMeta<*>.remove(name: String) = remove(name.toName())
fun MutableMeta<*>.setValue(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) operator fun MutableMeta<*>.set(name: Name, item: MetaItem<*>?) = setItem(name, item)
fun MutableMeta<*>.setValue(name: Name, value: Value) = setItem(name, MetaItem.ValueItem(value))
fun MutableMeta<*>.setValue(name: String, value: Value) = set(name.toName(), value) fun MutableMeta<*>.setValue(name: String, value: Value) = set(name.toName(), value)
fun MutableMeta<*>.setItem(name: Name, item: MetaItem<*>?) {
when (item) {
null -> remove(name)
is MetaItem.ValueItem -> setValue(name, item.value)
is MetaItem.NodeItem<*> -> setNode(name, item.node)
}
}
fun MutableMeta<*>.setItem(name: String, item: MetaItem<*>?) = setItem(name.toName(), item) fun MutableMeta<*>.setItem(name: String, item: MetaItem<*>?) = setItem(name.toName(), item)
fun MutableMeta<*>.setNode(name: Name, node: Meta) = fun MutableMeta<*>.setNode(name: Name, node: Meta) =
set(name, MetaItem.NodeItem(node)) setItem(name, MetaItem.NodeItem(node))
fun MutableMeta<*>.setNode(name: String, node: Meta) = setNode(name.toName(), node) fun MutableMeta<*>.setNode(name: String, node: Meta) = setNode(name.toName(), node)
@ -142,7 +139,7 @@ fun MutableMeta<*>.setIndexedItems(
items.forEachIndexed { index, meta -> items.forEachIndexed { index, meta ->
val indexedToken = NameToken(last.body, last.index + indexFactory(meta, index)) val indexedToken = NameToken(last.body, last.index + indexFactory(meta, index))
tokens[tokens.lastIndex] = indexedToken tokens[tokens.lastIndex] = indexedToken
set(Name(tokens), meta) setItem(Name(tokens), meta)
} }
} }

View File

@ -1,116 +0,0 @@
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 = 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): ReadWriteDelegateWrapper<Value?, R> =
map(reader = reader, writer = { Value.of(it) })
fun <R : Any> ReadWriteProperty<Any?, R?>.notNull(default: () -> R): ReadWriteProperty<Any?, R> {
return ReadWriteDelegateWrapper(this,
reader = { it ?: default() },
writer = { 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(default: M? = null, key: Name? = null) =
item(default, key = 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("lazyValue")
fun <M : MutableMeta<M>> M.string(key: Name? = null, default: () -> Value) =
lazyItem(key, default).transform { it.value!! }
@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

@ -2,10 +2,7 @@ package hep.dataforge.meta.descriptors
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.* import hep.dataforge.names.*
import hep.dataforge.values.False import hep.dataforge.values.*
import hep.dataforge.values.True
import hep.dataforge.values.Value
import hep.dataforge.values.ValueType
@DFBuilder @DFBuilder
sealed class ItemDescriptor(val config: Config) { sealed class ItemDescriptor(val config: Config) {
@ -241,9 +238,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @return
*/ */
var type: List<ValueType> by config.item().transform { var type: List<ValueType>? by config.listValue { ValueType.valueOf(it.string) }
it?.value?.list?.map { v -> ValueType.valueOf(v.string) } ?: emptyList()
}
fun type(vararg t: ValueType) { fun type(vararg t: ValueType) {
this.type = listOf(*t) this.type = listOf(*t)
@ -257,9 +252,8 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* @return * @return
*/ */
fun isAllowedValue(value: Value): Boolean { fun isAllowedValue(value: Value): Boolean {
return (type.isEmpty() || type.contains(ValueType.STRING) || type.contains(value.type)) && (allowedValues.isEmpty() || allowedValues.contains( return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
value && (allowedValues.isEmpty() || allowedValues.contains(value))
))
} }
/** /**
@ -268,13 +262,19 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* *
* @return * @return
*/ */
var allowedValues: List<Value> by config.value().transform { var allowedValues: List<Value> by config.item().convert(
reader = {
val value = it.value
when { when {
it?.list != null -> it.list value?.list != null -> value.list
type.size == 1 && type[0] === ValueType.BOOLEAN -> listOf(True, False) type?.let { type -> type.size == 1 && type[0] === ValueType.BOOLEAN} ?: false -> listOf(True, False)
else -> emptyList() else -> emptyList()
} }
},
writer = {
MetaItem.ValueItem(it.asValue())
} }
)
/** /**
* Allow given list of value and forbid others * Allow given list of value and forbid others

View File

@ -0,0 +1,83 @@
package hep.dataforge.meta
import hep.dataforge.meta.transformations.MetaConverter
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* Apply a converter to this delegate creating a delegate with a custom type
*/
fun <R : Any> ItemDelegate.convert(
converter: MetaConverter<R>
): ReadOnlyProperty<Any?, R?> = object : ReadOnlyProperty<Any?, R?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R? =
this@convert.getValue(thisRef, property)?.let(converter::itemToObject)
}
/*
*
*/
fun <R : Any> ItemDelegate.convert(
converter: MetaConverter<R>,
default: () -> R
): ReadOnlyProperty<Any?, R> = object : ReadOnlyProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
this@convert.getValue(thisRef, property)?.let(converter::itemToObject) ?: default()
}
/**
* A converter with a custom reader transformation
*/
fun <R> ItemDelegate.convert(
reader: (MetaItem<*>?) -> R
): ReadOnlyProperty<Any?, R> = object : ReadOnlyProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
this@convert.getValue(thisRef, property).let(reader)
}
/*Mutable converters*/
/**
* A type converter for a mutable [MetaItem] delegate
*/
fun <R : Any> MutableItemDelegate.convert(
converter: MetaConverter<R>
): ReadWriteProperty<Any?, R?> = object : ReadWriteProperty<Any?, R?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R? =
this@convert.getValue(thisRef, property)?.let(converter::itemToObject)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R?) {
val item = value?.let(converter::objectToMetaItem)
this@convert.setValue(thisRef, property, item)
}
}
fun <R : Any> MutableItemDelegate.convert(
converter: MetaConverter<R>,
default: () -> R
): ReadWriteProperty<Any?, R> = object : ReadWriteProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
this@convert.getValue(thisRef, property)?.let(converter::itemToObject) ?: default()
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
val item = value.let(converter::objectToMetaItem)
this@convert.setValue(thisRef, property, item)
}
}
fun <R> MutableItemDelegate.convert(
reader: (MetaItem<*>?) -> R,
writer: (R) -> MetaItem<*>?
): ReadWriteProperty<Any?, R> = object : ReadWriteProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
this@convert.getValue(thisRef, property).let(reader)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
val item = value?.let(writer)
this@convert.setValue(thisRef, property, item)
}
}

View File

@ -1,9 +1,6 @@
package hep.dataforge.meta.transformations package hep.dataforge.meta.transformations
import hep.dataforge.meta.Meta import hep.dataforge.meta.*
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.get
import hep.dataforge.meta.value
import hep.dataforge.values.* import hep.dataforge.values.*
/** /**
@ -56,6 +53,15 @@ interface MetaConverter<T : Any> {
override fun objectToMetaItem(obj: Boolean): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) override fun objectToMetaItem(obj: Boolean): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
} }
val number = object : MetaConverter<Number> {
override fun itemToObject(item: MetaItem<*>): Number = when (item) {
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
is MetaItem.ValueItem -> item.value
}.number
override fun objectToMetaItem(obj: Number): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
}
val double = object : MetaConverter<Double> { val double = object : MetaConverter<Double> {
override fun itemToObject(item: MetaItem<*>): Double = when (item) { override fun itemToObject(item: MetaItem<*>): Double = when (item) {
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
@ -65,6 +71,15 @@ interface MetaConverter<T : Any> {
override fun objectToMetaItem(obj: Double): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) override fun objectToMetaItem(obj: Double): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
} }
val float = object : MetaConverter<Float> {
override fun itemToObject(item: MetaItem<*>): Float = when (item) {
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
is MetaItem.ValueItem -> item.value
}.float
override fun objectToMetaItem(obj: Float): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
}
val int = object : MetaConverter<Int> { val int = object : MetaConverter<Int> {
override fun itemToObject(item: MetaItem<*>): Int = when (item) { override fun itemToObject(item: MetaItem<*>): Int = when (item) {
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
@ -73,8 +88,37 @@ interface MetaConverter<T : Any> {
override fun objectToMetaItem(obj: Int): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) override fun objectToMetaItem(obj: Int): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
} }
val long = object : MetaConverter<Long> {
override fun itemToObject(item: MetaItem<*>): Long = when (item) {
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
is MetaItem.ValueItem -> item.value
}.long
override fun objectToMetaItem(obj: Long): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
}
inline fun <reified E : Enum<E>> enum(): MetaConverter<E> = object : MetaConverter<E> {
@Suppress("USELESS_CAST")
override fun itemToObject(item: MetaItem<*>): E = item.enum<E>() as? E ?: error("The Item is not a Enum")
override fun objectToMetaItem(obj: E): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
}
fun <T> valueList(writer: (T) -> Value = {Value.of(it)}, reader: (Value) -> T): MetaConverter<List<T>> =
object : MetaConverter<List<T>> {
override fun itemToObject(item: MetaItem<*>): List<T> =
item.value?.list?.map(reader) ?: error("The item is not a value list")
override fun objectToMetaItem(obj: List<T>): MetaItem<*> =
MetaItem.ValueItem(obj.map(writer).asValue())
}
} }
} }
fun <T : Any> MetaConverter<T>.nullableItemToObject(item: MetaItem<*>?): T? = item?.let { itemToObject(it) }
fun <T : Any> MetaConverter<T>.nullableObjectToMetaItem(obj: T?): MetaItem<*>? = obj?.let { objectToMetaItem(it) }
fun <T : Any> MetaConverter<T>.metaToObject(meta: Meta): T = itemToObject(MetaItem.NodeItem(meta)) fun <T : Any> MetaConverter<T>.metaToObject(meta: Meta): T = itemToObject(MetaItem.NodeItem(meta))
fun <T : Any> MetaConverter<T>.valueToObject(value: Value): T = itemToObject(MetaItem.ValueItem(value)) fun <T : Any> MetaConverter<T>.valueToObject(value: Value): T = itemToObject(MetaItem.ValueItem(value))

View File

@ -40,7 +40,7 @@ data class KeepTransformationRule(val selector: (Name) -> Boolean) :
meta.sequence().map { it.first }.filter(selector) meta.sequence().map { it.first }.filter(selector)
override fun <M : MutableMeta<M>> transformItem(name: Name, item: MetaItem<*>?, target: M) { override fun <M : MutableMeta<M>> transformItem(name: Name, item: MetaItem<*>?, target: M) {
if (selector(name)) target[name] = item if (selector(name)) target.setItem(name, item)
} }
} }

View File

@ -19,7 +19,7 @@ class SchemeTest{
assertEquals(10, meta.values().count()) assertEquals(10, meta.values().count())
val bNode = styled.getProperty("b").node val bNode = styled.getItem("b").node
val aNodes = bNode?.getIndexed("a") val aNodes = bNode?.getIndexed("a")