From c280671e613e51cbc96af151f68515ef4e47513c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 5 Jul 2020 12:54:27 +0300 Subject: [PATCH] Yet another refactor of Meta and Configurable delegates (let's hope it is final) --- build.gradle.kts | 2 +- .../kotlin/hep/dataforge/io/MetaFormatTest.kt | 7 +- .../kotlin/hep/dataforge/meta/Configurable.kt | 66 ++++- .../dataforge/meta/ConfigurableDelegate.kt | 244 ------------------ .../kotlin/hep/dataforge/meta/ItemDelegate.kt | 66 +++++ .../kotlin/hep/dataforge/meta/JsonMeta.kt | 23 +- .../kotlin/hep/dataforge/meta/Meta.kt | 29 ++- .../kotlin/hep/dataforge/meta/MetaDelegate.kt | 98 ------- .../hep/dataforge/meta/MutableItemDelegate.kt | 167 ++++++++++++ .../kotlin/hep/dataforge/meta/MutableMeta.kt | 37 ++- .../hep/dataforge/meta/MutableMetaDelegate.kt | 116 --------- .../meta/descriptors/ItemDescriptor.kt | 32 +-- .../hep/dataforge/meta/propertyConverter.kt | 83 ++++++ .../meta/transformations/MetaConverter.kt | 54 +++- .../transformations/MetaTransformation.kt | 2 +- .../kotlin/hep/dataforge/meta/SchemeTest.kt | 2 +- 16 files changed, 490 insertions(+), 538 deletions(-) delete mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigurableDelegate.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt delete mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaDelegate.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt delete mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaDelegate.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/propertyConverter.kt diff --git a/build.gradle.kts b/build.gradle.kts index 2a951fcd..ac454902 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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") diff --git a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt index dcfc2265..a470cf0a 100644 --- a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt +++ b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt @@ -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() }) } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Configurable.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Configurable.kt index a078c37f..794292b9 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Configurable.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Configurable.kt @@ -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.configure(meta: Meta): T = this.apply { config.update(meta) } @DFBuilder inline fun T.configure(action: Config.() -> Unit): T = apply { config.apply(action) } + +/* Node delegates */ + +fun Configurable.config(key: Name? = null): ReadWriteProperty = + config.node(key) + +fun MutableItemProvider.node(key: Name? = null): ReadWriteProperty = item(key).convert( + reader = { it.node }, + writer = { it?.let { MetaItem.NodeItem(it) } } +) + +fun Configurable.spec( + spec: Specification, key: Name? = null +): ReadWriteProperty = object : ReadWriteProperty { + 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 Configurable.spec( + spec: Specification, default: T, key: Name? = null +): ReadWriteProperty = object : ReadWriteProperty { + 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 + } +} + diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigurableDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigurableDelegate.kt deleted file mode 100644 index 4b409a08..00000000 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigurableDelegate.kt +++ /dev/null @@ -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?> { - - 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 Configurable.item( - default: T? = null, - key: Name? = null, - writer: (T) -> MetaItem<*>? = { - MetaItem.of(it) - }, - reader: (MetaItem<*>?) -> T -): ReadWriteProperty = - ConfigurableDelegate( - this, - key, - default?.let { MetaItem.of(it) }).map(reader = reader, writer = writer) - -fun Configurable.value(default: Any? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value } - -fun Configurable.value( - default: T? = null, - key: Name? = null, - writer: (T) -> Value? = { Value.of(it) }, - reader: (Value?) -> T -): ReadWriteProperty = - 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 = - item(default, key).transform { it.value?.string } - -fun Configurable.boolean(default: Boolean? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value?.boolean } - -fun Configurable.number(default: Number? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value?.number } - -/* Number delegates*/ - -fun Configurable.int(default: Int? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value?.int } - -fun Configurable.double(default: Double? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value?.double } - -fun Configurable.long(default: Long? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value?.long } - -fun Configurable.short(default: Short? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value?.short } - -fun Configurable.float(default: Float? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value?.float } - - -@JvmName("safeString") -fun Configurable.string(default: String, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value!!.string } - -@JvmName("safeBoolean") -fun Configurable.boolean(default: Boolean, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value!!.boolean } - -@JvmName("safeNumber") -fun Configurable.number(default: Number, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value!!.number } - -/* Lazy initializers for values */ - -@JvmName("lazyString") -fun Configurable.string(key: Name? = null, default: () -> String): ReadWriteProperty = - lazyItem(key, default).transform { it.value!!.string } - -@JvmName("lazyBoolean") -fun Configurable.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty = - lazyItem(key, default).transform { it.value!!.boolean } - -@JvmName("lazyNumber") -fun Configurable.number(key: Name? = null, default: () -> Number): ReadWriteProperty = - lazyItem(key, default).transform { it.value!!.number } - -/* Safe number delegates*/ - -@JvmName("safeInt") -fun Configurable.int(default: Int, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value!!.int } - -@JvmName("safeDouble") -fun Configurable.double(default: Double, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value!!.double } - -@JvmName("safeLong") -fun Configurable.long(default: Long, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value!!.long } - -@JvmName("safeShort") -fun Configurable.short(default: Short, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value!!.short } - -@JvmName("safeFloat") -fun Configurable.float(default: Float, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value!!.float } - -/** - * Enum delegate - */ -inline fun > Configurable.enum( - default: E, key: Name? = null -): ReadWriteProperty = - item(default, key).transform { item -> item?.string?.let {str-> - @Suppress("USELESS_CAST") - enumValueOf(str) as E - } ?: default } - -/* - * Extra delegates for special cases - */ -fun Configurable.stringList(vararg strings: String, key: Name? = null): ReadWriteProperty> = - item(listOf(*strings), key) { - it?.value?.stringList ?: emptyList() - } - -fun Configurable.stringListOrNull(vararg strings: String, key: Name? = null): ReadWriteProperty?> = - item(listOf(*strings), key) { - it?.value?.stringList - } - -fun Configurable.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty> = - 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 = - 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 = - config.node(default,key) - -fun Configurable.node(key: Name? = null): ReadWriteProperty = item(key).map( - reader = { it.node }, - writer = { it?.let { MetaItem.NodeItem(it) } } -) - -fun Configurable.spec( - spec: Specification, key: Name? = null -): ReadWriteProperty = object : ReadWriteProperty { - 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 Configurable.spec( - spec: Specification, default: T, key: Name? = null -): ReadWriteProperty = object : ReadWriteProperty { - 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 - } -} - diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt new file mode 100644 index 00000000..aa2dec23 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt @@ -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?> { + 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 = + item(key).convert(MetaConverter.value) + +fun ItemProvider.string(key: Name? = null): ReadOnlyProperty = + item(key).convert(MetaConverter.string) + +fun ItemProvider.boolean(key: Name? = null): ReadOnlyProperty = + item(key).convert(MetaConverter.boolean) + +fun ItemProvider.number(key: Name? = null): ReadOnlyProperty = + item(key).convert(MetaConverter.number) + +fun ItemProvider.node(key: Name? = null): ReadOnlyProperty = + item(key).convert(MetaConverter.meta) + +fun ItemProvider.string(default: String, key: Name? = null): ReadOnlyProperty = + item(key).convert(MetaConverter.string) { default } + +fun ItemProvider.boolean(default: Boolean, key: Name? = null): ReadOnlyProperty = + item(key).convert(MetaConverter.boolean) { default } + +fun ItemProvider.number(default: Number, key: Name? = null): ReadOnlyProperty = + item(key).convert(MetaConverter.number) { default } + +inline fun > ItemProvider.enum(default: E, key: Name? = null): ReadOnlyProperty = + item(key).convert(MetaConverter.enum()) { default } + +fun ItemProvider.string(key: Name? = null, default: () -> String): ReadOnlyProperty = + item(key).convert(MetaConverter.string, default) + +fun ItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadOnlyProperty = + item(key).convert(MetaConverter.boolean, default) + +fun ItemProvider.number(key: Name? = null, default: () -> Number): ReadOnlyProperty = + item(key).convert(MetaConverter.number, default) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt index 0059f658..221b040d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt @@ -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 { - //We already checked that all values are primitives - (it as JsonPrimitive).toValue(descriptor as? ValueDescriptor) - } - ) + map { + //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> 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" + } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index f0e3725f..a89d79c9 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -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> + 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) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaDelegate.kt deleted file mode 100644 index 50764a7c..00000000 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaDelegate.kt +++ /dev/null @@ -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?> { - 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( - val delegate: ReadOnlyProperty, - val reader: (T) -> R -) : ReadOnlyProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): R { - return reader(delegate.getValue(thisRef, property)) - } -} - -fun ReadOnlyProperty.map(reader: (T) -> R): DelegateWrapper = - 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 > Meta.enum(default: E, key: Name? = null) = - item(default, key).map { it.enum()!! } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt new file mode 100644 index 00000000..3885b809 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt @@ -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?> { + + 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 = + item(key).convert(MetaConverter.value) + +fun MutableItemProvider.string(key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.string) + +fun MutableItemProvider.boolean(key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.boolean) + +fun MutableItemProvider.number(key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.number) + +fun MutableItemProvider.string(default: String, key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.string) { default } + +fun MutableItemProvider.boolean(default: Boolean, key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.boolean) { default } + +fun MutableItemProvider.number(default: Number, key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.number) { default } + +fun MutableItemProvider.value(key: Name? = null, default: () -> Value): ReadWriteProperty = + item(key).convert(MetaConverter.value, default) + +fun MutableItemProvider.string(key: Name? = null, default: () -> String): ReadWriteProperty = + item(key).convert(MetaConverter.string, default) + +fun MutableItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty = + item(key).convert(MetaConverter.boolean, default) + +fun MutableItemProvider.number(key: Name? = null, default: () -> Number): ReadWriteProperty = + item(key).convert(MetaConverter.number, default) + +inline fun > MutableItemProvider.enum(default: E, key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.enum()) { default } + +inline fun > M.node(key: Name? = null): ReadWriteProperty = + item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItem.NodeItem(it) } }) + + +fun MutableItemProvider.item( + default: T? = null, + key: Name? = null, + writer: (T) -> MetaItem<*>? = { MetaItem.of(it) }, + reader: (MetaItem<*>?) -> T +): ReadWriteProperty = MutableItemDelegate( + this, + key, + default?.let { MetaItem.of(it) } +).convert(reader = reader, writer = writer) + +fun Configurable.value(key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.value) + +fun MutableItemProvider.value( + default: T? = null, + key: Name? = null, + writer: (T) -> Value? = { Value.of(it) }, + reader: (Value?) -> T +): ReadWriteProperty = 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 = + item(key).convert(MetaConverter.int) + +fun MutableItemProvider.double(key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.double) + +fun MutableItemProvider.long(key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.long) + +fun MutableItemProvider.float(key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.float) + + +/* Safe number delegates*/ + +fun MutableItemProvider.int(default: Int, key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.int) { default } + +fun MutableItemProvider.double(default: Double, key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.double) { default } + +fun MutableItemProvider.long(default: Long, key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.long) { default } + +fun MutableItemProvider.float(default: Float, key: Name? = null): ReadWriteProperty = + item(key).convert(MetaConverter.float) { default } + + +/* + * Extra delegates for special cases + */ +fun MutableItemProvider.stringList(vararg strings: String, key: Name? = null): ReadWriteProperty> = + item(listOf(*strings), key) { + it?.value?.stringList ?: emptyList() + } + +fun MutableItemProvider.stringListOrNull( + vararg strings: String, + key: Name? = null +): ReadWriteProperty?> = + item(listOf(*strings), key) { + it?.value?.stringList + } + +fun MutableItemProvider.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty> = + 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 = + item(doubleArrayOf(*doubles), key) { + (it.value as? DoubleArrayValue)?.value + ?: it?.value?.list?.map { value -> value.number.toDouble() }?.toDoubleArray() + ?: doubleArrayOf() + } + +fun MutableItemProvider.listValue( + key: Name? = null, + writer: (T) -> Value = { Value.of(it) }, + reader: (Value) -> T +): ReadWriteProperty?> = item(key).convert(MetaConverter.valueList(writer, reader)) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index 91e74ce2..03245b00 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -3,9 +3,12 @@ package hep.dataforge.meta import hep.dataforge.names.* import hep.dataforge.values.Value -interface MutableMeta> : MetaNode { +interface MutableItemProvider : ItemProvider { + fun setItem(name: Name, item: MetaItem<*>?) +} + +interface MutableMeta> : MetaNode, MutableItemProvider { override val items: Map> - 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> : AbstractMetaNode(), */ 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> : AbstractMetaNode(), 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> : AbstractMetaNode(), @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.update(meta: Meta) { fun MutableMeta<*>.setIndexedItems( name: Name, items: Iterable>, - 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, - 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): Unit = setIndexed(name, metas) @@ -178,7 +175,7 @@ fun > M.append(name: String, value: Any?) = append(name.toNam */ @DFExperimental fun > 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 -> existingItem.node else -> error("Can't edit value meta item") diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaDelegate.kt deleted file mode 100644 index 78589fbd..00000000 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaDelegate.kt +++ /dev/null @@ -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>( - override val owner: M, - key: Name? = null, - default: MetaItem<*>? = null -) : MetaDelegate(owner, key, default), ReadWriteProperty?> { - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) { - val name = key ?: property.name.asName() - owner.setItem(name, value) - } -} - -class LazyMutableMetaDelegate>( - owner: M, - key: Name? = null, - defaultProvider: () -> MetaItem<*>? = { null } -) : MutableMetaDelegate(owner, key) { - override val default by lazy(defaultProvider) -} - -class ReadWriteDelegateWrapper( - val delegate: ReadWriteProperty, - val reader: (T) -> R, - val writer: (R) -> T -) : ReadWriteProperty { - 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 ReadWriteProperty.map(reader: (T) -> R, writer: (R) -> T): ReadWriteDelegateWrapper = - ReadWriteDelegateWrapper(this, reader, writer) - -fun ReadWriteProperty?>.transform(reader: (MetaItem<*>?) -> R): ReadWriteProperty = - map(reader = reader, writer = { MetaItem.of(it) }) - -fun ReadWriteProperty.transform(reader: (Value?) -> R): ReadWriteDelegateWrapper = - map(reader = reader, writer = { Value.of(it) }) - -fun ReadWriteProperty.notNull(default: () -> R): ReadWriteProperty { - return ReadWriteDelegateWrapper(this, - reader = { it ?: default() }, - writer = { it } - ) -} - -fun > M.item(default: Any? = null, key: Name? = null): MutableMetaDelegate = - MutableMetaDelegate(this, key, default?.let { MetaItem.of(it) }) - -fun > M.lazyItem(key: Name? = null, defaultProvider: () -> Any?): LazyMutableMetaDelegate = - LazyMutableMetaDelegate(this, key) { defaultProvider()?.let { MetaItem.of(it) } } - -//Read-write delegates - -/** - * A property delegate that uses custom key - */ -fun > M.value(default: Value? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.value } - -fun > M.string(default: String? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.string } - -fun > M.boolean(default: Boolean? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.boolean } - -fun > M.number(default: Number? = null, key: Name? = null): ReadWriteProperty = - item(default, key).transform { it.number } - -inline fun > M.node(default: M? = null, key: Name? = null) = - item(default, key = key).transform { it.node as? M } - -@JvmName("safeString") -fun > M.string(default: String, key: Name? = null) = - item(default, key).transform { it.string!! } - -@JvmName("safeBoolean") -fun > M.boolean(default: Boolean, key: Name? = null) = - item(default, key).transform { it.boolean!! } - -@JvmName("safeNumber") -fun > M.number(default: Number, key: Name? = null) = - item(default, key).transform { it.number!! } - -@JvmName("lazyValue") -fun > M.string(key: Name? = null, default: () -> Value) = - lazyItem(key, default).transform { it.value!! } - -@JvmName("lazyString") -fun > M.string(key: Name? = null, default: () -> String) = - lazyItem(key, default).transform { it.string!! } - -@JvmName("safeBoolean") -fun > M.boolean(key: Name? = null, default: () -> Boolean) = - lazyItem(key, default).transform { it.boolean!! } - -@JvmName("safeNumber") -fun > M.number(key: Name? = null, default: () -> Number) = - lazyItem(key, default).transform { it.number!! } - - -inline fun , reified E : Enum> M.enum(default: E, key: Name? = null) = - item(default, key).transform { it.enum()!! } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt index 2888ac59..a277e18d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt @@ -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 by config.item().transform { - it?.value?.list?.map { v -> ValueType.valueOf(v.string) } ?: emptyList() - } + var type: List? 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 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 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 diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/propertyConverter.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/propertyConverter.kt new file mode 100644 index 00000000..ebe55218 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/propertyConverter.kt @@ -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 ItemDelegate.convert( + converter: MetaConverter +): ReadOnlyProperty = object : ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): R? = + this@convert.getValue(thisRef, property)?.let(converter::itemToObject) +} + +/* + * + */ +fun ItemDelegate.convert( + converter: MetaConverter, + default: () -> R +): ReadOnlyProperty = object : ReadOnlyProperty { + 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 ItemDelegate.convert( + reader: (MetaItem<*>?) -> R +): ReadOnlyProperty = object : ReadOnlyProperty { + 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 MutableItemDelegate.convert( + converter: MetaConverter +): ReadWriteProperty = object : ReadWriteProperty { + + 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 MutableItemDelegate.convert( + converter: MetaConverter, + default: () -> R +): ReadWriteProperty = object : ReadWriteProperty { + + 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 MutableItemDelegate.convert( + reader: (MetaItem<*>?) -> R, + writer: (R) -> MetaItem<*>? +): ReadWriteProperty = object : ReadWriteProperty { + + 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) + } +} diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt index bd967a85..931e7cf3 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt @@ -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 { companion object { - val item = object :MetaConverter>{ + val item = object : MetaConverter> { override fun itemToObject(item: MetaItem<*>): MetaItem<*> = item override fun objectToMetaItem(obj: MetaItem<*>): MetaItem<*> = obj } @@ -56,6 +53,15 @@ interface MetaConverter { override fun objectToMetaItem(obj: Boolean): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) } + val number = object : MetaConverter { + 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 { 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 { override fun objectToMetaItem(obj: Double): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) } + val float = object : MetaConverter { + 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 { 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 { override fun objectToMetaItem(obj: Int): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) } + + val long = object : MetaConverter { + 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 > enum(): MetaConverter = object : MetaConverter { + @Suppress("USELESS_CAST") + override fun itemToObject(item: MetaItem<*>): E = item.enum() as? E ?: error("The Item is not a Enum") + + override fun objectToMetaItem(obj: E): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) + } + + fun valueList(writer: (T) -> Value = {Value.of(it)}, reader: (Value) -> T): MetaConverter> = + object : MetaConverter> { + override fun itemToObject(item: MetaItem<*>): List = + item.value?.list?.map(reader) ?: error("The item is not a value list") + + override fun objectToMetaItem(obj: List): MetaItem<*> = + MetaItem.ValueItem(obj.map(writer).asValue()) + } + } } +fun MetaConverter.nullableItemToObject(item: MetaItem<*>?): T? = item?.let { itemToObject(it) } +fun MetaConverter.nullableObjectToMetaItem(obj: T?): MetaItem<*>? = obj?.let { objectToMetaItem(it) } + fun MetaConverter.metaToObject(meta: Meta): T = itemToObject(MetaItem.NodeItem(meta)) fun MetaConverter.valueToObject(value: Value): T = itemToObject(MetaItem.ValueItem(value)) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt index f02545fe..38984520 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt @@ -40,7 +40,7 @@ data class KeepTransformationRule(val selector: (Name) -> Boolean) : meta.sequence().map { it.first }.filter(selector) override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { - if (selector(name)) target[name] = item + if (selector(name)) target.setItem(name, item) } } diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt index 939b8bb2..1f7d7e97 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt @@ -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")