Yet another refactor of Meta and Configurable delegates (let's hope it is final)
This commit is contained in:
parent
faeb737672
commit
c280671e61
@ -7,7 +7,7 @@ plugins {
|
||||
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 githubProject by extra("dataforge-core")
|
||||
|
@ -1,6 +1,7 @@
|
||||
package hep.dataforge.io
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
|
||||
import kotlinx.io.asBinary
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.json
|
||||
@ -74,9 +75,9 @@ class MetaFormatTest {
|
||||
}
|
||||
val meta = json.toMetaItem().node!!
|
||||
|
||||
assertEquals(true, meta["@value[0].@value[1].d"].boolean)
|
||||
assertEquals("value", meta["@value[1]"].string)
|
||||
assertEquals(listOf(1.0, 2.0, 3.0), meta["@value[2"].value?.list?.map { it.number.toDouble() })
|
||||
assertEquals(true, meta["$JSON_ARRAY_KEY[0].$JSON_ARRAY_KEY[1].d"].boolean)
|
||||
assertEquals("value", meta["$JSON_ARRAY_KEY[1]"].string)
|
||||
assertEquals(listOf(1.0, 2.0, 3.0), meta["$JSON_ARRAY_KEY[2"].value?.list?.map { it.number.toDouble() })
|
||||
}
|
||||
|
||||
}
|
@ -2,8 +2,11 @@ package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.meta.descriptors.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.values.Value
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* 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],
|
||||
* this information should be provided externally.
|
||||
*/
|
||||
interface Configurable : Described {
|
||||
interface Configurable : Described, MutableItemProvider {
|
||||
/**
|
||||
* Backing config
|
||||
*/
|
||||
@ -35,15 +38,15 @@ interface Configurable : Described {
|
||||
/**
|
||||
* Get a property with default
|
||||
*/
|
||||
fun getProperty(name: Name): MetaItem<*>? =
|
||||
override fun getItem(name: Name): MetaItem<*>? =
|
||||
config[name] ?: getDefaultItem(name) ?: descriptor?.get(name)?.defaultItem()
|
||||
|
||||
/**
|
||||
* Set a configurable property
|
||||
*/
|
||||
fun setProperty(name: Name, item: MetaItem<*>?) {
|
||||
override fun setItem(name: Name, item: MetaItem<*>?) {
|
||||
if (validateItem(name, item)) {
|
||||
config[name] = item
|
||||
config.setItem(name, item)
|
||||
} else {
|
||||
error("Validation failed for property $name with value $item")
|
||||
}
|
||||
@ -54,22 +57,59 @@ interface Configurable : Described {
|
||||
* Reset the property to its default value
|
||||
*/
|
||||
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.setProperty(name: Name, meta: Meta?) = setProperty(name, meta?.let { MetaItem.NodeItem(meta) })
|
||||
fun Configurable.setItem(name: Name, value: Value?) = setItem(name, value?.let { MetaItem.ValueItem(value) })
|
||||
fun Configurable.setItem(name: Name, meta: Meta?) = setItem(name, meta?.let { MetaItem.NodeItem(meta) })
|
||||
|
||||
fun Configurable.setProperty(key: String, item: MetaItem<*>?) {
|
||||
setProperty(key.toName(), item)
|
||||
}
|
||||
fun Configurable.setItem(key: String, item: MetaItem<*>?) = setItem(key.toName(), item)
|
||||
|
||||
fun Configurable.setProperty(key: String, value: Value?) = setProperty(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, value: Value?) = setItem(key, value?.let { MetaItem.ValueItem(value) })
|
||||
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) }
|
||||
|
||||
@DFBuilder
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
@ -2,6 +2,7 @@
|
||||
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
|
||||
import hep.dataforge.meta.descriptors.ItemDescriptor
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.meta.descriptors.ValueDescriptor
|
||||
@ -113,17 +114,15 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
|
||||
val value = if (isEmpty()) {
|
||||
Null
|
||||
} else {
|
||||
ListValue(
|
||||
map<JsonElement, Value> {
|
||||
//We already checked that all values are primitives
|
||||
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
|
||||
}
|
||||
)
|
||||
map<JsonElement, Value> {
|
||||
//We already checked that all values are primitives
|
||||
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
|
||||
}.asValue()
|
||||
}
|
||||
MetaItem.ValueItem(value)
|
||||
} else {
|
||||
//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 ->
|
||||
val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
|
||||
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)
|
||||
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)
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
@ -85,6 +85,10 @@ interface MetaRepr {
|
||||
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:
|
||||
* * [MetaItem.ValueItem] (leaf)
|
||||
@ -92,12 +96,23 @@ interface MetaRepr {
|
||||
*
|
||||
* * 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
|
||||
*/
|
||||
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 equals(other: Any?): Boolean
|
||||
@ -127,17 +142,7 @@ interface Meta : MetaRepr {
|
||||
*
|
||||
* If [name] is empty return current [Meta] as a [NodeItem]
|
||||
*/
|
||||
operator fun Meta?.get(name: Name): MetaItem<*>? {
|
||||
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(name: Name): MetaItem<*>? = this?.getItem(name)
|
||||
|
||||
operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token)
|
||||
|
||||
|
@ -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>()!! }
|
@ -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))
|
@ -3,9 +3,12 @@ package hep.dataforge.meta
|
||||
import hep.dataforge.names.*
|
||||
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>>
|
||||
operator fun set(name: Name, item: MetaItem<*>?)
|
||||
// fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
|
||||
// fun removeListener(owner: Any? = null)
|
||||
}
|
||||
@ -49,7 +52,7 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
|
||||
*/
|
||||
internal abstract fun empty(): M
|
||||
|
||||
override operator fun set(name: Name, item: MetaItem<*>?) {
|
||||
override fun setItem(name: Name, item: MetaItem<*>?) {
|
||||
when (name.length) {
|
||||
0 -> error("Can't setValue meta item for empty name")
|
||||
1 -> {
|
||||
@ -63,7 +66,7 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
|
||||
if (items[token] == null) {
|
||||
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")
|
||||
inline fun MutableMeta<*>.remove(name: Name) = set(name, null)
|
||||
inline fun MutableMeta<*>.remove(name: Name) = setItem(name, null)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
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<*>.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<*>.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)
|
||||
|
||||
@ -135,23 +132,23 @@ fun <M : MutableMeta<M>> M.update(meta: Meta) {
|
||||
fun MutableMeta<*>.setIndexedItems(
|
||||
name: Name,
|
||||
items: Iterable<MetaItem<*>>,
|
||||
indexFactory: (MetaItem<*>, index: Int) -> String = {_, index-> index.toString() }
|
||||
indexFactory: (MetaItem<*>, index: Int) -> String = { _, index -> index.toString() }
|
||||
) {
|
||||
val tokens = name.tokens.toMutableList()
|
||||
val last = tokens.last()
|
||||
items.forEachIndexed { index, meta ->
|
||||
val indexedToken = NameToken(last.body, last.index + indexFactory(meta, index))
|
||||
tokens[tokens.lastIndex] = indexedToken
|
||||
set(Name(tokens), meta)
|
||||
setItem(Name(tokens), meta)
|
||||
}
|
||||
}
|
||||
|
||||
fun MutableMeta<*>.setIndexed(
|
||||
name: Name,
|
||||
metas: Iterable<Meta>,
|
||||
indexFactory: (Meta, index: Int) -> String = { _, index-> index.toString() }
|
||||
indexFactory: (Meta, index: Int) -> String = { _, index -> index.toString() }
|
||||
) {
|
||||
setIndexedItems(name, metas.map { MetaItem.NodeItem(it) }){item, index ->indexFactory(item.node!!, index)}
|
||||
setIndexedItems(name, metas.map { MetaItem.NodeItem(it) }) { item, index -> indexFactory(item.node!!, index) }
|
||||
}
|
||||
|
||||
operator fun MutableMeta<*>.set(name: Name, metas: Iterable<Meta>): Unit = setIndexed(name, metas)
|
||||
@ -178,7 +175,7 @@ fun <M : MutableMeta<M>> M.append(name: String, value: Any?) = append(name.toNam
|
||||
*/
|
||||
@DFExperimental
|
||||
fun <M : AbstractMutableMeta<M>> M.edit(name: Name, builder: M.() -> Unit) {
|
||||
val item = when(val existingItem = get(name)){
|
||||
val item = when (val existingItem = get(name)) {
|
||||
null -> empty().also { set(name, it) }
|
||||
is MetaItem.NodeItem<M> -> existingItem.node
|
||||
else -> error("Can't edit value meta item")
|
||||
|
@ -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>()!! }
|
@ -2,10 +2,7 @@ package hep.dataforge.meta.descriptors
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.*
|
||||
import hep.dataforge.values.False
|
||||
import hep.dataforge.values.True
|
||||
import hep.dataforge.values.Value
|
||||
import hep.dataforge.values.ValueType
|
||||
import hep.dataforge.values.*
|
||||
|
||||
@DFBuilder
|
||||
sealed class ItemDescriptor(val config: Config) {
|
||||
@ -241,9 +238,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
var type: List<ValueType> by config.item().transform {
|
||||
it?.value?.list?.map { v -> ValueType.valueOf(v.string) } ?: emptyList()
|
||||
}
|
||||
var type: List<ValueType>? by config.listValue { ValueType.valueOf(it.string) }
|
||||
|
||||
fun type(vararg t: ValueType) {
|
||||
this.type = listOf(*t)
|
||||
@ -257,9 +252,8 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
* @return
|
||||
*/
|
||||
fun isAllowedValue(value: Value): Boolean {
|
||||
return (type.isEmpty() || type.contains(ValueType.STRING) || type.contains(value.type)) && (allowedValues.isEmpty() || allowedValues.contains(
|
||||
value
|
||||
))
|
||||
return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
|
||||
&& (allowedValues.isEmpty() || allowedValues.contains(value))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,13 +262,19 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
var allowedValues: List<Value> by config.value().transform {
|
||||
when {
|
||||
it?.list != null -> it.list
|
||||
type.size == 1 && type[0] === ValueType.BOOLEAN -> listOf(True, False)
|
||||
else -> emptyList()
|
||||
var allowedValues: List<Value> by config.item().convert(
|
||||
reader = {
|
||||
val value = it.value
|
||||
when {
|
||||
value?.list != null -> value.list
|
||||
type?.let { type -> type.size == 1 && type[0] === ValueType.BOOLEAN} ?: false -> listOf(True, False)
|
||||
else -> emptyList()
|
||||
}
|
||||
},
|
||||
writer = {
|
||||
MetaItem.ValueItem(it.asValue())
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Allow given list of value and forbid others
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,9 +1,6 @@
|
||||
package hep.dataforge.meta.transformations
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.value
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.values.*
|
||||
|
||||
/**
|
||||
@ -15,7 +12,7 @@ interface MetaConverter<T : Any> {
|
||||
|
||||
companion object {
|
||||
|
||||
val item = object :MetaConverter<MetaItem<*>>{
|
||||
val item = object : MetaConverter<MetaItem<*>> {
|
||||
override fun itemToObject(item: MetaItem<*>): MetaItem<*> = item
|
||||
override fun objectToMetaItem(obj: MetaItem<*>): MetaItem<*> = obj
|
||||
}
|
||||
@ -56,6 +53,15 @@ interface MetaConverter<T : Any> {
|
||||
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> {
|
||||
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")
|
||||
@ -65,6 +71,15 @@ interface MetaConverter<T : Any> {
|
||||
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> {
|
||||
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")
|
||||
@ -73,8 +88,37 @@ interface MetaConverter<T : Any> {
|
||||
|
||||
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>.valueToObject(value: Value): T = itemToObject(MetaItem.ValueItem(value))
|
||||
|
@ -40,7 +40,7 @@ data class KeepTransformationRule(val selector: (Name) -> Boolean) :
|
||||
meta.sequence().map { it.first }.filter(selector)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ class SchemeTest{
|
||||
|
||||
assertEquals(10, meta.values().count())
|
||||
|
||||
val bNode = styled.getProperty("b").node
|
||||
val bNode = styled.getItem("b").node
|
||||
|
||||
val aNodes = bNode?.getIndexed("a")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user