Merge pull request #52 from mipt-npm/dev
0.1.8
This commit is contained in:
commit
d951668911
12
README.md
12
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.
|
* **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.
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
<!--<hr>
|
||||||
|
|
||||||
**Q:** OK, but now I want to work directly with my measuring devices. How can I do that?
|
**Q:** OK, but now I want to work directly with my measuring devices. How can I do that?
|
||||||
|
|
||||||
**A:** The [dataforge-control](${site.url}/docs.html#control) module provides interfaces to interact with the hardware. Out of the box it supports safe communication with TCP/IP or COM/tty based devices. Specific device declaration could be done via additional modules. It is also possible to maintain data storage with [datforge-storage](${site.url}/docs.htm#storage) module.
|
**A:** The [dataforge-control](${site.url}/docs.html#control) module provides interfaces to interact with the hardware. Out of the box it supports safe communication with TCP/IP or COM/tty based devices. Specific device declaration could be done via additional modules. It is also possible to maintain data storage with [datforge-storage](${site.url}/docs.htm#storage) module.-->
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
**Q:** Declarations and metadata are good, but I want my scripts back!
|
<!--**Q:** Declarations and metadata are good, but I want my scripts back!
|
||||||
|
|
||||||
**A:** We can do that. [GRIND](${site.url}/docs.html#grind) provides a shell-like environment called GrindShell. It allows to run imperative scripts with full access to all of the DataForge functionality. Grind scripts are basically context-encapsulated. Also there are convenient feature wrappers called helpers that could be loaded into the shell when new features modules are added.
|
**A:** We can do that. [GRIND](${site.url}/docs.html#grind) provides a shell-like environment called GrindShell. It allows to run imperative scripts with full access to all of the DataForge functionality. Grind scripts are basically context-encapsulated. Also there are convenient feature wrappers called helpers that could be loaded into the shell when new features modules are added.-->
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
@ -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?
|
**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.
|
**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.
|
||||||
|
<!--
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
**Q:** Is it possible to use DataForge in notebook mode?
|
**Q:** Is it possible to use DataForge in notebook mode?
|
||||||
|
|
||||||
**A:** Yes, it is. DataForge can be used as is from [beaker/beakerx](http://beakernotebook.com/) groovy kernel with minor additional adjustments. It is planned to provide separate DataForge kernel to `beakerx` which will automatically call a specific GRIND shell.
|
**A:** Yes, it is. DataForge can be used as is from [beaker/beakerx](http://beakernotebook.com/) groovy kernel with minor additional adjustments. It is planned to provide separate DataForge kernel to `beakerx` which will automatically call a specific GRIND shell.-->
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ plugins {
|
|||||||
id("org.jetbrains.dokka") version "0.10.1"
|
id("org.jetbrains.dokka") version "0.10.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion by extra("0.1.8-dev-4")
|
val dataforgeVersion by extra("0.1.8")
|
||||||
|
|
||||||
val bintrayRepo by extra("dataforge")
|
val bintrayRepo by extra("dataforge")
|
||||||
val githubProject by extra("dataforge-core")
|
val githubProject by extra("dataforge-core")
|
||||||
|
@ -16,7 +16,6 @@ import kotlin.reflect.KClass
|
|||||||
@ExperimentalUnsignedTypes
|
@ExperimentalUnsignedTypes
|
||||||
data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
|
data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
|
||||||
|
|
||||||
|
|
||||||
interface EnvelopeFormat : IOFormat<Envelope> {
|
interface EnvelopeFormat : IOFormat<Envelope> {
|
||||||
val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat
|
val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat
|
||||||
|
|
||||||
@ -33,6 +32,10 @@ interface EnvelopeFormat : IOFormat<Envelope> {
|
|||||||
override fun Output.writeObject(obj: Envelope): Unit = writeEnvelope(obj)
|
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)
|
@Type(ENVELOPE_FORMAT_TYPE)
|
||||||
interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat {
|
interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat {
|
||||||
override val name: Name get() = "envelope".asName()
|
override val name: Name get() = "envelope".asName()
|
||||||
|
@ -166,6 +166,8 @@ class TaggedEnvelopeFormat(
|
|||||||
|
|
||||||
override fun Input.readObject(): Envelope =
|
override fun Input.readObject(): Envelope =
|
||||||
default.run { readObject() }
|
default.run { readObject() }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package hep.dataforge.io
|
package hep.dataforge.io
|
||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
|
||||||
import kotlinx.io.asBinary
|
import kotlinx.io.asBinary
|
||||||
import kotlinx.serialization.json.JsonPrimitive
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import kotlinx.serialization.json.json
|
import kotlinx.serialization.json.json
|
||||||
@ -74,9 +75,9 @@ class MetaFormatTest {
|
|||||||
}
|
}
|
||||||
val meta = json.toMetaItem().node!!
|
val meta = json.toMetaItem().node!!
|
||||||
|
|
||||||
assertEquals(true, meta["@value[0].@value[1].d"].boolean)
|
assertEquals(true, meta["$JSON_ARRAY_KEY[0].$JSON_ARRAY_KEY[1].d"].boolean)
|
||||||
assertEquals("value", meta["@value[1]"].string)
|
assertEquals("value", meta["$JSON_ARRAY_KEY[1]"].string)
|
||||||
assertEquals(listOf(1.0, 2.0, 3.0), meta["@value[2"].value?.list?.map { it.number.toDouble() })
|
assertEquals(listOf(1.0, 2.0, 3.0), meta["$JSON_ARRAY_KEY[2"].value?.list?.map { it.number.toDouble() })
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -2,8 +2,11 @@ package hep.dataforge.meta
|
|||||||
|
|
||||||
import hep.dataforge.meta.descriptors.*
|
import hep.dataforge.meta.descriptors.*
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A container that holds a [Config] and a default item provider.
|
* A container that holds a [Config] and a default item provider.
|
||||||
@ -11,7 +14,7 @@ import hep.dataforge.values.Value
|
|||||||
* It is not possible to know if some property is declared by provider just by looking on [Configurable],
|
* It is not possible to know if some property is declared by provider just by looking on [Configurable],
|
||||||
* this information should be provided externally.
|
* this information should be provided externally.
|
||||||
*/
|
*/
|
||||||
interface Configurable : Described {
|
interface Configurable : Described, MutableItemProvider {
|
||||||
/**
|
/**
|
||||||
* Backing config
|
* Backing config
|
||||||
*/
|
*/
|
||||||
@ -35,15 +38,15 @@ interface Configurable : Described {
|
|||||||
/**
|
/**
|
||||||
* Get a property with default
|
* Get a property with default
|
||||||
*/
|
*/
|
||||||
fun getProperty(name: Name): MetaItem<*>? =
|
override fun getItem(name: Name): MetaItem<*>? =
|
||||||
config[name] ?: getDefaultItem(name) ?: descriptor?.get(name)?.defaultItem()
|
config[name] ?: getDefaultItem(name) ?: descriptor?.get(name)?.defaultItem()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a configurable property
|
* Set a configurable property
|
||||||
*/
|
*/
|
||||||
fun setProperty(name: Name, item: MetaItem<*>?) {
|
override fun setItem(name: Name, item: MetaItem<*>?) {
|
||||||
if (validateItem(name, item)) {
|
if (validateItem(name, item)) {
|
||||||
config[name] = item
|
config.setItem(name, item)
|
||||||
} else {
|
} else {
|
||||||
error("Validation failed for property $name with value $item")
|
error("Validation failed for property $name with value $item")
|
||||||
}
|
}
|
||||||
@ -54,22 +57,59 @@ interface Configurable : Described {
|
|||||||
* Reset the property to its default value
|
* Reset the property to its default value
|
||||||
*/
|
*/
|
||||||
fun Configurable.resetProperty(name: Name) {
|
fun Configurable.resetProperty(name: Name) {
|
||||||
setProperty(name, null)
|
setItem(name, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Configurable.getProperty(key: String) = getProperty(key.toName())
|
fun Configurable.getItem(key: String) = getItem(key.toName())
|
||||||
|
|
||||||
fun Configurable.setProperty(name: Name, value: Value?) = setProperty(name, value?.let { MetaItem.ValueItem(value) })
|
fun Configurable.setItem(name: Name, value: Value?) = setItem(name, value?.let { MetaItem.ValueItem(value) })
|
||||||
fun Configurable.setProperty(name: Name, meta: Meta?) = setProperty(name, meta?.let { MetaItem.NodeItem(meta) })
|
fun Configurable.setItem(name: Name, meta: Meta?) = setItem(name, meta?.let { MetaItem.NodeItem(meta) })
|
||||||
|
|
||||||
fun Configurable.setProperty(key: String, item: MetaItem<*>?) {
|
fun Configurable.setItem(key: String, item: MetaItem<*>?) = setItem(key.toName(), item)
|
||||||
setProperty(key.toName(), item)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Configurable.setProperty(key: String, value: Value?) = setProperty(key, value?.let { MetaItem.ValueItem(value) })
|
fun Configurable.setItem(key: String, value: Value?) = setItem(key, value?.let { MetaItem.ValueItem(value) })
|
||||||
fun Configurable.setProperty(key: String, meta: Meta?) = setProperty(key, meta?.let { MetaItem.NodeItem(meta) })
|
fun Configurable.setItem(key: String, meta: Meta?) = setItem(key, meta?.let { MetaItem.NodeItem(meta) })
|
||||||
|
|
||||||
fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
|
fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
|
||||||
|
|
||||||
@DFBuilder
|
@DFBuilder
|
||||||
inline fun <T : Configurable> T.configure(action: Config.() -> Unit): T = apply { config.apply(action) }
|
inline fun <T : Configurable> T.configure(action: Config.() -> Unit): T = apply { config.apply(action) }
|
||||||
|
|
||||||
|
/* Node delegates */
|
||||||
|
|
||||||
|
fun Configurable.config(key: Name? = null): ReadWriteProperty<Any?, Config?> =
|
||||||
|
config.node(key)
|
||||||
|
|
||||||
|
fun MutableItemProvider.node(key: Name? = null): ReadWriteProperty<Any?, Meta?> = item(key).convert(
|
||||||
|
reader = { it.node },
|
||||||
|
writer = { it?.let { MetaItem.NodeItem(it) } }
|
||||||
|
)
|
||||||
|
|
||||||
|
fun <T : Configurable> Configurable.spec(
|
||||||
|
spec: Specification<T>, key: Name? = null
|
||||||
|
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
|
||||||
|
val name = key ?: property.name.asName()
|
||||||
|
return config[name].node?.let { spec.wrap(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
||||||
|
val name = key ?: property.name.asName()
|
||||||
|
config[name] = value?.config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Configurable> Configurable.spec(
|
||||||
|
spec: Specification<T>, default: T, key: Name? = null
|
||||||
|
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
|
val name = key ?: property.name.asName()
|
||||||
|
return config[name].node?.let { spec.wrap(it) } ?: default
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
|
val name = key ?: property.name.asName()
|
||||||
|
config[name] = value.config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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<Any?, MetaItem<*>?> {
|
|
||||||
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
return owner.getProperty(name) ?: default
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
owner.setProperty(name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LazyConfigurableDelegate(
|
|
||||||
configurable: Configurable,
|
|
||||||
key: Name? = null,
|
|
||||||
defaultProvider: () -> MetaItem<*>? = { null }
|
|
||||||
) : ConfigurableDelegate(configurable, key) {
|
|
||||||
override val default by lazy(defaultProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A property delegate that uses custom key
|
|
||||||
*/
|
|
||||||
fun Configurable.item(default: Any? = null, key: Name? = null): ConfigurableDelegate =
|
|
||||||
ConfigurableDelegate(
|
|
||||||
this,
|
|
||||||
key,
|
|
||||||
default?.let { MetaItem.of(it) })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generation of item delegate with lazy default.
|
|
||||||
* Lazy default could be used also for validation
|
|
||||||
*/
|
|
||||||
fun Configurable.lazyItem(key: Name? = null, default: () -> Any?): ConfigurableDelegate =
|
|
||||||
LazyConfigurableDelegate(this, key) {
|
|
||||||
default()?.let {
|
|
||||||
MetaItem.of(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Configurable.item(
|
|
||||||
default: T? = null,
|
|
||||||
key: Name? = null,
|
|
||||||
writer: (T) -> MetaItem<*>? = {
|
|
||||||
MetaItem.of(it)
|
|
||||||
},
|
|
||||||
reader: (MetaItem<*>?) -> T
|
|
||||||
): ReadWriteProperty<Any?, T> =
|
|
||||||
ConfigurableDelegate(
|
|
||||||
this,
|
|
||||||
key,
|
|
||||||
default?.let { MetaItem.of(it) }).map(reader = reader, writer = writer)
|
|
||||||
|
|
||||||
fun Configurable.value(default: Any? = null, key: Name? = null): ReadWriteProperty<Any?, Value?> =
|
|
||||||
item(default, key).transform { it.value }
|
|
||||||
|
|
||||||
fun <T> Configurable.value(
|
|
||||||
default: T? = null,
|
|
||||||
key: Name? = null,
|
|
||||||
writer: (T) -> Value? = { Value.of(it) },
|
|
||||||
reader: (Value?) -> T
|
|
||||||
): ReadWriteProperty<Any?, T> =
|
|
||||||
ConfigurableDelegate(
|
|
||||||
this,
|
|
||||||
key,
|
|
||||||
default?.let { MetaItem.of(it) }
|
|
||||||
).map(
|
|
||||||
reader = { reader(it.value) },
|
|
||||||
writer = { value -> writer(value)?.let { MetaItem.ValueItem(it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Configurable.string(default: String? = null, key: Name? = null): ReadWriteProperty<Any?, String?> =
|
|
||||||
item(default, key).transform { it.value?.string }
|
|
||||||
|
|
||||||
fun Configurable.boolean(default: Boolean? = null, key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
|
|
||||||
item(default, key).transform { it.value?.boolean }
|
|
||||||
|
|
||||||
fun Configurable.number(default: Number? = null, key: Name? = null): ReadWriteProperty<Any?, Number?> =
|
|
||||||
item(default, key).transform { it.value?.number }
|
|
||||||
|
|
||||||
/* Number delegates*/
|
|
||||||
|
|
||||||
fun Configurable.int(default: Int? = null, key: Name? = null): ReadWriteProperty<Any?, Int?> =
|
|
||||||
item(default, key).transform { it.value?.int }
|
|
||||||
|
|
||||||
fun Configurable.double(default: Double? = null, key: Name? = null): ReadWriteProperty<Any?, Double?> =
|
|
||||||
item(default, key).transform { it.value?.double }
|
|
||||||
|
|
||||||
fun Configurable.long(default: Long? = null, key: Name? = null): ReadWriteProperty<Any?, Long?> =
|
|
||||||
item(default, key).transform { it.value?.long }
|
|
||||||
|
|
||||||
fun Configurable.short(default: Short? = null, key: Name? = null): ReadWriteProperty<Any?, Short?> =
|
|
||||||
item(default, key).transform { it.value?.short }
|
|
||||||
|
|
||||||
fun Configurable.float(default: Float? = null, key: Name? = null): ReadWriteProperty<Any?, Float?> =
|
|
||||||
item(default, key).transform { it.value?.float }
|
|
||||||
|
|
||||||
|
|
||||||
@JvmName("safeString")
|
|
||||||
fun Configurable.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
|
|
||||||
item(default, key).transform { it.value!!.string }
|
|
||||||
|
|
||||||
@JvmName("safeBoolean")
|
|
||||||
fun Configurable.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
|
|
||||||
item(default, key).transform { it.value!!.boolean }
|
|
||||||
|
|
||||||
@JvmName("safeNumber")
|
|
||||||
fun Configurable.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
|
|
||||||
item(default, key).transform { it.value!!.number }
|
|
||||||
|
|
||||||
/* Lazy initializers for values */
|
|
||||||
|
|
||||||
@JvmName("lazyString")
|
|
||||||
fun Configurable.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
|
|
||||||
lazyItem(key, default).transform { it.value!!.string }
|
|
||||||
|
|
||||||
@JvmName("lazyBoolean")
|
|
||||||
fun Configurable.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
|
|
||||||
lazyItem(key, default).transform { it.value!!.boolean }
|
|
||||||
|
|
||||||
@JvmName("lazyNumber")
|
|
||||||
fun Configurable.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
|
|
||||||
lazyItem(key, default).transform { it.value!!.number }
|
|
||||||
|
|
||||||
/* Safe number delegates*/
|
|
||||||
|
|
||||||
@JvmName("safeInt")
|
|
||||||
fun Configurable.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
|
|
||||||
item(default, key).transform { it.value!!.int }
|
|
||||||
|
|
||||||
@JvmName("safeDouble")
|
|
||||||
fun Configurable.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
|
|
||||||
item(default, key).transform { it.value!!.double }
|
|
||||||
|
|
||||||
@JvmName("safeLong")
|
|
||||||
fun Configurable.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
|
|
||||||
item(default, key).transform { it.value!!.long }
|
|
||||||
|
|
||||||
@JvmName("safeShort")
|
|
||||||
fun Configurable.short(default: Short, key: Name? = null): ReadWriteProperty<Any?, Short> =
|
|
||||||
item(default, key).transform { it.value!!.short }
|
|
||||||
|
|
||||||
@JvmName("safeFloat")
|
|
||||||
fun Configurable.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
|
|
||||||
item(default, key).transform { it.value!!.float }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum delegate
|
|
||||||
*/
|
|
||||||
inline fun <reified E : Enum<E>> Configurable.enum(
|
|
||||||
default: E, key: Name? = null
|
|
||||||
): ReadWriteProperty<Any?, E> =
|
|
||||||
item(default, key).transform { item -> item?.string?.let {str->
|
|
||||||
@Suppress("USELESS_CAST")
|
|
||||||
enumValueOf<E>(str) as E
|
|
||||||
} ?: default }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Extra delegates for special cases
|
|
||||||
*/
|
|
||||||
fun Configurable.stringList(vararg strings: String, key: Name? = null): ReadWriteProperty<Any?, List<String>> =
|
|
||||||
item(listOf(*strings), key) {
|
|
||||||
it?.value?.stringList ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Configurable.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty<Any?, List<Number>> =
|
|
||||||
item(listOf(*numbers), key) { item ->
|
|
||||||
item?.value?.list?.map { it.number } ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A special delegate for double arrays
|
|
||||||
*/
|
|
||||||
fun Configurable.doubleArray(vararg doubles: Double, key: Name? = null): ReadWriteProperty<Any?, DoubleArray> =
|
|
||||||
item(doubleArrayOf(*doubles), key) {
|
|
||||||
(it.value as? DoubleArrayValue)?.value
|
|
||||||
?: it?.value?.list?.map { value -> value.number.toDouble() }?.toDoubleArray()
|
|
||||||
?: doubleArrayOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Node delegates */
|
|
||||||
|
|
||||||
fun Configurable.config(default: Config? = null, key: Name? = null): ReadWriteProperty<Any?, Config?> =
|
|
||||||
config.node(default,key)
|
|
||||||
|
|
||||||
fun Configurable.node(key: Name? = null): ReadWriteProperty<Any?, Meta?> = item(key).map(
|
|
||||||
reader = { it.node },
|
|
||||||
writer = { it?.let { MetaItem.NodeItem(it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
fun <T : Configurable> Configurable.spec(
|
|
||||||
spec: Specification<T>, key: Name? = null
|
|
||||||
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
return config[name].node?.let { spec.wrap(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
config[name] = value?.config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : Configurable> Configurable.spec(
|
|
||||||
spec: Specification<T>, default: T, key: Name? = null
|
|
||||||
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
return config[name].node?.let { spec.wrap(it) } ?: default
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
config[name] = value.config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
|||||||
|
package hep.dataforge.meta
|
||||||
|
|
||||||
|
import hep.dataforge.meta.transformations.MetaConverter
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/* Meta delegates */
|
||||||
|
|
||||||
|
open class ItemDelegate(
|
||||||
|
open val owner: ItemProvider,
|
||||||
|
val key: Name? = null,
|
||||||
|
open val default: MetaItem<*>? = null
|
||||||
|
) : ReadOnlyProperty<Any?, MetaItem<*>?> {
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
|
||||||
|
return owner.getItem(key ?: property.name.asName()) ?: default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ItemProvider.item(key: Name? = null): ItemDelegate = ItemDelegate(this, key)
|
||||||
|
|
||||||
|
//TODO add caching for sealed nodes
|
||||||
|
|
||||||
|
|
||||||
|
//Read-only delegates for Metas
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A property delegate that uses custom key
|
||||||
|
*/
|
||||||
|
fun ItemProvider.value(key: Name? = null): ReadOnlyProperty<Any?, Value?> =
|
||||||
|
item(key).convert(MetaConverter.value)
|
||||||
|
|
||||||
|
fun ItemProvider.string(key: Name? = null): ReadOnlyProperty<Any?, String?> =
|
||||||
|
item(key).convert(MetaConverter.string)
|
||||||
|
|
||||||
|
fun ItemProvider.boolean(key: Name? = null): ReadOnlyProperty<Any?, Boolean?> =
|
||||||
|
item(key).convert(MetaConverter.boolean)
|
||||||
|
|
||||||
|
fun ItemProvider.number(key: Name? = null): ReadOnlyProperty<Any?, Number?> =
|
||||||
|
item(key).convert(MetaConverter.number)
|
||||||
|
|
||||||
|
fun ItemProvider.node(key: Name? = null): ReadOnlyProperty<Any?, Meta?> =
|
||||||
|
item(key).convert(MetaConverter.meta)
|
||||||
|
|
||||||
|
fun ItemProvider.string(default: String, key: Name? = null): ReadOnlyProperty<Any?, String> =
|
||||||
|
item(key).convert(MetaConverter.string) { default }
|
||||||
|
|
||||||
|
fun ItemProvider.boolean(default: Boolean, key: Name? = null): ReadOnlyProperty<Any?, Boolean> =
|
||||||
|
item(key).convert(MetaConverter.boolean) { default }
|
||||||
|
|
||||||
|
fun ItemProvider.number(default: Number, key: Name? = null): ReadOnlyProperty<Any?, Number> =
|
||||||
|
item(key).convert(MetaConverter.number) { default }
|
||||||
|
|
||||||
|
inline fun <reified E : Enum<E>> ItemProvider.enum(default: E, key: Name? = null): ReadOnlyProperty<Any?, E> =
|
||||||
|
item(key).convert(MetaConverter.enum()) { default }
|
||||||
|
|
||||||
|
fun ItemProvider.string(key: Name? = null, default: () -> String): ReadOnlyProperty<Any?, String> =
|
||||||
|
item(key).convert(MetaConverter.string, default)
|
||||||
|
|
||||||
|
fun ItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadOnlyProperty<Any?, Boolean> =
|
||||||
|
item(key).convert(MetaConverter.boolean, default)
|
||||||
|
|
||||||
|
fun ItemProvider.number(key: Name? = null, default: () -> Number): ReadOnlyProperty<Any?, Number> =
|
||||||
|
item(key).convert(MetaConverter.number, default)
|
@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
package hep.dataforge.meta
|
package hep.dataforge.meta
|
||||||
|
|
||||||
|
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
|
||||||
import hep.dataforge.meta.descriptors.ItemDescriptor
|
import hep.dataforge.meta.descriptors.ItemDescriptor
|
||||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
import hep.dataforge.meta.descriptors.ValueDescriptor
|
import hep.dataforge.meta.descriptors.ValueDescriptor
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.withIndex
|
||||||
import hep.dataforge.values.*
|
import hep.dataforge.values.*
|
||||||
import kotlinx.serialization.json.*
|
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
|
* 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<String, JsonElement>()
|
val elementMap = HashMap<String, JsonElement>()
|
||||||
|
|
||||||
fun MetaItem<*>.toJsonElement(itemDescriptor: ItemDescriptor?, index: String? = null): JsonElement = when (this) {
|
fun MetaItem<*>.toJsonElement(itemDescriptor: ItemDescriptor?, index: String?): JsonElement = when (this) {
|
||||||
is MetaItem.ValueItem -> {
|
is MetaItem.ValueItem -> {
|
||||||
value.toJson(itemDescriptor as? ValueDescriptor)
|
value.toJson(itemDescriptor as? ValueDescriptor)
|
||||||
}
|
}
|
||||||
is MetaItem.NodeItem -> {
|
is MetaItem.NodeItem -> {
|
||||||
node.toJson(itemDescriptor as? NodeDescriptor, index)
|
node.toJsonWithIndex(itemDescriptor as? NodeDescriptor, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addElement(key: String) {
|
fun addElement(key: String) {
|
||||||
val itemDescriptor = descriptor?.items?.get(key)
|
val itemDescriptor = descriptor?.items?.get(key)
|
||||||
val jsonKey = key.toJsonKey(itemDescriptor)
|
val jsonKey = key.toJsonKey(itemDescriptor)
|
||||||
val items = getIndexed(key)
|
val items: Map<String, MetaItem<*>> = getIndexed(key)
|
||||||
when (items.size) {
|
when (items.size) {
|
||||||
0 -> {
|
0 -> {
|
||||||
//do nothing
|
//do nothing
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor)
|
elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor, null)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val array = jsonArray {
|
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)
|
((descriptor?.items?.keys ?: emptySet()) + items.keys.map { it.body }).forEach(::addElement)
|
||||||
|
|
||||||
|
|
||||||
if (index != null) {
|
if (indexValue != null) {
|
||||||
elementMap["@index"] = JsonPrimitive(index)
|
val indexKey = descriptor?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
|
||||||
|
elementMap[indexKey] = JsonPrimitive(indexValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return JsonObject(elementMap)
|
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 {
|
fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
JsonNull -> Null
|
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<JsonMet
|
|||||||
val value = if (isEmpty()) {
|
val value = if (isEmpty()) {
|
||||||
Null
|
Null
|
||||||
} else {
|
} else {
|
||||||
ListValue(
|
|
||||||
map<JsonElement, Value> {
|
map<JsonElement, Value> {
|
||||||
//We already checked that all values are primitives
|
//We already checked that all values are primitives
|
||||||
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
|
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
|
||||||
}
|
}.asValue()
|
||||||
)
|
|
||||||
}
|
}
|
||||||
MetaItem.ValueItem(value)
|
MetaItem.ValueItem(value)
|
||||||
} else {
|
} else {
|
||||||
json {
|
//We can't return multiple items therefore we create top level node
|
||||||
"@value" to this@toMetaItem
|
json { JSON_ARRAY_KEY to this@toMetaItem }.toMetaItem(descriptor)
|
||||||
}.toMetaItem(descriptor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,44 +132,52 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
|
|||||||
*/
|
*/
|
||||||
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() {
|
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() {
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
private fun buildItems(): Map<NameToken, MetaItem<JsonMeta>> {
|
||||||
private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement): Unit {
|
val map = LinkedHashMap<NameToken, MetaItem<JsonMeta>>()
|
||||||
val itemDescriptor = descriptor?.items?.get(key)
|
|
||||||
|
json.forEach { (jsonKey, value) ->
|
||||||
|
val key = NameToken(jsonKey)
|
||||||
|
val itemDescriptor = descriptor?.items?.get(jsonKey)
|
||||||
when (value) {
|
when (value) {
|
||||||
is JsonPrimitive -> {
|
is JsonPrimitive -> {
|
||||||
this[key] =
|
map[key] = MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor))
|
||||||
MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) as MetaItem<JsonMeta>
|
|
||||||
}
|
}
|
||||||
is JsonObject -> {
|
is JsonObject -> {
|
||||||
this[key] = MetaItem.NodeItem(
|
map[key] = MetaItem.NodeItem(
|
||||||
JsonMeta(
|
JsonMeta(
|
||||||
value,
|
value,
|
||||||
itemDescriptor as? NodeDescriptor
|
itemDescriptor as? NodeDescriptor
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is JsonArray -> {
|
is JsonArray -> if (value.all { it is JsonPrimitive }) {
|
||||||
when {
|
|
||||||
value.all { it is JsonPrimitive } -> {
|
|
||||||
val listValue = ListValue(
|
val listValue = ListValue(
|
||||||
value.map {
|
value.map {
|
||||||
//We already checked that all values are primitives
|
//We already checked that all values are primitives
|
||||||
(it as JsonPrimitive).toValue(itemDescriptor as? ValueDescriptor)
|
(it as JsonPrimitive).toValue(itemDescriptor as? ValueDescriptor)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
this[key] = MetaItem.ValueItem(listValue) as MetaItem<JsonMeta>
|
map[key] = MetaItem.ValueItem(listValue)
|
||||||
}
|
} else value.forEachIndexed { index, jsonElement ->
|
||||||
else -> value.forEachIndexed { index, jsonElement ->
|
val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
|
||||||
this["$key[$index]"] = jsonElement.toMetaItem(itemDescriptor)
|
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<NameToken, MetaItem<JsonMeta>> by lazy {
|
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy(::buildItems)
|
||||||
val map = LinkedHashMap<String, MetaItem<JsonMeta>>()
|
|
||||||
json.forEach { (key, value) -> map[key] = value }
|
companion object{
|
||||||
map.mapKeys { it.key.toName().first()!! }
|
/**
|
||||||
|
* A key representing top-level json array of nodes, which could not be directly represented by a meta node
|
||||||
|
*/
|
||||||
|
const val JSON_ARRAY_KEY = "@jsonArray"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -85,6 +85,10 @@ interface MetaRepr {
|
|||||||
fun toMeta(): Meta
|
fun toMeta(): Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ItemProvider{
|
||||||
|
fun getItem(name: Name): MetaItem<*>?
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic meta tree representation. Elements are [MetaItem] objects that could be represented by three different entities:
|
* Generic meta tree representation. Elements are [MetaItem] objects that could be represented by three different entities:
|
||||||
* * [MetaItem.ValueItem] (leaf)
|
* * [MetaItem.ValueItem] (leaf)
|
||||||
@ -92,12 +96,23 @@ interface MetaRepr {
|
|||||||
*
|
*
|
||||||
* * Same name siblings are supported via elements with the same [Name] but different queries
|
* * Same name siblings are supported via elements with the same [Name] but different queries
|
||||||
*/
|
*/
|
||||||
interface Meta : MetaRepr {
|
interface Meta : MetaRepr, ItemProvider {
|
||||||
/**
|
/**
|
||||||
* Top level items of meta tree
|
* Top level items of meta tree
|
||||||
*/
|
*/
|
||||||
val items: Map<NameToken, MetaItem<*>>
|
val items: Map<NameToken, MetaItem<*>>
|
||||||
|
|
||||||
|
override fun getItem(name: Name): MetaItem<*>? {
|
||||||
|
if (name.isEmpty()) return NodeItem(this)
|
||||||
|
return name.first()?.let { token ->
|
||||||
|
val tail = name.cutFirst()
|
||||||
|
when (tail.length) {
|
||||||
|
0 -> items[token]
|
||||||
|
else -> items[token]?.node?.get(tail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun toMeta(): Meta = seal()
|
override fun toMeta(): Meta = seal()
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean
|
override fun equals(other: Any?): Boolean
|
||||||
@ -127,17 +142,7 @@ interface Meta : MetaRepr {
|
|||||||
*
|
*
|
||||||
* If [name] is empty return current [Meta] as a [NodeItem]
|
* If [name] is empty return current [Meta] as a [NodeItem]
|
||||||
*/
|
*/
|
||||||
operator fun Meta?.get(name: Name): MetaItem<*>? {
|
operator fun Meta?.get(name: Name): MetaItem<*>? = this?.getItem(name)
|
||||||
if (this == null) return null
|
|
||||||
if (name.isEmpty()) return NodeItem(this)
|
|
||||||
return name.first()?.let { token ->
|
|
||||||
val tail = name.cutFirst()
|
|
||||||
when (tail.length) {
|
|
||||||
0 -> items[token]
|
|
||||||
else -> items[token]?.node?.get(tail)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token)
|
operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token)
|
||||||
|
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
package hep.dataforge.meta
|
|
||||||
|
|
||||||
import hep.dataforge.names.Name
|
|
||||||
import hep.dataforge.names.asName
|
|
||||||
import hep.dataforge.values.Value
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import kotlin.properties.ReadOnlyProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
/* Meta delegates */
|
|
||||||
|
|
||||||
open class MetaDelegate(
|
|
||||||
open val owner: Meta,
|
|
||||||
val key: Name? = null,
|
|
||||||
open val default: MetaItem<*>? = null
|
|
||||||
) : ReadOnlyProperty<Any?, MetaItem<*>?> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
|
|
||||||
return owner[key ?: property.name.asName()] ?: default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LazyMetaDelegate(
|
|
||||||
owner: Meta,
|
|
||||||
key: Name? = null,
|
|
||||||
defaultProvider: () -> MetaItem<*>? = { null }
|
|
||||||
) : MetaDelegate(owner, key) {
|
|
||||||
override val default by lazy(defaultProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
class DelegateWrapper<T, R>(
|
|
||||||
val delegate: ReadOnlyProperty<Any?, T>,
|
|
||||||
val reader: (T) -> R
|
|
||||||
) : ReadOnlyProperty<Any?, R> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): R {
|
|
||||||
return reader(delegate.getValue(thisRef, property))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T, R> ReadOnlyProperty<Any?, T>.map(reader: (T) -> R): DelegateWrapper<T, R> =
|
|
||||||
DelegateWrapper(this, reader)
|
|
||||||
|
|
||||||
|
|
||||||
fun Meta.item(default: Any? = null, key: Name? = null): MetaDelegate =
|
|
||||||
MetaDelegate(this, key, default?.let { MetaItem.of(it) })
|
|
||||||
|
|
||||||
fun Meta.lazyItem(key: Name? = null, defaultProvider: () -> Any?): LazyMetaDelegate =
|
|
||||||
LazyMetaDelegate(this, key) { defaultProvider()?.let { MetaItem.of(it) } }
|
|
||||||
|
|
||||||
//TODO add caching for sealed nodes
|
|
||||||
|
|
||||||
|
|
||||||
//Read-only delegates for Metas
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A property delegate that uses custom key
|
|
||||||
*/
|
|
||||||
fun Meta.value(default: Value? = null, key: Name? = null) =
|
|
||||||
item(default, key).map { it.value }
|
|
||||||
|
|
||||||
fun Meta.string(default: String? = null, key: Name? = null) =
|
|
||||||
item(default, key).map { it.string }
|
|
||||||
|
|
||||||
fun Meta.boolean(default: Boolean? = null, key: Name? = null) =
|
|
||||||
item(default, key).map { it.boolean }
|
|
||||||
|
|
||||||
fun Meta.number(default: Number? = null, key: Name? = null) =
|
|
||||||
item(default, key).map { it.number }
|
|
||||||
|
|
||||||
fun Meta.node(key: Name? = null) =
|
|
||||||
item(key).map { it.node }
|
|
||||||
|
|
||||||
@JvmName("safeString")
|
|
||||||
fun Meta.string(default: String, key: Name? = null) =
|
|
||||||
item(default, key).map { it.string!! }
|
|
||||||
|
|
||||||
@JvmName("safeBoolean")
|
|
||||||
fun Meta.boolean(default: Boolean, key: Name? = null) =
|
|
||||||
item(default, key).map { it.boolean!! }
|
|
||||||
|
|
||||||
@JvmName("safeNumber")
|
|
||||||
fun Meta.number(default: Number, key: Name? = null) =
|
|
||||||
item(default, key).map { it.number!! }
|
|
||||||
|
|
||||||
@JvmName("lazyString")
|
|
||||||
fun Meta.string(key: Name? = null, default: () -> String) =
|
|
||||||
lazyItem(key, default).map { it.string!! }
|
|
||||||
|
|
||||||
@JvmName("lazyBoolean")
|
|
||||||
fun Meta.boolean(key: Name? = null, default: () -> Boolean) =
|
|
||||||
lazyItem(key, default).map { it.boolean!! }
|
|
||||||
|
|
||||||
@JvmName("lazyNumber")
|
|
||||||
fun Meta.number(key: Name? = null, default: () -> Number) =
|
|
||||||
lazyItem(key, default).map { it.number!! }
|
|
||||||
|
|
||||||
|
|
||||||
inline fun <reified E : Enum<E>> Meta.enum(default: E, key: Name? = null) =
|
|
||||||
item(default, key).map { it.enum<E>()!! }
|
|
@ -0,0 +1,167 @@
|
|||||||
|
package hep.dataforge.meta
|
||||||
|
|
||||||
|
import hep.dataforge.meta.transformations.MetaConverter
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.values.DoubleArrayValue
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import hep.dataforge.values.stringList
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/* Read-write delegates */
|
||||||
|
|
||||||
|
open class MutableItemDelegate(
|
||||||
|
override val owner: MutableItemProvider,
|
||||||
|
key: Name? = null,
|
||||||
|
default: MetaItem<*>? = null
|
||||||
|
) : ItemDelegate(owner, key, default), ReadWriteProperty<Any?, MetaItem<*>?> {
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) {
|
||||||
|
val name = key ?: property.name.asName()
|
||||||
|
owner.setItem(name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate =
|
||||||
|
MutableItemDelegate(this, key)
|
||||||
|
|
||||||
|
//Read-write delegates
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A property delegate that uses custom key
|
||||||
|
*/
|
||||||
|
fun MutableItemProvider.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
|
||||||
|
item(key).convert(MetaConverter.value)
|
||||||
|
|
||||||
|
fun MutableItemProvider.string(key: Name? = null): ReadWriteProperty<Any?, String?> =
|
||||||
|
item(key).convert(MetaConverter.string)
|
||||||
|
|
||||||
|
fun MutableItemProvider.boolean(key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
|
||||||
|
item(key).convert(MetaConverter.boolean)
|
||||||
|
|
||||||
|
fun MutableItemProvider.number(key: Name? = null): ReadWriteProperty<Any?, Number?> =
|
||||||
|
item(key).convert(MetaConverter.number)
|
||||||
|
|
||||||
|
fun MutableItemProvider.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
|
||||||
|
item(key).convert(MetaConverter.string) { default }
|
||||||
|
|
||||||
|
fun MutableItemProvider.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
|
||||||
|
item(key).convert(MetaConverter.boolean) { default }
|
||||||
|
|
||||||
|
fun MutableItemProvider.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
|
||||||
|
item(key).convert(MetaConverter.number) { default }
|
||||||
|
|
||||||
|
fun MutableItemProvider.value(key: Name? = null, default: () -> Value): ReadWriteProperty<Any?, Value> =
|
||||||
|
item(key).convert(MetaConverter.value, default)
|
||||||
|
|
||||||
|
fun MutableItemProvider.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
|
||||||
|
item(key).convert(MetaConverter.string, default)
|
||||||
|
|
||||||
|
fun MutableItemProvider.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
|
||||||
|
item(key).convert(MetaConverter.boolean, default)
|
||||||
|
|
||||||
|
fun MutableItemProvider.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
|
||||||
|
item(key).convert(MetaConverter.number, default)
|
||||||
|
|
||||||
|
inline fun <reified E : Enum<E>> MutableItemProvider.enum(default: E, key: Name? = null): ReadWriteProperty<Any?, E> =
|
||||||
|
item(key).convert(MetaConverter.enum()) { default }
|
||||||
|
|
||||||
|
inline fun <reified M : MutableMeta<M>> M.node(key: Name? = null): ReadWriteProperty<Any?, M?> =
|
||||||
|
item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItem.NodeItem(it) } })
|
||||||
|
|
||||||
|
|
||||||
|
fun <T> MutableItemProvider.item(
|
||||||
|
default: T? = null,
|
||||||
|
key: Name? = null,
|
||||||
|
writer: (T) -> MetaItem<*>? = { MetaItem.of(it) },
|
||||||
|
reader: (MetaItem<*>?) -> T
|
||||||
|
): ReadWriteProperty<Any?, T> = MutableItemDelegate(
|
||||||
|
this,
|
||||||
|
key,
|
||||||
|
default?.let { MetaItem.of(it) }
|
||||||
|
).convert(reader = reader, writer = writer)
|
||||||
|
|
||||||
|
fun Configurable.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
|
||||||
|
item(key).convert(MetaConverter.value)
|
||||||
|
|
||||||
|
fun <T> MutableItemProvider.value(
|
||||||
|
default: T? = null,
|
||||||
|
key: Name? = null,
|
||||||
|
writer: (T) -> Value? = { Value.of(it) },
|
||||||
|
reader: (Value?) -> T
|
||||||
|
): ReadWriteProperty<Any?, T> = MutableItemDelegate(
|
||||||
|
this,
|
||||||
|
key,
|
||||||
|
default?.let { MetaItem.of(it) }
|
||||||
|
).convert(
|
||||||
|
reader = { reader(it.value) },
|
||||||
|
writer = { value -> writer(value)?.let { MetaItem.ValueItem(it) } }
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Number delegates*/
|
||||||
|
|
||||||
|
fun MutableItemProvider.int(key: Name? = null): ReadWriteProperty<Any?, Int?> =
|
||||||
|
item(key).convert(MetaConverter.int)
|
||||||
|
|
||||||
|
fun MutableItemProvider.double(key: Name? = null): ReadWriteProperty<Any?, Double?> =
|
||||||
|
item(key).convert(MetaConverter.double)
|
||||||
|
|
||||||
|
fun MutableItemProvider.long(key: Name? = null): ReadWriteProperty<Any?, Long?> =
|
||||||
|
item(key).convert(MetaConverter.long)
|
||||||
|
|
||||||
|
fun MutableItemProvider.float(key: Name? = null): ReadWriteProperty<Any?, Float?> =
|
||||||
|
item(key).convert(MetaConverter.float)
|
||||||
|
|
||||||
|
|
||||||
|
/* Safe number delegates*/
|
||||||
|
|
||||||
|
fun MutableItemProvider.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
|
||||||
|
item(key).convert(MetaConverter.int) { default }
|
||||||
|
|
||||||
|
fun MutableItemProvider.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
|
||||||
|
item(key).convert(MetaConverter.double) { default }
|
||||||
|
|
||||||
|
fun MutableItemProvider.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
|
||||||
|
item(key).convert(MetaConverter.long) { default }
|
||||||
|
|
||||||
|
fun MutableItemProvider.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
|
||||||
|
item(key).convert(MetaConverter.float) { default }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra delegates for special cases
|
||||||
|
*/
|
||||||
|
fun MutableItemProvider.stringList(vararg strings: String, key: Name? = null): ReadWriteProperty<Any?, List<String>> =
|
||||||
|
item(listOf(*strings), key) {
|
||||||
|
it?.value?.stringList ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MutableItemProvider.stringListOrNull(
|
||||||
|
vararg strings: String,
|
||||||
|
key: Name? = null
|
||||||
|
): ReadWriteProperty<Any?, List<String>?> =
|
||||||
|
item(listOf(*strings), key) {
|
||||||
|
it?.value?.stringList
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MutableItemProvider.numberList(vararg numbers: Number, key: Name? = null): ReadWriteProperty<Any?, List<Number>> =
|
||||||
|
item(listOf(*numbers), key) { item ->
|
||||||
|
item?.value?.list?.map { it.number } ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special delegate for double arrays
|
||||||
|
*/
|
||||||
|
fun MutableItemProvider.doubleArray(vararg doubles: Double, key: Name? = null): ReadWriteProperty<Any?, DoubleArray> =
|
||||||
|
item(doubleArrayOf(*doubles), key) {
|
||||||
|
(it.value as? DoubleArrayValue)?.value
|
||||||
|
?: it?.value?.list?.map { value -> value.number.toDouble() }?.toDoubleArray()
|
||||||
|
?: doubleArrayOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> MutableItemProvider.listValue(
|
||||||
|
key: Name? = null,
|
||||||
|
writer: (T) -> Value = { Value.of(it) },
|
||||||
|
reader: (Value) -> T
|
||||||
|
): ReadWriteProperty<Any?, List<T>?> = item(key).convert(MetaConverter.valueList(writer, reader))
|
@ -3,9 +3,12 @@ package hep.dataforge.meta
|
|||||||
import hep.dataforge.names.*
|
import hep.dataforge.names.*
|
||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
|
|
||||||
interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M> {
|
interface MutableItemProvider : ItemProvider {
|
||||||
|
fun setItem(name: Name, item: MetaItem<*>?)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M>, MutableItemProvider {
|
||||||
override val items: Map<NameToken, MetaItem<M>>
|
override val items: Map<NameToken, MetaItem<M>>
|
||||||
operator fun set(name: Name, item: MetaItem<*>?)
|
|
||||||
// fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
|
// fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
|
||||||
// fun removeListener(owner: Any? = null)
|
// fun removeListener(owner: Any? = null)
|
||||||
}
|
}
|
||||||
@ -49,7 +52,7 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
|
|||||||
*/
|
*/
|
||||||
internal abstract fun empty(): M
|
internal abstract fun empty(): M
|
||||||
|
|
||||||
override operator fun set(name: Name, item: MetaItem<*>?) {
|
override fun setItem(name: Name, item: MetaItem<*>?) {
|
||||||
when (name.length) {
|
when (name.length) {
|
||||||
0 -> error("Can't setValue meta item for empty name")
|
0 -> error("Can't setValue meta item for empty name")
|
||||||
1 -> {
|
1 -> {
|
||||||
@ -63,7 +66,7 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
|
|||||||
if (items[token] == null) {
|
if (items[token] == null) {
|
||||||
replaceItem(token, null, MetaItem.NodeItem(empty()))
|
replaceItem(token, null, MetaItem.NodeItem(empty()))
|
||||||
}
|
}
|
||||||
items[token]?.node!![name.cutFirst()] = item
|
items[token]?.node!!.setItem(name.cutFirst(), item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,27 +74,21 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
|
|||||||
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun MutableMeta<*>.remove(name: Name) = set(name, null)
|
inline fun MutableMeta<*>.remove(name: Name) = setItem(name, null)
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun MutableMeta<*>.remove(name: String) = remove(name.toName())
|
inline fun MutableMeta<*>.remove(name: String) = remove(name.toName())
|
||||||
|
|
||||||
fun MutableMeta<*>.setValue(name: Name, value: Value) = set(name, MetaItem.ValueItem(value))
|
operator fun MutableMeta<*>.set(name: Name, item: MetaItem<*>?) = setItem(name, item)
|
||||||
|
|
||||||
|
fun MutableMeta<*>.setValue(name: Name, value: Value) = setItem(name, MetaItem.ValueItem(value))
|
||||||
|
|
||||||
fun MutableMeta<*>.setValue(name: String, value: Value) = set(name.toName(), value)
|
fun MutableMeta<*>.setValue(name: String, value: Value) = set(name.toName(), value)
|
||||||
|
|
||||||
fun MutableMeta<*>.setItem(name: Name, item: MetaItem<*>?) {
|
|
||||||
when (item) {
|
|
||||||
null -> remove(name)
|
|
||||||
is MetaItem.ValueItem -> setValue(name, item.value)
|
|
||||||
is MetaItem.NodeItem<*> -> setNode(name, item.node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MutableMeta<*>.setItem(name: String, item: MetaItem<*>?) = setItem(name.toName(), item)
|
fun MutableMeta<*>.setItem(name: String, item: MetaItem<*>?) = setItem(name.toName(), item)
|
||||||
|
|
||||||
fun MutableMeta<*>.setNode(name: Name, node: Meta) =
|
fun MutableMeta<*>.setNode(name: Name, node: Meta) =
|
||||||
set(name, MetaItem.NodeItem(node))
|
setItem(name, MetaItem.NodeItem(node))
|
||||||
|
|
||||||
fun MutableMeta<*>.setNode(name: String, node: Meta) = setNode(name.toName(), node)
|
fun MutableMeta<*>.setNode(name: String, node: Meta) = setNode(name.toName(), node)
|
||||||
|
|
||||||
@ -135,23 +132,23 @@ fun <M : MutableMeta<M>> M.update(meta: Meta) {
|
|||||||
fun MutableMeta<*>.setIndexedItems(
|
fun MutableMeta<*>.setIndexedItems(
|
||||||
name: Name,
|
name: Name,
|
||||||
items: Iterable<MetaItem<*>>,
|
items: Iterable<MetaItem<*>>,
|
||||||
indexFactory: MetaItem<*>.(index: Int) -> String = { it.toString() }
|
indexFactory: (MetaItem<*>, index: Int) -> String = { _, index -> index.toString() }
|
||||||
) {
|
) {
|
||||||
val tokens = name.tokens.toMutableList()
|
val tokens = name.tokens.toMutableList()
|
||||||
val last = tokens.last()
|
val last = tokens.last()
|
||||||
items.forEachIndexed { index, meta ->
|
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
|
tokens[tokens.lastIndex] = indexedToken
|
||||||
set(Name(tokens), meta)
|
setItem(Name(tokens), meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MutableMeta<*>.setIndexed(
|
fun MutableMeta<*>.setIndexed(
|
||||||
name: Name,
|
name: Name,
|
||||||
metas: Iterable<Meta>,
|
metas: Iterable<Meta>,
|
||||||
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<Meta>): Unit = setIndexed(name, metas)
|
operator fun MutableMeta<*>.set(name: Name, metas: Iterable<Meta>): Unit = setIndexed(name, metas)
|
||||||
@ -178,7 +175,7 @@ fun <M : MutableMeta<M>> M.append(name: String, value: Any?) = append(name.toNam
|
|||||||
*/
|
*/
|
||||||
@DFExperimental
|
@DFExperimental
|
||||||
fun <M : AbstractMutableMeta<M>> M.edit(name: Name, builder: M.() -> Unit) {
|
fun <M : AbstractMutableMeta<M>> M.edit(name: Name, builder: M.() -> Unit) {
|
||||||
val item = when(val existingItem = get(name)){
|
val item = when (val existingItem = get(name)) {
|
||||||
null -> empty().also { set(name, it) }
|
null -> empty().also { set(name, it) }
|
||||||
is MetaItem.NodeItem<M> -> existingItem.node
|
is MetaItem.NodeItem<M> -> existingItem.node
|
||||||
else -> error("Can't edit value meta item")
|
else -> error("Can't edit value meta item")
|
||||||
|
@ -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<M : MutableMeta<M>>(
|
|
||||||
override val owner: M,
|
|
||||||
key: Name? = null,
|
|
||||||
default: MetaItem<*>? = null
|
|
||||||
) : MetaDelegate(owner, key, default), ReadWriteProperty<Any?, MetaItem<*>?> {
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) {
|
|
||||||
val name = key ?: property.name.asName()
|
|
||||||
owner.setItem(name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LazyMutableMetaDelegate<M : MutableMeta<M>>(
|
|
||||||
owner: M,
|
|
||||||
key: Name? = null,
|
|
||||||
defaultProvider: () -> MetaItem<*>? = { null }
|
|
||||||
) : MutableMetaDelegate<M>(owner, key) {
|
|
||||||
override val default by lazy(defaultProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReadWriteDelegateWrapper<T, R>(
|
|
||||||
val delegate: ReadWriteProperty<Any?, T>,
|
|
||||||
val reader: (T) -> R,
|
|
||||||
val writer: (R) -> T
|
|
||||||
) : ReadWriteProperty<Any?, R> {
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): R = reader(delegate.getValue(thisRef, property))
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
|
|
||||||
delegate.setValue(thisRef, property, writer(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T, R> ReadWriteProperty<Any?, T>.map(reader: (T) -> R, writer: (R) -> T): ReadWriteDelegateWrapper<T, R> =
|
|
||||||
ReadWriteDelegateWrapper(this, reader, writer)
|
|
||||||
|
|
||||||
fun <R> ReadWriteProperty<Any?, MetaItem<*>?>.transform(reader: (MetaItem<*>?) -> R): ReadWriteProperty<Any?, R> =
|
|
||||||
map(reader = reader, writer = { MetaItem.of(it) })
|
|
||||||
|
|
||||||
fun <R> ReadWriteProperty<Any?, Value?>.transform(reader: (Value?) -> R): ReadWriteDelegateWrapper<Value?, R> =
|
|
||||||
map(reader = reader, writer = { Value.of(it) })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A delegate that throws
|
|
||||||
*/
|
|
||||||
fun <R : Any> ReadWriteProperty<Any?, R?>.notNull(default: () -> R): ReadWriteProperty<Any?, R> {
|
|
||||||
return ReadWriteDelegateWrapper(this,
|
|
||||||
reader = { it ?: default() },
|
|
||||||
writer = { it }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun <M : MutableMeta<M>> M.item(default: Any? = null, key: Name? = null): MutableMetaDelegate<M> =
|
|
||||||
MutableMetaDelegate(this, key, default?.let { MetaItem.of(it) })
|
|
||||||
|
|
||||||
fun <M : MutableMeta<M>> M.lazyItem(key: Name? = null, defaultProvider: () -> Any?): LazyMutableMetaDelegate<M> =
|
|
||||||
LazyMutableMetaDelegate(this, key) { defaultProvider()?.let { MetaItem.of(it) } }
|
|
||||||
|
|
||||||
//Read-write delegates
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A property delegate that uses custom key
|
|
||||||
*/
|
|
||||||
fun <M : MutableMeta<M>> M.value(default: Value? = null, key: Name? = null): ReadWriteProperty<Any?, Value?> =
|
|
||||||
item(default, key).transform { it.value }
|
|
||||||
|
|
||||||
fun <M : MutableMeta<M>> M.string(default: String? = null, key: Name? = null): ReadWriteProperty<Any?, String?> =
|
|
||||||
item(default, key).transform { it.string }
|
|
||||||
|
|
||||||
fun <M : MutableMeta<M>> M.boolean(default: Boolean? = null, key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
|
|
||||||
item(default, key).transform { it.boolean }
|
|
||||||
|
|
||||||
fun <M : MutableMeta<M>> M.number(default: Number? = null, key: Name? = null): ReadWriteProperty<Any?, Number?> =
|
|
||||||
item(default, key).transform { it.number }
|
|
||||||
|
|
||||||
inline fun <reified M : MutableMeta<M>> M.node(default: M? = null, key: Name? = null) =
|
|
||||||
item(default, key = key).transform { it.node as? M }
|
|
||||||
|
|
||||||
@JvmName("safeString")
|
|
||||||
fun <M : MutableMeta<M>> M.string(default: String, key: Name? = null) =
|
|
||||||
item(default, key).transform { it.string!! }
|
|
||||||
|
|
||||||
@JvmName("safeBoolean")
|
|
||||||
fun <M : MutableMeta<M>> M.boolean(default: Boolean, key: Name? = null) =
|
|
||||||
item(default, key).transform { it.boolean!! }
|
|
||||||
|
|
||||||
@JvmName("safeNumber")
|
|
||||||
fun <M : MutableMeta<M>> M.number(default: Number, key: Name? = null) =
|
|
||||||
item(default, key).transform { it.number!! }
|
|
||||||
|
|
||||||
@JvmName("lazyValue")
|
|
||||||
fun <M : MutableMeta<M>> M.string(key: Name? = null, default: () -> Value) =
|
|
||||||
lazyItem(key, default).transform { it.value!! }
|
|
||||||
|
|
||||||
@JvmName("lazyString")
|
|
||||||
fun <M : MutableMeta<M>> M.string(key: Name? = null, default: () -> String) =
|
|
||||||
lazyItem(key, default).transform { it.string!! }
|
|
||||||
|
|
||||||
@JvmName("safeBoolean")
|
|
||||||
fun <M : MutableMeta<M>> M.boolean(key: Name? = null, default: () -> Boolean) =
|
|
||||||
lazyItem(key, default).transform { it.boolean!! }
|
|
||||||
|
|
||||||
@JvmName("safeNumber")
|
|
||||||
fun <M : MutableMeta<M>> M.number(key: Name? = null, default: () -> Number) =
|
|
||||||
lazyItem(key, default).transform { it.number!! }
|
|
||||||
|
|
||||||
|
|
||||||
inline fun <M : MutableMeta<M>, reified E : Enum<E>> M.enum(default: E, key: Name? = null) =
|
|
||||||
item(default, key).transform { it.enum<E>()!! }
|
|
@ -2,10 +2,7 @@ package hep.dataforge.meta.descriptors
|
|||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.*
|
import hep.dataforge.names.*
|
||||||
import hep.dataforge.values.False
|
import hep.dataforge.values.*
|
||||||
import hep.dataforge.values.True
|
|
||||||
import hep.dataforge.values.Value
|
|
||||||
import hep.dataforge.values.ValueType
|
|
||||||
|
|
||||||
@DFBuilder
|
@DFBuilder
|
||||||
sealed class ItemDescriptor(val config: Config) {
|
sealed class ItemDescriptor(val config: Config) {
|
||||||
@ -95,6 +92,11 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
|||||||
*/
|
*/
|
||||||
var default by config.node()
|
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<String, ItemDescriptor>
|
val items: Map<String, ItemDescriptor>
|
||||||
get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) ->
|
get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) ->
|
||||||
val node = item.node ?: error("Node descriptor must be a node")
|
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 ITEM_KEY = "item".asName()
|
||||||
val IS_NODE_KEY = "@isNode".asName()
|
val IS_NODE_KEY = "@isNode".asName()
|
||||||
|
|
||||||
|
const val DEFAULT_INDEX_KEY = "@index"
|
||||||
|
|
||||||
inline operator fun invoke(block: NodeDescriptor.() -> Unit) = NodeDescriptor().apply(block)
|
inline operator fun invoke(block: NodeDescriptor.() -> Unit) = NodeDescriptor().apply(block)
|
||||||
|
|
||||||
//TODO infer descriptor from spec
|
//TODO infer descriptor from spec
|
||||||
@ -234,9 +238,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
var type: List<ValueType> by config.item().transform {
|
var type: List<ValueType>? by config.listValue { ValueType.valueOf(it.string) }
|
||||||
it?.value?.list?.map { v -> ValueType.valueOf(v.string) } ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun type(vararg t: ValueType) {
|
fun type(vararg t: ValueType) {
|
||||||
this.type = listOf(*t)
|
this.type = listOf(*t)
|
||||||
@ -250,9 +252,8 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
fun isAllowedValue(value: Value): Boolean {
|
fun isAllowedValue(value: Value): Boolean {
|
||||||
return (type.isEmpty() || type.contains(ValueType.STRING) || type.contains(value.type)) && (allowedValues.isEmpty() || allowedValues.contains(
|
return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
|
||||||
value
|
&& (allowedValues.isEmpty() || allowedValues.contains(value))
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -261,13 +262,19 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
var allowedValues: List<Value> by config.value().transform {
|
var allowedValues: List<Value> by config.item().convert(
|
||||||
|
reader = {
|
||||||
|
val value = it.value
|
||||||
when {
|
when {
|
||||||
it?.list != null -> it.list
|
value?.list != null -> value.list
|
||||||
type.size == 1 && type[0] === ValueType.BOOLEAN -> listOf(True, False)
|
type?.let { type -> type.size == 1 && type[0] === ValueType.BOOLEAN} ?: false -> listOf(True, False)
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
writer = {
|
||||||
|
MetaItem.ValueItem(it.asValue())
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow given list of value and forbid others
|
* Allow given list of value and forbid others
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
package hep.dataforge.meta
|
||||||
|
|
||||||
|
import hep.dataforge.meta.transformations.MetaConverter
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a converter to this delegate creating a delegate with a custom type
|
||||||
|
*/
|
||||||
|
fun <R : Any> ItemDelegate.convert(
|
||||||
|
converter: MetaConverter<R>
|
||||||
|
): ReadOnlyProperty<Any?, R?> = object : ReadOnlyProperty<Any?, R?> {
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): R? =
|
||||||
|
this@convert.getValue(thisRef, property)?.let(converter::itemToObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun <R : Any> ItemDelegate.convert(
|
||||||
|
converter: MetaConverter<R>,
|
||||||
|
default: () -> R
|
||||||
|
): ReadOnlyProperty<Any?, R> = object : ReadOnlyProperty<Any?, R> {
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
|
||||||
|
this@convert.getValue(thisRef, property)?.let(converter::itemToObject) ?: default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A converter with a custom reader transformation
|
||||||
|
*/
|
||||||
|
fun <R> ItemDelegate.convert(
|
||||||
|
reader: (MetaItem<*>?) -> R
|
||||||
|
): ReadOnlyProperty<Any?, R> = object : ReadOnlyProperty<Any?, R> {
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
|
||||||
|
this@convert.getValue(thisRef, property).let(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Mutable converters*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A type converter for a mutable [MetaItem] delegate
|
||||||
|
*/
|
||||||
|
fun <R : Any> MutableItemDelegate.convert(
|
||||||
|
converter: MetaConverter<R>
|
||||||
|
): ReadWriteProperty<Any?, R?> = object : ReadWriteProperty<Any?, R?> {
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): R? =
|
||||||
|
this@convert.getValue(thisRef, property)?.let(converter::itemToObject)
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R?) {
|
||||||
|
val item = value?.let(converter::objectToMetaItem)
|
||||||
|
this@convert.setValue(thisRef, property, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <R : Any> MutableItemDelegate.convert(
|
||||||
|
converter: MetaConverter<R>,
|
||||||
|
default: () -> R
|
||||||
|
): ReadWriteProperty<Any?, R> = object : ReadWriteProperty<Any?, R> {
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
|
||||||
|
this@convert.getValue(thisRef, property)?.let(converter::itemToObject) ?: default()
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
|
||||||
|
val item = value.let(converter::objectToMetaItem)
|
||||||
|
this@convert.setValue(thisRef, property, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <R> MutableItemDelegate.convert(
|
||||||
|
reader: (MetaItem<*>?) -> R,
|
||||||
|
writer: (R) -> MetaItem<*>?
|
||||||
|
): ReadWriteProperty<Any?, R> = object : ReadWriteProperty<Any?, R> {
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): R =
|
||||||
|
this@convert.getValue(thisRef, property).let(reader)
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
|
||||||
|
val item = value?.let(writer)
|
||||||
|
this@convert.setValue(thisRef, property, item)
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,6 @@
|
|||||||
package hep.dataforge.meta.transformations
|
package hep.dataforge.meta.transformations
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.meta.MetaItem
|
|
||||||
import hep.dataforge.meta.get
|
|
||||||
import hep.dataforge.meta.value
|
|
||||||
import hep.dataforge.values.*
|
import hep.dataforge.values.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,7 +12,7 @@ interface MetaConverter<T : Any> {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val item = object :MetaConverter<MetaItem<*>>{
|
val item = object : MetaConverter<MetaItem<*>> {
|
||||||
override fun itemToObject(item: MetaItem<*>): MetaItem<*> = item
|
override fun itemToObject(item: MetaItem<*>): MetaItem<*> = item
|
||||||
override fun objectToMetaItem(obj: MetaItem<*>): MetaItem<*> = obj
|
override fun objectToMetaItem(obj: MetaItem<*>): MetaItem<*> = obj
|
||||||
}
|
}
|
||||||
@ -56,6 +53,15 @@ interface MetaConverter<T : Any> {
|
|||||||
override fun objectToMetaItem(obj: Boolean): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
override fun objectToMetaItem(obj: Boolean): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val number = object : MetaConverter<Number> {
|
||||||
|
override fun itemToObject(item: MetaItem<*>): Number = when (item) {
|
||||||
|
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
||||||
|
is MetaItem.ValueItem -> item.value
|
||||||
|
}.number
|
||||||
|
|
||||||
|
override fun objectToMetaItem(obj: Number): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
||||||
|
}
|
||||||
|
|
||||||
val double = object : MetaConverter<Double> {
|
val double = object : MetaConverter<Double> {
|
||||||
override fun itemToObject(item: MetaItem<*>): Double = when (item) {
|
override fun itemToObject(item: MetaItem<*>): Double = when (item) {
|
||||||
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
||||||
@ -65,6 +71,15 @@ interface MetaConverter<T : Any> {
|
|||||||
override fun objectToMetaItem(obj: Double): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
override fun objectToMetaItem(obj: Double): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val float = object : MetaConverter<Float> {
|
||||||
|
override fun itemToObject(item: MetaItem<*>): Float = when (item) {
|
||||||
|
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
||||||
|
is MetaItem.ValueItem -> item.value
|
||||||
|
}.float
|
||||||
|
|
||||||
|
override fun objectToMetaItem(obj: Float): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
||||||
|
}
|
||||||
|
|
||||||
val int = object : MetaConverter<Int> {
|
val int = object : MetaConverter<Int> {
|
||||||
override fun itemToObject(item: MetaItem<*>): Int = when (item) {
|
override fun itemToObject(item: MetaItem<*>): Int = when (item) {
|
||||||
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
||||||
@ -73,8 +88,37 @@ interface MetaConverter<T : Any> {
|
|||||||
|
|
||||||
override fun objectToMetaItem(obj: Int): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
override fun objectToMetaItem(obj: Int): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val long = object : MetaConverter<Long> {
|
||||||
|
override fun itemToObject(item: MetaItem<*>): Long = when (item) {
|
||||||
|
is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value")
|
||||||
|
is MetaItem.ValueItem -> item.value
|
||||||
|
}.long
|
||||||
|
|
||||||
|
override fun objectToMetaItem(obj: Long): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified E : Enum<E>> enum(): MetaConverter<E> = object : MetaConverter<E> {
|
||||||
|
@Suppress("USELESS_CAST")
|
||||||
|
override fun itemToObject(item: MetaItem<*>): E = item.enum<E>() as? E ?: error("The Item is not a Enum")
|
||||||
|
|
||||||
|
override fun objectToMetaItem(obj: E): MetaItem<*> = MetaItem.ValueItem(obj.asValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> valueList(writer: (T) -> Value = {Value.of(it)}, reader: (Value) -> T): MetaConverter<List<T>> =
|
||||||
|
object : MetaConverter<List<T>> {
|
||||||
|
override fun itemToObject(item: MetaItem<*>): List<T> =
|
||||||
|
item.value?.list?.map(reader) ?: error("The item is not a value list")
|
||||||
|
|
||||||
|
override fun objectToMetaItem(obj: List<T>): MetaItem<*> =
|
||||||
|
MetaItem.ValueItem(obj.map(writer).asValue())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T : Any> MetaConverter<T>.nullableItemToObject(item: MetaItem<*>?): T? = item?.let { itemToObject(it) }
|
||||||
|
fun <T : Any> MetaConverter<T>.nullableObjectToMetaItem(obj: T?): MetaItem<*>? = obj?.let { objectToMetaItem(it) }
|
||||||
|
|
||||||
fun <T : Any> MetaConverter<T>.metaToObject(meta: Meta): T = itemToObject(MetaItem.NodeItem(meta))
|
fun <T : Any> MetaConverter<T>.metaToObject(meta: Meta): T = itemToObject(MetaItem.NodeItem(meta))
|
||||||
fun <T : Any> MetaConverter<T>.valueToObject(value: Value): T = itemToObject(MetaItem.ValueItem(value))
|
fun <T : Any> MetaConverter<T>.valueToObject(value: Value): T = itemToObject(MetaItem.ValueItem(value))
|
||||||
|
@ -40,7 +40,7 @@ data class KeepTransformationRule(val selector: (Name) -> Boolean) :
|
|||||||
meta.sequence().map { it.first }.filter(selector)
|
meta.sequence().map { it.first }.filter(selector)
|
||||||
|
|
||||||
override fun <M : MutableMeta<M>> transformItem(name: Name, item: MetaItem<*>?, target: M) {
|
override fun <M : MutableMeta<M>> transformItem(name: Name, item: MetaItem<*>?, target: M) {
|
||||||
if (selector(name)) target[name] = item
|
if (selector(name)) target.setItem(name, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
* 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.
|
* This operation is rather heavy so it should be used with care in high performance code.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package hep.dataforge.meta
|
package hep.dataforge.meta
|
||||||
|
|
||||||
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
import kotlinx.serialization.json.int
|
import kotlinx.serialization.json.int
|
||||||
import kotlinx.serialization.json.json
|
import kotlinx.serialization.json.json
|
||||||
import kotlinx.serialization.json.jsonArray
|
import kotlinx.serialization.json.jsonArray
|
||||||
@ -17,23 +18,34 @@ class JsonMetaTest {
|
|||||||
}
|
}
|
||||||
"nodeArray" to jsonArray {
|
"nodeArray" to jsonArray {
|
||||||
+json {
|
+json {
|
||||||
"index" to 1
|
"index" to "1"
|
||||||
|
"value" to 2
|
||||||
}
|
}
|
||||||
+json {
|
+json {
|
||||||
"index" to 2
|
"index" to "2"
|
||||||
|
"value" to 3
|
||||||
}
|
}
|
||||||
+json {
|
+json {
|
||||||
"index" to 3
|
"index" to "3"
|
||||||
|
"value" to 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val descriptor = NodeDescriptor{
|
||||||
|
node("nodeArray"){
|
||||||
|
indexKey = "index"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun jsonMetaConversion() {
|
fun jsonMetaConversion() {
|
||||||
val meta = json.toMeta()
|
|
||||||
val reconstructed = meta.toJson()
|
|
||||||
println(json)
|
println(json)
|
||||||
|
val meta = json.toMeta(descriptor)
|
||||||
|
//println(meta)
|
||||||
|
val reconstructed = meta.toJson(descriptor)
|
||||||
println(reconstructed)
|
println(reconstructed)
|
||||||
assertEquals(2, reconstructed["nodeArray"]?.jsonArray?.get(1)?.jsonObject?.get("index")?.int)
|
assertEquals(2, reconstructed["nodeArray"]?.jsonArray?.get(1)?.jsonObject?.get("index")?.int)
|
||||||
|
assertEquals(json,reconstructed)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,7 +19,7 @@ class SchemeTest{
|
|||||||
|
|
||||||
assertEquals(10, meta.values().count())
|
assertEquals(10, meta.values().count())
|
||||||
|
|
||||||
val bNode = styled.getProperty("b").node
|
val bNode = styled.getItem("b").node
|
||||||
|
|
||||||
val aNodes = bNode?.getIndexed("a")
|
val aNodes = bNode?.getIndexed("a")
|
||||||
|
|
||||||
|
@ -1,28 +1,23 @@
|
|||||||
package hep.dataforge.tables
|
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<C : Any>(override val columns: Collection<Column<C>>) : Table<C> {
|
class ColumnTable<T : Any>(override val columns: Collection<Column<T>>) : Table<T> {
|
||||||
private val rowsNum = columns.first().size
|
private val rowsNum = columns.first().size
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(columns.all { it.size == rowsNum }) { "All columns must be of the same size" }
|
require(columns.all { it.size == rowsNum }) { "All columns must be of the same size" }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val rows: List<Row<C>>
|
override val rows: List<Row<T>>
|
||||||
get() = (0 until rowsNum).map { VirtualRow(this, it) }
|
get() = (0 until rowsNum).map { VirtualRow(this, it) }
|
||||||
|
|
||||||
override fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T? {
|
override fun getValue(row: Int, column: String): T? = columns[column]?.get(row)
|
||||||
val value = columns[column]?.get(row)
|
|
||||||
return type.cast(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class VirtualRow<C : Any>(val table: Table<C>, val index: Int) : Row<C> {
|
internal class VirtualRow<T : Any>(val table: Table<T>, val index: Int) : Row<T> {
|
||||||
override fun <T : C> getValue(column: String, type: KClass<out T>): T? = table.getValue(index, column, type)
|
override fun getValue(column: String): T? = table.getValue(index, column)
|
||||||
|
|
||||||
// override fun <T : C> get(columnHeader: ColumnHeader<T>): T? {
|
// override fun <T : C> get(columnHeader: ColumnHeader<T>): T? {
|
||||||
// return table.co[columnHeader][index]
|
// return table.co[columnHeader][index]
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package hep.dataforge.tables
|
|
||||||
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
inline class MapRow<C: Any>(val values: Map<String, C?>) : Row<C> {
|
|
||||||
override fun <T : C> getValue(column: String, type: KClass<out T>): T? {
|
|
||||||
val value = values[column]
|
|
||||||
return type.cast(value)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,5 @@
|
|||||||
package hep.dataforge.tables
|
package hep.dataforge.tables
|
||||||
|
|
||||||
import hep.dataforge.meta.Meta
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mutable table with a fixed size, but dynamic columns
|
* Mutable table with a fixed size, but dynamic columns
|
||||||
*/
|
*/
|
||||||
@ -14,10 +11,7 @@ class MutableColumnTable<C: Any>(val size: Int) : Table<C> {
|
|||||||
VirtualRow(this, it)
|
VirtualRow(this, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T? {
|
override fun getValue(row: Int, column: String): C? = columns[column]?.get(row)
|
||||||
val value = columns[column]?.get(row)
|
|
||||||
return type.cast(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a fixed column to the end of the table
|
* Add a fixed column to the end of the table
|
||||||
@ -35,27 +29,3 @@ class MutableColumnTable<C: Any>(val size: Int) : Table<C> {
|
|||||||
_columns.add(index, column)
|
_columns.add(index, column)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapColumn<T : Any, R : Any>(
|
|
||||||
val source: Column<T>,
|
|
||||||
override val type: KClass<out R>,
|
|
||||||
override val name: String,
|
|
||||||
override val meta: Meta = source.meta,
|
|
||||||
val mapper: (T?) -> R?
|
|
||||||
) : Column<R> {
|
|
||||||
override val size: Int get() = source.size
|
|
||||||
|
|
||||||
override fun get(index: Int): R? = mapper(source[index])
|
|
||||||
}
|
|
||||||
|
|
||||||
class CachedMapColumn<T : Any, R : Any>(
|
|
||||||
val source: Column<T>,
|
|
||||||
override val type: KClass<out R>,
|
|
||||||
override val name: String,
|
|
||||||
override val meta: Meta = source.meta,
|
|
||||||
val mapper: (T?) -> R?
|
|
||||||
) : Column<R> {
|
|
||||||
override val size: Int get() = source.size
|
|
||||||
private val values: HashMap<Int, R?> = HashMap()
|
|
||||||
override fun get(index: Int): R? = values.getOrPut(index) { mapper(source[index]) }
|
|
||||||
}
|
|
@ -9,7 +9,7 @@ class MutableTable<C : Any>(
|
|||||||
override val header: MutableList<ColumnHeader<C>>
|
override val header: MutableList<ColumnHeader<C>>
|
||||||
) : RowTable<C>(rows, header) {
|
) : RowTable<C>(rows, header) {
|
||||||
|
|
||||||
fun <T : C> column(name: String, type: KClass<out T>, meta: Meta): ColumnHeader<T> {
|
fun <R : C> column(name: String, type: KClass<out R>, meta: Meta): ColumnHeader<R> {
|
||||||
val column = SimpleColumnHeader(name, type, meta)
|
val column = SimpleColumnHeader(name, type, meta)
|
||||||
header.add(column)
|
header.add(column)
|
||||||
return column
|
return column
|
||||||
|
@ -4,6 +4,10 @@ import hep.dataforge.meta.Meta
|
|||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
inline class MapRow<C : Any>(val values: Map<String, C?>) : Row<C> {
|
||||||
|
override fun getValue(column: String): C? = values[column]
|
||||||
|
}
|
||||||
|
|
||||||
internal class RowTableColumn<C : Any, T : C>(val table: Table<C>, val header: ColumnHeader<T>) : Column<T> {
|
internal class RowTableColumn<C : Any, T : C>(val table: Table<C>, val header: ColumnHeader<T>) : Column<T> {
|
||||||
override val name: String get() = header.name
|
override val name: String get() = header.name
|
||||||
override val type: KClass<out T> get() = header.type
|
override val type: KClass<out T> get() = header.type
|
||||||
@ -14,8 +18,7 @@ internal class RowTableColumn<C : Any, T : C>(val table: Table<C>, val header: C
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class RowTable<C : Any>(override val rows: List<Row<C>>, override val header: List<ColumnHeader<C>>) : Table<C> {
|
open class RowTable<C : Any>(override val rows: List<Row<C>>, override val header: List<ColumnHeader<C>>) : Table<C> {
|
||||||
override fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T? =
|
override fun getValue(row: Int, column: String): C? = rows[row].getValue(column)
|
||||||
rows[row].getValue(column, type)
|
|
||||||
|
|
||||||
override val columns: List<Column<C>> get() = header.map { RowTableColumn(this, it) }
|
override val columns: List<Column<C>> get() = header.map { RowTableColumn(this, it) }
|
||||||
}
|
}
|
||||||
|
@ -3,32 +3,23 @@ package hep.dataforge.tables
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.cast
|
||||||
//TODO to be removed in 1.3.70
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
internal fun <T : Any> KClass<T>.cast(value: Any?): T? {
|
|
||||||
return when {
|
|
||||||
value == null -> null
|
|
||||||
!isInstance(value) -> error("Expected type is $this, but found ${value::class}")
|
|
||||||
else -> value as T
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finite or infinite row set. Rows are produced in a lazy suspendable [Flow].
|
* Finite or infinite row set. Rows are produced in a lazy suspendable [Flow].
|
||||||
* Each row must contain at least all the fields mentioned in [header].
|
* Each row must contain at least all the fields mentioned in [header].
|
||||||
*/
|
*/
|
||||||
interface Rows<C : Any> {
|
interface Rows<out T : Any> {
|
||||||
val header: TableHeader<C>
|
val header: TableHeader<T>
|
||||||
fun rowFlow(): Flow<Row<C>>
|
fun rowFlow(): Flow<Row<T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Table<C : Any> : Rows<C> {
|
interface Table<out T : Any> : Rows<T> {
|
||||||
fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T?
|
fun getValue(row: Int, column: String): T?
|
||||||
val columns: Collection<Column<C>>
|
val columns: Collection<Column<T>>
|
||||||
override val header: TableHeader<C> get() = columns.toList()
|
override val header: TableHeader<T> get() = columns.toList()
|
||||||
val rows: List<Row<C>>
|
val rows: List<Row<T>>
|
||||||
override fun rowFlow(): Flow<Row<C>> = rows.asFlow()
|
override fun rowFlow(): Flow<Row<T>> = rows.asFlow()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply typed query to this table and return lazy [Flow] of resulting rows. The flow could be empty.
|
* Apply typed query to this table and return lazy [Flow] of resulting rows. The flow could be empty.
|
||||||
@ -40,14 +31,18 @@ interface Table<C : Any> : Rows<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun Collection<Column<*>>.get(name: String): Column<*>? = find { it.name == name }
|
fun <C : Any, T : C> Table<C>.getValue(row: Int, column: String, type: KClass<out T>): T? =
|
||||||
|
type.cast(getValue(row, column))
|
||||||
|
|
||||||
|
operator fun <T : Any> Collection<Column<T>>.get(name: String): Column<T>? = find { it.name == name }
|
||||||
|
|
||||||
inline operator fun <C : Any, reified T : C> Table<C>.get(row: Int, column: String): T? =
|
inline operator fun <C : Any, reified T : C> Table<C>.get(row: Int, column: String): T? =
|
||||||
getValue(row, column, T::class)
|
getValue(row, column, T::class)
|
||||||
|
|
||||||
operator fun <C : Any, T : C> Table<C>.get(row: Int, column: ColumnHeader<T>): T? = getValue(row, column.name, column.type)
|
operator fun <C : Any, T : C> Table<C>.get(row: Int, column: ColumnHeader<T>): T? =
|
||||||
|
getValue(row, column.name, column.type)
|
||||||
|
|
||||||
interface Column<T : Any> : ColumnHeader<T> {
|
interface Column<out T : Any> : ColumnHeader<T> {
|
||||||
val size: Int
|
val size: Int
|
||||||
operator fun get(index: Int): T?
|
operator fun get(index: Int): T?
|
||||||
}
|
}
|
||||||
@ -60,9 +55,11 @@ operator fun <T : Any> Column<T>.iterator() = iterator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Row<C: Any> {
|
interface Row<out T : Any> {
|
||||||
fun <T : C> getValue(column: String, type: KClass<out T>): T?
|
fun getValue(column: String): T?
|
||||||
}
|
}
|
||||||
|
|
||||||
inline operator fun <C : Any, reified T : C> Row<C>.get(column: String): T? = getValue(column, T::class)
|
fun <C : Any, T : C> Row<C>.getValue(column: String, type: KClass<out T>): T? = type.cast(getValue(column))
|
||||||
|
|
||||||
|
inline operator fun <reified T : Any> Row<T>.get(column: String): T? = T::class.cast(getValue(column))
|
||||||
operator fun <C : Any, T : C> Row<C>.get(column: ColumnHeader<T>): T? = getValue(column.name, column.type)
|
operator fun <C : Any, T : C> Row<C>.get(column: ColumnHeader<T>): T? = getValue(column.name, column.type)
|
@ -0,0 +1,60 @@
|
|||||||
|
package hep.dataforge.tables
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A virtual column obtained by transforming Given row to a single value
|
||||||
|
*/
|
||||||
|
class TransformationColumn<T : Any, R : Any>(
|
||||||
|
val table: Table<T>,
|
||||||
|
override val type: KClass<out R>,
|
||||||
|
override val name: String,
|
||||||
|
override val meta: Meta,
|
||||||
|
val mapper: (Row<T>) -> R?
|
||||||
|
) : Column<R> {
|
||||||
|
override val size: Int get() = table.rows.size
|
||||||
|
|
||||||
|
override fun get(index: Int): R? = mapper(table.rows[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A virtual column obtained via transformation of single column with caching results on call (evaluation is lazy).
|
||||||
|
*
|
||||||
|
* Calls are not thread safe
|
||||||
|
*/
|
||||||
|
class CachedTransformationColumn<T : Any, R : Any>(
|
||||||
|
val table: Table<T>,
|
||||||
|
override val type: KClass<out R>,
|
||||||
|
override val name: String,
|
||||||
|
override val meta: Meta,
|
||||||
|
val mapper: (Row<T>) -> R?
|
||||||
|
) : Column<R> {
|
||||||
|
override val size: Int get() = table.rows.size
|
||||||
|
private val values: HashMap<Int, R?> = HashMap()
|
||||||
|
override fun get(index: Int): R? = values.getOrPut(index) { mapper(table.rows[index]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a virtual column from a given column
|
||||||
|
*/
|
||||||
|
inline fun <T : Any, reified R : Any> Table<T>.mapRows(
|
||||||
|
name: String,
|
||||||
|
meta: Meta = Meta.EMPTY,
|
||||||
|
cache: Boolean = false,
|
||||||
|
noinline mapper: (Row<T>) -> R?
|
||||||
|
): Column<R> = if (cache) {
|
||||||
|
CachedTransformationColumn(this, R::class, name, meta, mapper)
|
||||||
|
} else {
|
||||||
|
TransformationColumn(this, R::class, name, meta, mapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Any> Table<T>.mapRowsToDouble(name: String, meta: Meta = Meta.EMPTY, block: (Row<T>) -> Double): RealColumn {
|
||||||
|
val data = DoubleArray(rows.size) { block(rows[it]) }
|
||||||
|
return RealColumn(name, data, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Any> Table<T>.mapRowsToInt(name: String, meta: Meta = Meta.EMPTY, block: (Row<T>) -> Int): IntColumn {
|
||||||
|
val data = IntArray(rows.size) { block(rows[it]) }
|
||||||
|
return IntColumn(name, data, meta)
|
||||||
|
}
|
@ -13,7 +13,6 @@ import kotlinx.io.text.forEachUtf8Line
|
|||||||
import kotlinx.io.text.readUtf8Line
|
import kotlinx.io.text.readUtf8Line
|
||||||
import kotlinx.io.text.readUtf8StringUntilDelimiter
|
import kotlinx.io.text.readUtf8StringUntilDelimiter
|
||||||
import kotlinx.io.text.writeUtf8String
|
import kotlinx.io.text.writeUtf8String
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a lin as a fixed width [Row]
|
* Read a lin as a fixed width [Row]
|
||||||
@ -92,9 +91,9 @@ class TextTable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T : Value> getValue(row: Int, column: String, type: KClass<out T>): T? {
|
override fun getValue(row: Int, column: String): Value? {
|
||||||
val offset = index[row]
|
val offset = index[row]
|
||||||
return type.cast(readAt(offset)[column])
|
return readAt(offset)[column]
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -3,9 +3,8 @@ package hep.dataforge.tables
|
|||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
//interface NumberColumn<N : Number> : Column<N>
|
|
||||||
|
|
||||||
data class RealColumn(
|
class RealColumn(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
val data: DoubleArray,
|
val data: DoubleArray,
|
||||||
override val meta: Meta = Meta.EMPTY
|
override val meta: Meta = Meta.EMPTY
|
||||||
@ -44,12 +43,7 @@ data class RealColumn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> Column<T>.map(meta: Meta = this.meta, block: (T?) -> Double): RealColumn {
|
class IntColumn(
|
||||||
val data = DoubleArray(size) { block(get(it)) }
|
|
||||||
return RealColumn(name, data, meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class IntColumn(
|
|
||||||
override val name: String,
|
override val name: String,
|
||||||
val data: IntArray,
|
val data: IntArray,
|
||||||
override val meta: Meta = Meta.EMPTY
|
override val meta: Meta = Meta.EMPTY
|
||||||
@ -87,8 +81,3 @@ data class IntColumn(
|
|||||||
): IntColumn = IntColumn(name, data, ColumnScheme(metaBuilder).toMeta())
|
): IntColumn = IntColumn(name, data, ColumnScheme(metaBuilder).toMeta())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> Column<T>.map(meta: Meta = this.meta, block: (T?) -> Int): IntColumn {
|
|
||||||
val data = IntArray(size) { block(get(it)) }
|
|
||||||
return IntColumn(name, data, meta)
|
|
||||||
}
|
|
@ -36,7 +36,11 @@ data class TaskModel(
|
|||||||
val dataDependencies = dependencies.filterIsInstance<DataDependency>()
|
val dataDependencies = dependencies.filterIsInstance<DataDependency>()
|
||||||
val taskDependencies = dependencies.filterIsInstance<TaskDependency<*>>()
|
val taskDependencies = dependencies.filterIsInstance<TaskDependency<*>>()
|
||||||
setIndexed("data".toName(), dataDependencies.map { it.toMeta() })
|
setIndexed("data".toName(), dataDependencies.map { it.toMeta() })
|
||||||
setIndexed("task".toName(), taskDependencies.map { it.toMeta() }) { taskDependencies[it].name.toString() }
|
setIndexed(
|
||||||
|
"task".toName(),
|
||||||
|
taskDependencies.map { it.toMeta() }) { _, index ->
|
||||||
|
taskDependencies[index].name.toString()
|
||||||
|
}
|
||||||
//TODO ensure all dependencies are listed
|
//TODO ensure all dependencies are listed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
131
docs/images/df.svg
Normal file
131
docs/images/df.svg
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="210mm"
|
||||||
|
height="297mm"
|
||||||
|
viewBox="0 0 744.09448819 1052.3622047"
|
||||||
|
id="svg3570"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="df_3.svg">
|
||||||
|
<defs
|
||||||
|
id="defs3572">
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath134">
|
||||||
|
<path
|
||||||
|
d="m 0,595.28 841.89,0 L 841.89,0 0,0 0,595.28 Z"
|
||||||
|
id="path136"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.979899"
|
||||||
|
inkscape:cx="290.99791"
|
||||||
|
inkscape:cy="622.26982"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1018"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3575">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<g
|
||||||
|
id="g46"
|
||||||
|
transform="matrix(1.25,0,0,-1.25,447.15562,475.52655)"
|
||||||
|
inkscape:export-xdpi="299.41061"
|
||||||
|
inkscape:export-ydpi="299.41061">
|
||||||
|
<path
|
||||||
|
d="m 0,0 0,16.432 c -7.415,0.038 -12.81,2.792 -14.994,9.821 -2,6.436 0.476,11.31 8.212,16.808 -2.372,4.23 -4.767,8.502 -7.287,12.996 -6.34,-4.254 -12.473,-4.711 -17.872,0.18 -6.348,5.75 -5.782,12.569 -1.036,19.595 -4.983,2.94 -9.579,5.652 -14.504,8.558 -3.428,-7.01 -8.655,-10.518 -15.95,-8.816 -8.161,1.906 -10.745,8.234 -10.582,16.105 l -16.599,0 c 0.4,-7.676 -2.268,-13.46 -9.793,-15.509 -7.719,-2.101 -13.026,1.654 -16.73,8.84 -4.456,-2.528 -8.687,-4.93 -12.927,-7.336 4.661,-7.839 4.704,-13.692 -0.087,-18.435 -1.862,-1.844 -4.611,-3.8 -7.011,-3.873 -4.26,-0.128 -8.561,1.142 -13.704,1.965 -2.108,-3.527 -4.849,-8.114 -7.734,-12.94 7.167,-3.404 11.145,-8.53 9.409,-16.072 -1.924,-8.364 -8.313,-11.09 -16.737,-10.521 l 0,-16.238 c 10.187,-0.896 14.702,-3.561 16.078,-10.002 0.551,-2.58 0.817,-5.899 -0.354,-8.015 -1.955,-3.534 -5.169,-6.371 -8.335,-10.065 1.904,-3.356 4.318,-7.609 6.714,-11.829 8.429,4.235 14.172,3.834 18.801,-0.978 4.637,-4.82 4.883,-10.766 0.327,-19.009 4.859,-2.85 9.546,-5.599 14.193,-8.326 5.229,7.735 10.197,10.412 16.46,8.681 7.893,-2.182 10.451,-8.222 10.32,-15.979 l 16.681,0 c -0.482,7.551 2.121,13.044 9.165,15.339 8.143,2.654 13.424,-1.565 17.358,-8.607 4.381,2.493 8.538,4.859 12.816,7.294 -3.79,6.509 -4.092,12.616 1.096,17.873 5.58,5.655 12.009,5.168 18.663,0.894 2.977,5.01 5.765,9.702 8.619,14.505 -6.371,3.744 -9.773,8.633 -8.323,15.701 C -14.296,-4.379 -9.626,-1.19 0,0"
|
||||||
|
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path48"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g50"
|
||||||
|
transform="matrix(1.25,0,0,-1.25,343.65424,533.29343)"
|
||||||
|
inkscape:export-xdpi="299.41061"
|
||||||
|
inkscape:export-ydpi="299.41061">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c -29.975,0 -54.362,24.387 -54.362,54.362 0,29.976 24.387,54.363 54.362,54.363 29.975,0 54.362,-24.387 54.362,-54.363 C 54.362,24.387 29.975,0 0,0"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path52"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g54"
|
||||||
|
transform="matrix(1.25,0,0,-1.25,343.65424,404.40267)"
|
||||||
|
inkscape:export-xdpi="299.41061"
|
||||||
|
inkscape:export-ydpi="299.41061">
|
||||||
|
<path
|
||||||
|
d="m 0,0 c -26.881,0 -48.75,-21.869 -48.75,-48.75 0,-26.881 21.869,-48.75 48.75,-48.75 26.881,0 48.75,21.869 48.75,48.75 C 48.75,-21.869 26.881,0 0,0"
|
||||||
|
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path56"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(1.25,0,0,-1.25,-100.98176,778.91455)"
|
||||||
|
clip-path="url(#clipPath134)"
|
||||||
|
id="g132"
|
||||||
|
inkscape:export-xdpi="299.41061"
|
||||||
|
inkscape:export-ydpi="299.41061">
|
||||||
|
<g
|
||||||
|
transform="translate(355.3533,210.288)"
|
||||||
|
id="g138">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path140"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 c -22.83,0.215 -41.145,18.741 -40.964,41.436 0.183,22.8 18.723,41.22 41.357,41.089 C 23.53,82.392 41.585,63.584 41.363,39.847 41.16,18.168 22.085,-0.208 0,0" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(339.7856,261.6331)"
|
||||||
|
id="g142">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path144"
|
||||||
|
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 -3.934,-13.762 7.014,0 L 7.014,0 0,0 Z m 15.961,24.284 5.261,0 c 1.002,0 1.376,-1.026 1.124,-3.076 L 11.562,-16.514 c -0.9,-2.051 -1.902,-3.076 -3.008,-3.076 l -13.231,0.162 -8.142,0 c -1.044,0 -1.45,0.917 -1.217,2.753 L -9.5,-0.81 c 2.289,3.994 4.531,5.991 6.723,5.991 l 11.272,0 4.721,16.513 c 0.87,1.726 1.785,2.59 2.745,2.59" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(365.52,266.6515)"
|
||||||
|
id="g146">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path148"
|
||||||
|
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 18.538,0 c 1.35,-0.243 1.797,-1.161 1.342,-2.753 l -2.175,-7.608 c -0.679,-2.374 -2.062,-3.561 -4.149,-3.561 l -3.007,0 c -2.087,0 -2.838,1.024 -2.252,3.075 l 1.62,5.666 -7.014,0 -3.935,-13.761 4.886,0 c 1.356,0 1.815,-1.025 1.375,-3.076 -0.378,-1.321 -1.377,-2.185 -2.995,-2.59 l -4.886,0 -4.674,-16.351 c -0.868,-1.943 -1.815,-2.915 -2.837,-2.915 l -5.01,0 c -1.403,0.351 -1.877,1.323 -1.422,2.915 l 9.998,34.968 C -4.287,-1.997 -2.088,0 0,0" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 6.4 KiB |
Loading…
Reference in New Issue
Block a user