Merge pull request #52 from mipt-npm/dev

0.1.8
This commit is contained in:
Alexander Nozik 2020-07-05 15:32:13 +03:00 committed by GitHub
commit d951668911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 817 additions and 710 deletions

View File

@ -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.
<hr>
<!--<hr>
**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>
**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>
@ -88,12 +88,12 @@ In this section we will try to cover DataForge main ideas in the form of questio
**Q:** How does DataForge compare to cluster computation frameworks like Hadoop or Spark?
**A:** Again, it is not the purpose of DataForge to replace cluster software. DataForge has some internal parallelism mechanics and implementations, but they are most certainly worse then specially developed programs. Still, DataForge is not fixed on one single implementation. Your favourite parallel processing tool could be still used as a back-end for the DataForge. With full benefit of configuration tools, integrations and no performance overhead.
<!--
<hr>
**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>

View File

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

View File

@ -16,7 +16,6 @@ import kotlin.reflect.KClass
@ExperimentalUnsignedTypes
data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
interface EnvelopeFormat : IOFormat<Envelope> {
val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat
@ -33,6 +32,10 @@ interface EnvelopeFormat : IOFormat<Envelope> {
override fun Output.writeObject(obj: Envelope): Unit = writeEnvelope(obj)
}
fun EnvelopeFormat.readPartial(input: Input) = input.readPartial()
fun EnvelopeFormat.read(input: Input) = input.readObject()
@Type(ENVELOPE_FORMAT_TYPE)
interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat {
override val name: Name get() = "envelope".asName()

View File

@ -166,6 +166,8 @@ class TaggedEnvelopeFormat(
override fun Input.readObject(): Envelope =
default.run { readObject() }
}
}

View File

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

View File

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

View File

@ -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
}
}

View File

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

View File

@ -2,11 +2,12 @@
package hep.dataforge.meta
import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY
import hep.dataforge.meta.descriptors.ItemDescriptor
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.names.NameToken
import hep.dataforge.names.toName
import hep.dataforge.names.withIndex
import hep.dataforge.values.*
import kotlinx.serialization.json.*
@ -35,29 +36,29 @@ private fun String.toJsonKey(descriptor: ItemDescriptor?) = descriptor?.attribut
/**
* Convert given [Meta] to [JsonObject]. Primitives and nodes are copied as is, same name siblings are treated as json arrays
*/
fun Meta.toJson(descriptor: NodeDescriptor? = null, index: String? = null): JsonObject {
private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String?): JsonObject {
val elementMap = HashMap<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 -> {
value.toJson(itemDescriptor as? ValueDescriptor)
}
is MetaItem.NodeItem -> {
node.toJson(itemDescriptor as? NodeDescriptor, index)
node.toJsonWithIndex(itemDescriptor as? NodeDescriptor, index)
}
}
fun addElement(key: String) {
val itemDescriptor = descriptor?.items?.get(key)
val jsonKey = key.toJsonKey(itemDescriptor)
val items = getIndexed(key)
val items: Map<String, MetaItem<*>> = getIndexed(key)
when (items.size) {
0 -> {
//do nothing
}
1 -> {
elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor)
elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor, null)
}
else -> {
val array = jsonArray {
@ -73,38 +74,29 @@ fun Meta.toJson(descriptor: NodeDescriptor? = null, index: String? = null): Json
((descriptor?.items?.keys ?: emptySet()) + items.keys.map { it.body }).forEach(::addElement)
if (index != null) {
elementMap["@index"] = JsonPrimitive(index)
if (indexValue != null) {
val indexKey = descriptor?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
elementMap[indexKey] = JsonPrimitive(indexValue)
}
return JsonObject(elementMap)
// // use descriptor keys in the order they are declared
// val keys = (descriptor?.items?.keys ?: emptySet()) + this.items.keys.map { it.body }
//
// //TODO search for same name siblings and arrange them into arrays
// val map = this.items.entries.associate { (name, item) ->
// val itemDescriptor = descriptor?.items?.get(name.body)
// val key = name.toJsonKey(itemDescriptor)
// val value = when (item) {
// is MetaItem.ValueItem -> {
// item.value.toJson(itemDescriptor as? ValueDescriptor)
// }
// is MetaItem.NodeItem -> {
// item.node.toJson(itemDescriptor as? NodeDescriptor)
// }
// }
// key to value
// }
// return JsonObject(map)
}
fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): Meta = JsonMeta(this, descriptor)
fun Meta.toJson(descriptor: NodeDescriptor? = null): JsonObject = toJsonWithIndex(descriptor, null)
fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): JsonMeta = JsonMeta(this, descriptor)
fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
return when (this) {
JsonNull -> Null
else -> this.content.parseValue() // Optimize number and boolean parsing
is JsonLiteral -> {
when (body) {
true -> True
false -> False
is Number -> NumberValue(body as Number)
else -> StringValue(content)
}
}
}
}
@ -122,18 +114,15 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
val value = if (isEmpty()) {
Null
} else {
ListValue(
map<JsonElement, Value> {
//We already checked that all values are primitives
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
}
)
map<JsonElement, Value> {
//We already checked that all values are primitives
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
}.asValue()
}
MetaItem.ValueItem(value)
} else {
json {
"@value" to this@toMetaItem
}.toMetaItem(descriptor)
//We can't return multiple items therefore we create top level node
json { JSON_ARRAY_KEY to this@toMetaItem }.toMetaItem(descriptor)
}
}
}
@ -143,44 +132,52 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
*/
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() {
@Suppress("UNCHECKED_CAST")
private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement): Unit {
val itemDescriptor = descriptor?.items?.get(key)
when (value) {
is JsonPrimitive -> {
this[key] =
MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) as MetaItem<JsonMeta>
}
is JsonObject -> {
this[key] = MetaItem.NodeItem(
JsonMeta(
value,
itemDescriptor as? NodeDescriptor
)
)
}
is JsonArray -> {
when {
value.all { it is JsonPrimitive } -> {
val listValue = ListValue(
value.map {
//We already checked that all values are primitives
(it as JsonPrimitive).toValue(itemDescriptor as? ValueDescriptor)
}
private fun buildItems(): Map<NameToken, MetaItem<JsonMeta>> {
val map = LinkedHashMap<NameToken, MetaItem<JsonMeta>>()
json.forEach { (jsonKey, value) ->
val key = NameToken(jsonKey)
val itemDescriptor = descriptor?.items?.get(jsonKey)
when (value) {
is JsonPrimitive -> {
map[key] = MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor))
}
is JsonObject -> {
map[key] = MetaItem.NodeItem(
JsonMeta(
value,
itemDescriptor as? NodeDescriptor
)
this[key] = MetaItem.ValueItem(listValue) as MetaItem<JsonMeta>
}
else -> value.forEachIndexed { index, jsonElement ->
this["$key[$index]"] = jsonElement.toMetaItem(itemDescriptor)
}
)
}
is JsonArray -> if (value.all { it is JsonPrimitive }) {
val listValue = ListValue(
value.map {
//We already checked that all values are primitives
(it as JsonPrimitive).toValue(itemDescriptor as? ValueDescriptor)
}
)
map[key] = MetaItem.ValueItem(listValue)
} else value.forEachIndexed { index, jsonElement ->
val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: NodeDescriptor.DEFAULT_INDEX_KEY
val indexValue: String = (jsonElement as? JsonObject)
?.get(indexKey)?.contentOrNull
?: index.toString() //In case index is non-string, the backward transformation will be broken.
val token = key.withIndex(indexValue)
map[token] = jsonElement.toMetaItem(itemDescriptor)
}
}
}
return map
}
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy {
val map = LinkedHashMap<String, MetaItem<JsonMeta>>()
json.forEach { (key, value) -> map[key] = value }
map.mapKeys { it.key.toName().first()!! }
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy(::buildItems)
companion object{
/**
* A key representing top-level json array of nodes, which could not be directly represented by a meta node
*/
const val JSON_ARRAY_KEY = "@jsonArray"
}
}

View File

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

View File

@ -1,98 +0,0 @@
package hep.dataforge.meta
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.values.Value
import kotlin.jvm.JvmName
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/* Meta delegates */
open class MetaDelegate(
open val owner: Meta,
val key: Name? = null,
open val default: MetaItem<*>? = null
) : ReadOnlyProperty<Any?, MetaItem<*>?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? {
return owner[key ?: property.name.asName()] ?: default
}
}
class LazyMetaDelegate(
owner: Meta,
key: Name? = null,
defaultProvider: () -> MetaItem<*>? = { null }
) : MetaDelegate(owner, key) {
override val default by lazy(defaultProvider)
}
class DelegateWrapper<T, R>(
val delegate: ReadOnlyProperty<Any?, T>,
val reader: (T) -> R
) : ReadOnlyProperty<Any?, R> {
override fun getValue(thisRef: Any?, property: KProperty<*>): R {
return reader(delegate.getValue(thisRef, property))
}
}
fun <T, R> ReadOnlyProperty<Any?, T>.map(reader: (T) -> R): DelegateWrapper<T, R> =
DelegateWrapper(this, reader)
fun Meta.item(default: Any? = null, key: Name? = null): MetaDelegate =
MetaDelegate(this, key, default?.let { MetaItem.of(it) })
fun Meta.lazyItem(key: Name? = null, defaultProvider: () -> Any?): LazyMetaDelegate =
LazyMetaDelegate(this, key) { defaultProvider()?.let { MetaItem.of(it) } }
//TODO add caching for sealed nodes
//Read-only delegates for Metas
/**
* A property delegate that uses custom key
*/
fun Meta.value(default: Value? = null, key: Name? = null) =
item(default, key).map { it.value }
fun Meta.string(default: String? = null, key: Name? = null) =
item(default, key).map { it.string }
fun Meta.boolean(default: Boolean? = null, key: Name? = null) =
item(default, key).map { it.boolean }
fun Meta.number(default: Number? = null, key: Name? = null) =
item(default, key).map { it.number }
fun Meta.node(key: Name? = null) =
item(key).map { it.node }
@JvmName("safeString")
fun Meta.string(default: String, key: Name? = null) =
item(default, key).map { it.string!! }
@JvmName("safeBoolean")
fun Meta.boolean(default: Boolean, key: Name? = null) =
item(default, key).map { it.boolean!! }
@JvmName("safeNumber")
fun Meta.number(default: Number, key: Name? = null) =
item(default, key).map { it.number!! }
@JvmName("lazyString")
fun Meta.string(key: Name? = null, default: () -> String) =
lazyItem(key, default).map { it.string!! }
@JvmName("lazyBoolean")
fun Meta.boolean(key: Name? = null, default: () -> Boolean) =
lazyItem(key, default).map { it.boolean!! }
@JvmName("lazyNumber")
fun Meta.number(key: Name? = null, default: () -> Number) =
lazyItem(key, default).map { it.number!! }
inline fun <reified E : Enum<E>> Meta.enum(default: E, key: Name? = null) =
item(default, key).map { it.enum<E>()!! }

View File

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

View File

@ -3,9 +3,12 @@ package hep.dataforge.meta
import hep.dataforge.names.*
import hep.dataforge.values.Value
interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M> {
interface MutableItemProvider : ItemProvider {
fun setItem(name: Name, item: MetaItem<*>?)
}
interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M>, MutableItemProvider {
override val items: Map<NameToken, MetaItem<M>>
operator fun set(name: Name, item: MetaItem<*>?)
// fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit)
// fun removeListener(owner: Any? = null)
}
@ -49,7 +52,7 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
*/
internal abstract fun empty(): M
override operator fun set(name: Name, item: MetaItem<*>?) {
override fun setItem(name: Name, item: MetaItem<*>?) {
when (name.length) {
0 -> error("Can't setValue meta item for empty name")
1 -> {
@ -63,7 +66,7 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
if (items[token] == null) {
replaceItem(token, null, MetaItem.NodeItem(empty()))
}
items[token]?.node!![name.cutFirst()] = item
items[token]?.node!!.setItem(name.cutFirst(), item)
}
}
}
@ -71,27 +74,21 @@ abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(),
@Suppress("NOTHING_TO_INLINE")
inline fun MutableMeta<*>.remove(name: Name) = set(name, null)
inline fun MutableMeta<*>.remove(name: Name) = setItem(name, null)
@Suppress("NOTHING_TO_INLINE")
inline fun MutableMeta<*>.remove(name: String) = remove(name.toName())
fun MutableMeta<*>.setValue(name: Name, value: Value) = set(name, MetaItem.ValueItem(value))
operator fun MutableMeta<*>.set(name: Name, item: MetaItem<*>?) = setItem(name, item)
fun MutableMeta<*>.setValue(name: Name, value: Value) = setItem(name, MetaItem.ValueItem(value))
fun MutableMeta<*>.setValue(name: String, value: Value) = set(name.toName(), value)
fun MutableMeta<*>.setItem(name: Name, item: MetaItem<*>?) {
when (item) {
null -> remove(name)
is MetaItem.ValueItem -> setValue(name, item.value)
is MetaItem.NodeItem<*> -> setNode(name, item.node)
}
}
fun MutableMeta<*>.setItem(name: String, item: MetaItem<*>?) = setItem(name.toName(), item)
fun MutableMeta<*>.setNode(name: Name, node: Meta) =
set(name, MetaItem.NodeItem(node))
setItem(name, MetaItem.NodeItem(node))
fun MutableMeta<*>.setNode(name: String, node: Meta) = setNode(name.toName(), node)
@ -135,23 +132,23 @@ fun <M : MutableMeta<M>> M.update(meta: Meta) {
fun MutableMeta<*>.setIndexedItems(
name: Name,
items: Iterable<MetaItem<*>>,
indexFactory: MetaItem<*>.(index: Int) -> String = { it.toString() }
indexFactory: (MetaItem<*>, index: Int) -> String = { _, index -> index.toString() }
) {
val tokens = name.tokens.toMutableList()
val last = tokens.last()
items.forEachIndexed { index, meta ->
val indexedToken = NameToken(last.body, last.index + meta.indexFactory(index))
val indexedToken = NameToken(last.body, last.index + indexFactory(meta, index))
tokens[tokens.lastIndex] = indexedToken
set(Name(tokens), meta)
setItem(Name(tokens), meta)
}
}
fun MutableMeta<*>.setIndexed(
name: Name,
metas: Iterable<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)
@ -178,7 +175,7 @@ fun <M : MutableMeta<M>> M.append(name: String, value: Any?) = append(name.toNam
*/
@DFExperimental
fun <M : AbstractMutableMeta<M>> M.edit(name: Name, builder: M.() -> Unit) {
val item = when(val existingItem = get(name)){
val item = when (val existingItem = get(name)) {
null -> empty().also { set(name, it) }
is MetaItem.NodeItem<M> -> existingItem.node
else -> error("Can't edit value meta item")

View File

@ -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>()!! }

View File

@ -2,10 +2,7 @@ package hep.dataforge.meta.descriptors
import hep.dataforge.meta.*
import hep.dataforge.names.*
import hep.dataforge.values.False
import hep.dataforge.values.True
import hep.dataforge.values.Value
import hep.dataforge.values.ValueType
import hep.dataforge.values.*
@DFBuilder
sealed class ItemDescriptor(val config: Config) {
@ -95,6 +92,11 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
*/
var default by config.node()
/**
* An index field by which this node is identified in case of same name siblings construct
*/
var indexKey by config.string(DEFAULT_INDEX_KEY)
val items: Map<String, ItemDescriptor>
get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) ->
val node = item.node ?: error("Node descriptor must be a node")
@ -182,6 +184,8 @@ class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) {
val ITEM_KEY = "item".asName()
val IS_NODE_KEY = "@isNode".asName()
const val DEFAULT_INDEX_KEY = "@index"
inline operator fun invoke(block: NodeDescriptor.() -> Unit) = NodeDescriptor().apply(block)
//TODO infer descriptor from spec
@ -234,9 +238,7 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
*
* @return
*/
var type: List<ValueType> by config.item().transform {
it?.value?.list?.map { v -> ValueType.valueOf(v.string) } ?: emptyList()
}
var type: List<ValueType>? by config.listValue { ValueType.valueOf(it.string) }
fun type(vararg t: ValueType) {
this.type = listOf(*t)
@ -250,9 +252,8 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
* @return
*/
fun isAllowedValue(value: Value): Boolean {
return (type.isEmpty() || type.contains(ValueType.STRING) || type.contains(value.type)) && (allowedValues.isEmpty() || allowedValues.contains(
value
))
return (type?.let { it.contains(ValueType.STRING) || it.contains(value.type) } ?: true)
&& (allowedValues.isEmpty() || allowedValues.contains(value))
}
/**
@ -261,13 +262,19 @@ class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) {
*
* @return
*/
var allowedValues: List<Value> by config.value().transform {
when {
it?.list != null -> it.list
type.size == 1 && type[0] === ValueType.BOOLEAN -> listOf(True, False)
else -> emptyList()
var allowedValues: List<Value> by config.item().convert(
reader = {
val value = it.value
when {
value?.list != null -> value.list
type?.let { type -> type.size == 1 && type[0] === ValueType.BOOLEAN} ?: false -> listOf(True, False)
else -> emptyList()
}
},
writer = {
MetaItem.ValueItem(it.asValue())
}
}
)
/**
* Allow given list of value and forbid others

View File

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

View File

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

View File

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

View File

@ -113,6 +113,8 @@ data class NameToken(val body: String, val index: String = "") {
}
}
fun NameToken.withIndex(newIndex: String) = NameToken(body, newIndex)
/**
* Convert a [String] to name parsing it and extracting name tokens and index syntax.
* This operation is rather heavy so it should be used with care in high performance code.

View File

@ -1,5 +1,6 @@
package hep.dataforge.meta
import hep.dataforge.meta.descriptors.NodeDescriptor
import kotlinx.serialization.json.int
import kotlinx.serialization.json.json
import kotlinx.serialization.json.jsonArray
@ -17,23 +18,34 @@ class JsonMetaTest {
}
"nodeArray" to jsonArray {
+json {
"index" to 1
"index" to "1"
"value" to 2
}
+json {
"index" to 2
"index" to "2"
"value" to 3
}
+json {
"index" to 3
"index" to "3"
"value" to 4
}
}
}
val descriptor = NodeDescriptor{
node("nodeArray"){
indexKey = "index"
}
}
@Test
fun jsonMetaConversion() {
val meta = json.toMeta()
val reconstructed = meta.toJson()
println(json)
val meta = json.toMeta(descriptor)
//println(meta)
val reconstructed = meta.toJson(descriptor)
println(reconstructed)
assertEquals(2, reconstructed["nodeArray"]?.jsonArray?.get(1)?.jsonObject?.get("index")?.int)
assertEquals(json,reconstructed)
}
}

View File

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

View File

@ -1,28 +1,23 @@
package hep.dataforge.tables
import kotlin.reflect.KClass
/**
* @param C bottom type for all columns in the table
* @param T bottom type for all columns in the table
*/
class ColumnTable<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
init {
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) }
override fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T? {
val value = columns[column]?.get(row)
return type.cast(value)
}
override fun getValue(row: Int, column: String): T? = columns[column]?.get(row)
}
internal class VirtualRow<C : Any>(val table: Table<C>, val index: Int) : Row<C> {
override fun <T : C> getValue(column: String, type: KClass<out T>): T? = table.getValue(index, column, type)
internal class VirtualRow<T : Any>(val table: Table<T>, val index: Int) : Row<T> {
override fun getValue(column: String): T? = table.getValue(index, column)
// override fun <T : C> get(columnHeader: ColumnHeader<T>): T? {
// return table.co[columnHeader][index]

View File

@ -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)
}
}

View File

@ -1,8 +1,5 @@
package hep.dataforge.tables
import hep.dataforge.meta.Meta
import kotlin.reflect.KClass
/**
* 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)
}
override fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T? {
val value = columns[column]?.get(row)
return type.cast(value)
}
override fun getValue(row: Int, column: String): C? = columns[column]?.get(row)
/**
* 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)
}
}
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]) }
}

View File

@ -9,7 +9,7 @@ class MutableTable<C : Any>(
override val header: MutableList<ColumnHeader<C>>
) : 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)
header.add(column)
return column

View File

@ -4,6 +4,10 @@ import hep.dataforge.meta.Meta
import kotlinx.coroutines.flow.toList
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> {
override val name: String get() = header.name
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> {
override fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T? =
rows[row].getValue(column, type)
override fun getValue(row: Int, column: String): C? = rows[row].getValue(column)
override val columns: List<Column<C>> get() = header.map { RowTableColumn(this, it) }
}

View File

@ -3,32 +3,23 @@ package hep.dataforge.tables
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlin.reflect.KClass
//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
}
}
import kotlin.reflect.cast
/**
* 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].
*/
interface Rows<C : Any> {
val header: TableHeader<C>
fun rowFlow(): Flow<Row<C>>
interface Rows<out T : Any> {
val header: TableHeader<T>
fun rowFlow(): Flow<Row<T>>
}
interface Table<C : Any> : Rows<C> {
fun <T : C> getValue(row: Int, column: String, type: KClass<out T>): T?
val columns: Collection<Column<C>>
override val header: TableHeader<C> get() = columns.toList()
val rows: List<Row<C>>
override fun rowFlow(): Flow<Row<C>> = rows.asFlow()
interface Table<out T : Any> : Rows<T> {
fun getValue(row: Int, column: String): T?
val columns: Collection<Column<T>>
override val header: TableHeader<T> get() = columns.toList()
val rows: List<Row<T>>
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.
@ -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? =
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
operator fun get(index: Int): T?
}
@ -60,9 +55,11 @@ operator fun <T : Any> Column<T>.iterator() = iterator {
}
}
interface Row<C: Any> {
fun <T : C> getValue(column: String, type: KClass<out T>): T?
interface Row<out T : Any> {
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)

View File

@ -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)
}

View File

@ -13,7 +13,6 @@ import kotlinx.io.text.forEachUtf8Line
import kotlinx.io.text.readUtf8Line
import kotlinx.io.text.readUtf8StringUntilDelimiter
import kotlinx.io.text.writeUtf8String
import kotlin.reflect.KClass
/**
* 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]
return type.cast(readAt(offset)[column])
return readAt(offset)[column]
}
companion object {

View File

@ -3,9 +3,8 @@ package hep.dataforge.tables
import hep.dataforge.meta.Meta
import kotlin.reflect.KClass
//interface NumberColumn<N : Number> : Column<N>
data class RealColumn(
class RealColumn(
override val name: String,
val data: DoubleArray,
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 {
val data = DoubleArray(size) { block(get(it)) }
return RealColumn(name, data, meta)
}
data class IntColumn(
class IntColumn(
override val name: String,
val data: IntArray,
override val meta: Meta = Meta.EMPTY
@ -86,9 +80,4 @@ data class IntColumn(
noinline metaBuilder: ColumnScheme.() -> Unit
): 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)
}

View File

@ -36,7 +36,11 @@ data class TaskModel(
val dataDependencies = dependencies.filterIsInstance<DataDependency>()
val taskDependencies = dependencies.filterIsInstance<TaskDependency<*>>()
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
}
}

131
docs/images/df.svg Normal file
View 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