diff --git a/README.md b/README.md
index a6e6ed99..4bd6df9f 100644
--- a/README.md
+++ b/README.md
@@ -63,17 +63,17 @@ In this section we will try to cover DataForge main ideas in the form of questio
* **Context encapsulation**. Every DataForge task is executed in some context. The context isolates environment for the task and also works as dependency injection base and specifies interaction of the task with the external world.
-
+
-**Q:** Declarations and metadata are good, but I want my scripts back!
+
@@ -88,12 +88,12 @@ In this section we will try to cover DataForge main ideas in the form of questio
**Q:** How does DataForge compare to cluster computation frameworks like Hadoop or Spark?
**A:** Again, it is not the purpose of DataForge to replace cluster software. DataForge has some internal parallelism mechanics and implementations, but they are most certainly worse then specially developed programs. Still, DataForge is not fixed on one single implementation. Your favourite parallel processing tool could be still used as a back-end for the DataForge. With full benefit of configuration tools, integrations and no performance overhead.
-
+
diff --git a/build.gradle.kts b/build.gradle.kts
index dd159c0b..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-4")
+val dataforgeVersion by extra("0.1.8")
val bintrayRepo by extra("dataforge")
val githubProject by extra("dataforge-core")
diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt
index c634d696..9b819a97 100644
--- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt
+++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt
@@ -16,7 +16,6 @@ import kotlin.reflect.KClass
@ExperimentalUnsignedTypes
data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
-
interface EnvelopeFormat : IOFormat {
val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat
@@ -33,6 +32,10 @@ interface EnvelopeFormat : IOFormat {
override fun Output.writeObject(obj: Envelope): Unit = writeEnvelope(obj)
}
+fun EnvelopeFormat.readPartial(input: Input) = input.readPartial()
+
+fun EnvelopeFormat.read(input: Input) = input.readObject()
+
@Type(ENVELOPE_FORMAT_TYPE)
interface EnvelopeFormatFactory : IOFormatFactory, EnvelopeFormat {
override val name: Name get() = "envelope".asName()
diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaggedEnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaggedEnvelopeFormat.kt
index 17fbc589..ebcca243 100644
--- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaggedEnvelopeFormat.kt
+++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/TaggedEnvelopeFormat.kt
@@ -166,6 +166,8 @@ class TaggedEnvelopeFormat(
override fun Input.readObject(): Envelope =
default.run { readObject() }
+
+
}
}
\ No newline at end of file
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 a94b3667..00000000
--- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ConfigurableDelegate.kt
+++ /dev/null
@@ -1,239 +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.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 48ef6c4e..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,11 +2,12 @@
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
import hep.dataforge.names.NameToken
-import hep.dataforge.names.toName
+import hep.dataforge.names.withIndex
import hep.dataforge.values.*
import kotlinx.serialization.json.*
@@ -35,29 +36,29 @@ private fun String.toJsonKey(descriptor: ItemDescriptor?) = descriptor?.attribut
/**
* Convert given [Meta] to [JsonObject]. Primitives and nodes are copied as is, same name siblings are treated as json arrays
*/
-fun Meta.toJson(descriptor: NodeDescriptor? = null, index: String? = null): JsonObject {
+private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String?): JsonObject {
val elementMap = HashMap()
- fun MetaItem<*>.toJsonElement(itemDescriptor: ItemDescriptor?, index: String? = null): JsonElement = when (this) {
+ fun MetaItem<*>.toJsonElement(itemDescriptor: ItemDescriptor?, index: String?): JsonElement = when (this) {
is MetaItem.ValueItem -> {
value.toJson(itemDescriptor as? ValueDescriptor)
}
is MetaItem.NodeItem -> {
- node.toJson(itemDescriptor as? NodeDescriptor, index)
+ node.toJsonWithIndex(itemDescriptor as? NodeDescriptor, index)
}
}
fun addElement(key: String) {
val itemDescriptor = descriptor?.items?.get(key)
val jsonKey = key.toJsonKey(itemDescriptor)
- val items = getIndexed(key)
+ val items: Map> = getIndexed(key)
when (items.size) {
0 -> {
//do nothing
}
1 -> {
- elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor)
+ elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor, null)
}
else -> {
val array = jsonArray {
@@ -73,38 +74,29 @@ fun Meta.toJson(descriptor: NodeDescriptor? = null, index: String? = null): Json
((descriptor?.items?.keys ?: emptySet()) + items.keys.map { it.body }).forEach(::addElement)
- if (index != null) {
- elementMap["@index"] = JsonPrimitive(index)
+ if (indexValue != null) {
+ val indexKey = descriptor?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
+ elementMap[indexKey] = JsonPrimitive(indexValue)
}
return JsonObject(elementMap)
-
-// // use descriptor keys in the order they are declared
-// val keys = (descriptor?.items?.keys ?: emptySet()) + this.items.keys.map { it.body }
-//
-// //TODO search for same name siblings and arrange them into arrays
-// val map = this.items.entries.associate { (name, item) ->
-// val itemDescriptor = descriptor?.items?.get(name.body)
-// val key = name.toJsonKey(itemDescriptor)
-// val value = when (item) {
-// is MetaItem.ValueItem -> {
-// item.value.toJson(itemDescriptor as? ValueDescriptor)
-// }
-// is MetaItem.NodeItem -> {
-// item.node.toJson(itemDescriptor as? NodeDescriptor)
-// }
-// }
-// key to value
-// }
-// return JsonObject(map)
}
-fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): Meta = JsonMeta(this, descriptor)
+fun Meta.toJson(descriptor: NodeDescriptor? = null): JsonObject = toJsonWithIndex(descriptor, null)
+
+fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): JsonMeta = JsonMeta(this, descriptor)
fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
return when (this) {
JsonNull -> Null
- else -> this.content.parseValue() // Optimize number and boolean parsing
+ is JsonLiteral -> {
+ when (body) {
+ true -> True
+ false -> False
+ is Number -> NumberValue(body as Number)
+ else -> StringValue(content)
+ }
+ }
}
}
@@ -122,18 +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 {
- json {
- "@value" to this@toMetaItem
- }.toMetaItem(descriptor)
+ //We can't return multiple items therefore we create top level node
+ json { JSON_ARRAY_KEY to this@toMetaItem }.toMetaItem(descriptor)
}
}
}
@@ -143,44 +132,52 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem>.set(key: String, value: JsonElement): Unit {
- val itemDescriptor = descriptor?.items?.get(key)
- when (value) {
- is JsonPrimitive -> {
- this[key] =
- MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) as MetaItem
- }
- is JsonObject -> {
- this[key] = MetaItem.NodeItem(
- JsonMeta(
- value,
- itemDescriptor as? NodeDescriptor
- )
- )
- }
- is JsonArray -> {
- when {
- value.all { it is JsonPrimitive } -> {
- val listValue = ListValue(
- value.map {
- //We already checked that all values are primitives
- (it as JsonPrimitive).toValue(itemDescriptor as? ValueDescriptor)
- }
+ private fun buildItems(): Map> {
+ val map = LinkedHashMap>()
+
+ json.forEach { (jsonKey, value) ->
+ val key = NameToken(jsonKey)
+ val itemDescriptor = descriptor?.items?.get(jsonKey)
+ when (value) {
+ is JsonPrimitive -> {
+ map[key] = MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor))
+ }
+ is JsonObject -> {
+ map[key] = MetaItem.NodeItem(
+ JsonMeta(
+ value,
+ itemDescriptor as? NodeDescriptor
)
- this[key] = MetaItem.ValueItem(listValue) as MetaItem
- }
- else -> value.forEachIndexed { index, jsonElement ->
- this["$key[$index]"] = jsonElement.toMetaItem(itemDescriptor)
- }
+ )
+ }
+ is JsonArray -> if (value.all { it is JsonPrimitive }) {
+ val listValue = ListValue(
+ value.map {
+ //We already checked that all values are primitives
+ (it as JsonPrimitive).toValue(itemDescriptor as? ValueDescriptor)
+ }
+ )
+ map[key] = MetaItem.ValueItem(listValue)
+ } 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.
+
+ val token = key.withIndex(indexValue)
+ map[token] = jsonElement.toMetaItem(itemDescriptor)
}
}
}
+ return map
}
- override val items: Map> by lazy {
- val map = LinkedHashMap>()
- json.forEach { (key, value) -> map[key] = value }
- map.mapKeys { it.key.toName().first()!! }
+ 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 16fcf40d..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 = { it.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 + meta.indexFactory(index))
+ 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: MetaItem<*>.(index: Int) -> String = { it.toString() }
+ indexFactory: (Meta, index: Int) -> String = { _, index -> index.toString() }
) {
- setIndexedItems(name, metas.map { MetaItem.NodeItem(it) }, indexFactory)
+ 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 3230afbe..00000000
--- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMetaDelegate.kt
+++ /dev/null
@@ -1,120 +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) })
-
-/**
- * A delegate that throws
- */
-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()!! }
\ No newline at end of file
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 7f748ff2..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) {
@@ -95,6 +92,11 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
*/
var default by config.node()
+ /**
+ * An index field by which this node is identified in case of same name siblings construct
+ */
+ var indexKey by config.string(DEFAULT_INDEX_KEY)
+
val items: Map
get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) ->
val node = item.node ?: error("Node descriptor must be a node")
@@ -182,6 +184,8 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
val ITEM_KEY = "item".asName()
val IS_NODE_KEY = "@isNode".asName()
+ const val DEFAULT_INDEX_KEY = "@index"
+
inline operator fun invoke(block: NodeDescriptor.() -> Unit) = NodeDescriptor().apply(block)
//TODO infer descriptor from spec
@@ -234,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)
@@ -250,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))
}
/**
@@ -261,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/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt
index 141af13c..b3f8231a 100644
--- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt
+++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt
@@ -113,6 +113,8 @@ data class NameToken(val body: String, val index: String = "") {
}
}
+fun NameToken.withIndex(newIndex: String) = NameToken(body, newIndex)
+
/**
* Convert a [String] to name parsing it and extracting name tokens and index syntax.
* This operation is rather heavy so it should be used with care in high performance code.
diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/JsonMetaTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/JsonMetaTest.kt
index 127649da..9086e156 100644
--- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/JsonMetaTest.kt
+++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/JsonMetaTest.kt
@@ -1,5 +1,6 @@
package hep.dataforge.meta
+import hep.dataforge.meta.descriptors.NodeDescriptor
import kotlinx.serialization.json.int
import kotlinx.serialization.json.json
import kotlinx.serialization.json.jsonArray
@@ -17,23 +18,34 @@ class JsonMetaTest {
}
"nodeArray" to jsonArray {
+json {
- "index" to 1
+ "index" to "1"
+ "value" to 2
}
+json {
- "index" to 2
+ "index" to "2"
+ "value" to 3
}
+json {
- "index" to 3
+ "index" to "3"
+ "value" to 4
}
}
}
+ val descriptor = NodeDescriptor{
+ node("nodeArray"){
+ indexKey = "index"
+ }
+ }
+
@Test
fun jsonMetaConversion() {
- val meta = json.toMeta()
- val reconstructed = meta.toJson()
println(json)
+ val meta = json.toMeta(descriptor)
+ //println(meta)
+ val reconstructed = meta.toJson(descriptor)
println(reconstructed)
assertEquals(2, reconstructed["nodeArray"]?.jsonArray?.get(1)?.jsonObject?.get("index")?.int)
+ assertEquals(json,reconstructed)
}
}
\ No newline at end of file
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")
diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnTable.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnTable.kt
index dbb90cf2..db80b6fa 100644
--- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnTable.kt
+++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnTable.kt
@@ -1,28 +1,23 @@
package hep.dataforge.tables
-import kotlin.reflect.KClass
-
/**
- * @param C bottom type for all columns in the table
+ * @param T bottom type for all columns in the table
*/
-class ColumnTable(override val columns: Collection>) : Table {
+class ColumnTable(override val columns: Collection>) : Table {
private val rowsNum = columns.first().size
init {
require(columns.all { it.size == rowsNum }) { "All columns must be of the same size" }
}
- override val rows: List>
+ override val rows: List>
get() = (0 until rowsNum).map { VirtualRow(this, it) }
- override fun getValue(row: Int, column: String, type: KClass): T? {
- val value = columns[column]?.get(row)
- return type.cast(value)
- }
+ override fun getValue(row: Int, column: String): T? = columns[column]?.get(row)
}
-internal class VirtualRow(val table: Table, val index: Int) : Row