From 1b46d00a91b80b41667a644b632dd1924d661828 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 29 Nov 2020 10:18:52 +0300 Subject: [PATCH 01/59] Changed the logic of `Value::isList` for serialization --- CHANGELOG.md | 3 +++ build.gradle.kts | 2 +- dataforge-meta/api/dataforge-meta.api | 6 ++++-- .../commonMain/kotlin/hep/dataforge/values/Value.kt | 12 +++++++----- .../kotlin/hep/dataforge/values/exoticValues.kt | 10 ++++++---- .../kotlin/hep/dataforge/values/valueExtensions.kt | 5 +++-- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5018fb97..5904ceae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added ### Changed +- `ListValue` and `DoubleArrayValue` implement `Iterable`. +- Changed the logic of `Value::isList` to check for type instead of size ### Deprecated @@ -12,6 +14,7 @@ ### Fixed ### Security + ## [0.2.0] ### Added diff --git a/build.gradle.kts b/build.gradle.kts index 7d138083..00f0dd33 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.2.0") +val dataforgeVersion by extra("0.2.1-dev-1") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index f4b8d3ed..4100ccb7 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -813,7 +813,7 @@ public final class hep/dataforge/names/NameTokenKt { public static final fun withIndex (Lhep/dataforge/names/NameToken;Ljava/lang/String;)Lhep/dataforge/names/NameToken; } -public final class hep/dataforge/values/DoubleArrayValue : hep/dataforge/values/Value { +public final class hep/dataforge/values/DoubleArrayValue : hep/dataforge/values/Value, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { public fun ([D)V public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; @@ -824,6 +824,7 @@ public final class hep/dataforge/values/DoubleArrayValue : hep/dataforge/values/ public synthetic fun getValue ()Ljava/lang/Object; public fun getValue ()[D public fun hashCode ()I + public fun iterator ()Ljava/util/Iterator; public fun toString ()Ljava/lang/String; } @@ -869,7 +870,7 @@ public final class hep/dataforge/values/LazyParsedValue : hep/dataforge/values/V public fun toString ()Ljava/lang/String; } -public final class hep/dataforge/values/ListValue : hep/dataforge/values/Value { +public final class hep/dataforge/values/ListValue : hep/dataforge/values/Value, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { public fun (Ljava/util/List;)V public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; @@ -879,6 +880,7 @@ public final class hep/dataforge/values/ListValue : hep/dataforge/values/Value { public synthetic fun getValue ()Ljava/lang/Object; public fun getValue ()Ljava/util/List; public fun hashCode ()I + public fun iterator ()Ljava/util/Iterator; public fun toString ()Ljava/lang/String; } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt index ca78f0ae..c81651df 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt @@ -105,7 +105,7 @@ public object Null : Value { * Singleton true value */ public object True : Value { - override val value: Any? get() = true + override val value: Any get() = true override val type: ValueType get() = ValueType.BOOLEAN override val number: Number get() = 1.0 override val string: String get() = "true" @@ -120,7 +120,7 @@ public object True : Value { * Singleton false value */ public object False : Value { - override val value: Any? get() = false + override val value: Any get() = false override val type: ValueType get() = ValueType.BOOLEAN override val number: Number get() = -1.0 override val string: String get() = "false" @@ -132,7 +132,7 @@ public object False : Value { } public class NumberValue(override val number: Number) : Value { - override val value: Any? get() = number + override val value: Any get() = number override val type: ValueType get() = ValueType.NUMBER override val string: String get() = number.toString() @@ -155,7 +155,7 @@ public class NumberValue(override val number: Number) : Value { } public class StringValue(override val string: String) : Value { - override val value: Any? get() = string + override val value: Any get() = string override val type: ValueType get() = ValueType.STRING override val number: Number get() = string.toDouble() @@ -182,7 +182,7 @@ public class EnumValue>(override val value: E) : Value { override fun toString(): String = value.toString() } -public class ListValue(override val list: List) : Value { +public class ListValue(override val list: List) : Value, Iterable { init { require(list.isNotEmpty()) { "Can't create list value from empty list" } } @@ -194,6 +194,8 @@ public class ListValue(override val list: List) : Value { override fun toString(): String = list.joinToString(prefix = "[", postfix = "]") + override fun iterator(): Iterator = list.iterator() + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Value) return false diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/exoticValues.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/exoticValues.kt index 4b04a036..d961b4a9 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/exoticValues.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/exoticValues.kt @@ -15,7 +15,7 @@ public class LazyParsedValue(override val string: String) : Value { override fun equals(other: Any?): Boolean = other is Value && this.parsedValue == other - override fun hashCode(): Int = string.hashCode() + override fun hashCode(): Int = string.hashCode() } public fun String.lazyParseValue(): LazyParsedValue = LazyParsedValue(this) @@ -23,7 +23,7 @@ public fun String.lazyParseValue(): LazyParsedValue = LazyParsedValue(this) /** * A performance optimized version of list value for doubles */ -public class DoubleArrayValue(override val value: DoubleArray) : Value { +public class DoubleArrayValue(override val value: DoubleArray) : Value, Iterable { override val type: ValueType get() = ValueType.NUMBER override val number: Double get() = value.first() override val string: String get() = value.first().toString() @@ -43,7 +43,9 @@ public class DoubleArrayValue(override val value: DoubleArray) : Value { return value.contentHashCode() } - override fun toString(): String = list.joinToString (prefix = "[", postfix = "]") + override fun toString(): String = list.joinToString(prefix = "[", postfix = "]") + + override fun iterator(): Iterator = value.iterator() } -public fun DoubleArray.asValue(): Value = if(isEmpty()) Null else DoubleArrayValue(this) +public fun DoubleArray.asValue(): Value = if (isEmpty()) Null else DoubleArrayValue(this) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/valueExtensions.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/valueExtensions.kt index 2171c4ea..50b15aed 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/valueExtensions.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/valueExtensions.kt @@ -9,9 +9,10 @@ import hep.dataforge.meta.MetaBuilder public fun Value.isNull(): Boolean = this == Null /** - * Check if value is list + * Check if value is list. This method checks the type of the value, not the number of the elements. + * So it will return `true` for empty lists and lists of one elements. */ -public fun Value.isList(): Boolean = this.list.size > 1 +public fun Value.isList(): Boolean = this is Iterable<*> public val Value.boolean: Boolean get() = this == True From 47d49d1e0e0679506b384da681e1aa12bb21020f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 3 Dec 2020 13:35:19 +0300 Subject: [PATCH 02/59] Minor change to meta serialization descriptors --- build.gradle.kts | 2 +- .../src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt | 2 +- .../commonMain/kotlin/hep/dataforge/values/ValueSerializer.kt | 1 - settings.gradle.kts | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 00f0dd33..6e9893cb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.2.1-dev-1") +val dataforgeVersion by extra("0.2.1-dev-2") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt index b20adba8..9bf5560d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt @@ -58,7 +58,7 @@ public object MetaSerializer : KSerializer { MetaItemSerializer//MetaItem.serializer(MetaSerializer) ) - override val descriptor: SerialDescriptor get() = mapSerializer.descriptor + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Meta") override fun deserialize(decoder: Decoder): Meta { return if (decoder is JsonDecoder) { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/ValueSerializer.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/ValueSerializer.kt index 08b87c18..9dd15b7d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/ValueSerializer.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/ValueSerializer.kt @@ -27,7 +27,6 @@ public object ValueSerializer : KSerializer { } } - override fun deserialize(decoder: Decoder): Value { val isList = decoder.decodeBoolean() return if (isList) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 1d15b239..ac734625 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,7 +27,7 @@ pluginManagement { include( ":dataforge-meta", ":dataforge-io", -// ":dataforge-io:dataforge-io-yaml", + ":dataforge-io:dataforge-io-yaml", ":dataforge-context", ":dataforge-data", // ":dataforge-output", From 4c98d62e8f94a53a1d5baaea21e136a367508d66 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 4 Dec 2020 10:19:42 +0300 Subject: [PATCH 03/59] Implement YamlMetaFormat with yaml.kt --- .../dataforge-io-yaml/build.gradle.kts | 24 ++-- .../io/yaml/FrontMatterEnvelopeFormat.kt | 2 +- .../hep/dataforge/io/yaml/YamlMetaFormat.kt | 122 ++++++++++++++++++ .../dataforge/io/yaml/YamlMetaFormatTest.kt | 0 .../hep/dataforge/io/yaml/YamlMetaFormat.kt | 56 -------- .../hep/dataforge/meta/MetaSerializer.kt | 2 +- 6 files changed, 140 insertions(+), 66 deletions(-) rename dataforge-io/dataforge-io-yaml/src/{main => jvmMain}/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt (98%) create mode 100644 dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt rename dataforge-io/dataforge-io-yaml/src/{test => jvmTest}/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt (100%) delete mode 100644 dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt diff --git a/dataforge-io/dataforge-io-yaml/build.gradle.kts b/dataforge-io/dataforge-io-yaml/build.gradle.kts index 720e1fba..e5b23ce7 100644 --- a/dataforge-io/dataforge-io-yaml/build.gradle.kts +++ b/dataforge-io/dataforge-io-yaml/build.gradle.kts @@ -1,16 +1,24 @@ plugins { - id("ru.mipt.npm.jvm") + id("ru.mipt.npm.mpp") } description = "YAML meta IO" -kscience { - useSerialization { - yaml() - } +repositories{ + jcenter() } -dependencies { - api(project(":dataforge-io")) - api("org.yaml:snakeyaml:1.26") +kscience { + useSerialization() +} + +kotlin { + sourceSets { + jvmMain{ + dependencies { + api(project(":dataforge-io")) + api("net.mamoe.yamlkt:yamlkt:${ru.mipt.npm.gradle.KScienceVersions.Serialization.yamlKtVersion}") + } + } + } } diff --git a/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt similarity index 98% rename from dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt rename to dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt index aa2537be..56a01d92 100644 --- a/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt @@ -86,7 +86,7 @@ public class FrontMatterEnvelopeFormat( } public companion object : EnvelopeFormatFactory { - public const val SEPARATOR = "---" + public const val SEPARATOR: String = "---" private val metaTypeRegex = "---(\\w*)\\s*".toRegex() diff --git a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt new file mode 100644 index 00000000..5769c5b1 --- /dev/null +++ b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt @@ -0,0 +1,122 @@ +package hep.dataforge.io.yaml + +import hep.dataforge.context.Context +import hep.dataforge.io.IOFormat.Companion.META_KEY +import hep.dataforge.io.IOFormat.Companion.NAME_KEY +import hep.dataforge.io.MetaFormat +import hep.dataforge.io.MetaFormatFactory +import hep.dataforge.meta.* +import hep.dataforge.meta.descriptors.ItemDescriptor +import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.names.NameToken +import hep.dataforge.names.withIndex +import hep.dataforge.values.ListValue +import hep.dataforge.values.Null +import hep.dataforge.values.parseValue +import kotlinx.io.Input +import kotlinx.io.Output +import kotlinx.io.text.readUtf8String +import kotlinx.io.text.writeUtf8String +import net.mamoe.yamlkt.* + +public fun Meta.toYaml(): YamlMap { + val map: Map = items.entries.associate { (key, item) -> + key.toString() to when (item) { + is MetaItem.ValueItem -> { + item.value.value + } + is MetaItem.NodeItem -> { + item.node.toYaml() + } + } + } + return YamlMap(map) +} + +private class YamlMeta(private val yamlMap: YamlMap, private val descriptor: NodeDescriptor? = null) : MetaBase() { + + private fun buildItems(): Map> { + val map = LinkedHashMap>() + + yamlMap.content.entries.forEach { (key, value) -> + val stringKey = key.toString() + val itemDescriptor = descriptor?.items?.get(stringKey) + val token = NameToken(stringKey) + when (value) { + YamlNull -> Null.asMetaItem() + is YamlLiteral -> map[token] = value.content.parseValue().asMetaItem() + is YamlMap -> map[token] = value.toMeta().asMetaItem() + is YamlList -> if (value.all { it is YamlLiteral }) { + val listValue = ListValue( + value.map { + //We already checked that all values are primitives + (it as YamlLiteral).content.parseValue() + } + ) + map[token] = MetaItem.ValueItem(listValue) + } else value.forEachIndexed { index, yamlElement -> + val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: ItemDescriptor.DEFAULT_INDEX_KEY + val indexValue: String = (yamlElement as? YamlMap)?.getStringOrNull(indexKey) + ?: index.toString() //In case index is non-string, the backward transformation will be broken. + + val tokenWithIndex = token.withIndex(indexValue) + map[tokenWithIndex] = yamlElement.toMetaItem(itemDescriptor) + } + } + } + return map + } + + override val items: Map> get() = buildItems() +} + +public fun YamlElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<*> = when (this) { + YamlNull -> Null.asMetaItem() + is YamlLiteral -> content.parseValue().asMetaItem() + is YamlMap -> toMeta().asMetaItem() + //We can't return multiple items therefore we create top level node + is YamlList -> YamlMap(mapOf("@yamlArray" to this)).toMetaItem(descriptor) +} + +public fun YamlMap.toMeta(): Meta = YamlMeta(this) + + +/** + * Represent meta as Yaml + */ +@DFExperimental +public class YamlMetaFormat(private val meta: Meta) : MetaFormat { + private val coder = Yaml.default + + override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) { + val yaml = meta.toYaml() + val string = coder.encodeToString(yaml) + output.writeUtf8String(string) + } + + override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta { + val yaml = coder.decodeYamlMapFromString(input.readUtf8String()) + return yaml.toMeta() + } + + override fun toMeta(): Meta = Meta { + NAME_KEY put FrontMatterEnvelopeFormat.name.toString() + META_KEY put meta + } + + public companion object : MetaFormatFactory { + override fun invoke(meta: Meta, context: Context): MetaFormat = YamlMetaFormat(meta) + + override val shortName: String = "yaml" + + override val key: Short = 0x594d //YM + + private val default = YamlMetaFormat() + + override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit = + default.writeMeta(output, meta, descriptor) + + override fun readMeta(input: kotlinx.io.Input, descriptor: NodeDescriptor?): Meta = + default.readMeta(input, descriptor) + } +} \ No newline at end of file diff --git a/dataforge-io/dataforge-io-yaml/src/test/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt b/dataforge-io/dataforge-io-yaml/src/jvmTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt similarity index 100% rename from dataforge-io/dataforge-io-yaml/src/test/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt rename to dataforge-io/dataforge-io-yaml/src/jvmTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt diff --git a/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt b/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt deleted file mode 100644 index 18e8af06..00000000 --- a/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt +++ /dev/null @@ -1,56 +0,0 @@ -package hep.dataforge.io.yaml - -import hep.dataforge.context.Context -import hep.dataforge.io.IOFormat.Companion.META_KEY -import hep.dataforge.io.IOFormat.Companion.NAME_KEY -import hep.dataforge.io.MetaFormat -import hep.dataforge.io.MetaFormatFactory -import hep.dataforge.meta.DFExperimental -import hep.dataforge.meta.Meta -import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.toMap -import hep.dataforge.meta.toMeta -import kotlinx.io.Input -import kotlinx.io.Output -import kotlinx.io.asInputStream -import kotlinx.io.text.writeUtf8String -import org.yaml.snakeyaml.Yaml - -/** - * Represent meta as Yaml - */ -@DFExperimental -public class YamlMetaFormat(private val meta: Meta) : MetaFormat { - private val yaml = Yaml() - - override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) { - val string = yaml.dump(meta.toMap(descriptor)) - output.writeUtf8String(string) - } - - override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta { - val map: Map = yaml.load(input.asInputStream()) - return map.toMeta(descriptor) - } - - override fun toMeta(): Meta = Meta{ - NAME_KEY put FrontMatterEnvelopeFormat.name.toString() - META_KEY put meta - } - - public companion object : MetaFormatFactory { - override fun invoke(meta: Meta, context: Context): MetaFormat = YamlMetaFormat(meta) - - override val shortName: String = "yaml" - - override val key: Short = 0x594d //YM - - private val default = YamlMetaFormat() - - override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit = - default.writeMeta(output, meta, descriptor) - - override fun readMeta(input: kotlinx.io.Input, descriptor: NodeDescriptor?): Meta = - default.readMeta(input, descriptor) - } -} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt index 9bf5560d..ef7be41c 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt @@ -18,7 +18,7 @@ public object MetaItemSerializer : KSerializer> { @OptIn(InternalSerializationApi::class) override val descriptor: SerialDescriptor = buildClassSerialDescriptor("hep.dataforge.meta.MetaItem") { element("isNode") - element("content", buildSerialDescriptor("MetaItem.content", PolymorphicKind.SEALED)) + element("content", buildSerialDescriptor("MetaItem.content", SerialKind.CONTEXTUAL)) } override fun deserialize(decoder: Decoder): MetaItem<*> { From 2098d965615dae4d701350943faf3e70183422ae Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 4 Dec 2020 10:38:56 +0300 Subject: [PATCH 04/59] Fix build and update internals of MetaSerializer --- build.gradle.kts | 4 + .../api/dataforge-io-yaml.api | 7 + .../hep/dataforge/meta/MetaSerializer.kt | 7 +- dataforge-tables/api/dataforge-tables.api | 302 ------------------ .../kotlin/hep/dataforge/tables/CastColumn.kt | 8 +- 5 files changed, 18 insertions(+), 310 deletions(-) delete mode 100644 dataforge-tables/api/dataforge-tables.api diff --git a/build.gradle.kts b/build.gradle.kts index 6e9893cb..28bccc9b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,4 +21,8 @@ allprojects { subprojects { apply(plugin = "ru.mipt.npm.publish") +} + +apiValidation{ + ignoredProjects.add("dataforge-tables") } \ No newline at end of file diff --git a/dataforge-io/dataforge-io-yaml/api/dataforge-io-yaml.api b/dataforge-io/dataforge-io-yaml/api/dataforge-io-yaml.api index de7b3ab2..d137dcdc 100644 --- a/dataforge-io/dataforge-io-yaml/api/dataforge-io-yaml.api +++ b/dataforge-io/dataforge-io-yaml/api/dataforge-io-yaml.api @@ -57,3 +57,10 @@ public final class hep/dataforge/io/yaml/YamlMetaFormat$Companion : hep/dataforg public synthetic fun writeObject (Lkotlinx/io/Output;Ljava/lang/Object;)V } +public final class hep/dataforge/io/yaml/YamlMetaFormatKt { + public static final fun toMeta (Lnet/mamoe/yamlkt/YamlMap;)Lhep/dataforge/meta/Meta; + public static final fun toMetaItem (Lnet/mamoe/yamlkt/YamlElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;)Lhep/dataforge/meta/MetaItem; + public static synthetic fun toMetaItem$default (Lnet/mamoe/yamlkt/YamlElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/MetaItem; + public static final fun toYaml (Lhep/dataforge/meta/Meta;)Lnet/mamoe/yamlkt/YamlMap; +} + diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt index ef7be41c..81730b5d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt @@ -12,13 +12,12 @@ import kotlinx.serialization.json.JsonDecoder import kotlinx.serialization.json.JsonEncoder import kotlinx.serialization.json.JsonObject -@OptIn(ExperimentalSerializationApi::class) public object MetaItemSerializer : KSerializer> { - @OptIn(InternalSerializationApi::class) - override val descriptor: SerialDescriptor = buildClassSerialDescriptor("hep.dataforge.meta.MetaItem") { + @OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class) + override val descriptor: SerialDescriptor = buildSerialDescriptor("MetaItem", PolymorphicKind.SEALED) { element("isNode") - element("content", buildSerialDescriptor("MetaItem.content", SerialKind.CONTEXTUAL)) + element("value", buildSerialDescriptor("MetaItem.value", SerialKind.CONTEXTUAL)) } override fun deserialize(decoder: Decoder): MetaItem<*> { diff --git a/dataforge-tables/api/dataforge-tables.api b/dataforge-tables/api/dataforge-tables.api deleted file mode 100644 index e037773a..00000000 --- a/dataforge-tables/api/dataforge-tables.api +++ /dev/null @@ -1,302 +0,0 @@ -public final class hep/dataforge/tables/CachedTransformationColumn : hep/dataforge/tables/Column { - public fun (Lhep/dataforge/tables/Table;Lkotlin/reflect/KClass;Ljava/lang/String;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function1;)V - public fun get (I)Ljava/lang/Object; - public final fun getMapper ()Lkotlin/jvm/functions/Function1; - public fun getMeta ()Lhep/dataforge/meta/Meta; - public fun getName ()Ljava/lang/String; - public fun getSize ()I - public final fun getTable ()Lhep/dataforge/tables/Table; - public fun getType ()Lkotlin/reflect/KClass; -} - -public final class hep/dataforge/tables/CastColumn : hep/dataforge/tables/Column { - public fun (Lhep/dataforge/tables/Column;Lkotlin/reflect/KClass;)V - public fun get (I)Ljava/lang/Object; - public fun getMeta ()Lhep/dataforge/meta/Meta; - public fun getName ()Ljava/lang/String; - public final fun getOrigin ()Lhep/dataforge/tables/Column; - public fun getSize ()I - public fun getType ()Lkotlin/reflect/KClass; -} - -public final class hep/dataforge/tables/CastColumnKt { - public static final fun cast (Lhep/dataforge/tables/Column;Lkotlin/reflect/KClass;)Lhep/dataforge/tables/Column; - public static final fun get (Ljava/util/Collection;Lhep/dataforge/tables/ColumnHeader;)Lhep/dataforge/tables/Column; -} - -public abstract interface class hep/dataforge/tables/Column : hep/dataforge/tables/ColumnHeader { - public abstract fun get (I)Ljava/lang/Object; - public abstract fun getSize ()I -} - -public final class hep/dataforge/tables/ColumnDef : hep/dataforge/tables/ColumnHeader { - public fun (Ljava/lang/String;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lkotlin/reflect/KClass; - public final fun component3 ()Lhep/dataforge/meta/Meta; - public final fun copy (Ljava/lang/String;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;)Lhep/dataforge/tables/ColumnDef; - public static synthetic fun copy$default (Lhep/dataforge/tables/ColumnDef;Ljava/lang/String;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/tables/ColumnDef; - public fun equals (Ljava/lang/Object;)Z - public fun getMeta ()Lhep/dataforge/meta/Meta; - public fun getName ()Ljava/lang/String; - public fun getType ()Lkotlin/reflect/KClass; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public abstract interface class hep/dataforge/tables/ColumnHeader { - public abstract fun getMeta ()Lhep/dataforge/meta/Meta; - public abstract fun getName ()Ljava/lang/String; - public abstract fun getType ()Lkotlin/reflect/KClass; -} - -public final class hep/dataforge/tables/ColumnHeaderKt { - public static final fun getTextWidth (Lhep/dataforge/tables/ColumnHeader;)I - public static final fun getValueType (Lhep/dataforge/tables/ColumnHeader;)Lhep/dataforge/values/ValueType; -} - -public final class hep/dataforge/tables/ColumnProperty : kotlin/properties/ReadOnlyProperty { - public fun (Lhep/dataforge/tables/Table;Lkotlin/reflect/KClass;)V - public final fun getTable ()Lhep/dataforge/tables/Table; - public final fun getType ()Lkotlin/reflect/KClass; - public fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Lhep/dataforge/tables/Column; - public synthetic fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object; -} - -public class hep/dataforge/tables/ColumnScheme : hep/dataforge/meta/Scheme { - public static final field Companion Lhep/dataforge/tables/ColumnScheme$Companion; - public fun ()V - public final fun getTitle ()Ljava/lang/String; - public final fun setTitle (Ljava/lang/String;)V -} - -public final class hep/dataforge/tables/ColumnScheme$Companion : hep/dataforge/meta/SchemeSpec { -} - -public final class hep/dataforge/tables/ColumnTable : hep/dataforge/tables/Table { - public fun (Ljava/util/Collection;)V - public fun getColumns ()Ljava/util/Collection; - public fun getHeader ()Ljava/util/List; - public fun getRows ()Ljava/util/List; - public fun getValue (ILjava/lang/String;)Ljava/lang/Object; - public fun rowFlow ()Lkotlinx/coroutines/flow/Flow; -} - -public final class hep/dataforge/tables/IntColumn : hep/dataforge/tables/Column { - public static final field Companion Lhep/dataforge/tables/IntColumn$Companion; - public fun (Ljava/lang/String;[ILhep/dataforge/meta/Meta;)V - public synthetic fun (Ljava/lang/String;[ILhep/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun equals (Ljava/lang/Object;)Z - public fun get (I)Ljava/lang/Integer; - public synthetic fun get (I)Ljava/lang/Object; - public final fun getData ()[I - public fun getMeta ()Lhep/dataforge/meta/Meta; - public fun getName ()Ljava/lang/String; - public fun getSize ()I - public fun getType ()Lkotlin/reflect/KClass; - public fun hashCode ()I -} - -public final class hep/dataforge/tables/IntColumn$Companion { -} - -public final class hep/dataforge/tables/ListColumn : hep/dataforge/tables/Column { - public static final field Companion Lhep/dataforge/tables/ListColumn$Companion; - public fun (Ljava/lang/String;Ljava/util/List;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;)V - public fun get (I)Ljava/lang/Object; - public fun getMeta ()Lhep/dataforge/meta/Meta; - public fun getName ()Ljava/lang/String; - public fun getSize ()I - public fun getType ()Lkotlin/reflect/KClass; -} - -public final class hep/dataforge/tables/ListColumn$Companion { -} - -public final class hep/dataforge/tables/MapRow : hep/dataforge/tables/Row { - public static final synthetic fun box-impl (Ljava/util/Map;)Lhep/dataforge/tables/MapRow; - public static fun constructor-impl (Ljava/util/Map;)Ljava/util/Map; - public fun equals (Ljava/lang/Object;)Z - public static fun equals-impl (Ljava/util/Map;Ljava/lang/Object;)Z - public static final fun equals-impl0 (Ljava/util/Map;Ljava/util/Map;)Z - public fun getValue (Ljava/lang/String;)Ljava/lang/Object; - public static fun getValue-impl (Ljava/util/Map;Ljava/lang/String;)Ljava/lang/Object; - public fun hashCode ()I - public static fun hashCode-impl (Ljava/util/Map;)I - public fun toString ()Ljava/lang/String; - public static fun toString-impl (Ljava/util/Map;)Ljava/lang/String; - public final synthetic fun unbox-impl ()Ljava/util/Map; -} - -public final class hep/dataforge/tables/MutableColumnTable : hep/dataforge/tables/Table { - public fun (I)V - public final fun add (Lhep/dataforge/tables/Column;)V - public synthetic fun getColumns ()Ljava/util/Collection; - public fun getColumns ()Ljava/util/List; - public fun getHeader ()Ljava/util/List; - public fun getRows ()Ljava/util/List; - public final fun getSize ()I - public fun getValue (ILjava/lang/String;)Ljava/lang/Object; - public final fun insert (ILhep/dataforge/tables/Column;)V - public fun rowFlow ()Lkotlinx/coroutines/flow/Flow; -} - -public final class hep/dataforge/tables/MutableTable : hep/dataforge/tables/RowTable { - public fun (Ljava/util/List;Ljava/util/List;)V - public final fun column (Ljava/lang/String;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;)Lhep/dataforge/tables/ColumnHeader; - public fun getHeader ()Ljava/util/List; - public fun getRows ()Ljava/util/List; - public final fun row (Ljava/util/Map;)Lhep/dataforge/tables/Row; - public final fun row ([Lkotlin/Pair;)Lhep/dataforge/tables/Row; -} - -public final class hep/dataforge/tables/MutableTableKt { - public static final fun edit (Lhep/dataforge/tables/Table;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/tables/Table; - public static final fun row (Lhep/dataforge/tables/MutableTable;[Lkotlin/Pair;)Lhep/dataforge/tables/Row; -} - -public final class hep/dataforge/tables/RealColumn : hep/dataforge/tables/Column { - public static final field Companion Lhep/dataforge/tables/RealColumn$Companion; - public fun (Ljava/lang/String;[DLhep/dataforge/meta/Meta;)V - public synthetic fun (Ljava/lang/String;[DLhep/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun equals (Ljava/lang/Object;)Z - public fun get (I)Ljava/lang/Double; - public synthetic fun get (I)Ljava/lang/Object; - public final fun getData ()[D - public fun getMeta ()Lhep/dataforge/meta/Meta; - public fun getName ()Ljava/lang/String; - public fun getSize ()I - public fun getType ()Lkotlin/reflect/KClass; - public fun hashCode ()I -} - -public final class hep/dataforge/tables/RealColumn$Companion { -} - -public abstract interface class hep/dataforge/tables/Row { - public abstract fun getValue (Ljava/lang/String;)Ljava/lang/Object; -} - -public class hep/dataforge/tables/RowTable : hep/dataforge/tables/Table { - public fun (Ljava/util/List;Ljava/util/List;)V - public synthetic fun getColumns ()Ljava/util/Collection; - public fun getColumns ()Ljava/util/List; - public fun getHeader ()Ljava/util/List; - public fun getRows ()Ljava/util/List; - public fun getValue (ILjava/lang/String;)Ljava/lang/Object; - public fun rowFlow ()Lkotlinx/coroutines/flow/Flow; -} - -public final class hep/dataforge/tables/RowTableKt { - public static final fun collect (Lhep/dataforge/tables/Rows;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - -public abstract interface class hep/dataforge/tables/Rows { - public abstract fun getHeader ()Ljava/util/List; - public abstract fun rowFlow ()Lkotlinx/coroutines/flow/Flow; -} - -public final class hep/dataforge/tables/SimpleColumnHeader : hep/dataforge/tables/ColumnHeader { - public fun (Ljava/lang/String;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lkotlin/reflect/KClass; - public final fun component3 ()Lhep/dataforge/meta/Meta; - public final fun copy (Ljava/lang/String;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;)Lhep/dataforge/tables/SimpleColumnHeader; - public static synthetic fun copy$default (Lhep/dataforge/tables/SimpleColumnHeader;Ljava/lang/String;Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/tables/SimpleColumnHeader; - public fun equals (Ljava/lang/Object;)Z - public fun getMeta ()Lhep/dataforge/meta/Meta; - public fun getName ()Ljava/lang/String; - public fun getType ()Lkotlin/reflect/KClass; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public abstract interface class hep/dataforge/tables/Table : hep/dataforge/tables/Rows { - public static final field Companion Lhep/dataforge/tables/Table$Companion; - public abstract fun getColumns ()Ljava/util/Collection; - public abstract fun getHeader ()Ljava/util/List; - public abstract fun getRows ()Ljava/util/List; - public abstract fun getValue (ILjava/lang/String;)Ljava/lang/Object; - public abstract fun rowFlow ()Lkotlinx/coroutines/flow/Flow; -} - -public final class hep/dataforge/tables/Table$Companion { - public final fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/tables/Table; -} - -public final class hep/dataforge/tables/Table$DefaultImpls { - public static fun getHeader (Lhep/dataforge/tables/Table;)Ljava/util/List; - public static fun rowFlow (Lhep/dataforge/tables/Table;)Lkotlinx/coroutines/flow/Flow; -} - -public final class hep/dataforge/tables/TableKt { - public static final fun get (Lhep/dataforge/tables/Row;Lhep/dataforge/tables/ColumnHeader;)Ljava/lang/Object; - public static final fun get (Lhep/dataforge/tables/Table;ILhep/dataforge/tables/ColumnHeader;)Ljava/lang/Object; - public static final fun get (Ljava/util/Collection;Ljava/lang/String;)Lhep/dataforge/tables/Column; - public static final fun getIndices (Lhep/dataforge/tables/Column;)Lkotlin/ranges/IntRange; - public static final fun getValue (Lhep/dataforge/tables/Row;Ljava/lang/String;Lkotlin/reflect/KClass;)Ljava/lang/Object; - public static final fun getValue (Lhep/dataforge/tables/Table;ILjava/lang/String;Lkotlin/reflect/KClass;)Ljava/lang/Object; - public static final fun iterator (Lhep/dataforge/tables/Column;)Ljava/util/Iterator; -} - -public final class hep/dataforge/tables/TransformationColumn : hep/dataforge/tables/Column { - public fun (Lhep/dataforge/tables/Table;Lkotlin/reflect/KClass;Ljava/lang/String;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function1;)V - public fun get (I)Ljava/lang/Object; - public final fun getMapper ()Lkotlin/jvm/functions/Function1; - public fun getMeta ()Lhep/dataforge/meta/Meta; - public fun getName ()Ljava/lang/String; - public fun getSize ()I - public final fun getTable ()Lhep/dataforge/tables/Table; - public fun getType ()Lkotlin/reflect/KClass; -} - -public final class hep/dataforge/tables/TransformationColumnKt { - public static final fun mapRowsToDouble (Lhep/dataforge/tables/Table;Ljava/lang/String;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/tables/RealColumn; - public static synthetic fun mapRowsToDouble$default (Lhep/dataforge/tables/Table;Ljava/lang/String;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/tables/RealColumn; - public static final fun mapRowsToInt (Lhep/dataforge/tables/Table;Ljava/lang/String;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/tables/IntColumn; - public static synthetic fun mapRowsToInt$default (Lhep/dataforge/tables/Table;Ljava/lang/String;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/tables/IntColumn; -} - -public final class hep/dataforge/tables/ValueColumnScheme : hep/dataforge/tables/ColumnScheme { - public fun ()V - public final fun getValueType ()Lhep/dataforge/values/ValueType; - public final fun setValueType (Lhep/dataforge/values/ValueType;)V -} - -public final class hep/dataforge/tables/io/TextRows : hep/dataforge/tables/Rows { - public static final field Companion Lhep/dataforge/tables/io/TextRows$Companion; - public fun (Ljava/util/List;Lkotlinx/io/Binary;)V - public fun getHeader ()Ljava/util/List; - public final fun indexFlow ()Lkotlinx/coroutines/flow/Flow; - public fun rowFlow ()Lkotlinx/coroutines/flow/Flow; -} - -public final class hep/dataforge/tables/io/TextRows$Companion { -} - -public final class hep/dataforge/tables/io/TextRowsKt { - public static final fun buildRowIndex (Lhep/dataforge/tables/io/TextRows;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun writeRows (Lkotlinx/io/Output;Lhep/dataforge/tables/Rows;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - -public final class hep/dataforge/tables/io/TextTable : hep/dataforge/tables/Table { - public static final field Companion Lhep/dataforge/tables/io/TextTable$Companion; - public fun (Ljava/util/List;Lkotlinx/io/Binary;Ljava/util/List;)V - public fun getColumns ()Ljava/util/Collection; - public fun getHeader ()Ljava/util/List; - public final fun getIndex ()Ljava/util/List; - public fun getRows ()Ljava/util/List; - public fun getValue (ILjava/lang/String;)Lhep/dataforge/values/Value; - public synthetic fun getValue (ILjava/lang/String;)Ljava/lang/Object; - public fun rowFlow ()Lkotlinx/coroutines/flow/Flow; -} - -public final class hep/dataforge/tables/io/TextTable$Companion { - public final fun invoke (Ljava/util/List;Lkotlinx/io/Binary;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - -public final class hep/dataforge/tables/io/TextTableEnvelopeKt { - public static final fun readEnvelope (Lhep/dataforge/tables/io/TextRows$Companion;Lhep/dataforge/io/Envelope;)Lhep/dataforge/tables/io/TextRows; - public static final fun wrap (Lhep/dataforge/tables/Table;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - diff --git a/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt b/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt index a0bcb75e..6990f1c6 100644 --- a/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt +++ b/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt @@ -8,7 +8,7 @@ import kotlin.reflect.full.cast import kotlin.reflect.full.isSubclassOf @Suppress("UNCHECKED_CAST") -fun Column<*>.cast(type: KClass): Column { +public fun Column<*>.cast(type: KClass): Column { return if (type.isSubclassOf(this.type)) { this as Column } else { @@ -16,7 +16,7 @@ fun Column<*>.cast(type: KClass): Column { } } -class CastColumn(val origin: Column<*>, override val type: KClass) : Column { +public class CastColumn(private val origin: Column<*>, override val type: KClass) : Column { override val name: String get() = origin.name override val meta: Meta get() = origin.meta override val size: Int get() = origin.size @@ -25,12 +25,12 @@ class CastColumn(val origin: Column<*>, override val type: KClass(val table: Table, val type: KClass) : ReadOnlyProperty> { +public class ColumnProperty(public val table: Table, public val type: KClass) : ReadOnlyProperty> { override fun getValue(thisRef: Any?, property: KProperty<*>): Column { val name = property.name return (table.columns[name] ?: error("Column with name $name not found in the table")).cast(type) } } -operator fun Collection>.get(header: ColumnHeader): Column? = +public operator fun Collection>.get(header: ColumnHeader): Column? = find { it.name == header.name }?.cast(header.type) From 221cc65b78d7151086e4509d6adef6d55aae312b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 4 Dec 2020 11:20:20 +0300 Subject: [PATCH 05/59] Meta builder made inline --- CHANGELOG.md | 2 ++ .../src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5904ceae..d3fbfd51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,12 @@ ## [Unreleased] ### Added +- Yaml meta format based on yaml.kt ### Changed - `ListValue` and `DoubleArrayValue` implement `Iterable`. - Changed the logic of `Value::isList` to check for type instead of size +- `Meta{}` builder made inline ### Deprecated diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index cd041275..63ce1961 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -142,4 +142,4 @@ public fun Meta.edit(builder: MetaBuilder.() -> Unit): MetaBuilder = builder().a * Build a [MetaBuilder] using given transformation */ @Suppress("FunctionName") -public fun Meta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder) \ No newline at end of file +public inline fun Meta(builder: MetaBuilder.() -> Unit): MetaBuilder = MetaBuilder().apply(builder) \ No newline at end of file From 17c9bf3d5467ea00eeaaaabe72175da5af509bfc Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 4 Dec 2020 11:26:03 +0300 Subject: [PATCH 06/59] Move envelope builder to the top level. --- CHANGELOG.md | 1 + .../src/commonMain/kotlin/hep/dataforge/io/Envelope.kt | 3 +++ .../commonMain/kotlin/hep/dataforge/io/EnvelopeBuilder.kt | 6 ++++++ .../src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt | 2 +- 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3fbfd51..1a543111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `ListValue` and `DoubleArrayValue` implement `Iterable`. - Changed the logic of `Value::isList` to check for type instead of size - `Meta{}` builder made inline +- Moved `Envelope` builder to a top level function. Companion invoke is deprecated. ### Deprecated diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt index 992a080f..d938fe5a 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt @@ -29,6 +29,9 @@ public interface Envelope { /** * Build a static envelope using provided builder */ + @Deprecated("Use top level function instead", + replaceWith = ReplaceWith("Envelope(block)", "hep.dataforge.io.Envelope") + ) public inline operator fun invoke(block: EnvelopeBuilder.() -> Unit): Envelope = EnvelopeBuilder().apply(block).seal() } diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeBuilder.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeBuilder.kt index 73775af3..20710198 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeBuilder.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeBuilder.kt @@ -44,6 +44,12 @@ public class EnvelopeBuilder : Envelope { } + +/** + * Build a static envelope using provided [builder] + */ +public inline fun Envelope(builder: EnvelopeBuilder.() -> Unit): Envelope = EnvelopeBuilder().apply(builder).seal() + //@ExperimentalContracts //suspend fun EnvelopeBuilder.buildData(block: suspend Output.() -> Unit): Binary{ // contract { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index 63ce1961..12730d46 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -136,7 +136,7 @@ public fun Meta.builder(): MetaBuilder { /** * Create a deep copy of this meta and apply builder to it */ -public fun Meta.edit(builder: MetaBuilder.() -> Unit): MetaBuilder = builder().apply(builder) +public inline fun Meta.edit(builder: MetaBuilder.() -> Unit): MetaBuilder = builder().apply(builder) /** * Build a [MetaBuilder] using given transformation From eb16294a7e27a55368eaac080a577b3f8db93eae Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 4 Dec 2020 14:07:00 +0300 Subject: [PATCH 07/59] Fix api definitions --- build.gradle.kts | 2 +- dataforge-io/api/dataforge-io.api | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 28bccc9b..734da38d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.2.1-dev-2") +val dataforgeVersion by extra("0.2.1-dev-3") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-io/api/dataforge-io.api b/dataforge-io/api/dataforge-io.api index 1b8f876b..417f531a 100644 --- a/dataforge-io/api/dataforge-io.api +++ b/dataforge-io/api/dataforge-io.api @@ -77,6 +77,10 @@ public final class hep/dataforge/io/EnvelopeBuilder : hep/dataforge/io/Envelope public final fun setType (Ljava/lang/String;)V } +public final class hep/dataforge/io/EnvelopeBuilderKt { + public static final fun Envelope (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/io/Envelope; +} + public abstract interface class hep/dataforge/io/EnvelopeFormat : hep/dataforge/io/IOFormat { public abstract fun getDefaultMetaFormat ()Lhep/dataforge/io/MetaFormatFactory; public abstract fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/io/Envelope; From 2ba4121a3665a9f2326cd6b9c4bff981d5cb0b3e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 4 Dec 2020 17:34:12 +0300 Subject: [PATCH 08/59] Logging moved to extensions --- CHANGELOG.md | 2 ++ .../kotlin/hep/dataforge/context/Context.kt | 16 -------------- .../kotlin/hep/dataforge/context/logging.kt | 21 ++++++++++++++++++ .../kotlin/hep/dataforge/provider/Path.kt | 22 +++++++++++++++++-- 4 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/logging.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a543111..2e83f215 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,14 @@ ## [Unreleased] ### Added - Yaml meta format based on yaml.kt +- `Path` builders ### Changed - `ListValue` and `DoubleArrayValue` implement `Iterable`. - Changed the logic of `Value::isList` to check for type instead of size - `Meta{}` builder made inline - Moved `Envelope` builder to a top level function. Companion invoke is deprecated. +- Context logging moved to the extension ### Deprecated diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index 605e6f8b..1646d45c 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -5,13 +5,10 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.sequence import hep.dataforge.names.Name -import hep.dataforge.names.plus import hep.dataforge.provider.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob -import mu.KLogger -import mu.KotlinLogging import kotlin.coroutines.CoroutineContext /** @@ -40,11 +37,6 @@ public open class Context( Laminate(meta, parent.properties) } - /** - * Context logger - */ - public val logger: KLogger = KotlinLogging.logger(name.toString()) - /** * A [PluginManager] for current context */ @@ -106,12 +98,4 @@ public interface ContextAware { * @return */ public val context: Context - - public val logger: KLogger - get() = if (this is Named) { - KotlinLogging.logger((context.name + this.name).toString()) - } else { - context.logger - } - } \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/logging.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/logging.kt new file mode 100644 index 00000000..87ad9dbe --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/logging.kt @@ -0,0 +1,21 @@ +package hep.dataforge.context + +import hep.dataforge.provider.Path +import mu.KLogger +import mu.KotlinLogging + +/** + * The logger specific to this context + */ +public val Context.logger: KLogger get() = KotlinLogging.logger(name.toString()) + +/** + * The logger + */ +public val ContextAware.logger: KLogger + get() = if (this is Named) { + KotlinLogging.logger(Path(context.name, this.name).toString()) + } else { + context.logger + } + diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt index 6cd0d7fa..77f05771 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt @@ -47,7 +47,7 @@ public inline class Path(public val tokens: List) : Iterable): Path = Path(tokens.map { PathToken(it.first, it.second) }) \ No newline at end of file From e317b67a48b3b2368592aff8c3436133567daac1 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 4 Dec 2020 17:56:33 +0300 Subject: [PATCH 09/59] Api dump and import fixes --- dataforge-context/api/dataforge-context.api | 20 +++++++++---------- .../hep/dataforge/scripting/Builders.kt | 1 + .../api/dataforge-workspace.api | 2 -- .../hep/dataforge/workspace/GenericTask.kt | 3 ++- .../hep/dataforge/workspace/TaskBuilder.kt | 1 + .../workspace/SimpleWorkspaceTest.kt | 1 + 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/dataforge-context/api/dataforge-context.api b/dataforge-context/api/dataforge-context.api index 39fa72d8..f433949c 100644 --- a/dataforge-context/api/dataforge-context.api +++ b/dataforge-context/api/dataforge-context.api @@ -10,7 +10,6 @@ public abstract class hep/dataforge/context/AbstractPlugin : hep/dataforge/conte public fun getContext ()Lhep/dataforge/context/Context; public fun getDefaultChainTarget ()Ljava/lang/String; public fun getDefaultTarget ()Ljava/lang/String; - public fun getLogger ()Lmu/KLogger; public fun getMeta ()Lhep/dataforge/meta/Meta; public fun getName ()Lhep/dataforge/names/Name; protected final fun require (Lhep/dataforge/context/PluginFactory;)Lkotlin/properties/ReadOnlyProperty; @@ -47,7 +46,6 @@ public class hep/dataforge/context/Context : hep/dataforge/context/Named, hep/da public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; public fun getDefaultChainTarget ()Ljava/lang/String; public fun getDefaultTarget ()Ljava/lang/String; - public final fun getLogger ()Lmu/KLogger; public final fun getName ()Lhep/dataforge/names/Name; public final fun getParent ()Lhep/dataforge/context/Context; public final fun getPlugins ()Lhep/dataforge/context/PluginManager; @@ -59,11 +57,6 @@ public final class hep/dataforge/context/Context$Companion { public abstract interface class hep/dataforge/context/ContextAware { public abstract fun getContext ()Lhep/dataforge/context/Context; - public abstract fun getLogger ()Lmu/KLogger; -} - -public final class hep/dataforge/context/ContextAware$DefaultImpls { - public static fun getLogger (Lhep/dataforge/context/ContextAware;)Lmu/KLogger; } public final class hep/dataforge/context/ContextBuilder { @@ -100,6 +93,11 @@ public final class hep/dataforge/context/Global : hep/dataforge/context/Context public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; } +public final class hep/dataforge/context/LoggingKt { + public static final fun getLogger (Lhep/dataforge/context/Context;)Lmu/KLogger; + public static final fun getLogger (Lhep/dataforge/context/ContextAware;)Lmu/KLogger; +} + public abstract interface class hep/dataforge/context/Named { public static final field Companion Lhep/dataforge/context/Named$Companion; public abstract fun getName ()Lhep/dataforge/names/Name; @@ -133,7 +131,6 @@ public final class hep/dataforge/context/Plugin$DefaultImpls { public static fun content (Lhep/dataforge/context/Plugin;Ljava/lang/String;)Ljava/util/Map; public static fun getDefaultChainTarget (Lhep/dataforge/context/Plugin;)Ljava/lang/String; public static fun getDefaultTarget (Lhep/dataforge/context/Plugin;)Ljava/lang/String; - public static fun getLogger (Lhep/dataforge/context/Plugin;)Lmu/KLogger; public static fun getName (Lhep/dataforge/context/Plugin;)Lhep/dataforge/names/Name; public static fun toMeta (Lhep/dataforge/context/Plugin;)Lhep/dataforge/meta/Meta; } @@ -162,7 +159,6 @@ public final class hep/dataforge/context/PluginManager : hep/dataforge/context/C public static synthetic fun get$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginTag;ZILjava/lang/Object;)Lhep/dataforge/context/Plugin; public static synthetic fun get$default (Lhep/dataforge/context/PluginManager;Lkotlin/reflect/KClass;Lhep/dataforge/context/PluginTag;ZILjava/lang/Object;)Ljava/lang/Object; public fun getContext ()Lhep/dataforge/context/Context; - public fun getLogger ()Lmu/KLogger; public fun iterator ()Ljava/util/Iterator; public final fun list (Z)Ljava/util/Collection; public final fun load (Lhep/dataforge/context/Plugin;)Lhep/dataforge/context/Plugin; @@ -286,8 +282,12 @@ public final class hep/dataforge/provider/Path$Companion { } public final class hep/dataforge/provider/PathKt { + public static final fun Path ([Lhep/dataforge/names/Name;)Ljava/util/List; + public static final fun Path ([Lkotlin/Pair;)Ljava/util/List; + public static final fun asPath (Lhep/dataforge/names/Name;Ljava/lang/String;)Ljava/util/List; + public static final fun asPath (Lhep/dataforge/provider/PathToken;)Ljava/util/List; + public static synthetic fun asPath$default (Lhep/dataforge/names/Name;Ljava/lang/String;ILjava/lang/Object;)Ljava/util/List; public static final fun plus-MQiGgVU (Ljava/util/List;Ljava/util/List;)Ljava/util/List; - public static final fun toPath (Lhep/dataforge/provider/PathToken;)Ljava/util/List; } public final class hep/dataforge/provider/PathToken { diff --git a/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt index e8b1d378..1e325595 100644 --- a/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt +++ b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt @@ -2,6 +2,7 @@ package hep.dataforge.scripting import hep.dataforge.context.Context import hep.dataforge.context.Global +import hep.dataforge.context.logger import hep.dataforge.workspace.SimpleWorkspaceBuilder import hep.dataforge.workspace.Workspace import hep.dataforge.workspace.WorkspaceBuilder diff --git a/dataforge-workspace/api/dataforge-workspace.api b/dataforge-workspace/api/dataforge-workspace.api index c410f094..c599d391 100644 --- a/dataforge-workspace/api/dataforge-workspace.api +++ b/dataforge-workspace/api/dataforge-workspace.api @@ -65,7 +65,6 @@ public final class hep/dataforge/workspace/SimpleWorkspace : hep/dataforge/works public fun getData ()Lhep/dataforge/data/DataNode; public fun getDefaultChainTarget ()Ljava/lang/String; public fun getDefaultTarget ()Ljava/lang/String; - public fun getLogger ()Lmu/KLogger; public fun getTargets ()Ljava/util/Map; public fun getTasks ()Ljava/util/Map; public fun run (Lhep/dataforge/workspace/Task;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode; @@ -219,7 +218,6 @@ public final class hep/dataforge/workspace/Workspace$DefaultImpls { public static fun content (Lhep/dataforge/workspace/Workspace;Ljava/lang/String;)Ljava/util/Map; public static fun getDefaultChainTarget (Lhep/dataforge/workspace/Workspace;)Ljava/lang/String; public static fun getDefaultTarget (Lhep/dataforge/workspace/Workspace;)Ljava/lang/String; - public static fun getLogger (Lhep/dataforge/workspace/Workspace;)Lmu/KLogger; public static fun run (Lhep/dataforge/workspace/Workspace;Lhep/dataforge/workspace/Task;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode; } diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt index d3844096..dc540a21 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt @@ -1,5 +1,6 @@ package hep.dataforge.workspace +import hep.dataforge.context.logger import hep.dataforge.data.DataNode import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.NodeDescriptor @@ -27,7 +28,7 @@ public class GenericTask( val input = model.buildInput(workspace)// gather(workspace, model) //execute - workspace.context.logger.info{"Starting task '$name' on ${model.target} with meta: \n${model.meta}"} + workspace.logger.info{"Starting task '$name' on ${model.target} with meta: \n${model.meta}"} val output = dataTransform(workspace).invoke(model, input) //handle result diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index 4cb48843..f25faa97 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -1,6 +1,7 @@ package hep.dataforge.workspace import hep.dataforge.context.Context +import hep.dataforge.context.logger import hep.dataforge.data.* import hep.dataforge.meta.DFBuilder import hep.dataforge.meta.Meta diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index a4df6a4b..e97f4be8 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -1,6 +1,7 @@ package hep.dataforge.workspace import hep.dataforge.context.PluginTag +import hep.dataforge.context.logger import hep.dataforge.data.* import hep.dataforge.meta.boolean import hep.dataforge.meta.builder From 7c4d69ec1b2f8f4f48f97428b7aa10c2727f7f25 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 9 Dec 2020 12:04:10 +0300 Subject: [PATCH 10/59] Refactor Value. --- CHANGELOG.md | 2 + .../kotlin/hep/dataforge/provider/Provider.kt | 2 +- .../io/yaml/FrontMatterEnvelopeFormat.kt | 1 + .../hep/dataforge/io/BinaryMetaFormat.kt | 79 +++++++++-------- .../kotlin/hep/dataforge/io/Envelope.kt | 4 +- .../hep/dataforge/io/EnvelopeFormatTest.kt | 2 +- .../kotlin/hep/dataforge/io/MetaFormatTest.kt | 1 + dataforge-meta/api/dataforge-meta.api | 32 ++----- .../kotlin/hep/dataforge/meta/JsonMeta.kt | 18 ++-- .../kotlin/hep/dataforge/meta/Meta.kt | 7 +- .../hep/dataforge/meta/MutableItemDelegate.kt | 8 +- .../kotlin/hep/dataforge/values/Value.kt | 86 ++++++++----------- .../hep/dataforge/values/ValueSerializer.kt | 2 + .../hep/dataforge/values/exoticValues.kt | 7 +- .../hep/dataforge/values/valueExtensions.kt | 5 +- .../hep/dataforge/meta/DynamicMetaTest.kt | 1 + .../hep/dataforge/tables/ColumnHeader.kt | 1 + .../hep/dataforge/tables/io/TextRows.kt | 4 +- .../hep/dataforge/tables/io/TextRowsTest.kt | 1 + .../hep/dataforge/workspace/fileData.kt | 6 +- 20 files changed, 117 insertions(+), 152 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e83f215..fc102680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added - Yaml meta format based on yaml.kt - `Path` builders +- Special ValueType for lists ### Changed - `ListValue` and `DoubleArrayValue` implement `Iterable`. @@ -11,6 +12,7 @@ - `Meta{}` builder made inline - Moved `Envelope` builder to a top level function. Companion invoke is deprecated. - Context logging moved to the extension +- `number` and `string` methods on `Value` moved to extensions (breaking change) ### Deprecated diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt index 3a153d86..8d3ae30d 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt @@ -84,6 +84,6 @@ public fun Provider.top(target: String, type: KClass): Map Provider.top(target: String): Map = top(target, T::class) +public inline fun Provider.top(target: String ): Map = top(target, T::class) diff --git a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt index 56a01d92..5abcec3f 100644 --- a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt @@ -17,6 +17,7 @@ public class FrontMatterEnvelopeFormat( ) : EnvelopeFormat { override fun readPartial(input: Input): PartialEnvelope { + @Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER") var line = "" var offset = 0u do { diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt index 547d567c..ab6f94bb 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt @@ -32,57 +32,56 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { writeUtf8String(str) } - public fun Output.writeValue(value: Value) { - if (value.isList()) { + public fun Output.writeValue(value: Value): Unit = when (value.type) { + ValueType.NUMBER -> when (value.value) { + is Short -> { + writeChar('s') + writeShort(value.short) + } + is Int -> { + writeChar('i') + writeInt(value.int) + } + is Long -> { + writeChar('l') + writeLong(value.long) + } + is Float -> { + writeChar('f') + writeFloat(value.float) + } + else -> { + writeChar('d') + writeDouble(value.double) + } + } + ValueType.STRING -> { + writeChar('S') + writeString(value.string) + } + ValueType.BOOLEAN -> { + if (value.boolean) { + writeChar('+') + } else { + writeChar('-') + } + } + ValueType.NULL -> { + writeChar('N') + } + ValueType.LIST -> { writeChar('L') writeInt(value.list.size) value.list.forEach { writeValue(it) } - } else when (value.type) { - ValueType.NUMBER -> when (value.value) { - is Short -> { - writeChar('s') - writeShort(value.number.toShort()) - } - is Int -> { - writeChar('i') - writeInt(value.number.toInt()) - } - is Long -> { - writeChar('l') - writeLong(value.number.toLong()) - } - is Float -> { - writeChar('f') - writeFloat(value.number.toFloat()) - } - else -> { - writeChar('d') - writeDouble(value.number.toDouble()) - } - } - ValueType.STRING -> { - writeChar('S') - writeString(value.string) - } - ValueType.BOOLEAN -> { - if (value.boolean) { - writeChar('+') - } else { - writeChar('-') - } - } - ValueType.NULL -> { - writeChar('N') - } } } override fun writeMeta( output: kotlinx.io.Output, meta: hep.dataforge.meta.Meta, - descriptor: hep.dataforge.meta.descriptors.NodeDescriptor? + descriptor: hep.dataforge.meta.descriptors.NodeDescriptor?, ) { output.writeChar('M') output.writeInt(meta.items.size) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt index d938fe5a..123da62c 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Envelope.kt @@ -29,9 +29,7 @@ public interface Envelope { /** * Build a static envelope using provided builder */ - @Deprecated("Use top level function instead", - replaceWith = ReplaceWith("Envelope(block)", "hep.dataforge.io.Envelope") - ) + @Deprecated("Use top level function instead") public inline operator fun invoke(block: EnvelopeBuilder.() -> Unit): Envelope = EnvelopeBuilder().apply(block).seal() } diff --git a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/EnvelopeFormatTest.kt b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/EnvelopeFormatTest.kt index 8bcc54cd..2ce87c19 100644 --- a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/EnvelopeFormatTest.kt +++ b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/EnvelopeFormatTest.kt @@ -7,7 +7,7 @@ import kotlin.test.assertEquals class EnvelopeFormatTest { - val envelope = Envelope.invoke { + val envelope = Envelope { type = "test.format" meta{ "d" put 22.2 diff --git a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt index cb8ba54b..af33cfde 100644 --- a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt +++ b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt @@ -2,6 +2,7 @@ package hep.dataforge.io import hep.dataforge.meta.* import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY +import hep.dataforge.values.number import kotlinx.io.asBinary import kotlinx.serialization.json.* import kotlin.test.Test diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 4100ccb7..76de2263 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -817,9 +817,6 @@ public final class hep/dataforge/values/DoubleArrayValue : hep/dataforge/values/ public fun ([D)V public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; - public fun getNumber ()Ljava/lang/Double; - public synthetic fun getNumber ()Ljava/lang/Number; - public fun getString ()Ljava/lang/String; public fun getType ()Lhep/dataforge/values/ValueType; public synthetic fun getValue ()Ljava/lang/Object; public fun getValue ()[D @@ -832,8 +829,6 @@ public final class hep/dataforge/values/EnumValue : hep/dataforge/values/Value { public fun (Ljava/lang/Enum;)V public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; - public fun getNumber ()Ljava/lang/Number; - public fun getString ()Ljava/lang/String; public fun getType ()Lhep/dataforge/values/ValueType; public fun getValue ()Ljava/lang/Enum; public synthetic fun getValue ()Ljava/lang/Object; @@ -850,8 +845,6 @@ public final class hep/dataforge/values/False : hep/dataforge/values/Value { public static final field INSTANCE Lhep/dataforge/values/False; public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; - public fun getNumber ()Ljava/lang/Number; - public fun getString ()Ljava/lang/String; public fun getType ()Lhep/dataforge/values/ValueType; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I @@ -862,8 +855,7 @@ public final class hep/dataforge/values/LazyParsedValue : hep/dataforge/values/V public fun (Ljava/lang/String;)V public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; - public fun getNumber ()Ljava/lang/Number; - public fun getString ()Ljava/lang/String; + public final fun getString ()Ljava/lang/String; public fun getType ()Lhep/dataforge/values/ValueType; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I @@ -874,8 +866,6 @@ public final class hep/dataforge/values/ListValue : hep/dataforge/values/Value, public fun (Ljava/util/List;)V public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; - public fun getNumber ()Ljava/lang/Number; - public fun getString ()Ljava/lang/String; public fun getType ()Lhep/dataforge/values/ValueType; public synthetic fun getValue ()Ljava/lang/Object; public fun getValue ()Ljava/util/List; @@ -888,8 +878,6 @@ public final class hep/dataforge/values/Null : hep/dataforge/values/Value { public static final field INSTANCE Lhep/dataforge/values/Null; public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; - public fun getNumber ()Ljava/lang/Number; - public fun getString ()Ljava/lang/String; public fun getType ()Lhep/dataforge/values/ValueType; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I @@ -900,8 +888,7 @@ public final class hep/dataforge/values/NumberValue : hep/dataforge/values/Value public fun (Ljava/lang/Number;)V public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; - public fun getNumber ()Ljava/lang/Number; - public fun getString ()Ljava/lang/String; + public final fun getNumber ()Ljava/lang/Number; public fun getType ()Lhep/dataforge/values/ValueType; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I @@ -912,8 +899,7 @@ public final class hep/dataforge/values/StringValue : hep/dataforge/values/Value public fun (Ljava/lang/String;)V public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; - public fun getNumber ()Ljava/lang/Number; - public fun getString ()Ljava/lang/String; + public final fun getString ()Ljava/lang/String; public fun getType ()Lhep/dataforge/values/ValueType; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I @@ -924,8 +910,6 @@ public final class hep/dataforge/values/True : hep/dataforge/values/Value { public static final field INSTANCE Lhep/dataforge/values/True; public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; - public fun getNumber ()Ljava/lang/Number; - public fun getString ()Ljava/lang/String; public fun getType ()Lhep/dataforge/values/ValueType; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I @@ -934,18 +918,16 @@ public final class hep/dataforge/values/True : hep/dataforge/values/Value { public abstract interface class hep/dataforge/values/Value { public static final field Companion Lhep/dataforge/values/Value$Companion; - public static final field TARGET Ljava/lang/String; + public static final field TYPE Ljava/lang/String; public abstract fun equals (Ljava/lang/Object;)Z public abstract fun getList ()Ljava/util/List; - public abstract fun getNumber ()Ljava/lang/Number; - public abstract fun getString ()Ljava/lang/String; public abstract fun getType ()Lhep/dataforge/values/ValueType; public abstract fun getValue ()Ljava/lang/Object; public abstract fun hashCode ()I } public final class hep/dataforge/values/Value$Companion { - public static final field TARGET Ljava/lang/String; + public static final field TYPE Ljava/lang/String; public final fun of (Ljava/lang/Object;)Lhep/dataforge/values/Value; } @@ -978,6 +960,9 @@ public final class hep/dataforge/values/ValueKt { public static final fun asValue ([I)Lhep/dataforge/values/Value; public static final fun asValue ([J)Lhep/dataforge/values/Value; public static final fun asValue ([S)Lhep/dataforge/values/Value; + public static final fun getNumber (Lhep/dataforge/values/Value;)Ljava/lang/Number; + public static final fun getNumberOrNull (Lhep/dataforge/values/Value;)Ljava/lang/Number; + public static final fun getString (Lhep/dataforge/values/Value;)Ljava/lang/String; public static final fun parseValue (Ljava/lang/String;)Lhep/dataforge/values/Value; } @@ -993,6 +978,7 @@ public final class hep/dataforge/values/ValueSerializer : kotlinx/serialization/ public final class hep/dataforge/values/ValueType : java/lang/Enum { public static final field BOOLEAN Lhep/dataforge/values/ValueType; public static final field Companion Lhep/dataforge/values/ValueType$Companion; + public static final field LIST Lhep/dataforge/values/ValueType; public static final field NULL Lhep/dataforge/values/ValueType; public static final field NUMBER Lhep/dataforge/values/ValueType; public static final field STRING Lhep/dataforge/values/ValueType; diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt index 2cabc8f1..6dcfe76a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt @@ -16,20 +16,16 @@ import kotlinx.serialization.json.* /** * @param descriptor reserved for custom serialization in future */ -public fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement { - return if (isList()) { - JsonArray(list.map { it.toJson() }) - } else { - when (type) { - ValueType.NUMBER -> JsonPrimitive(number) - ValueType.STRING -> JsonPrimitive(string) - ValueType.BOOLEAN -> JsonPrimitive(boolean) - ValueType.NULL -> JsonNull - } - } +public fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement = when (type) { + ValueType.NUMBER -> JsonPrimitive(numberOrNull) + ValueType.STRING -> JsonPrimitive(string) + ValueType.BOOLEAN -> JsonPrimitive(boolean) + ValueType.LIST -> JsonArray(list.map { it.toJson() }) + ValueType.NULL -> JsonNull } //Use these methods to customize JSON key mapping +@Suppress("NULLABLE_EXTENSION_OPERATOR_WITH_SAFE_CALL_RECEIVER") private fun String.toJsonKey(descriptor: ItemDescriptor?) = descriptor?.attributes["jsonName"].string ?: toString() //private fun NodeDescriptor?.getDescriptor(key: String) = this?.items?.get(key) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index c8ed6755..fb014593 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -4,10 +4,7 @@ import hep.dataforge.meta.Meta.Companion.VALUE_KEY import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.names.* -import hep.dataforge.values.EnumValue -import hep.dataforge.values.Null -import hep.dataforge.values.Value -import hep.dataforge.values.boolean +import hep.dataforge.values.* import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -245,7 +242,7 @@ public val MetaItem<*>?.value: Value? public val MetaItem<*>?.string: String? get() = value?.string public val MetaItem<*>?.boolean: Boolean? get() = value?.boolean -public val MetaItem<*>?.number: Number? get() = value?.number +public val MetaItem<*>?.number: Number? get() = value?.numberOrNull public val MetaItem<*>?.double: Double? get() = number?.toDouble() public val MetaItem<*>?.float: Float? get() = number?.toFloat() public val MetaItem<*>?.int: Int? get() = number?.toInt() diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt index 32a7c7d4..6e5cfc96 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt @@ -3,10 +3,7 @@ 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.asValue -import hep.dataforge.values.doubleArray +import hep.dataforge.values.* import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -72,7 +69,6 @@ public fun MutableItemDelegate.convert( } - /* Read-write delegates for [MutableItemProvider] */ /** @@ -171,7 +167,7 @@ public fun MutableItemProvider.numberList( vararg default: Number, key: Name? = null, ): ReadWriteProperty> = item(key).convert( - reader = { it?.value?.list?.map { value -> value.number } ?: listOf(*default) }, + reader = { it?.value?.list?.map { value -> value.numberOrNull ?: Double.NaN } ?: listOf(*default) }, writer = { it.map { num -> num.asValue() }.asValue().asMetaItem() } ) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt index c81651df..d3b08b1f 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt @@ -11,7 +11,7 @@ import kotlinx.serialization.Serializable */ @Serializable public enum class ValueType { - NUMBER, STRING, BOOLEAN, NULL + NUMBER, STRING, BOOLEAN, LIST, NULL } /** @@ -31,16 +31,6 @@ public interface Value { */ public val type: ValueType - /** - * get this value represented as Number - */ - public val number: Number - - /** - * get this value represented as String - */ - public val string: String - /** * get this value represented as List */ @@ -51,7 +41,7 @@ public interface Value { override fun hashCode(): Int public companion object { - public const val TARGET: String = "value" + public const val TYPE: String = "value" /** * Convert object to value @@ -86,16 +76,27 @@ public interface Value { } } +public val Value.string: String get() = toString() + +/** + * get this value represented as Number + */ +public val Value.numberOrNull: Number? + get() = if (this is NumberValue) number else string.toDoubleOrNull() + +/** + * Return [Value] number content or throw error if value is not a number + */ +public val Value.number: Number + get() = (if (this is NumberValue) number else numberOrNull ?: error("The value is not a number")) + /** * A singleton null value */ public object Null : Value { override val value: Any? get() = null override val type: ValueType get() = ValueType.NULL - override val number: Number get() = Double.NaN - override val string: String get() = "@null" - - override fun toString(): String = value.toString() + override fun toString(): String = "@null" override fun equals(other: Any?): Boolean = other === Null override fun hashCode(): Int = 0 @@ -107,9 +108,6 @@ public object Null : Value { public object True : Value { override val value: Any get() = true override val type: ValueType get() = ValueType.BOOLEAN - override val number: Number get() = 1.0 - override val string: String get() = "true" - override fun toString(): String = value.toString() override fun equals(other: Any?): Boolean = other === True @@ -122,42 +120,40 @@ public object True : Value { public object False : Value { override val value: Any get() = false override val type: ValueType get() = ValueType.BOOLEAN - override val number: Number get() = -1.0 - override val string: String get() = "false" - override fun toString(): String = value.toString() override fun equals(other: Any?): Boolean = other === False override fun hashCode(): Int = -1 } -public class NumberValue(override val number: Number) : Value { +public class NumberValue(public val number: Number) : Value { override val value: Any get() = number override val type: ValueType get() = ValueType.NUMBER - override val string: String get() = number.toString() + + override fun toString(): String = number.toString() override fun equals(other: Any?): Boolean { if (other !is Value) return false - return when (number) { - is Short -> number.toShort() == other.number.toShort() - is Long -> number.toLong() == other.number.toLong() - is Byte -> number.toByte() == other.number.toByte() - is Int -> number.toInt() == other.number.toInt() - is Float -> number.toFloat() == other.number.toFloat() - is Double -> number.toDouble() == other.number.toDouble() - else -> number.toString() == other.number.toString() + + val otherNumber = other.numberOrNull ?: return false + + return when (numberOrNull) { + is Short -> number.toShort() == otherNumber.toShort() + is Long -> number.toLong() == otherNumber.toLong() + is Byte -> number.toByte() == otherNumber.toByte() + is Int -> number.toInt() == otherNumber.toInt() + is Float -> number.toFloat() == otherNumber.toFloat() + is Double -> number.toDouble() == otherNumber.toDouble() + else -> number.toString() == otherNumber.toString() } } - override fun hashCode(): Int = number.hashCode() - - override fun toString(): String = value.toString() + override fun hashCode(): Int = numberOrNull.hashCode() } -public class StringValue(override val string: String) : Value { +public class StringValue(public val string: String) : Value { override val value: Any get() = string override val type: ValueType get() = ValueType.STRING - override val number: Number get() = string.toDouble() override fun equals(other: Any?): Boolean { return this.string == (other as? Value)?.string @@ -165,21 +161,19 @@ public class StringValue(override val string: String) : Value { override fun hashCode(): Int = string.hashCode() - override fun toString(): String = "\"${value.toString()}\"" + override fun toString(): String = string } public class EnumValue>(override val value: E) : Value { override val type: ValueType get() = ValueType.STRING - override val number: Number get() = value.ordinal - override val string: String get() = value.name + + override fun toString(): String = value.toString() override fun equals(other: Any?): Boolean { return string == (other as? Value)?.string } override fun hashCode(): Int = value.hashCode() - - override fun toString(): String = value.toString() } public class ListValue(override val list: List) : Value, Iterable { @@ -188,9 +182,7 @@ public class ListValue(override val list: List) : Value, Iterable } override val value: List get() = list - override val type: ValueType get() = list.first().type - override val number: Number get() = list.first().number - override val string: String get() = list.first().string + override val type: ValueType get() = ValueType.LIST override fun toString(): String = list.joinToString(prefix = "[", postfix = "]") @@ -200,7 +192,7 @@ public class ListValue(override val list: List) : Value, Iterable if (this === other) return true if (other !is Value) return false if (other is DoubleArrayValue) { - return DoubleArray(list.size) { list[it].number.toDouble() }.contentEquals(other.value) + return DoubleArray(list.size) { list[it].numberOrNull?.toDouble() ?: Double.NaN }.contentEquals(other.value) } return list == other.list } @@ -208,8 +200,6 @@ public class ListValue(override val list: List) : Value, Iterable override fun hashCode(): Int { return list.hashCode() } - - } public fun Number.asValue(): Value = NumberValue(this) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/ValueSerializer.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/ValueSerializer.kt index 9dd15b7d..d46dd4be 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/ValueSerializer.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/ValueSerializer.kt @@ -24,6 +24,7 @@ public object ValueSerializer : KSerializer { ValueType.NUMBER -> decodeDouble().asValue() //TODO differentiate? ValueType.BOOLEAN -> decodeBoolean().asValue() ValueType.STRING -> decodeString().asValue() + ValueType.LIST -> decodeSerializableValue(ListSerializer(ValueSerializer)).asValue() } } @@ -45,6 +46,7 @@ public object ValueSerializer : KSerializer { ValueType.NUMBER -> encodeDouble(value.double) ValueType.BOOLEAN -> encodeBoolean(value.boolean) ValueType.STRING -> encodeString(value.string) + ValueType.LIST -> encodeSerializableValue(ListSerializer(ValueSerializer),value.list) } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/exoticValues.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/exoticValues.kt index d961b4a9..b04524dc 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/exoticValues.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/exoticValues.kt @@ -4,12 +4,11 @@ package hep.dataforge.values /** * A value built from string which content and type are parsed on-demand */ -public class LazyParsedValue(override val string: String) : Value { +public class LazyParsedValue(public val string: String) : Value { private val parsedValue by lazy { string.parseValue() } override val value: Any? get() = parsedValue.value override val type: ValueType get() = parsedValue.type - override val number: Number get() = parsedValue.number override fun toString(): String = string @@ -24,9 +23,7 @@ public fun String.lazyParseValue(): LazyParsedValue = LazyParsedValue(this) * A performance optimized version of list value for doubles */ public class DoubleArrayValue(override val value: DoubleArray) : Value, Iterable { - override val type: ValueType get() = ValueType.NUMBER - override val number: Double get() = value.first() - override val string: String get() = value.first().toString() + override val type: ValueType get() = ValueType.LIST override val list: List get() = value.map { NumberValue(it) } override fun equals(other: Any?): Boolean { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/valueExtensions.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/valueExtensions.kt index 50b15aed..9347642b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/valueExtensions.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/valueExtensions.kt @@ -9,10 +9,9 @@ import hep.dataforge.meta.MetaBuilder public fun Value.isNull(): Boolean = this == Null /** - * Check if value is list. This method checks the type of the value, not the number of the elements. - * So it will return `true` for empty lists and lists of one elements. + * Check if value is list. */ -public fun Value.isList(): Boolean = this is Iterable<*> +public fun Value.isList(): Boolean = this.type == ValueType.LIST public val Value.boolean: Boolean get() = this == True diff --git a/dataforge-meta/src/jsTest/kotlin/hep/dataforge/meta/DynamicMetaTest.kt b/dataforge-meta/src/jsTest/kotlin/hep/dataforge/meta/DynamicMetaTest.kt index b546ae6d..a2dc2de9 100644 --- a/dataforge-meta/src/jsTest/kotlin/hep/dataforge/meta/DynamicMetaTest.kt +++ b/dataforge-meta/src/jsTest/kotlin/hep/dataforge/meta/DynamicMetaTest.kt @@ -19,6 +19,7 @@ class DynamicMetaTest { d.ob.booleanNode = true val meta = DynamicMeta(d) + println(meta) assertEquals(true, meta["ob.booleanNode"].boolean) assertEquals(2, meta["array"].value?.list?.get(1)?.int) assertEquals(4, meta.items.size) diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnHeader.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnHeader.kt index a226de26..4aeb3124 100644 --- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnHeader.kt +++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/ColumnHeader.kt @@ -32,5 +32,6 @@ public val ColumnHeader.textWidth: Int ValueType.STRING -> 16 ValueType.BOOLEAN -> 5 ValueType.NULL -> 5 + ValueType.LIST -> 32 null -> 16 } diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/TextRows.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/TextRows.kt index 31f5baaa..e9e6e31e 100644 --- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/TextRows.kt +++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/TextRows.kt @@ -111,8 +111,8 @@ public class TextTable( private fun Output.writeValue(value: Value, width: Int, left: Boolean = true) { require(width > 5) { "Width could not be less than 5" } val str: String = when (value.type) { - ValueType.NUMBER -> value.number.toString() //TODO apply decimal format - ValueType.STRING -> value.string.take(width) + ValueType.NUMBER -> value.numberOrNull.toString() //TODO apply decimal format + ValueType.STRING, ValueType.LIST -> value.string.take(width) ValueType.BOOLEAN -> if (value.boolean) { "true" } else { diff --git a/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt b/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt index 02d97caf..d97484ed 100644 --- a/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt +++ b/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt @@ -6,6 +6,7 @@ import hep.dataforge.tables.get import hep.dataforge.tables.row import hep.dataforge.values.Value import hep.dataforge.values.int +import hep.dataforge.values.string import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import kotlinx.io.ExperimentalIoApi diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt index 97ae77e6..8dfcc63d 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt @@ -16,9 +16,7 @@ import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream import kotlin.reflect.KClass -typealias FileFormatResolver = (Path, Meta) -> IOFormat - - +public typealias FileFormatResolver = (Path, Meta) -> IOFormat private fun newZFS(path: Path): FileSystem { val fsProvider = FileSystemProvider.installedProviders().find { it.scheme == "jar" } @@ -84,7 +82,7 @@ public fun DataTreeBuilder.file( * Read the directory as a data node. If [path] is a zip archive, read it as directory */ @DFExperimental -fun IOPlugin.readDataDirectory( +public fun IOPlugin.readDataDirectory( path: Path, type: KClass, formatResolver: FileFormatResolver From 4c5f0e9e43cd26d87cd3ae8be3a2113906bcd2cd Mon Sep 17 00:00:00 2001 From: darksnake Date: Sun, 13 Dec 2020 19:58:20 +0300 Subject: [PATCH 11/59] Add synchronization to Config listeners --- build.gradle.kts | 2 +- .../src/commonMain/kotlin/hep/dataforge/meta/Config.kt | 4 ++++ .../src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt | 2 -- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 734da38d..453064e8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.2.1-dev-3") +val dataforgeVersion by extra("0.2.1-dev-4") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index 2674d0a8..6dc8c441 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -10,6 +10,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlin.collections.set +import kotlin.jvm.Synchronized //TODO add validator to configuration @@ -31,6 +32,7 @@ public class Config() : AbstractMutableMeta(), ObservableMeta { private val listeners = HashSet() + @Synchronized private fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) { listeners.forEach { it.action(name, oldItem, newItem) } } @@ -38,6 +40,7 @@ public class Config() : AbstractMutableMeta(), ObservableMeta { /** * Add change listener to this meta. Owner is declared to be able to remove listeners later. Listener without owner could not be removed */ + @Synchronized override fun onChange(owner: Any?, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) { listeners.add(MetaListener(owner, action)) } @@ -45,6 +48,7 @@ public class Config() : AbstractMutableMeta(), ObservableMeta { /** * Remove all listeners belonging to given owner */ + @Synchronized override fun removeListener(owner: Any?) { listeners.removeAll { it.owner === owner } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index 7903ba87..658f77cc 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -4,8 +4,6 @@ import hep.dataforge.names.* public interface MutableMeta> : MetaNode, MutableItemProvider { override val items: Map> -// fun onChange(owner: Any? = null, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) -// fun removeListener(owner: Any? = null) } /** diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 12d38de6..4d9ca164 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 62e08a1b75d121e98d845940189a7ce9492da791 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 13 Dec 2020 23:00:58 +0300 Subject: [PATCH 12/59] Refactor Value::toString --- .../src/commonMain/kotlin/hep/dataforge/values/Value.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt index d3b08b1f..676f7134 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt @@ -40,6 +40,8 @@ public interface Value { override fun hashCode(): Int + override fun toString(): String + public companion object { public const val TYPE: String = "value" From 95e6925d55d7b592a7a5ae5df12e226d3f4f077e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 13 Dec 2020 23:09:53 +0300 Subject: [PATCH 13/59] bump api --- dataforge-meta/api/dataforge-meta.api | 1 + 1 file changed, 1 insertion(+) diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 76de2263..03ace8ed 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -924,6 +924,7 @@ public abstract interface class hep/dataforge/values/Value { public abstract fun getType ()Lhep/dataforge/values/ValueType; public abstract fun getValue ()Ljava/lang/Object; public abstract fun hashCode ()I + public abstract fun toString ()Ljava/lang/String; } public final class hep/dataforge/values/Value$Companion { From 1f773cc2304c691ef297ee6d09ca717d48ce1b3d Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 19 Dec 2020 20:54:55 +0300 Subject: [PATCH 14/59] Config API change --- build.gradle.kts | 2 +- dataforge-meta/api/dataforge-meta.api | 39 ++++++++----------- .../kotlin/hep/dataforge/meta/Config.kt | 10 ++--- .../meta/descriptors/ItemDescriptor.kt | 2 +- .../transformations/MetaTransformation.kt | 2 +- 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 453064e8..e0c08431 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.2.1-dev-4") +val dataforgeVersion by extra("0.2.1-dev-5") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 03ace8ed..5e88b4fa 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -14,7 +14,7 @@ public abstract class hep/dataforge/meta/AbstractMutableMeta : hep/dataforge/met protected abstract fun wrapNode (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MutableMeta; } -public final class hep/dataforge/meta/Config : hep/dataforge/meta/AbstractMutableMeta, hep/dataforge/meta/ObservableMeta { +public final class hep/dataforge/meta/Config : hep/dataforge/meta/AbstractMutableMeta, hep/dataforge/meta/ObservableItemProvider { public static final field ConfigSerializer Lhep/dataforge/meta/Config$ConfigSerializer; public fun ()V public synthetic fun empty$dataforge_meta ()Lhep/dataforge/meta/MutableMeta; @@ -108,6 +108,20 @@ public final class hep/dataforge/meta/ItemDelegateKt { public static synthetic fun value$default (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; } +public final class hep/dataforge/meta/ItemListener { + public fun (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V + public synthetic fun (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Object; + public final fun component2 ()Lkotlin/jvm/functions/Function3; + public final fun copy (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/meta/ItemListener; + public static synthetic fun copy$default (Lhep/dataforge/meta/ItemListener;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/meta/ItemListener; + public fun equals (Ljava/lang/Object;)Z + public final fun getAction ()Lkotlin/jvm/functions/Function3; + public final fun getOwner ()Ljava/lang/Object; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public abstract interface class hep/dataforge/meta/ItemProvider { public static final field Companion Lhep/dataforge/meta/ItemProvider$Companion; public abstract fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; @@ -309,20 +323,6 @@ public final class hep/dataforge/meta/MetaKt { public static final fun values (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; } -public final class hep/dataforge/meta/MetaListener { - public fun (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V - public synthetic fun (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/lang/Object; - public final fun component2 ()Lkotlin/jvm/functions/Function3; - public final fun copy (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/meta/MetaListener; - public static synthetic fun copy$default (Lhep/dataforge/meta/MetaListener;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/meta/MetaListener; - public fun equals (Ljava/lang/Object;)Z - public final fun getAction ()Lkotlin/jvm/functions/Function3; - public final fun getOwner ()Ljava/lang/Object; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - public abstract interface class hep/dataforge/meta/MetaNode : hep/dataforge/meta/Meta { public abstract fun getItems ()Ljava/util/Map; } @@ -456,16 +456,11 @@ public final class hep/dataforge/meta/MutableMetaKt { public static final fun update (Lhep/dataforge/meta/MutableMeta;Lhep/dataforge/meta/Meta;)V } -public abstract interface class hep/dataforge/meta/ObservableMeta : hep/dataforge/meta/Meta { +public abstract interface class hep/dataforge/meta/ObservableItemProvider : hep/dataforge/meta/ItemProvider { public abstract fun onChange (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V public abstract fun removeListener (Ljava/lang/Object;)V } -public final class hep/dataforge/meta/ObservableMeta$DefaultImpls { - public static fun getItem (Lhep/dataforge/meta/ObservableMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; - public static fun toMeta (Lhep/dataforge/meta/ObservableMeta;)Lhep/dataforge/meta/Meta; -} - public class hep/dataforge/meta/Scheme : hep/dataforge/meta/Configurable, hep/dataforge/meta/MetaRepr, hep/dataforge/meta/MutableItemProvider, hep/dataforge/meta/descriptors/Described { public fun ()V public fun (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V @@ -671,7 +666,7 @@ public final class hep/dataforge/meta/transformations/MetaTransformation { public fun equals (Ljava/lang/Object;)Z public static fun equals-impl (Ljava/util/Collection;Ljava/lang/Object;)Z public static final fun equals-impl0 (Ljava/util/Collection;Ljava/util/Collection;)Z - public static final fun generate-impl (Ljava/util/Collection;Lhep/dataforge/meta/Config;)Lhep/dataforge/meta/ObservableMeta; + public static final fun generate-impl (Ljava/util/Collection;Lhep/dataforge/meta/Config;)Lhep/dataforge/meta/ObservableItemProvider; public static final fun generate-impl (Ljava/util/Collection;Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Meta; public final fun getTransformations ()Ljava/util/Collection; public fun hashCode ()I diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index 6dc8c441..d7fc3fe0 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -14,12 +14,12 @@ import kotlin.jvm.Synchronized //TODO add validator to configuration -public data class MetaListener( +public data class ItemListener( val owner: Any? = null, val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit ) -public interface ObservableMeta : Meta { +public interface ObservableItemProvider : ItemProvider { public fun onChange(owner: Any?, action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit) public fun removeListener(owner: Any?) } @@ -28,9 +28,9 @@ public interface ObservableMeta : Meta { * Mutable meta representing object state */ @Serializable(Config.Companion::class) -public class Config() : AbstractMutableMeta(), ObservableMeta { +public class Config() : AbstractMutableMeta(), ObservableItemProvider { - private val listeners = HashSet() + private val listeners = HashSet() @Synchronized private fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) { @@ -42,7 +42,7 @@ public class Config() : AbstractMutableMeta(), ObservableMeta { */ @Synchronized override fun onChange(owner: Any?, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) { - listeners.add(MetaListener(owner, action)) + listeners.add(ItemListener(owner, action)) } /** diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt index cbb56fa1..91ad461f 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt @@ -46,7 +46,7 @@ public sealed class ItemDescriptor(public val config: Config) { /** * Configure attributes of the descriptor, creating an attributes node if needed. */ -public fun ItemDescriptor.attributes(block: Config.() -> Unit) { +public inline fun ItemDescriptor.attributes(block: Config.() -> Unit) { (attributes ?: Config().also { this.attributes = it }).apply(block) } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt index 9529b28e..8e2c4d02 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt @@ -102,7 +102,7 @@ public inline class MetaTransformation(public val transformations: Collection rule.selectItems(source).forEach { name -> rule.transformItem(name, source[name], this) From 702589f7b3b4cdf5997c5eee8773885bd6f489f4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 20 Dec 2020 18:22:50 +0300 Subject: [PATCH 15/59] add copy method to descriptors --- CHANGELOG.md | 1 + .../commonMain/kotlin/hep/dataforge/meta/Config.kt | 13 +++++++++++-- .../dataforge/meta/descriptors/ItemDescriptor.kt | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc102680..40e80dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Yaml meta format based on yaml.kt - `Path` builders - Special ValueType for lists +- `copy` method to descriptors ### Changed - `ListValue` and `DoubleArrayValue` implement `Iterable`. diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index d7fc3fe0..3fb557bd 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -94,7 +94,11 @@ public class Config() : AbstractMutableMeta(), ObservableItemProvider { public operator fun Config.get(token: NameToken): MetaItem? = items[token] -public fun Meta.asConfig(): Config = this as? Config ?: Config().also { builder -> +/** + * Create a mutable copy of this [Meta]. The copy is created event if initial [Meta] is a [Config]. + * Listeners are not preserved + */ +public fun Meta.toConfig(): Config = Config().also { builder -> this.items.mapValues { entry -> val item = entry.value builder[entry.key.asName()] = when (item) { @@ -102,4 +106,9 @@ public fun Meta.asConfig(): Config = this as? Config ?: Config().also { builder is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.asConfig()) } } -} \ No newline at end of file +} + +/** + * Return this [Meta] as [Config] if it is [Config] and create a new copy otherwise + */ +public fun Meta.asConfig(): Config = this as? Config ?: toConfig() \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt index 91ad461f..ac1c4693 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt @@ -38,6 +38,8 @@ public sealed class ItemDescriptor(public val config: Config) { */ public var indexKey: String by config.string(DEFAULT_INDEX_KEY) + public abstract fun copy(): ItemDescriptor + public companion object{ public const val DEFAULT_INDEX_KEY: String = "@index" } @@ -180,6 +182,8 @@ public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) value(name.toName(), block) } + override fun copy(): NodeDescriptor = NodeDescriptor(config.toConfig()) + public companion object { internal val ITEM_KEY: Name = "item".asName() @@ -281,6 +285,8 @@ public class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) public fun allow(vararg v: Any) { this.allowedValues = v.map { Value.of(it) } } + + override fun copy(): ValueDescriptor = ValueDescriptor(config.toConfig()) } /** From 89f0d627b83856995bfc294d278da52303c8d01d Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 23 Dec 2020 19:09:02 +0300 Subject: [PATCH 16/59] A major refactor MutableItemProvider instead of Config --- build.gradle.kts | 2 +- .../kotlin/hep/dataforge/context/Context.kt | 6 +- .../dataforge/properties/ConfigProperty.kt | 3 +- .../hep/dataforge/io/BinaryMetaFormat.kt | 4 +- .../kotlin/hep/dataforge/io/EnvelopeParts.kt | 7 +- dataforge-meta/api/dataforge-meta.api | 150 ++++++++-------- .../kotlin/hep/dataforge/meta/ItemDelegate.kt | 2 +- .../kotlin/hep/dataforge/meta/ItemProvider.kt | 37 ++++ .../kotlin/hep/dataforge/meta/Meta.kt | 162 ++---------------- .../kotlin/hep/dataforge/meta/MetaItem.kt | 87 ++++++++++ .../hep/dataforge/meta/MetaWithDefault.kt | 2 +- .../hep/dataforge/meta/MutableItemDelegate.kt | 9 +- .../hep/dataforge/meta/MutableItemProvider.kt | 67 +++++--- .../kotlin/hep/dataforge/meta/MutableMeta.kt | 24 +-- .../kotlin/hep/dataforge/meta/Scheme.kt | 41 +++-- .../kotlin/hep/dataforge/meta/SealedMeta.kt | 23 +++ .../hep/dataforge/meta/Specification.kt | 51 ++---- .../meta/descriptors/ItemDescriptor.kt | 2 +- .../meta/descriptors/descriptorExtensions.kt | 18 ++ .../kotlin/hep/dataforge/meta/getIndexed.kt | 4 +- .../kotlin/hep/dataforge/meta/mapMeta.kt | 4 +- .../transformations/MetaTransformation.kt | 8 +- .../hep/dataforge/meta/MetaBuilderTest.kt | 2 +- .../hep/dataforge/meta/MetaDelegateTest.kt | 2 +- .../kotlin/hep/dataforge/meta/SchemeTest.kt | 4 +- .../hep/dataforge/meta/SpecificationTest.kt | 9 +- 26 files changed, 379 insertions(+), 351 deletions(-) create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/descriptorExtensions.kt diff --git a/build.gradle.kts b/build.gradle.kts index e0c08431..c79a03bb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.2.1-dev-5") +val dataforgeVersion by extra("0.2.1-dev-6") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index 1646d45c..9ea626e9 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -3,7 +3,7 @@ package hep.dataforge.context import hep.dataforge.meta.Laminate import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr -import hep.dataforge.meta.sequence +import hep.dataforge.meta.itemSequence import hep.dataforge.names.Name import hep.dataforge.provider.Provider import kotlinx.coroutines.CoroutineScope @@ -47,13 +47,13 @@ public open class Context( public fun content(target: String, inherit: Boolean): Map { return if (inherit) { when (target) { - PROPERTY_TARGET -> properties.sequence().toMap() + PROPERTY_TARGET -> properties.itemSequence().toMap() Plugin.TARGET -> plugins.list(true).associateBy { it.name } else -> emptyMap() } } else { when (target) { - PROPERTY_TARGET -> properties.layers.firstOrNull()?.sequence()?.toMap() ?: emptyMap() + PROPERTY_TARGET -> properties.layers.firstOrNull()?.itemSequence()?.toMap() ?: emptyMap() Plugin.TARGET -> plugins.list(false).associateBy { it.name } else -> emptyMap() } diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/ConfigProperty.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/ConfigProperty.kt index 329662b5..98a43b69 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/ConfigProperty.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/ConfigProperty.kt @@ -3,6 +3,7 @@ package hep.dataforge.properties import hep.dataforge.meta.Config import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.get +import hep.dataforge.meta.set import hep.dataforge.meta.transformations.MetaConverter import hep.dataforge.meta.transformations.nullableItemToObject import hep.dataforge.meta.transformations.nullableObjectToMetaItem @@ -18,7 +19,7 @@ public class ConfigProperty( override var value: T? get() = converter.nullableItemToObject(config[name]) set(value) { - config.setItem(name,converter.nullableObjectToMetaItem(value)) + config[name] = converter.nullableObjectToMetaItem(value) } override fun onChange(owner: Any?, callback: (T?) -> Unit) { diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt index ab6f94bb..f09cdc04 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt @@ -5,7 +5,7 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaItem import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.setItem +import hep.dataforge.meta.set import hep.dataforge.values.* import kotlinx.io.* import kotlinx.io.text.readUtf8String @@ -126,7 +126,7 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { (1..length).forEach { _ -> val name = readString() val item = readMetaItem() - setItem(name, item) + set(name, item) } } MetaItem.NodeItem(meta) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeParts.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeParts.kt index cae87229..45bfd988 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeParts.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeParts.kt @@ -10,13 +10,14 @@ import hep.dataforge.io.PartDescriptor.Companion.SEPARATOR_KEY import hep.dataforge.meta.* import hep.dataforge.names.asName import hep.dataforge.names.plus +import hep.dataforge.names.toName import kotlinx.io.Binary import kotlinx.io.writeBinary private class PartDescriptor : Scheme() { var offset by int(0) var size by int(0) - var meta by node() + var partMeta by node("meta".toName()) companion object : SchemeSpec(::PartDescriptor) { val MULTIPART_KEY = ENVELOPE_NODE_KEY + "multipart" @@ -48,7 +49,7 @@ public fun EnvelopeBuilder.multipart( PartDescriptor { offset = offsetCounter size = binary.size - meta = description + partMeta = description }.also { offsetCounter += binary.size } @@ -95,7 +96,7 @@ public fun Envelope.parts(): EnvelopeParts { } else { parts.map { val binary = data!!.view(it.offset, it.size) - val meta = Laminate(it.meta, meta[MULTIPART_KEY].node) + val meta = Laminate(it.partMeta, meta[MULTIPART_KEY].node) EnvelopePart(binary, meta) } } diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 5e88b4fa..ca03c72a 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -1,10 +1,4 @@ -public abstract class hep/dataforge/meta/AbstractMetaNode : hep/dataforge/meta/MetaBase, hep/dataforge/meta/MetaNode { - public fun ()V - public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; - public fun toMeta ()Lhep/dataforge/meta/Meta; -} - -public abstract class hep/dataforge/meta/AbstractMutableMeta : hep/dataforge/meta/AbstractMetaNode, hep/dataforge/meta/MutableMeta { +public abstract class hep/dataforge/meta/AbstractMutableMeta : hep/dataforge/meta/AbstractTypedMeta, hep/dataforge/meta/MutableMeta { public fun ()V protected final fun getChildren ()Ljava/util/Map; public fun getItems ()Ljava/util/Map; @@ -14,6 +8,12 @@ public abstract class hep/dataforge/meta/AbstractMutableMeta : hep/dataforge/met protected abstract fun wrapNode (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MutableMeta; } +public abstract class hep/dataforge/meta/AbstractTypedMeta : hep/dataforge/meta/MetaBase, hep/dataforge/meta/TypedMeta { + public fun ()V + public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public fun toMeta ()Lhep/dataforge/meta/Meta; +} + public final class hep/dataforge/meta/Config : hep/dataforge/meta/AbstractMutableMeta, hep/dataforge/meta/ObservableItemProvider { public static final field ConfigSerializer Lhep/dataforge/meta/Config$ConfigSerializer; public fun ()V @@ -36,6 +36,7 @@ public final class hep/dataforge/meta/Config$ConfigSerializer : kotlinx/serializ public final class hep/dataforge/meta/ConfigKt { public static final fun asConfig (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Config; public static final fun get (Lhep/dataforge/meta/Config;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/MetaItem; + public static final fun toConfig (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Config; } public abstract interface class hep/dataforge/meta/Configurable { @@ -58,8 +59,8 @@ public abstract interface annotation class hep/dataforge/meta/DFExperimental : j public final class hep/dataforge/meta/GetIndexedKt { public static final fun getIndexed (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/Name;)Ljava/util/Map; public static final fun getIndexed (Lhep/dataforge/meta/Meta;Ljava/lang/String;)Ljava/util/Map; - public static final fun getIndexed (Lhep/dataforge/meta/MetaNode;Lhep/dataforge/names/Name;)Ljava/util/Map; - public static final fun getIndexed (Lhep/dataforge/meta/MetaNode;Ljava/lang/String;)Ljava/util/Map; + public static final fun getIndexed (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Ljava/util/Map; + public static final fun getIndexed (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Ljava/util/Map; } public final class hep/dataforge/meta/ItemDelegateKt { @@ -131,6 +132,12 @@ public final class hep/dataforge/meta/ItemProvider$Companion { public final fun getEMPTY ()Lhep/dataforge/meta/ItemProvider; } +public final class hep/dataforge/meta/ItemProviderKt { + public static final fun get (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public static final fun get (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; + public static final fun withDefault (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/ItemProvider; +} + public final class hep/dataforge/meta/JsonMeta : hep/dataforge/meta/MetaBase { public static final field Companion Lhep/dataforge/meta/JsonMeta$Companion; public static final field JSON_ARRAY_KEY Ljava/lang/String; @@ -286,24 +293,9 @@ public final class hep/dataforge/meta/MetaItem$ValueItem$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class hep/dataforge/meta/MetaItemSerializer : kotlinx/serialization/KSerializer { - public static final field INSTANCE Lhep/dataforge/meta/MetaItemSerializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lhep/dataforge/meta/MetaItem; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lhep/dataforge/meta/MetaItem;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V -} - -public final class hep/dataforge/meta/MetaKt { +public final class hep/dataforge/meta/MetaItemKt { public static final fun asMetaItem (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MetaItem$NodeItem; public static final fun asMetaItem (Lhep/dataforge/values/Value;)Lhep/dataforge/meta/MetaItem$ValueItem; - public static final fun get (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; - public static final fun get (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/MetaItem; - public static final fun get (Lhep/dataforge/meta/Meta;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; - public static final fun get (Lhep/dataforge/meta/MetaNode;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; - public static final fun get (Lhep/dataforge/meta/MetaNode;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/MetaItem; - public static final fun get (Lhep/dataforge/meta/MetaNode;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; public static final fun getBoolean (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Boolean; public static final fun getDouble (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Double; public static final fun getFloat (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Float; @@ -315,21 +307,26 @@ public final class hep/dataforge/meta/MetaKt { public static final fun getString (Lhep/dataforge/meta/MetaItem;)Ljava/lang/String; public static final fun getStringList (Lhep/dataforge/meta/MetaItem;)Ljava/util/List; public static final fun getValue (Lhep/dataforge/meta/MetaItem;)Lhep/dataforge/values/Value; +} + +public final class hep/dataforge/meta/MetaItemSerializer : kotlinx/serialization/KSerializer { + public static final field INSTANCE Lhep/dataforge/meta/MetaItemSerializer; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lhep/dataforge/meta/MetaItem; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lhep/dataforge/meta/MetaItem;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +} + +public final class hep/dataforge/meta/MetaKt { + public static final fun get (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/MetaItem; + public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/MetaItem; + public static final fun get (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; public static final fun isEmpty (Lhep/dataforge/meta/Meta;)Z + public static final fun itemSequence (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; public static final fun iterator (Lhep/dataforge/meta/Meta;)Ljava/util/Iterator; - public static final fun seal (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/SealedMeta; - public static final fun seal (Lhep/dataforge/meta/MetaItem;)Lhep/dataforge/meta/MetaItem; - public static final fun sequence (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; - public static final fun values (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; -} - -public abstract interface class hep/dataforge/meta/MetaNode : hep/dataforge/meta/Meta { - public abstract fun getItems ()Ljava/util/Map; -} - -public final class hep/dataforge/meta/MetaNode$DefaultImpls { - public static fun getItem (Lhep/dataforge/meta/MetaNode;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; - public static fun toMeta (Lhep/dataforge/meta/MetaNode;)Lhep/dataforge/meta/Meta; + public static final fun valueSequence (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; } public abstract interface class hep/dataforge/meta/MetaRepr { @@ -389,6 +386,8 @@ public final class hep/dataforge/meta/MutableItemDelegateKt { public static final fun long (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun long$default (Lhep/dataforge/meta/MutableItemProvider;JLhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun long$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun node (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun node$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static final fun number (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static final fun number (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadWriteProperty; public static final fun number (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/Number;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; @@ -418,14 +417,16 @@ public abstract interface class hep/dataforge/meta/MutableItemProvider : hep/dat } public final class hep/dataforge/meta/MutableItemProviderKt { - public static final fun getItem (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; - public static final fun node (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun node$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static final fun remove (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)V public static final fun remove (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;)V + public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/meta/Meta;)V + public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)V + public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/values/Value;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Ljava/lang/Iterable;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Ljava/lang/Object;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/NameToken;Ljava/lang/Object;)V + public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Lhep/dataforge/meta/Meta;)V + public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Lhep/dataforge/meta/MetaItem;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Ljava/lang/Iterable;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Ljava/lang/Object;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V @@ -433,14 +434,11 @@ public final class hep/dataforge/meta/MutableItemProviderKt { public static synthetic fun setIndexed$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V public static final fun setIndexedItems (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)V public static synthetic fun setIndexedItems$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V - public static final fun setItem (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Lhep/dataforge/meta/MetaItem;)V - public static final fun setNode (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/meta/Meta;)V - public static final fun setNode (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Lhep/dataforge/meta/Meta;)V - public static final fun setValue (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/values/Value;)V - public static final fun setValue (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Lhep/dataforge/values/Value;)V + public static final fun update (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Meta;)V + public static final fun withDefault (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; } -public abstract interface class hep/dataforge/meta/MutableMeta : hep/dataforge/meta/MetaNode, hep/dataforge/meta/MutableItemProvider { +public abstract interface class hep/dataforge/meta/MutableMeta : hep/dataforge/meta/MutableItemProvider, hep/dataforge/meta/TypedMeta { public abstract fun getItems ()Ljava/util/Map; } @@ -453,7 +451,6 @@ public final class hep/dataforge/meta/MutableMetaKt { public static final fun append (Lhep/dataforge/meta/MutableMeta;Lhep/dataforge/names/Name;Ljava/lang/Object;)V public static final fun append (Lhep/dataforge/meta/MutableMeta;Ljava/lang/String;Ljava/lang/Object;)V public static final fun edit (Lhep/dataforge/meta/AbstractMutableMeta;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)V - public static final fun update (Lhep/dataforge/meta/MutableMeta;Lhep/dataforge/meta/Meta;)V } public abstract interface class hep/dataforge/meta/ObservableItemProvider : hep/dataforge/meta/ItemProvider { @@ -461,21 +458,20 @@ public abstract interface class hep/dataforge/meta/ObservableItemProvider : hep/ public abstract fun removeListener (Ljava/lang/Object;)V } -public class hep/dataforge/meta/Scheme : hep/dataforge/meta/Configurable, hep/dataforge/meta/MetaRepr, hep/dataforge/meta/MutableItemProvider, hep/dataforge/meta/descriptors/Described { +public class hep/dataforge/meta/Scheme : hep/dataforge/meta/MetaRepr, hep/dataforge/meta/MutableItemProvider, hep/dataforge/meta/descriptors/Described { public fun ()V - public fun (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V - public synthetic fun (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun getConfig ()Lhep/dataforge/meta/Config; - public final fun getDefaultItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public fun (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V + public synthetic fun (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getDefaultLayer ()Lhep/dataforge/meta/Meta; public synthetic fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; public fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public final fun getItems ()Lhep/dataforge/meta/MutableItemProvider; public final fun isEmpty ()Z public fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)V public fun toMeta ()Lhep/dataforge/meta/Laminate; public synthetic fun toMeta ()Lhep/dataforge/meta/Meta; - public final fun validateItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)Z + public fun validateItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)Z } public final class hep/dataforge/meta/SchemeKt { @@ -494,42 +490,51 @@ public class hep/dataforge/meta/SchemeSpec : hep/dataforge/meta/Specification, h public fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; public synthetic fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public final fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Scheme; - public synthetic fun read (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; - public fun read (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; - public synthetic fun wrap (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; - public fun wrap (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; + public synthetic fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; + public fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; + public synthetic fun write (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; + public fun write (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; } -public final class hep/dataforge/meta/SealedMeta : hep/dataforge/meta/AbstractMetaNode { +public final class hep/dataforge/meta/SealedMeta : hep/dataforge/meta/AbstractTypedMeta { public fun getItems ()Ljava/util/Map; } +public final class hep/dataforge/meta/SealedMetaKt { + public static final fun seal (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/SealedMeta; + public static final fun seal (Lhep/dataforge/meta/MetaItem;)Lhep/dataforge/meta/MetaItem; +} + public abstract interface class hep/dataforge/meta/Specification { public abstract fun empty ()Lhep/dataforge/meta/MutableItemProvider; public abstract fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; - public abstract fun read (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; - public abstract fun wrap (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; + public abstract fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; + public abstract fun write (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; } public final class hep/dataforge/meta/Specification$DefaultImpls { public static fun empty (Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; public static fun invoke (Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; - public static synthetic fun read$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/ItemProvider;ILjava/lang/Object;)Lhep/dataforge/meta/MutableItemProvider; - public static fun wrap (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; - public static synthetic fun wrap$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;ILjava/lang/Object;)Lhep/dataforge/meta/MutableItemProvider; + public static synthetic fun write$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;ILjava/lang/Object;)Lhep/dataforge/meta/MutableItemProvider; } public final class hep/dataforge/meta/SpecificationKt { - public static final fun configSpec (Lhep/dataforge/meta/MetaItem;Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; - public static final fun configure (Lhep/dataforge/meta/MetaRepr;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MetaRepr; - public static final fun createStyle (Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Meta; - public static final fun spec (Lhep/dataforge/meta/MetaItem;Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; public static final fun spec (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Scheme;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static final fun spec (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun spec$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Scheme;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun spec$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun update (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public static final fun update (Lhep/dataforge/meta/Configurable;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Configurable; - public static final fun update (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; + public static final fun withSpec (Lhep/dataforge/meta/MetaItem;Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; +} + +public abstract interface class hep/dataforge/meta/TypedMeta : hep/dataforge/meta/Meta { + public abstract fun getItems ()Ljava/util/Map; +} + +public final class hep/dataforge/meta/TypedMeta$DefaultImpls { + public static fun getItem (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public static fun toMeta (Lhep/dataforge/meta/TypedMeta;)Lhep/dataforge/meta/Meta; } public abstract interface class hep/dataforge/meta/descriptors/Described { @@ -545,10 +550,11 @@ public final class hep/dataforge/meta/descriptors/DescriptorMetaKt { public static final fun defaultMeta (Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Laminate; } -public abstract class hep/dataforge/meta/descriptors/ItemDescriptor { +public abstract class hep/dataforge/meta/descriptors/ItemDescriptor : hep/dataforge/meta/Configurable { public static final field Companion Lhep/dataforge/meta/descriptors/ItemDescriptor$Companion; public static final field DEFAULT_INDEX_KEY Ljava/lang/String; public synthetic fun (Lhep/dataforge/meta/Config;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public abstract fun copy ()Lhep/dataforge/meta/descriptors/ItemDescriptor; public final fun getAttributes ()Lhep/dataforge/meta/Config; public final fun getConfig ()Lhep/dataforge/meta/Config; public final fun getIndexKey ()Ljava/lang/String; @@ -578,6 +584,8 @@ public final class hep/dataforge/meta/descriptors/NodeDescriptor : hep/dataforge public fun ()V public fun (Lhep/dataforge/meta/Config;)V public synthetic fun (Lhep/dataforge/meta/Config;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun copy ()Lhep/dataforge/meta/descriptors/ItemDescriptor; + public fun copy ()Lhep/dataforge/meta/descriptors/NodeDescriptor; public final fun getDefault ()Lhep/dataforge/meta/Config; public final fun getItems ()Ljava/util/Map; public final fun getNodes ()Ljava/util/Map; @@ -602,6 +610,8 @@ public final class hep/dataforge/meta/descriptors/ValueDescriptor : hep/dataforg public fun (Lhep/dataforge/meta/Config;)V public synthetic fun (Lhep/dataforge/meta/Config;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun allow ([Ljava/lang/Object;)V + public synthetic fun copy ()Lhep/dataforge/meta/descriptors/ItemDescriptor; + public fun copy ()Lhep/dataforge/meta/descriptors/ValueDescriptor; public final fun default (Ljava/lang/Object;)V public final fun getAllowedValues ()Ljava/util/List; public final fun getDefault ()Lhep/dataforge/values/Value; diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt index eab2573f..1c8c590b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt @@ -11,7 +11,7 @@ import kotlin.properties.ReadOnlyProperty public typealias ItemDelegate = ReadOnlyProperty?> public fun ItemProvider.item(key: Name? = null): ItemDelegate = ReadOnlyProperty { _, property -> - getItem(key ?: property.name.asName()) + get(key ?: property.name.asName()) } //TODO add caching for sealed nodes diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt new file mode 100644 index 00000000..dd3194b6 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt @@ -0,0 +1,37 @@ +package hep.dataforge.meta + +import hep.dataforge.meta.MetaItem.NodeItem +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken +import hep.dataforge.names.toName + +public fun interface ItemProvider { + //getItem used instead of get in order to provide extension freedom + public fun getItem(name: Name): MetaItem<*>? + + public companion object { + public val EMPTY: ItemProvider = ItemProvider { null } + } +} + + +/* Get operations*/ + +/** + * Perform recursive item search using given [name]. Each [NameToken] is treated as a name in [Meta.items] of a parent node. + * + * If [name] is empty return current [Meta] as a [NodeItem] + */ +public operator fun ItemProvider?.get(name: Name): MetaItem<*>? = this?.getItem(name) + +/** + * Parse [Name] from [key] using full name notation and pass it to [Meta.get] + */ +public operator fun ItemProvider?.get(key: String): MetaItem<*>? = get(key.toName()) + +/** + * Create a provider that uses given provider for default values if those are not found in this provider + */ +public fun ItemProvider.withDefault(default: ItemProvider): ItemProvider = ItemProvider { + this[it] ?: default[it] +} diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index fb014593..44b05903 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -1,64 +1,12 @@ package hep.dataforge.meta -import hep.dataforge.meta.Meta.Companion.VALUE_KEY import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.names.* -import hep.dataforge.values.* -import kotlinx.serialization.Serializable +import hep.dataforge.values.Value import kotlinx.serialization.json.Json -/** - * A member of the meta tree. Could be represented as one of following: - * * a [ValueItem] (leaf) - * * a [NodeItem] (node) - */ -@Serializable(MetaItemSerializer::class) -public sealed class MetaItem() { - - abstract override fun equals(other: Any?): Boolean - - abstract override fun hashCode(): Int - - @Serializable(MetaItemSerializer::class) - public class ValueItem(public val value: Value) : MetaItem() { - override fun toString(): String = value.toString() - - override fun equals(other: Any?): Boolean { - return this.value == (other as? ValueItem)?.value - } - - override fun hashCode(): Int { - return value.hashCode() - } - } - - @Serializable(MetaItemSerializer::class) - public class NodeItem(public val node: M) : MetaItem() { - //Fixing serializer for node could cause class cast problems, but it should not since Meta descendants are not serializeable - override fun toString(): String = node.toString() - - override fun equals(other: Any?): Boolean = node == (other as? NodeItem<*>)?.node - - override fun hashCode(): Int = node.hashCode() - } - - public companion object { - public fun of(arg: Any?): MetaItem<*> { - return when (arg) { - null -> ValueItem(Null) - is MetaItem<*> -> arg - is Meta -> NodeItem(arg) - else -> ValueItem(Value.of(arg)) - } - } - } -} - -public fun Value.asMetaItem(): ValueItem = ValueItem(this) -public fun M.asMetaItem(): NodeItem = NodeItem(this) - /** * The object that could be represented as [Meta]. Meta provided by [toMeta] method should fully represent object state. * Meaning that two states with the same meta are equal. @@ -68,14 +16,6 @@ public interface MetaRepr { public fun toMeta(): Meta } -public fun interface ItemProvider { - public fun getItem(name: Name): MetaItem<*>? - - public companion object { - public val EMPTY: ItemProvider = ItemProvider { null } - } -} - /** * Generic meta tree representation. Elements are [MetaItem] objects that could be represented by three different entities: * * [MetaItem.ValueItem] (leaf) @@ -100,7 +40,7 @@ public interface Meta : MetaRepr, ItemProvider { } } - override fun toMeta(): Meta = seal() + override fun toMeta(): Meta = this override fun equals(other: Any?): Boolean @@ -122,30 +62,16 @@ public interface Meta : MetaRepr, ItemProvider { } } -/* Get operations*/ - -/** - * Perform recursive item search using given [name]. Each [NameToken] is treated as a name in [Meta.items] of a parent node. - * - * If [name] is empty return current [Meta] as a [NodeItem] - */ -public operator fun Meta?.get(name: Name): MetaItem<*>? = this?.getItem(name) - public operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token) -/** - * Parse [Name] from [key] using full name notation and pass it to [Meta.get] - */ -public operator fun Meta?.get(key: String): MetaItem<*>? = get(key.toName()) - /** * Get a sequence of [Name]-[Value] pairs */ -public fun Meta.values(): Sequence> { +public fun Meta.valueSequence(): Sequence> { return items.asSequence().flatMap { (key, item) -> when (item) { is ValueItem -> sequenceOf(key.asName() to item.value) - is NodeItem -> item.node.values().map { pair -> (key.asName() + pair.first) to pair.second } + is NodeItem -> item.node.valueSequence().map { pair -> (key.asName() + pair.first) to pair.second } } } } @@ -153,41 +79,38 @@ public fun Meta.values(): Sequence> { /** * Get a sequence of all [Name]-[MetaItem] pairs for all items including nodes */ -public fun Meta.sequence(): Sequence>> { - return sequence { - items.forEach { (key, item) -> - yield(key.asName() to item) - if (item is NodeItem<*>) { - yieldAll(item.node.sequence().map { (innerKey, innerItem) -> - (key + innerKey) to innerItem - }) - } +public fun Meta.itemSequence(): Sequence>> = sequence { + items.forEach { (key, item) -> + yield(key.asName() to item) + if (item is NodeItem<*>) { + yieldAll(item.node.itemSequence().map { (innerKey, innerItem) -> + (key + innerKey) to innerItem + }) } } } -public operator fun Meta.iterator(): Iterator>> = sequence().iterator() +public operator fun Meta.iterator(): Iterator>> = itemSequence().iterator() /** * A meta node that ensures that all of its descendants has at least the same type */ -public interface MetaNode> : Meta { +public interface TypedMeta> : Meta { override val items: Map> } /** * The same as [Meta.get], but with specific node type */ -public operator fun > M?.get(name: Name): MetaItem? = if (this == null) { +public operator fun > M?.get(name: Name): MetaItem? = if (this == null) { null } else { @Suppress("UNCHECKED_CAST", "ReplaceGetOrSet") (this as Meta).get(name) as MetaItem? // Do not change } -public operator fun > M?.get(key: String): MetaItem? = this[key.toName()] - -public operator fun > M?.get(key: NameToken): MetaItem? = this[key.asName()] +public operator fun > M?.get(key: String): MetaItem? = this[key.toName()] +public operator fun > M?.get(key: NameToken): MetaItem? = this[key.asName()] /** * Equals, hashcode and to string for any meta @@ -211,57 +134,6 @@ public abstract class MetaBase : Meta { /** * Equals and hash code implementation for meta node */ -public abstract class AbstractMetaNode> : MetaNode, MetaBase() - -/** - * The meta implementation which is guaranteed to be immutable. - * - * If the argument is possibly mutable node, it is copied on creation - */ -public class SealedMeta internal constructor( - override val items: Map>, -) : AbstractMetaNode() - -/** - * Generate sealed node from [this]. If it is already sealed return it as is - */ -public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.mapValues { entry -> entry.value.seal() }) - -@Suppress("UNCHECKED_CAST") -public fun MetaItem<*>.seal(): MetaItem = when (this) { - is ValueItem -> this - is NodeItem -> NodeItem(node.seal()) -} - -/** - * Unsafe methods to access values and nodes directly from [MetaItem] - */ -public val MetaItem<*>?.value: Value? - get() = (this as? ValueItem)?.value - ?: (this?.node?.get(VALUE_KEY) as? ValueItem)?.value - -public val MetaItem<*>?.string: String? get() = value?.string -public val MetaItem<*>?.boolean: Boolean? get() = value?.boolean -public val MetaItem<*>?.number: Number? get() = value?.numberOrNull -public val MetaItem<*>?.double: Double? get() = number?.toDouble() -public val MetaItem<*>?.float: Float? get() = number?.toFloat() -public val MetaItem<*>?.int: Int? get() = number?.toInt() -public val MetaItem<*>?.long: Long? get() = number?.toLong() -public val MetaItem<*>?.short: Short? get() = number?.toShort() - -public inline fun > MetaItem<*>?.enum(): E? = if (this is ValueItem && this.value is EnumValue<*>) { - this.value.value as E -} else { - string?.let { enumValueOf(it) } -} - -public val MetaItem<*>.stringList: List? get() = value?.list?.map { it.string } - -public val MetaItem?.node: M? - get() = when (this) { - null -> null - is ValueItem -> null//error("Trying to interpret value meta item as node item") - is NodeItem -> node - } +public abstract class AbstractTypedMeta> : TypedMeta, MetaBase() public fun Meta.isEmpty(): Boolean = this === Meta.EMPTY || this.items.isEmpty() \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt new file mode 100644 index 00000000..899ca1f9 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt @@ -0,0 +1,87 @@ +package hep.dataforge.meta + +import hep.dataforge.meta.MetaItem.NodeItem +import hep.dataforge.meta.MetaItem.ValueItem +import hep.dataforge.values.* + +/** + * A member of the meta tree. Could be represented as one of following: + * * a [ValueItem] (leaf) + * * a [NodeItem] (node) + */ +@Serializable(MetaItemSerializer::class) +public sealed class MetaItem() { + + abstract override fun equals(other: Any?): Boolean + + abstract override fun hashCode(): Int + + @Serializable(MetaItemSerializer::class) + public class ValueItem(public val value: Value) : MetaItem() { + override fun toString(): String = value.toString() + + override fun equals(other: Any?): Boolean { + return this.value == (other as? ValueItem)?.value + } + + override fun hashCode(): Int { + return value.hashCode() + } + } + + @Serializable(MetaItemSerializer::class) + public class NodeItem(public val node: M) : MetaItem() { + //Fixing serializer for node could cause class cast problems, but it should not since Meta descendants are not serializeable + override fun toString(): String = node.toString() + + override fun equals(other: Any?): Boolean = node == (other as? NodeItem<*>)?.node + + override fun hashCode(): Int = node.hashCode() + } + + public companion object { + public fun of(arg: Any?): MetaItem<*> { + return when (arg) { + null -> ValueItem(Null) + is MetaItem<*> -> arg + is Meta -> NodeItem(arg) + else -> ValueItem(Value.of(arg)) + } + } + } +} + +public fun Value.asMetaItem(): MetaItem.ValueItem = MetaItem.ValueItem(this) +public fun M.asMetaItem(): MetaItem.NodeItem = MetaItem.NodeItem(this) + + +/** + * Unsafe methods to access values and nodes directly from [MetaItem] + */ +public val MetaItem<*>?.value: Value? + get() = (this as? MetaItem.ValueItem)?.value + ?: (this?.node?.get(Meta.VALUE_KEY) as? MetaItem.ValueItem)?.value + +public val MetaItem<*>?.string: String? get() = value?.string +public val MetaItem<*>?.boolean: Boolean? get() = value?.boolean +public val MetaItem<*>?.number: Number? get() = value?.numberOrNull +public val MetaItem<*>?.double: Double? get() = number?.toDouble() +public val MetaItem<*>?.float: Float? get() = number?.toFloat() +public val MetaItem<*>?.int: Int? get() = number?.toInt() +public val MetaItem<*>?.long: Long? get() = number?.toLong() +public val MetaItem<*>?.short: Short? get() = number?.toShort() + +public inline fun > MetaItem<*>?.enum(): E? = if (this is MetaItem.ValueItem && this.value is EnumValue<*>) { + this.value.value as E +} else { + string?.let { enumValueOf(it) } +} + +public val MetaItem<*>.stringList: List? get() = value?.list?.map { it.string } + +public val MetaItem?.node: M? + get() = when (this) { + null -> null + is MetaItem.ValueItem -> null//error("Trying to interpret value meta item as node item") + is MetaItem.NodeItem -> node + } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt index 2a225121..ebf20f13 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt @@ -11,7 +11,7 @@ public class MetaWithDefault(public val meta: Meta, public val default: ItemProv get() = meta.items override fun getItem(name: Name): MetaItem<*>? { - return meta[name] ?: default.getItem(name) + return meta[name] ?: default[name] } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt index 6e5cfc96..53f98997 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt @@ -13,12 +13,12 @@ public typealias MutableItemDelegate = ReadWriteProperty?> public fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate = object : MutableItemDelegate { override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? { - return getItem(key ?: property.name.asName()) + return get(key ?: property.name.asName()) } override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) { val name = key ?: property.name.asName() - setItem(name, value) + set(name, value) } } @@ -113,6 +113,11 @@ public inline fun > MutableItemProvider.enum( ): ReadWriteProperty = item(key).convert(MetaConverter.enum()) { default } +public fun MutableItemProvider.node(key: Name? = null): ReadWriteProperty = item(key).convert( + reader = { it.node }, + writer = { it?.asMetaItem() } +) + public inline fun > M.node(key: Name? = null): ReadWriteProperty = item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItem.NodeItem(it) } }) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt index 6a43af6e..854bba62 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt @@ -2,32 +2,20 @@ package hep.dataforge.meta import hep.dataforge.names.* import hep.dataforge.values.Value -import kotlin.properties.ReadWriteProperty public interface MutableItemProvider : ItemProvider { public fun setItem(name: Name, item: MetaItem<*>?) } -public fun MutableItemProvider.getItem(key: String): MetaItem<*>? = getItem(key.toName()) +public operator fun MutableItemProvider.set(name: Name, item: MetaItem<*>?): Unit = setItem(name, item) -public fun MutableItemProvider.setValue(name: Name, value: Value?): Unit = - setItem(name, value?.let { MetaItem.ValueItem(value) }) +public operator fun MutableItemProvider.set(name: Name, value: Value?): Unit = set(name, value?.asMetaItem()) -public fun MutableItemProvider.setNode(name: Name, meta: Meta?): Unit = - setItem(name, meta?.let { MetaItem.NodeItem(meta) }) +public operator fun MutableItemProvider.set(name: Name, meta: Meta?): Unit = set(name, meta?.asMetaItem()) -public fun MutableItemProvider.setItem(key: String, item: MetaItem<*>?): Unit = setItem(key.toName(), item) +public operator fun MutableItemProvider.set(key: String, item: MetaItem<*>?): Unit = set(key.toName(), item) -public fun MutableItemProvider.setValue(key: String, value: Value?): Unit = - setItem(key, value?.let { MetaItem.ValueItem(value) }) - -public fun MutableItemProvider.setNode(key: String, meta: Meta?): Unit = - setItem(key, meta?.let { MetaItem.NodeItem(meta) }) - -public fun MutableItemProvider.node(key: Name? = null): ReadWriteProperty = item(key).convert( - reader = { it.node }, - writer = { it?.let { MetaItem.NodeItem(it) } } -) +public operator fun MutableItemProvider.set(key: String, meta: Meta?): Unit = set(key, meta?.asMetaItem()) @Suppress("NOTHING_TO_INLINE") public inline fun MutableItemProvider.remove(name: Name): Unit = setItem(name, null) @@ -35,17 +23,16 @@ public inline fun MutableItemProvider.remove(name: Name): Unit = setItem(name, n @Suppress("NOTHING_TO_INLINE") public inline fun MutableItemProvider.remove(name: String): Unit = remove(name.toName()) - /** * Universal unsafe set method */ public operator fun MutableItemProvider.set(name: Name, value: Any?) { when (value) { null -> remove(name) - is MetaItem<*> -> setItem(name, value) - is Meta -> setNode(name, value) - is Configurable -> setNode(name, value.config) - else -> setValue(name, Value.of(value)) + is MetaItem<*> -> set(name, value) + is Meta -> set(name, value) + is Configurable -> set(name, value.config) + else -> set(name, Value.of(value)) } } @@ -59,30 +46,56 @@ public operator fun MutableItemProvider.set(key: String, index: String, value: A set(key.toName().withIndex(index), value) - /* Same name siblings generation */ public fun MutableItemProvider.setIndexedItems( name: Name, items: Iterable>, - indexFactory: (MetaItem<*>, index: Int) -> String = { _, index -> index.toString() } + indexFactory: (MetaItem<*>, index: Int) -> String = { _, index -> index.toString() }, ) { val tokens = name.tokens.toMutableList() val last = tokens.last() items.forEachIndexed { index, meta -> val indexedToken = NameToken(last.body, last.index + indexFactory(meta, index)) tokens[tokens.lastIndex] = indexedToken - setItem(Name(tokens), meta) + set(Name(tokens), meta) } } public fun MutableItemProvider.setIndexed( name: Name, metas: Iterable, - indexFactory: (Meta, index: Int) -> String = { _, index -> index.toString() } + indexFactory: (Meta, index: Int) -> String = { _, index -> index.toString() }, ) { setIndexedItems(name, metas.map { MetaItem.NodeItem(it) }) { item, index -> indexFactory(item.node!!, index) } } public operator fun MutableItemProvider.set(name: Name, metas: Iterable): Unit = setIndexed(name, metas) -public operator fun MutableItemProvider.set(name: String, metas: Iterable): Unit = setIndexed(name.toName(), metas) +public operator fun MutableItemProvider.set(name: String, metas: Iterable): Unit = + setIndexed(name.toName(), metas) + +/** + * Update existing mutable node with another node. The rules are following: + * * value replaces anything + * * node updates node and replaces anything but node + * * node list updates node list if number of nodes in the list is the same and replaces anything otherwise + */ +public fun MutableItemProvider.update(meta: Meta) { + meta.valueSequence().forEach { (name, value) -> set(name, value) } +} + +/** + * Create a mutable item provider that uses given provider for default values if those are not found in this provider. + * Changes are propagated only to this provider. + */ +public fun MutableItemProvider.withDefault(default: ItemProvider): MutableItemProvider = + if (default is Meta && default.isEmpty()) { + //Optimize for use with empty default + this + } else object : MutableItemProvider { + override fun setItem(name: Name, item: MetaItem<*>?) { + this@withDefault.setItem(name, item) + } + + override fun getItem(name: Name): MetaItem<*>? = this@withDefault.getItem(name) ?: default.getItem(name) + } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index 658f77cc..c0520792 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -2,7 +2,7 @@ package hep.dataforge.meta import hep.dataforge.names.* -public interface MutableMeta> : MetaNode, MutableItemProvider { +public interface MutableMeta> : TypedMeta, MutableItemProvider { override val items: Map> } @@ -11,7 +11,7 @@ public interface MutableMeta> : MetaNode, MutableItemP * * Changes in Meta are not thread safe. */ -public abstract class AbstractMutableMeta> : AbstractMetaNode(), MutableMeta { +public abstract class AbstractMutableMeta> : AbstractTypedMeta(), MutableMeta { protected val children: MutableMap> = LinkedHashMap() override val items: Map> @@ -50,7 +50,7 @@ public abstract class AbstractMutableMeta> : AbstractMetaNode 0 -> error("Can't setValue meta item for empty name") 1 -> { val token = name.firstOrNull()!! - @Suppress("UNCHECKED_CAST") val oldItem: MetaItem? = get(name) as? MetaItem + @Suppress("UNCHECKED_CAST") val oldItem: MetaItem? = getItem(name) as? MetaItem replaceItem(token, oldItem, wrapItem(item)) } else -> { @@ -59,28 +59,12 @@ public abstract class AbstractMutableMeta> : AbstractMetaNode if (items[token] == null) { replaceItem(token, null, MetaItem.NodeItem(empty())) } - items[token]?.node!!.setItem(name.cutFirst(), item) + items[token]?.node!!.set(name.cutFirst(), item) } } } } -/** - * Update existing mutable node with another node. The rules are following: - * * value replaces anything - * * node updates node and replaces anything but node - * * node list updates node list if number of nodes in the list is the same and replaces anything otherwise - */ -public fun > M.update(meta: Meta) { - meta.items.forEach { entry -> - when (val value = entry.value) { - is MetaItem.ValueItem -> setValue(entry.key.asName(), value.value) - is MetaItem.NodeItem -> (this[entry.key.asName()] as? MetaItem.NodeItem)?.node?.update(value.node) - ?: run { setNode(entry.key.asName(), value.node) } - } - } -} - /** * Append the node with a same-name-sibling, automatically generating numerical index */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index 1fc74316..f2cb2bb7 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -10,34 +10,34 @@ import hep.dataforge.names.asName * Default item provider and [NodeDescriptor] are optional */ public open class Scheme( - config: Config = Config(), + items: MutableItemProvider = Config(), internal var default: ItemProvider? = null, descriptor: NodeDescriptor? = null, -) : Configurable, MutableItemProvider, Described, MetaRepr { +) : MutableItemProvider, Described, MetaRepr { - override var config: Config = config + public var items: MutableItemProvider = items internal set(value) { //Fix problem with `init` blocks in specifications - field = value.apply { update(field) } + field = value.withDefault(field) } override var descriptor: NodeDescriptor? = descriptor internal set - public fun getDefaultItem(name: Name): MetaItem<*>? { - return default?.getItem(name) ?: descriptor?.get(name)?.defaultItem() + private fun getDefaultItem(name: Name): MetaItem<*>? { + return default?.get(name) ?: descriptor?.get(name)?.defaultItem() } /** * Get a property with default */ - override fun getItem(name: Name): MetaItem<*>? = config[name] ?: getDefaultItem(name) + override fun getItem(name: Name): MetaItem<*>? = items[name] ?: getDefaultItem(name) /** * Check if property with given [name] could be assigned to [item] */ - public fun validateItem(name: Name, item: MetaItem<*>?): Boolean { + public open fun validateItem(name: Name, item: MetaItem<*>?): Boolean { val descriptor = descriptor?.get(name) return descriptor?.validateItem(item) ?: true } @@ -47,16 +47,16 @@ public open class Scheme( */ override fun setItem(name: Name, item: MetaItem<*>?) { if (validateItem(name, item)) { - config.setItem(name, item) + items[name] = item } else { error("Validation failed for property $name with value $item") } } /** - * Provide a default layer which returns items from [defaultProvider] and falls back to descriptor + * Provide a default layer which returns items from [default] and falls back to descriptor * values if default value is unavailable. - * Values from [defaultProvider] completely replace + * Values from [default] completely replace */ public open val defaultLayer: Meta get() = object : MetaBase() { @@ -64,7 +64,7 @@ public open class Scheme( descriptor?.items?.forEach { (key, itemDescriptor) -> val token = NameToken(key) val name = token.asName() - val item = default?.getItem(name) ?: itemDescriptor.defaultItem() + val item = default?.get(name) ?: itemDescriptor.defaultItem() if (item != null) { put(token, item) } @@ -72,9 +72,9 @@ public open class Scheme( } } - override fun toMeta(): Laminate = Laminate(config, defaultLayer) + override fun toMeta(): Laminate = Laminate(items[Name.EMPTY].node, defaultLayer) - public fun isEmpty(): Boolean = config.isEmpty() + public fun isEmpty(): Boolean = toMeta().isEmpty() } /** @@ -91,18 +91,17 @@ public open class SchemeSpec( public constructor(emptyBuilder: () -> T) : this({ config: Config, defaultProvider: ItemProvider, descriptor: NodeDescriptor? -> emptyBuilder().apply { - this.config = config + this.items = config this.default = defaultProvider this.descriptor = descriptor } }) - override fun read(meta: Meta, defaultProvider: ItemProvider): T = - builder(Config(), meta.withDefault(defaultProvider), descriptor) + override fun read(items: ItemProvider): T = + builder(Config(), items, descriptor) - override fun wrap(config: Config, defaultProvider: ItemProvider): T { - return builder(config, defaultProvider, descriptor) - } + override fun write(config: Config, defaultProvider: ItemProvider): T = + builder(config, defaultProvider, descriptor) //TODO Generate descriptor from Scheme class override val descriptor: NodeDescriptor? get() = null @@ -123,7 +122,7 @@ public open class SchemeSpec( //} public fun Meta.asScheme(): Scheme = Scheme().apply { - config = this@asScheme.asConfig() + items = this@asScheme.asConfig() } public fun Meta.toScheme(spec: Specification, block: T.() -> Unit = {}): T = diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt new file mode 100644 index 00000000..bcd69eaa --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt @@ -0,0 +1,23 @@ +package hep.dataforge.meta + +import hep.dataforge.names.NameToken + +/** + * The meta implementation which is guaranteed to be immutable. + * + * If the argument is possibly mutable node, it is copied on creation + */ +public class SealedMeta internal constructor( + override val items: Map>, +) : AbstractTypedMeta() + +/** + * Generate sealed node from [this]. If it is already sealed return it as is + */ +public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.mapValues { entry -> entry.value.seal() }) + +@Suppress("UNCHECKED_CAST") +public fun MetaItem<*>.seal(): MetaItem = when (this) { + is MetaItem.ValueItem -> this + is MetaItem.NodeItem -> MetaItem.NodeItem(node.seal()) +} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt index 05c31dc3..3cf0c0b5 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -2,7 +2,6 @@ package hep.dataforge.meta import hep.dataforge.names.Name import hep.dataforge.names.asName -import kotlin.jvm.JvmName import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -15,13 +14,12 @@ public interface Specification { /** * Read generic read-only meta with this [Specification] producing instance of desired type. */ - public fun read(meta: Meta, defaultProvider: ItemProvider = ItemProvider.EMPTY): T + public fun read(items: ItemProvider): T /** - * Wrap mutable [Config], using it as inner storage (changes to [Specification] are reflected on [Config] + * Wrap [MutableItemProvider], using it as inner storage (changes to [Specification] are reflected on [MutableItemProvider] */ - public fun wrap(config: Config, defaultProvider: ItemProvider = ItemProvider.EMPTY): T = - read(config as Meta, defaultProvider) + public fun write(config: Config, defaultProvider: ItemProvider = ItemProvider.EMPTY): T /** * Generate an empty object @@ -35,17 +33,10 @@ public interface Specification { } /** - * Update given configuration using given type as a builder + * Update a [Config] using given specification */ -public fun Specification.update(meta: Meta, action: T.() -> Unit): T = - read(meta).apply(action) - - -/** - * Apply specified configuration to configurable - */ -public fun > T.configure(spec: S, action: C.() -> Unit): T = - apply { spec.update(toMeta(), action) } +public fun Config.update(spec: Specification, action: T.() -> Unit): T = + spec.write(this).apply(action) /** * Update configuration using given specification @@ -53,35 +44,23 @@ public fun > T.confi public fun > Configurable.update( spec: S, action: C.() -> Unit, -): Configurable = - apply { spec.update(config, action) } +): Configurable = apply { config.update(spec, action) } -/** - * Create a style based on given specification - */ -public fun > S.createStyle(action: C.() -> Unit): Meta = - Config().also { update(it, action) } - -public fun MetaItem<*>.spec(spec: Specification): T? = node?.let { - spec.wrap( - Config(), it - ) -} - -@JvmName("configSpec") -public fun MetaItem.spec(spec: Specification): T? = node?.let { spec.wrap(it) } +public fun MetaItem.withSpec(spec: Specification): T? = + node?.let { spec.write(it) } public fun MutableItemProvider.spec( - spec: Specification, key: Name? = null, + spec: Specification, + key: Name? = null, ): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): T? { val name = key ?: property.name.asName() - return getItem(name).node?.let { spec.read(it) } + return get(name).node?.let { spec.read(it) } } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { val name = key ?: property.name.asName() - setItem(name, value?.toMeta()?.asMetaItem()) + set(name, value?.toMeta()?.asMetaItem()) } } @@ -92,11 +71,11 @@ public fun MutableItemProvider.spec( ): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): T { val name = key ?: property.name.asName() - return getItem(name).node?.let { spec.read(it) } ?: default + return get(name).node?.let { spec.read(it) } ?: default } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { val name = key ?: property.name.asName() - setItem(name, value.toMeta().asMetaItem()) + set(name, value.toMeta().asMetaItem()) } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt index ac1c4693..3fae4e20 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt @@ -8,7 +8,7 @@ import hep.dataforge.values.* * A common parent for [ValueDescriptor] and [NodeDescriptor]. Describes a single [MetaItem] or a group of same-name-siblings. */ @DFBuilder -public sealed class ItemDescriptor(public val config: Config) { +public sealed class ItemDescriptor(final override val config: Config): Configurable { /** * True if same name siblings with this name are allowed diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/descriptorExtensions.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/descriptorExtensions.kt new file mode 100644 index 00000000..f05af69e --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/descriptorExtensions.kt @@ -0,0 +1,18 @@ +package hep.dataforge.meta.descriptors + +import hep.dataforge.names.Name +import hep.dataforge.values.ValueType +import hep.dataforge.values.asValue + +public inline fun > NodeDescriptor.enum( + key: Name, + default: E?, + crossinline modifier: ValueDescriptor.() -> Unit = {}, +): Unit = value(key) { + type(ValueType.STRING) + default?.let { + default(default) + } + allowedValues = enumValues().map { it.asValue() } + modifier() +} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/getIndexed.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/getIndexed.kt index 20ce93dc..6b01723a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/getIndexed.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/getIndexed.kt @@ -29,8 +29,8 @@ public fun Meta.getIndexed(name: String): Map> = this@getIn * Get all items matching given name. */ @Suppress("UNCHECKED_CAST") -public fun > M.getIndexed(name: Name): Map> = +public fun > M.getIndexed(name: Name): Map> = (this as Meta).getIndexed(name) as Map> -public fun > M.getIndexed(name: String): Map> = +public fun > M.getIndexed(name: String): Map> = getIndexed(name.toName()) \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt index 774a736a..6949987a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt @@ -35,12 +35,12 @@ public fun Map.toMeta(descriptor: NodeDescriptor? = null): Meta = if (value is List<*>) { val items = value.map { toItem(it) } if (items.all { it is MetaItem.ValueItem }) { - setValue(key, ListValue(items.map { it.value!! })) + set(key, ListValue(items.map { it.value!! })) } else { setIndexedItems(key.toName(), value.map { toItem(it) }) } } else { - setItem(key, toItem(value)) + set(key, toItem(value)) } } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt index 8e2c4d02..7048fe64 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt @@ -19,7 +19,7 @@ public interface TransformationRule { * @return a sequence of item paths to be transformed */ public fun selectItems(meta: Meta): Sequence = - meta.sequence().filter { matches(it.first, it.second) }.map { it.first } + meta.itemSequence().filter { matches(it.first, it.second) }.map { it.first } /** * Apply transformation for a single item (Node or Value) to the target @@ -37,10 +37,10 @@ public data class KeepTransformationRule(val selector: (Name) -> Boolean) : } override fun selectItems(meta: Meta): Sequence = - meta.sequence().map { it.first }.filter(selector) + meta.itemSequence().map { it.first }.filter(selector) override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { - if (selector(name)) target.setItem(name, item) + if (selector(name)) target.set(name, item) } } @@ -170,7 +170,7 @@ public class MetaTransformationBuilder { public fun keep(regex: String) { transformations.add( RegexItemTransformationRule(regex.toRegex()) { name, _, metaItem -> - setItem(name, metaItem) + set(name, metaItem) }) } diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt index 593401d1..ca5898e6 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaBuilderTest.kt @@ -30,7 +30,7 @@ class MetaBuilderTest { "b.a[$it]" put it } }.seal() - assertEquals(10, meta.values().count()) + assertEquals(10, meta.valueSequence().count()) val nodes = meta.getIndexed("b.a") diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt index 6b8442d1..a6b514d4 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt @@ -30,7 +30,7 @@ class MetaDelegateTest { fun delegateTest() { val testObject = TestScheme.empty() - testObject.setValue("myValue","theString".asValue()) + testObject.set("myValue","theString".asValue()) testObject.enumValue = TestEnum.NO testObject.inner = InnerSpec { innerValue = "ddd" } diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt index 1f7d7e97..14085f08 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt @@ -17,9 +17,9 @@ class SchemeTest{ val meta = styled.toMeta() - assertEquals(10, meta.values().count()) + assertEquals(10, meta.valueSequence().count()) - val bNode = styled.getItem("b").node + val bNode = styled.get("b").node val aNodes = bNode?.getIndexed("a") diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt index 587e2049..5bb93649 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt @@ -4,15 +4,14 @@ import kotlin.test.Test import kotlin.test.assertEquals class SpecificationTest { - class TestStyled : Scheme() { + class TestStyled(config: Config, default: ItemProvider?) : Scheme(config, default) { var list by numberList(1, 2, 3) companion object : Specification { - override fun read(meta: Meta, defaultProvider: ItemProvider): TestStyled = - TestStyled().apply { - this.config = meta.asConfig() + override fun read(items: ItemProvider): TestStyled = TestStyled(Config(), items) - } + override fun write(config: Config, defaultProvider: ItemProvider): TestStyled = + TestStyled(config, defaultProvider) } } From 617ed13efaaab882e74e6b117402cd097c4df85b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 23 Dec 2020 19:15:47 +0300 Subject: [PATCH 17/59] fix imports --- dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt | 1 + .../src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 44b05903..20cac1dd 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.names.* import hep.dataforge.values.Value +import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt index 899ca1f9..e4934ed9 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt @@ -3,6 +3,7 @@ package hep.dataforge.meta import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.values.* +import kotlinx.serialization.Serializable /** * A member of the meta tree. Could be represented as one of following: From e931994b751fc610598094e001642d0a30d54c36 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 23 Dec 2020 21:22:47 +0300 Subject: [PATCH 18/59] Cleanup --- dataforge-meta/api/dataforge-meta.api | 21 +++++---- .../kotlin/hep/dataforge/meta/ItemProvider.kt | 44 +++++++++++++++++-- .../kotlin/hep/dataforge/meta/JsonMeta.kt | 3 +- .../kotlin/hep/dataforge/meta/Meta.kt | 15 +++---- .../kotlin/hep/dataforge/meta/MetaItem.kt | 3 +- .../kotlin/hep/dataforge/meta/MutableMeta.kt | 3 +- .../kotlin/hep/dataforge/meta/Scheme.kt | 10 ++--- .../hep/dataforge/meta/Specification.kt | 2 +- .../kotlin/hep/dataforge/meta/getIndexed.kt | 36 --------------- .../hep/dataforge/meta/SpecificationTest.kt | 6 +-- 10 files changed, 69 insertions(+), 74 deletions(-) delete mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/getIndexed.kt diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index ca03c72a..a72681ba 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -56,13 +56,6 @@ public abstract interface annotation class hep/dataforge/meta/DFBuilder : java/l public abstract interface annotation class hep/dataforge/meta/DFExperimental : java/lang/annotation/Annotation { } -public final class hep/dataforge/meta/GetIndexedKt { - public static final fun getIndexed (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/Name;)Ljava/util/Map; - public static final fun getIndexed (Lhep/dataforge/meta/Meta;Ljava/lang/String;)Ljava/util/Map; - public static final fun getIndexed (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Ljava/util/Map; - public static final fun getIndexed (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Ljava/util/Map; -} - public final class hep/dataforge/meta/ItemDelegateKt { public static final fun boolean (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; public static final fun boolean (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadOnlyProperty; @@ -135,6 +128,11 @@ public final class hep/dataforge/meta/ItemProvider$Companion { public final class hep/dataforge/meta/ItemProviderKt { public static final fun get (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; public static final fun get (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; + public static final fun getIndexed (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Ljava/util/Map; + public static final fun getIndexed (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Ljava/util/Map; + public static final fun getIndexed (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Ljava/util/Map; + public static final fun getIndexed (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Ljava/util/Map; + public static final fun getRootNode (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Meta; public static final fun withDefault (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/ItemProvider; } @@ -492,8 +490,8 @@ public class hep/dataforge/meta/SchemeSpec : hep/dataforge/meta/Specification, h public final fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Scheme; public synthetic fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; public fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; - public synthetic fun write (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; - public fun write (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; + public synthetic fun write (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; + public fun write (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; } public final class hep/dataforge/meta/SealedMeta : hep/dataforge/meta/AbstractTypedMeta { @@ -509,13 +507,13 @@ public abstract interface class hep/dataforge/meta/Specification { public abstract fun empty ()Lhep/dataforge/meta/MutableItemProvider; public abstract fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public abstract fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; - public abstract fun write (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; + public abstract fun write (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; } public final class hep/dataforge/meta/Specification$DefaultImpls { public static fun empty (Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; public static fun invoke (Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; - public static synthetic fun write$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Config;Lhep/dataforge/meta/ItemProvider;ILjava/lang/Object;)Lhep/dataforge/meta/MutableItemProvider; + public static synthetic fun write$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;ILjava/lang/Object;)Lhep/dataforge/meta/MutableItemProvider; } public final class hep/dataforge/meta/SpecificationKt { @@ -529,6 +527,7 @@ public final class hep/dataforge/meta/SpecificationKt { } public abstract interface class hep/dataforge/meta/TypedMeta : hep/dataforge/meta/Meta { + public abstract fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; public abstract fun getItems ()Ljava/util/Map; } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt index dd3194b6..736753c2 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt @@ -1,9 +1,7 @@ package hep.dataforge.meta import hep.dataforge.meta.MetaItem.NodeItem -import hep.dataforge.names.Name -import hep.dataforge.names.NameToken -import hep.dataforge.names.toName +import hep.dataforge.names.* public fun interface ItemProvider { //getItem used instead of get in order to provide extension freedom @@ -24,10 +22,15 @@ public fun interface ItemProvider { */ public operator fun ItemProvider?.get(name: Name): MetaItem<*>? = this?.getItem(name) +/** + * The root node of this item provider if it is present + */ +public val ItemProvider.rootNode: Meta? get() = get(Name.EMPTY).node + /** * Parse [Name] from [key] using full name notation and pass it to [Meta.get] */ -public operator fun ItemProvider?.get(key: String): MetaItem<*>? = get(key.toName()) +public operator fun ItemProvider?.get(key: String): MetaItem<*>? = this?.get(key.toName()) /** * Create a provider that uses given provider for default values if those are not found in this provider @@ -35,3 +38,36 @@ public operator fun ItemProvider?.get(key: String): MetaItem<*>? = get(key.toNam public fun ItemProvider.withDefault(default: ItemProvider): ItemProvider = ItemProvider { this[it] ?: default[it] } + +/** + * Get all items matching given name. The index of the last element, if present is used as a [Regex], + * against which indexes of elements are matched. + */ +public fun ItemProvider.getIndexed(name: Name): Map> { + val root: Meta = when (name.length) { + 0 -> error("Can't use empty name for 'getIndexed'") + 1 -> this.rootNode ?: return emptyMap() + else -> this[name.cutLast()].node ?: return emptyMap() + } + + val (body, index) = name.lastOrNull()!! + return if (index == null) { + root.items.filter { it.key.body == body }.mapKeys { it.key.index } + } else { + val regex = index.toRegex() + root.items.filter { it.key.body == body && (regex.matches(it.key.index ?: "")) } + .mapKeys { it.key.index } + } +} + +public fun ItemProvider.getIndexed(name: String): Map> = this@getIndexed.getIndexed(name.toName()) + +/** + * Get all items matching given name. + */ +@Suppress("UNCHECKED_CAST") +public fun > M.getIndexed(name: Name): Map> = + (this as Meta).getIndexed(name) as Map> + +public fun > M.getIndexed(name: String): Map> = + getIndexed(name.toName()) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt index 6dcfe76a..1b028477 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt @@ -2,7 +2,6 @@ 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.Companion.DEFAULT_INDEX_KEY import hep.dataforge.meta.descriptors.NodeDescriptor @@ -26,7 +25,7 @@ public fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement = when //Use these methods to customize JSON key mapping @Suppress("NULLABLE_EXTENSION_OPERATOR_WITH_SAFE_CALL_RECEIVER") -private fun String.toJsonKey(descriptor: ItemDescriptor?) = descriptor?.attributes["jsonName"].string ?: toString() +private fun String.toJsonKey(descriptor: ItemDescriptor?) = descriptor?.attributes?.get("jsonName").string ?: toString() //private fun NodeDescriptor?.getDescriptor(key: String) = this?.items?.get(key) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 20cac1dd..cad22948 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -4,7 +4,6 @@ import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.names.* import hep.dataforge.values.Value -import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -63,7 +62,7 @@ public interface Meta : MetaRepr, ItemProvider { } } -public operator fun Meta?.get(token: NameToken): MetaItem<*>? = this?.items?.get(token) +public operator fun Meta.get(token: NameToken): MetaItem<*>? = items.get(token) /** * Get a sequence of [Name]-[Value] pairs @@ -98,17 +97,17 @@ public operator fun Meta.iterator(): Iterator>> = itemSeq */ public interface TypedMeta> : Meta { override val items: Map> + + @Suppress("UNCHECKED_CAST") + override fun getItem(name: Name): MetaItem? = super.getItem(name)?.let { it as MetaItem } + //Typed meta guarantees that all children have M type } /** * The same as [Meta.get], but with specific node type */ -public operator fun > M?.get(name: Name): MetaItem? = if (this == null) { - null -} else { - @Suppress("UNCHECKED_CAST", "ReplaceGetOrSet") - (this as Meta).get(name) as MetaItem? // Do not change -} +public operator fun > M?.get(name: Name): MetaItem? = this?.getItem(name) + public operator fun > M?.get(key: String): MetaItem? = this[key.toName()] public operator fun > M?.get(key: NameToken): MetaItem? = this[key.asName()] diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt index e4934ed9..c85d8ba2 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt @@ -3,7 +3,6 @@ package hep.dataforge.meta import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.values.* -import kotlinx.serialization.Serializable /** * A member of the meta tree. Could be represented as one of following: @@ -72,7 +71,7 @@ public val MetaItem<*>?.int: Int? get() = number?.toInt() public val MetaItem<*>?.long: Long? get() = number?.toLong() public val MetaItem<*>?.short: Short? get() = number?.toShort() -public inline fun > MetaItem<*>?.enum(): E? = if (this is MetaItem.ValueItem && this.value is EnumValue<*>) { +public inline fun > MetaItem<*>?.enum(): E? = if (this is ValueItem && this.value is EnumValue<*>) { this.value.value as E } else { string?.let { enumValueOf(it) } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index c0520792..e4ec6c4b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -28,7 +28,6 @@ public abstract class AbstractMutableMeta> : AbstractTypedMet //itemChanged(key.asName(), oldItem, newItem) } - @Suppress("UNCHECKED_CAST") protected fun wrapItem(item: MetaItem<*>?): MetaItem? = when (item) { null -> null is MetaItem.ValueItem -> item @@ -50,7 +49,7 @@ public abstract class AbstractMutableMeta> : AbstractTypedMet 0 -> error("Can't setValue meta item for empty name") 1 -> { val token = name.firstOrNull()!! - @Suppress("UNCHECKED_CAST") val oldItem: MetaItem? = getItem(name) as? MetaItem + val oldItem: MetaItem? = getItem(name) replaceItem(token, oldItem, wrapItem(item)) } else -> { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index f2cb2bb7..e307010d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -86,12 +86,12 @@ public inline operator fun T.invoke(block: T.() -> Unit): T = apply * A specification for simplified generation of wrappers */ public open class SchemeSpec( - private val builder: (config: Config, defaultProvider: ItemProvider, descriptor: NodeDescriptor?) -> T, + private val builder: (target: MutableItemProvider, defaultProvider: ItemProvider, descriptor: NodeDescriptor?) -> T, ) : Specification, Described { - public constructor(emptyBuilder: () -> T) : this({ config: Config, defaultProvider: ItemProvider, descriptor: NodeDescriptor? -> + public constructor(emptyBuilder: () -> T) : this({ target: MutableItemProvider, defaultProvider: ItemProvider, descriptor: NodeDescriptor? -> emptyBuilder().apply { - this.items = config + this.items = target this.default = defaultProvider this.descriptor = descriptor } @@ -100,8 +100,8 @@ public open class SchemeSpec( override fun read(items: ItemProvider): T = builder(Config(), items, descriptor) - override fun write(config: Config, defaultProvider: ItemProvider): T = - builder(config, defaultProvider, descriptor) + override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): T = + builder(target, defaultProvider, descriptor) //TODO Generate descriptor from Scheme class override val descriptor: NodeDescriptor? get() = null diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt index 3cf0c0b5..a4e18069 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -19,7 +19,7 @@ public interface Specification { /** * Wrap [MutableItemProvider], using it as inner storage (changes to [Specification] are reflected on [MutableItemProvider] */ - public fun write(config: Config, defaultProvider: ItemProvider = ItemProvider.EMPTY): T + public fun write(target: MutableItemProvider, defaultProvider: ItemProvider = ItemProvider.EMPTY): T /** * Generate an empty object diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/getIndexed.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/getIndexed.kt deleted file mode 100644 index 6b01723a..00000000 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/getIndexed.kt +++ /dev/null @@ -1,36 +0,0 @@ -package hep.dataforge.meta - -import hep.dataforge.names.* - -/** - * Get all items matching given name. The index of the last element, if present is used as a [Regex], - * against which indexes of elements are matched. - */ -public fun Meta.getIndexed(name: Name): Map> { - val root = when (name.length) { - 0 -> error("Can't use empty name for 'getIndexed'") - 1 -> this - else -> this[name.cutLast()].node ?: return emptyMap() - } - - val (body, index) = name.lastOrNull()!! - return if (index == null) { - root.items.filter { it.key.body == body }.mapKeys { it.key.index } - } else { - val regex = index.toRegex() - root.items.filter { it.key.body == body && (regex.matches(it.key.index ?: "")) } - .mapKeys { it.key.index } - } -} - -public fun Meta.getIndexed(name: String): Map> = this@getIndexed.getIndexed(name.toName()) - -/** - * Get all items matching given name. - */ -@Suppress("UNCHECKED_CAST") -public fun > M.getIndexed(name: Name): Map> = - (this as Meta).getIndexed(name) as Map> - -public fun > M.getIndexed(name: String): Map> = - getIndexed(name.toName()) \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt index 5bb93649..93934154 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt @@ -4,14 +4,14 @@ import kotlin.test.Test import kotlin.test.assertEquals class SpecificationTest { - class TestStyled(config: Config, default: ItemProvider?) : Scheme(config, default) { + class TestStyled(target: MutableItemProvider, default: ItemProvider?) : Scheme(target, default) { var list by numberList(1, 2, 3) companion object : Specification { override fun read(items: ItemProvider): TestStyled = TestStyled(Config(), items) - override fun write(config: Config, defaultProvider: ItemProvider): TestStyled = - TestStyled(config, defaultProvider) + override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): TestStyled = + TestStyled(target, defaultProvider) } } From 397a19fb323a843da05d9877b75bab2339baf4a2 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 24 Dec 2020 09:48:14 +0300 Subject: [PATCH 19/59] Cleanup --- dataforge-meta/api/dataforge-meta.api | 4 ++- .../kotlin/hep/dataforge/meta/ItemProvider.kt | 7 +++++ .../kotlin/hep/dataforge/meta/JsonMeta.kt | 1 + .../kotlin/hep/dataforge/meta/Meta.kt | 1 + .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 5 --- .../kotlin/hep/dataforge/meta/MetaItem.kt | 1 + .../hep/dataforge/meta/MutableItemProvider.kt | 31 +++++++++++++++++++ .../kotlin/hep/dataforge/meta/MutableMeta.kt | 2 +- .../kotlin/hep/dataforge/meta/Scheme.kt | 11 ------- .../transformations/MetaTransformation.kt | 6 ++-- 10 files changed, 48 insertions(+), 21 deletions(-) diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index a72681ba..13a1ee29 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -128,6 +128,7 @@ public final class hep/dataforge/meta/ItemProvider$Companion { public final class hep/dataforge/meta/ItemProviderKt { public static final fun get (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; public static final fun get (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; + public static final fun getChild (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/ItemProvider; public static final fun getIndexed (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Ljava/util/Map; public static final fun getIndexed (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Ljava/util/Map; public static final fun getIndexed (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Ljava/util/Map; @@ -251,7 +252,6 @@ public final class hep/dataforge/meta/MetaBuilder : hep/dataforge/meta/AbstractM public final class hep/dataforge/meta/MetaBuilderKt { public static final fun Meta (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MetaBuilder; public static final fun builder (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MetaBuilder; - public static final fun edit (Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MetaBuilder; } public abstract class hep/dataforge/meta/MetaItem { @@ -415,6 +415,8 @@ public abstract interface class hep/dataforge/meta/MutableItemProvider : hep/dat } public final class hep/dataforge/meta/MutableItemProviderKt { + public static final fun editChild (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; + public static final fun getChild (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MutableItemProvider; public static final fun remove (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)V public static final fun remove (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/meta/Meta;)V diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt index 736753c2..dcb369a0 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt @@ -62,6 +62,13 @@ public fun ItemProvider.getIndexed(name: Name): Map> { public fun ItemProvider.getIndexed(name: String): Map> = this@getIndexed.getIndexed(name.toName()) +/** + * Return a provider referencing a child node + */ +public fun ItemProvider.getChild(childName: Name): ItemProvider = get(childName).node ?: ItemProvider.EMPTY + +public fun ItemProvider.getChild(childName: String): ItemProvider = getChild(childName.toName()) + /** * Get all items matching given name. */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt index 1b028477..f41da67e 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt @@ -2,6 +2,7 @@ package hep.dataforge.meta +import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY import hep.dataforge.meta.descriptors.ItemDescriptor import hep.dataforge.meta.descriptors.ItemDescriptor.Companion.DEFAULT_INDEX_KEY import hep.dataforge.meta.descriptors.NodeDescriptor diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index cad22948..719fc744 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.names.* import hep.dataforge.values.Value +import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index 12730d46..8ebceaa3 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -133,11 +133,6 @@ public fun Meta.builder(): MetaBuilder { } } -/** - * Create a deep copy of this meta and apply builder to it - */ -public inline fun Meta.edit(builder: MetaBuilder.() -> Unit): MetaBuilder = builder().apply(builder) - /** * Build a [MetaBuilder] using given transformation */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt index c85d8ba2..fd1de97a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt @@ -3,6 +3,7 @@ package hep.dataforge.meta import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.values.* +import kotlinx.serialization.Serializable /** * A member of the meta tree. Could be represented as one of following: diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt index 854bba62..d2a1de38 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt @@ -74,6 +74,31 @@ public operator fun MutableItemProvider.set(name: Name, metas: Iterable): public operator fun MutableItemProvider.set(name: String, metas: Iterable): Unit = setIndexed(name.toName(), metas) +/** + * Get a [MutableItemProvider] referencing a child node + */ +public fun MutableItemProvider.getChild(childName: Name): MutableItemProvider { + fun createProvider() = object : MutableItemProvider { + override fun setItem(name: Name, item: MetaItem<*>?) { + this@getChild.setItem(childName + name, item) + } + + override fun getItem(name: Name): MetaItem<*>? = this@getChild.getItem(childName + name) + } + + return when { + childName.isEmpty() -> this + this is MutableMeta<*> -> { + get(childName).node ?: createProvider() + } + else -> { + createProvider() + } + } +} + +public fun MutableItemProvider.getChild(childName: String): MutableItemProvider = getChild(childName.toName()) + /** * Update existing mutable node with another node. The rules are following: * * value replaces anything @@ -84,6 +109,12 @@ public fun MutableItemProvider.update(meta: Meta) { meta.valueSequence().forEach { (name, value) -> set(name, value) } } +/** + * Edit a provider child at given name location + */ +public fun MutableItemProvider.editChild(name: Name, builder: MutableItemProvider.() -> Unit): MutableItemProvider = + getChild(name).apply(builder) + /** * Create a mutable item provider that uses given provider for default values if those are not found in this provider. * Changes are propagated only to this provider. diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index e4ec6c4b..aaa44998 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -46,7 +46,7 @@ public abstract class AbstractMutableMeta> : AbstractTypedMet override fun setItem(name: Name, item: MetaItem<*>?) { when (name.length) { - 0 -> error("Can't setValue meta item for empty name") + 0 -> error("Can't set a meta item for empty name") 1 -> { val token = name.firstOrNull()!! val oldItem: MetaItem? = getItem(name) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index e307010d..222c439a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -110,17 +110,6 @@ public open class SchemeSpec( final override inline operator fun invoke(action: T.() -> Unit): T = empty().apply(action) } -///** -// * A scheme that uses [Meta] as a default layer -// */ -//public open class MetaScheme( -// private val meta: Meta, -// override val descriptor: NodeDescriptor? = null, -// config: Config = Config(), -//) : Scheme(config, meta) { -// override val defaultLayer: Meta get() = Laminate(meta, descriptor?.defaultItem().node) -//} - public fun Meta.asScheme(): Scheme = Scheme().apply { items = this@asScheme.asConfig() } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt index 7048fe64..7f8e8097 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt @@ -49,7 +49,7 @@ public data class KeepTransformationRule(val selector: (Name) -> Boolean) : */ public data class SingleItemTransformationRule( val from: Name, - val transform: MutableMeta<*>.(Name, MetaItem<*>?) -> Unit + val transform: MutableMeta<*>.(Name, MetaItem<*>?) -> Unit, ) : TransformationRule { override fun matches(name: Name, item: MetaItem<*>?): Boolean { return name == from @@ -66,7 +66,7 @@ public data class SingleItemTransformationRule( public data class RegexItemTransformationRule( val from: Regex, - val transform: MutableMeta<*>.(name: Name, MatchResult, MetaItem<*>?) -> Unit + val transform: MutableMeta<*>.(name: Name, MatchResult, MetaItem<*>?) -> Unit, ) : TransformationRule { override fun matches(name: Name, item: MetaItem<*>?): Boolean { return from.matches(name.toString()) @@ -116,7 +116,7 @@ public inline class MetaTransformation(public val transformations: Collection rule.selectItems(source).forEach { name -> remove(name) From 1c89543d739c24d42322d549f4bfe6d2caab3f32 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 24 Dec 2020 10:32:11 +0300 Subject: [PATCH 20/59] Cleanup --- CHANGELOG.md | 1 + dataforge-meta/api/dataforge-meta.api | 8 +++---- .../kotlin/hep/dataforge/meta/ItemProvider.kt | 18 +++++++-------- .../kotlin/hep/dataforge/meta/Meta.kt | 2 +- .../kotlin/hep/dataforge/meta/MetaItem.kt | 12 +++++----- .../hep/dataforge/meta/MutableItemProvider.kt | 8 ++++++- .../kotlin/hep/dataforge/meta/MutableMeta.kt | 8 +++---- .../meta/descriptors/ItemDescriptor.kt | 22 +++++++++++-------- 8 files changed, 45 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40e80dd5..d1d7af27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Moved `Envelope` builder to a top level function. Companion invoke is deprecated. - Context logging moved to the extension - `number` and `string` methods on `Value` moved to extensions (breaking change) +- \[Major breaking change\] Schemes and configurables us `MutableItemProvider` instead of `Config` ### Deprecated diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 13a1ee29..3b35c2c9 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -129,10 +129,9 @@ public final class hep/dataforge/meta/ItemProviderKt { public static final fun get (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; public static final fun get (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; public static final fun getChild (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/ItemProvider; + public static final fun getChild (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/ItemProvider; public static final fun getIndexed (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Ljava/util/Map; public static final fun getIndexed (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Ljava/util/Map; - public static final fun getIndexed (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Ljava/util/Map; - public static final fun getIndexed (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Ljava/util/Map; public static final fun getRootNode (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Meta; public static final fun withDefault (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/ItemProvider; } @@ -417,6 +416,7 @@ public abstract interface class hep/dataforge/meta/MutableItemProvider : hep/dat public final class hep/dataforge/meta/MutableItemProviderKt { public static final fun editChild (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public static final fun getChild (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MutableItemProvider; + public static final fun getChild (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/MutableItemProvider; public static final fun remove (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)V public static final fun remove (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/meta/Meta;)V @@ -448,8 +448,8 @@ public final class hep/dataforge/meta/MutableMeta$DefaultImpls { } public final class hep/dataforge/meta/MutableMetaKt { - public static final fun append (Lhep/dataforge/meta/MutableMeta;Lhep/dataforge/names/Name;Ljava/lang/Object;)V - public static final fun append (Lhep/dataforge/meta/MutableMeta;Ljava/lang/String;Ljava/lang/Object;)V + public static final fun append (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Ljava/lang/Object;)V + public static final fun append (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Ljava/lang/Object;)V public static final fun edit (Lhep/dataforge/meta/AbstractMutableMeta;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)V } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt index dcb369a0..18f0df9a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt @@ -69,12 +69,12 @@ public fun ItemProvider.getChild(childName: Name): ItemProvider = get(childName) public fun ItemProvider.getChild(childName: String): ItemProvider = getChild(childName.toName()) -/** - * Get all items matching given name. - */ -@Suppress("UNCHECKED_CAST") -public fun > M.getIndexed(name: Name): Map> = - (this as Meta).getIndexed(name) as Map> - -public fun > M.getIndexed(name: String): Map> = - getIndexed(name.toName()) +///** +// * Get all items matching given name. +// */ +//@Suppress("UNCHECKED_CAST") +//public fun > M.getIndexed(name: Name): Map> = +// (this as Meta).getIndexed(name) as Map> +// +//public fun > M.getIndexed(name: String): Map> = +// getIndexed(name.toName()) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 719fc744..355f5982 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -83,7 +83,7 @@ public fun Meta.valueSequence(): Sequence> { public fun Meta.itemSequence(): Sequence>> = sequence { items.forEach { (key, item) -> yield(key.asName() to item) - if (item is NodeItem<*>) { + if (item is NodeItem) { yieldAll(item.node.itemSequence().map { (innerKey, innerItem) -> (key + innerKey) to innerItem }) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt index fd1de97a..1649393d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt @@ -52,16 +52,16 @@ public sealed class MetaItem() { } } -public fun Value.asMetaItem(): MetaItem.ValueItem = MetaItem.ValueItem(this) -public fun M.asMetaItem(): MetaItem.NodeItem = MetaItem.NodeItem(this) +public fun Value.asMetaItem(): ValueItem = ValueItem(this) +public fun M.asMetaItem(): NodeItem = NodeItem(this) /** * Unsafe methods to access values and nodes directly from [MetaItem] */ public val MetaItem<*>?.value: Value? - get() = (this as? MetaItem.ValueItem)?.value - ?: (this?.node?.get(Meta.VALUE_KEY) as? MetaItem.ValueItem)?.value + get() = (this as? ValueItem)?.value + ?: (this?.node?.get(Meta.VALUE_KEY) as? ValueItem)?.value public val MetaItem<*>?.string: String? get() = value?.string public val MetaItem<*>?.boolean: Boolean? get() = value?.boolean @@ -83,6 +83,6 @@ public val MetaItem<*>.stringList: List? get() = value?.list?.map { it.s public val MetaItem?.node: M? get() = when (this) { null -> null - is MetaItem.ValueItem -> null//error("Trying to interpret value meta item as node item") - is MetaItem.NodeItem -> node + is ValueItem -> null//error("Trying to interpret value meta item as node item") + is NodeItem -> node } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt index d2a1de38..5efa89a3 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt @@ -2,6 +2,7 @@ package hep.dataforge.meta import hep.dataforge.names.* import hep.dataforge.values.Value +import hep.dataforge.values.asValue public interface MutableItemProvider : ItemProvider { public fun setItem(name: Name, item: MetaItem<*>?) @@ -29,8 +30,13 @@ public inline fun MutableItemProvider.remove(name: String): Unit = remove(name.t public operator fun MutableItemProvider.set(name: Name, value: Any?) { when (value) { null -> remove(name) + is Value -> set(name, value) + is Number -> set(name, value.asValue()) + is String -> set(name, value.asValue()) + is Boolean -> set(name, value.asValue()) is MetaItem<*> -> set(name, value) is Meta -> set(name, value) + is MetaRepr -> set(name, value.toMeta()) is Configurable -> set(name, value.config) else -> set(name, Value.of(value)) } @@ -97,7 +103,7 @@ public fun MutableItemProvider.getChild(childName: Name): MutableItemProvider { } } -public fun MutableItemProvider.getChild(childName: String): MutableItemProvider = getChild(childName.toName()) +public fun MutableItemProvider.getChild(childName: String): MutableItemProvider = getChild(childName.toName()) /** * Update existing mutable node with another node. The rules are following: diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index aaa44998..23decbff 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -28,7 +28,7 @@ public abstract class AbstractMutableMeta> : AbstractTypedMet //itemChanged(key.asName(), oldItem, newItem) } - protected fun wrapItem(item: MetaItem<*>?): MetaItem? = when (item) { + private fun wrapItem(item: MetaItem<*>?): MetaItem? = when (item) { null -> null is MetaItem.ValueItem -> item is MetaItem.NodeItem -> MetaItem.NodeItem(wrapNode(item.node)) @@ -67,18 +67,18 @@ public abstract class AbstractMutableMeta> : AbstractTypedMet /** * Append the node with a same-name-sibling, automatically generating numerical index */ -public fun > M.append(name: Name, value: Any?) { +public fun MutableItemProvider.append(name: Name, value: Any?) { require(!name.isEmpty()) { "Name could not be empty for append operation" } val newIndex = name.lastOrNull()!!.index if (newIndex != null) { set(name, value) } else { - val index = (getIndexed(name).keys.mapNotNull { it.toIntOrNull() }.maxOrNull() ?: -1) + 1 + val index = (getIndexed(name).keys.mapNotNull { it?.toIntOrNull() }.maxOrNull() ?: -1) + 1 set(name.withIndex(index.toString()), value) } } -public fun > M.append(name: String, value: Any?): Unit = append(name.toName(), value) +public fun MutableItemProvider.append(name: String, value: Any?): Unit = append(name.toName(), value) /** * Apply existing node with given [builder] or create a new element with it. diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt index 3fae4e20..dd3af2af 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt @@ -8,7 +8,7 @@ import hep.dataforge.values.* * A common parent for [ValueDescriptor] and [NodeDescriptor]. Describes a single [MetaItem] or a group of same-name-siblings. */ @DFBuilder -public sealed class ItemDescriptor(final override val config: Config): Configurable { +public sealed class ItemDescriptor(final override val config: Config) : Configurable { /** * True if same name siblings with this name are allowed @@ -40,7 +40,7 @@ public sealed class ItemDescriptor(final override val config: Config): Configura public abstract fun copy(): ItemDescriptor - public companion object{ + public companion object { public const val DEFAULT_INDEX_KEY: String = "@index" } } @@ -95,12 +95,13 @@ public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) * The map of children item descriptors (both nodes and values) */ public val items: Map - get() = config.getIndexed(ITEM_KEY).mapValues { (_, item) -> + get() = config.getIndexed(ITEM_KEY).entries.associate { (name, item) -> + if (name == null) error("Child item index should not be null") val node = item.node ?: error("Node descriptor must be a node") if (node[IS_NODE_KEY].boolean == true) { - NodeDescriptor(node) + name to NodeDescriptor(node as Config) } else { - ValueDescriptor(node) + name to ValueDescriptor(node as Config) } } @@ -112,8 +113,9 @@ public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) get() = config.getIndexed(ITEM_KEY).entries.filter { it.value.node[IS_NODE_KEY].boolean == true }.associate { (name, item) -> + if (name == null) error("Child node index should not be null") val node = item.node ?: error("Node descriptor must be a node") - name to NodeDescriptor(node) + name to NodeDescriptor(node as Config) } /** @@ -123,8 +125,9 @@ public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) get() = config.getIndexed(ITEM_KEY).entries.filter { it.value.node[IS_NODE_KEY].boolean != true }.associate { (name, item) -> + if (name == null) error("Child value index should not be null") val node = item.node ?: error("Node descriptor must be a node") - name to ValueDescriptor(node) + name to ValueDescriptor(node as Config) } private fun buildNode(name: Name): NodeDescriptor { @@ -189,7 +192,8 @@ public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) internal val ITEM_KEY: Name = "item".asName() internal val IS_NODE_KEY: Name = "@isNode".asName() - public inline operator fun invoke(block: NodeDescriptor.() -> Unit): NodeDescriptor = NodeDescriptor().apply(block) + public inline operator fun invoke(block: NodeDescriptor.() -> Unit): NodeDescriptor = + NodeDescriptor().apply(block) //TODO infer descriptor from spec } @@ -270,7 +274,7 @@ public class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) val value = it.value when { value?.list != null -> value.list - type?.let { type -> type.size == 1 && type[0] === ValueType.BOOLEAN} ?: false -> listOf(True, False) + type?.let { type -> type.size == 1 && type[0] === ValueType.BOOLEAN } ?: false -> listOf(True, False) else -> emptyList() } }, From 6389a25aaf4389b0bcf52d6f5db424fc78b15418 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 24 Dec 2020 10:48:51 +0300 Subject: [PATCH 21/59] Moved ValueItem and NodeItem to the top level --- CHANGELOG.md | 2 + build.gradle.kts | 2 +- dataforge-io/api/dataforge-io.api | 6 +- .../api/dataforge-io-yaml.api | 4 +- .../hep/dataforge/io/yaml/YamlMetaFormat.kt | 14 +- .../hep/dataforge/io/BinaryMetaFormat.kt | 37 ++-- .../kotlin/hep/dataforge/io/IOFormat.kt | 4 +- .../kotlin/hep/dataforge/io/IOPlugin.kt | 4 +- .../hep/dataforge/io/MetaSerializerTest.kt | 4 +- dataforge-meta/api/dataforge-meta.api | 199 +++++++++--------- .../kotlin/hep/dataforge/meta/Config.kt | 20 +- .../kotlin/hep/dataforge/meta/ItemDelegate.kt | 4 +- .../kotlin/hep/dataforge/meta/ItemProvider.kt | 11 +- .../kotlin/hep/dataforge/meta/JsonMeta.kt | 28 +-- .../kotlin/hep/dataforge/meta/Laminate.kt | 22 +- .../kotlin/hep/dataforge/meta/Meta.kt | 32 ++- .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 6 +- .../kotlin/hep/dataforge/meta/MetaItem.kt | 88 -------- .../hep/dataforge/meta/MetaSerializer.kt | 16 +- .../hep/dataforge/meta/MetaWithDefault.kt | 4 +- .../hep/dataforge/meta/MutableItemDelegate.kt | 14 +- .../hep/dataforge/meta/MutableItemProvider.kt | 22 +- .../kotlin/hep/dataforge/meta/MutableMeta.kt | 22 +- .../kotlin/hep/dataforge/meta/Scheme.kt | 10 +- .../kotlin/hep/dataforge/meta/SealedMeta.kt | 8 +- .../hep/dataforge/meta/Specification.kt | 2 +- .../hep/dataforge/meta/TypedMetaItem.kt | 88 ++++++++ .../meta/descriptors/DescriptorMeta.kt | 23 +- .../meta/descriptors/ItemDescriptor.kt | 6 +- .../kotlin/hep/dataforge/meta/mapMeta.kt | 18 +- .../meta/transformations/MetaConverter.kt | 102 ++++----- .../transformations/MetaTransformation.kt | 22 +- .../kotlin/hep/dataforge/meta/DynamicMeta.kt | 26 +-- 33 files changed, 431 insertions(+), 439 deletions(-) delete mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMetaItem.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index d1d7af27..a586067c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ - Context logging moved to the extension - `number` and `string` methods on `Value` moved to extensions (breaking change) - \[Major breaking change\] Schemes and configurables us `MutableItemProvider` instead of `Config` +- \[Major breaking change\] `MetaItem` renamed to `TypedMetaItem` and `MetaItem` is now an alias for `TypedMetaItem<*>` +- \[Major breaking change\] Moved `NodeItem` and `ValueItem` to a top level ### Deprecated diff --git a/build.gradle.kts b/build.gradle.kts index c79a03bb..aa39db90 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.2.1-dev-6") +val dataforgeVersion by extra("0.3.0-dev-SNAPSHOT") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-io/api/dataforge-io.api b/dataforge-io/api/dataforge-io.api index 417f531a..2ef69061 100644 --- a/dataforge-io/api/dataforge-io.api +++ b/dataforge-io/api/dataforge-io.api @@ -7,7 +7,7 @@ public final class hep/dataforge/io/BinaryMetaFormat : hep/dataforge/io/MetaForm public fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Lhep/dataforge/io/MetaFormat; public synthetic fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object; public fun readMeta (Lkotlinx/io/Input;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Meta; - public final fun readMetaItem (Lkotlinx/io/Input;)Lhep/dataforge/meta/MetaItem; + public final fun readMetaItem (Lkotlinx/io/Input;)Lhep/dataforge/meta/TypedMetaItem; public fun readObject (Lkotlinx/io/Input;)Lhep/dataforge/meta/Meta; public synthetic fun readObject (Lkotlinx/io/Input;)Ljava/lang/Object; public fun toMeta ()Lhep/dataforge/meta/Meta; @@ -219,8 +219,8 @@ public final class hep/dataforge/io/IOPlugin : hep/dataforge/context/AbstractPlu public final fun getIoFormatFactories ()Ljava/util/Collection; public final fun getMetaFormatFactories ()Ljava/util/Collection; public fun getTag ()Lhep/dataforge/context/PluginTag; - public final fun resolveEnvelopeFormat (Lhep/dataforge/meta/MetaItem;)Lhep/dataforge/io/EnvelopeFormat; - public final fun resolveIOFormat (Lhep/dataforge/meta/MetaItem;Lkotlin/reflect/KClass;)Lhep/dataforge/io/IOFormat; + public final fun resolveEnvelopeFormat (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/io/EnvelopeFormat; + public final fun resolveIOFormat (Lhep/dataforge/meta/TypedMetaItem;Lkotlin/reflect/KClass;)Lhep/dataforge/io/IOFormat; public final fun resolveMetaFormat (Ljava/lang/String;Lhep/dataforge/meta/Meta;)Lhep/dataforge/io/MetaFormat; public final fun resolveMetaFormat (SLhep/dataforge/meta/Meta;)Lhep/dataforge/io/MetaFormat; public static synthetic fun resolveMetaFormat$default (Lhep/dataforge/io/IOPlugin;Ljava/lang/String;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/io/MetaFormat; diff --git a/dataforge-io/dataforge-io-yaml/api/dataforge-io-yaml.api b/dataforge-io/dataforge-io-yaml/api/dataforge-io-yaml.api index d137dcdc..61c5dea9 100644 --- a/dataforge-io/dataforge-io-yaml/api/dataforge-io-yaml.api +++ b/dataforge-io/dataforge-io-yaml/api/dataforge-io-yaml.api @@ -59,8 +59,8 @@ public final class hep/dataforge/io/yaml/YamlMetaFormat$Companion : hep/dataforg public final class hep/dataforge/io/yaml/YamlMetaFormatKt { public static final fun toMeta (Lnet/mamoe/yamlkt/YamlMap;)Lhep/dataforge/meta/Meta; - public static final fun toMetaItem (Lnet/mamoe/yamlkt/YamlElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;)Lhep/dataforge/meta/MetaItem; - public static synthetic fun toMetaItem$default (Lnet/mamoe/yamlkt/YamlElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/MetaItem; + public static final fun toMetaItem (Lnet/mamoe/yamlkt/YamlElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;)Lhep/dataforge/meta/TypedMetaItem; + public static synthetic fun toMetaItem$default (Lnet/mamoe/yamlkt/YamlElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/TypedMetaItem; public static final fun toYaml (Lhep/dataforge/meta/Meta;)Lnet/mamoe/yamlkt/YamlMap; } diff --git a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt index 5769c5b1..84cb2d3e 100644 --- a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt @@ -22,10 +22,10 @@ import net.mamoe.yamlkt.* public fun Meta.toYaml(): YamlMap { val map: Map = items.entries.associate { (key, item) -> key.toString() to when (item) { - is MetaItem.ValueItem -> { + is ValueItem -> { item.value.value } - is MetaItem.NodeItem -> { + is NodeItem -> { item.node.toYaml() } } @@ -35,8 +35,8 @@ public fun Meta.toYaml(): YamlMap { private class YamlMeta(private val yamlMap: YamlMap, private val descriptor: NodeDescriptor? = null) : MetaBase() { - private fun buildItems(): Map> { - val map = LinkedHashMap>() + private fun buildItems(): Map { + val map = LinkedHashMap() yamlMap.content.entries.forEach { (key, value) -> val stringKey = key.toString() @@ -53,7 +53,7 @@ private class YamlMeta(private val yamlMap: YamlMap, private val descriptor: Nod (it as YamlLiteral).content.parseValue() } ) - map[token] = MetaItem.ValueItem(listValue) + map[token] = ValueItem(listValue) } else value.forEachIndexed { index, yamlElement -> val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: ItemDescriptor.DEFAULT_INDEX_KEY val indexValue: String = (yamlElement as? YamlMap)?.getStringOrNull(indexKey) @@ -67,10 +67,10 @@ private class YamlMeta(private val yamlMap: YamlMap, private val descriptor: Nod return map } - override val items: Map> get() = buildItems() + override val items: Map get() = buildItems() } -public fun YamlElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<*> = when (this) { +public fun YamlElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem = when (this) { YamlNull -> Null.asMetaItem() is YamlLiteral -> content.parseValue().asMetaItem() is YamlMap -> toMeta().asMetaItem() diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt index f09cdc04..c0186d66 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt @@ -1,11 +1,8 @@ package hep.dataforge.io import hep.dataforge.context.Context -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaBuilder -import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.set import hep.dataforge.values.* import kotlinx.io.* import kotlinx.io.text.readUtf8String @@ -22,7 +19,7 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { override fun invoke(meta: Meta, context: Context): MetaFormat = this override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta { - return (input.readMetaItem() as MetaItem.NodeItem).node + return (input.readMetaItem() as NodeItem).node } private fun Output.writeChar(char: Char) = writeByte(char.toByte()) @@ -88,10 +85,10 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { meta.items.forEach { (key, item) -> output.writeString(key.toString()) when (item) { - is MetaItem.ValueItem -> { + is ValueItem -> { output.writeValue(item.value) } - is MetaItem.NodeItem -> { + is NodeItem -> { writeObject(output, item.node) } } @@ -104,21 +101,21 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { } @Suppress("UNCHECKED_CAST") - public fun Input.readMetaItem(): MetaItem { + public fun Input.readMetaItem(): TypedMetaItem { return when (val keyChar = readByte().toChar()) { - 'S' -> MetaItem.ValueItem(StringValue(readString())) - 'N' -> MetaItem.ValueItem(Null) - '+' -> MetaItem.ValueItem(True) - '-' -> MetaItem.ValueItem(True) - 's' -> MetaItem.ValueItem(NumberValue(readShort())) - 'i' -> MetaItem.ValueItem(NumberValue(readInt())) - 'l' -> MetaItem.ValueItem(NumberValue(readInt())) - 'f' -> MetaItem.ValueItem(NumberValue(readFloat())) - 'd' -> MetaItem.ValueItem(NumberValue(readDouble())) + 'S' -> ValueItem(StringValue(readString())) + 'N' -> ValueItem(Null) + '+' -> ValueItem(True) + '-' -> ValueItem(True) + 's' -> ValueItem(NumberValue(readShort())) + 'i' -> ValueItem(NumberValue(readInt())) + 'l' -> ValueItem(NumberValue(readInt())) + 'f' -> ValueItem(NumberValue(readFloat())) + 'd' -> ValueItem(NumberValue(readDouble())) 'L' -> { val length = readInt() - val list = (1..length).map { (readMetaItem() as MetaItem.ValueItem).value } - MetaItem.ValueItem(Value.of(list)) + val list = (1..length).map { (readMetaItem() as ValueItem).value } + ValueItem(Value.of(list)) } 'M' -> { val length = readInt() @@ -129,7 +126,7 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { set(name, item) } } - MetaItem.NodeItem(meta) + NodeItem(meta) } else -> error("Unknown serialization key character: $keyChar") } diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt index 7fe4c970..aac83ae6 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt @@ -6,8 +6,8 @@ import hep.dataforge.context.Named import hep.dataforge.io.IOFormat.Companion.NAME_KEY import hep.dataforge.io.IOFormatFactory.Companion.IO_FORMAT_TYPE import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaRepr +import hep.dataforge.meta.ValueItem import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.provider.Type @@ -121,7 +121,7 @@ public object ValueIOFormat : IOFormat, IOFormatFactory { } override fun readObject(input: Input): Value { - return (BinaryMetaFormat.run { input.readMetaItem() } as? MetaItem.ValueItem)?.value + return (BinaryMetaFormat.run { input.readMetaItem() } as? ValueItem)?.value ?: error("The item is not a value") } } \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt index f0c47609..ad4341e1 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt @@ -18,7 +18,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { context.gather>(IO_FORMAT_TYPE).values } - public fun resolveIOFormat(item: MetaItem<*>, type: KClass): IOFormat? { + public fun resolveIOFormat(item: MetaItem, type: KClass): IOFormat? { val key = item.string ?: item.node[NAME_KEY]?.string ?: error("Format name not defined") val name = key.toName() return ioFormatFactories.find { it.name == name }?.let { @@ -46,7 +46,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { private fun resolveEnvelopeFormat(name: Name, meta: Meta = Meta.EMPTY): EnvelopeFormat? = envelopeFormatFactories.find { it.name == name }?.invoke(meta, context) - public fun resolveEnvelopeFormat(item: MetaItem<*>): EnvelopeFormat? { + public fun resolveEnvelopeFormat(item: MetaItem): EnvelopeFormat? { val name = item.string ?: item.node[NAME_KEY]?.string ?: error("Envelope format name not defined") val meta = item.node[META_KEY].node ?: Meta.EMPTY return resolveEnvelopeFormat(name.toName(), meta) diff --git a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaSerializerTest.kt b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaSerializerTest.kt index 23fef5d5..bf53aed9 100644 --- a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaSerializerTest.kt +++ b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaSerializerTest.kt @@ -1,8 +1,8 @@ package hep.dataforge.io import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaSerializer +import hep.dataforge.meta.TypedMetaItem import hep.dataforge.names.Name import hep.dataforge.names.toName import kotlinx.serialization.ExperimentalSerializationApi @@ -52,7 +52,7 @@ class MetaSerializerTest { @OptIn(ExperimentalSerializationApi::class) @Test fun testMetaItemDescriptor() { - val descriptor = MetaItem.serializer(MetaSerializer).descriptor.getElementDescriptor(0) + val descriptor = TypedMetaItem.serializer(MetaSerializer).descriptor.getElementDescriptor(0) println(descriptor) } } \ No newline at end of file diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 3b35c2c9..c130e09e 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -2,15 +2,14 @@ public abstract class hep/dataforge/meta/AbstractMutableMeta : hep/dataforge/met public fun ()V protected final fun getChildren ()Ljava/util/Map; public fun getItems ()Ljava/util/Map; - protected fun replaceItem (Lhep/dataforge/names/NameToken;Lhep/dataforge/meta/MetaItem;Lhep/dataforge/meta/MetaItem;)V - public fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)V - protected final fun wrapItem (Lhep/dataforge/meta/MetaItem;)Lhep/dataforge/meta/MetaItem; + protected fun replaceItem (Lhep/dataforge/names/NameToken;Lhep/dataforge/meta/TypedMetaItem;Lhep/dataforge/meta/TypedMetaItem;)V + public fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)V protected abstract fun wrapNode (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MutableMeta; } public abstract class hep/dataforge/meta/AbstractTypedMeta : hep/dataforge/meta/MetaBase, hep/dataforge/meta/TypedMeta { public fun ()V - public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public fun toMeta ()Lhep/dataforge/meta/Meta; } @@ -35,7 +34,7 @@ public final class hep/dataforge/meta/Config$ConfigSerializer : kotlinx/serializ public final class hep/dataforge/meta/ConfigKt { public static final fun asConfig (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Config; - public static final fun get (Lhep/dataforge/meta/Config;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/MetaItem; + public static final fun get (Lhep/dataforge/meta/Config;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/TypedMetaItem; public static final fun toConfig (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Config; } @@ -118,7 +117,7 @@ public final class hep/dataforge/meta/ItemListener { public abstract interface class hep/dataforge/meta/ItemProvider { public static final field Companion Lhep/dataforge/meta/ItemProvider$Companion; - public abstract fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public abstract fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; } public final class hep/dataforge/meta/ItemProvider$Companion { @@ -126,8 +125,8 @@ public final class hep/dataforge/meta/ItemProvider$Companion { } public final class hep/dataforge/meta/ItemProviderKt { - public static final fun get (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; - public static final fun get (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; + public static final fun get (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; + public static final fun get (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/TypedMetaItem; public static final fun getChild (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/ItemProvider; public static final fun getChild (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/ItemProvider; public static final fun getIndexed (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Ljava/util/Map; @@ -154,8 +153,8 @@ public final class hep/dataforge/meta/JsonMetaKt { public static synthetic fun toJson$default (Lhep/dataforge/values/Value;Lhep/dataforge/meta/descriptors/ValueDescriptor;ILjava/lang/Object;)Lkotlinx/serialization/json/JsonElement; public static final fun toMeta (Lkotlinx/serialization/json/JsonObject;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/JsonMeta; public static synthetic fun toMeta$default (Lkotlinx/serialization/json/JsonObject;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/JsonMeta; - public static final fun toMetaItem (Lkotlinx/serialization/json/JsonElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;)Lhep/dataforge/meta/MetaItem; - public static synthetic fun toMetaItem$default (Lkotlinx/serialization/json/JsonElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/MetaItem; + public static final fun toMetaItem (Lkotlinx/serialization/json/JsonElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;)Lhep/dataforge/meta/TypedMetaItem; + public static synthetic fun toMetaItem$default (Lkotlinx/serialization/json/JsonElement;Lhep/dataforge/meta/descriptors/ItemDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/TypedMetaItem; public static final fun toValue (Lkotlinx/serialization/json/JsonPrimitive;Lhep/dataforge/meta/descriptors/ValueDescriptor;)Lhep/dataforge/values/Value; } @@ -174,7 +173,7 @@ public final class hep/dataforge/meta/Laminate$Companion { public final class hep/dataforge/meta/LaminateKt { public static final fun Laminate ([Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Laminate; - public static final fun getFirst (Lhep/dataforge/meta/Laminate;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public static final fun getFirst (Lhep/dataforge/meta/Laminate;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public static final fun withBottom (Lhep/dataforge/meta/Laminate;Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Laminate; public static final fun withTop (Lhep/dataforge/meta/Laminate;Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Laminate; } @@ -191,7 +190,7 @@ public abstract interface class hep/dataforge/meta/Meta : hep/dataforge/meta/Ite public static final field TYPE Ljava/lang/String; public static final field VALUE_KEY Ljava/lang/String; public abstract fun equals (Ljava/lang/Object;)Z - public abstract fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public abstract fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public abstract fun getItems ()Ljava/util/Map; public abstract fun hashCode ()I public abstract fun toMeta ()Lhep/dataforge/meta/Meta; @@ -205,14 +204,14 @@ public final class hep/dataforge/meta/Meta$Companion { } public final class hep/dataforge/meta/Meta$DefaultImpls { - public static fun getItem (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public static fun getItem (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public static fun toMeta (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Meta; } public abstract class hep/dataforge/meta/MetaBase : hep/dataforge/meta/Meta { public fun ()V public fun equals (Ljava/lang/Object;)Z - public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public fun hashCode ()I public fun toMeta ()Lhep/dataforge/meta/Meta; public fun toString ()Ljava/lang/String; @@ -230,8 +229,8 @@ public final class hep/dataforge/meta/MetaBuilder : hep/dataforge/meta/AbstractM public final fun put (Lhep/dataforge/names/Name;Ljava/lang/String;)V public final fun put (Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)V public final fun put (Ljava/lang/String;Lhep/dataforge/meta/Meta;)V - public final fun put (Ljava/lang/String;Lhep/dataforge/meta/MetaItem;)V public final fun put (Ljava/lang/String;Lhep/dataforge/meta/MetaRepr;)V + public final fun put (Ljava/lang/String;Lhep/dataforge/meta/TypedMetaItem;)V public final fun put (Ljava/lang/String;Lhep/dataforge/values/Value;)V public final fun put (Ljava/lang/String;Ljava/lang/Boolean;)V public final fun put (Ljava/lang/String;Ljava/lang/Enum;)V @@ -253,73 +252,20 @@ public final class hep/dataforge/meta/MetaBuilderKt { public static final fun builder (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MetaBuilder; } -public abstract class hep/dataforge/meta/MetaItem { - public static final field Companion Lhep/dataforge/meta/MetaItem$Companion; - public abstract fun equals (Ljava/lang/Object;)Z - public abstract fun hashCode ()I -} - -public final class hep/dataforge/meta/MetaItem$Companion { - public final fun of (Ljava/lang/Object;)Lhep/dataforge/meta/MetaItem; - public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; -} - -public final class hep/dataforge/meta/MetaItem$NodeItem : hep/dataforge/meta/MetaItem { - public static final field Companion Lhep/dataforge/meta/MetaItem$NodeItem$Companion; - public fun (Lhep/dataforge/meta/Meta;)V - public fun equals (Ljava/lang/Object;)Z - public final fun getNode ()Lhep/dataforge/meta/Meta; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class hep/dataforge/meta/MetaItem$NodeItem$Companion { - public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; -} - -public final class hep/dataforge/meta/MetaItem$ValueItem : hep/dataforge/meta/MetaItem { - public static final field Companion Lhep/dataforge/meta/MetaItem$ValueItem$Companion; - public fun (Lhep/dataforge/values/Value;)V - public fun equals (Ljava/lang/Object;)Z - public final fun getValue ()Lhep/dataforge/values/Value; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class hep/dataforge/meta/MetaItem$ValueItem$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - -public final class hep/dataforge/meta/MetaItemKt { - public static final fun asMetaItem (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MetaItem$NodeItem; - public static final fun asMetaItem (Lhep/dataforge/values/Value;)Lhep/dataforge/meta/MetaItem$ValueItem; - public static final fun getBoolean (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Boolean; - public static final fun getDouble (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Double; - public static final fun getFloat (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Float; - public static final fun getInt (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Integer; - public static final fun getLong (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Long; - public static final fun getNode (Lhep/dataforge/meta/MetaItem;)Lhep/dataforge/meta/Meta; - public static final fun getNumber (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Number; - public static final fun getShort (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Short; - public static final fun getString (Lhep/dataforge/meta/MetaItem;)Ljava/lang/String; - public static final fun getStringList (Lhep/dataforge/meta/MetaItem;)Ljava/util/List; - public static final fun getValue (Lhep/dataforge/meta/MetaItem;)Lhep/dataforge/values/Value; -} - public final class hep/dataforge/meta/MetaItemSerializer : kotlinx/serialization/KSerializer { public static final field INSTANCE Lhep/dataforge/meta/MetaItemSerializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lhep/dataforge/meta/MetaItem; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lhep/dataforge/meta/TypedMetaItem; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lhep/dataforge/meta/MetaItem;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lhep/dataforge/meta/TypedMetaItem;)V public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } public final class hep/dataforge/meta/MetaKt { - public static final fun get (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/MetaItem; - public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; - public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/MetaItem; - public static final fun get (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Lhep/dataforge/meta/MetaItem; + public static final fun get (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/TypedMetaItem; + public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; + public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/TypedMetaItem; + public static final fun get (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Lhep/dataforge/meta/TypedMetaItem; public static final fun isEmpty (Lhep/dataforge/meta/Meta;)Z public static final fun itemSequence (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; public static final fun iterator (Lhep/dataforge/meta/Meta;)Ljava/util/Iterator; @@ -342,7 +288,7 @@ public final class hep/dataforge/meta/MetaSerializer : kotlinx/serialization/KSe public final class hep/dataforge/meta/MetaWithDefault : hep/dataforge/meta/MetaBase { public fun (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/ItemProvider;)V public final fun getDefault ()Lhep/dataforge/meta/ItemProvider; - public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public fun getItems ()Ljava/util/Map; public final fun getMeta ()Lhep/dataforge/meta/Meta; } @@ -410,7 +356,7 @@ public final class hep/dataforge/meta/MutableItemDelegateKt { } public abstract interface class hep/dataforge/meta/MutableItemProvider : hep/dataforge/meta/ItemProvider { - public abstract fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)V + public abstract fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)V } public final class hep/dataforge/meta/MutableItemProviderKt { @@ -420,13 +366,13 @@ public final class hep/dataforge/meta/MutableItemProviderKt { public static final fun remove (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)V public static final fun remove (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/meta/Meta;)V - public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)V + public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lhep/dataforge/values/Value;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Ljava/lang/Iterable;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Ljava/lang/Object;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/NameToken;Ljava/lang/Object;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Lhep/dataforge/meta/Meta;)V - public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Lhep/dataforge/meta/MetaItem;)V + public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Lhep/dataforge/meta/TypedMetaItem;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Ljava/lang/Iterable;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Ljava/lang/Object;)V public static final fun set (Lhep/dataforge/meta/MutableItemProvider;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V @@ -443,7 +389,7 @@ public abstract interface class hep/dataforge/meta/MutableMeta : hep/dataforge/m } public final class hep/dataforge/meta/MutableMeta$DefaultImpls { - public static fun getItem (Lhep/dataforge/meta/MutableMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public static fun getItem (Lhep/dataforge/meta/MutableMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public static fun toMeta (Lhep/dataforge/meta/MutableMeta;)Lhep/dataforge/meta/Meta; } @@ -453,6 +399,19 @@ public final class hep/dataforge/meta/MutableMetaKt { public static final fun edit (Lhep/dataforge/meta/AbstractMutableMeta;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)V } +public final class hep/dataforge/meta/NodeItem : hep/dataforge/meta/TypedMetaItem { + public static final field Companion Lhep/dataforge/meta/NodeItem$Companion; + public fun (Lhep/dataforge/meta/Meta;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getNode ()Lhep/dataforge/meta/Meta; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class hep/dataforge/meta/NodeItem$Companion { + public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; +} + public abstract interface class hep/dataforge/meta/ObservableItemProvider : hep/dataforge/meta/ItemProvider { public abstract fun onChange (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V public abstract fun removeListener (Ljava/lang/Object;)V @@ -465,13 +424,13 @@ public class hep/dataforge/meta/Scheme : hep/dataforge/meta/MetaRepr, hep/datafo public fun getDefaultLayer ()Lhep/dataforge/meta/Meta; public synthetic fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; public fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; - public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public final fun getItems ()Lhep/dataforge/meta/MutableItemProvider; public final fun isEmpty ()Z - public fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)V + public fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)V public fun toMeta ()Lhep/dataforge/meta/Laminate; public synthetic fun toMeta ()Lhep/dataforge/meta/Meta; - public fun validateItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)Z + public fun validateItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)Z } public final class hep/dataforge/meta/SchemeKt { @@ -502,7 +461,7 @@ public final class hep/dataforge/meta/SealedMeta : hep/dataforge/meta/AbstractTy public final class hep/dataforge/meta/SealedMetaKt { public static final fun seal (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/SealedMeta; - public static final fun seal (Lhep/dataforge/meta/MetaItem;)Lhep/dataforge/meta/MetaItem; + public static final fun seal (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/meta/TypedMetaItem; } public abstract interface class hep/dataforge/meta/Specification { @@ -525,19 +484,59 @@ public final class hep/dataforge/meta/SpecificationKt { public static synthetic fun spec$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static final fun update (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public static final fun update (Lhep/dataforge/meta/Configurable;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Configurable; - public static final fun withSpec (Lhep/dataforge/meta/MetaItem;Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; + public static final fun withSpec (Lhep/dataforge/meta/TypedMetaItem;Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; } public abstract interface class hep/dataforge/meta/TypedMeta : hep/dataforge/meta/Meta { - public abstract fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public abstract fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public abstract fun getItems ()Ljava/util/Map; } public final class hep/dataforge/meta/TypedMeta$DefaultImpls { - public static fun getItem (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/MetaItem; + public static fun getItem (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; public static fun toMeta (Lhep/dataforge/meta/TypedMeta;)Lhep/dataforge/meta/Meta; } +public abstract class hep/dataforge/meta/TypedMetaItem { + public static final field Companion Lhep/dataforge/meta/TypedMetaItem$Companion; + public abstract fun equals (Ljava/lang/Object;)Z + public abstract fun hashCode ()I +} + +public final class hep/dataforge/meta/TypedMetaItem$Companion { + public final fun of (Ljava/lang/Object;)Lhep/dataforge/meta/TypedMetaItem; + public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; +} + +public final class hep/dataforge/meta/TypedMetaItemKt { + public static final fun asMetaItem (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/NodeItem; + public static final fun asMetaItem (Lhep/dataforge/values/Value;)Lhep/dataforge/meta/ValueItem; + public static final fun getBoolean (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Boolean; + public static final fun getDouble (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Double; + public static final fun getFloat (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Float; + public static final fun getInt (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Integer; + public static final fun getLong (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Long; + public static final fun getNode (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/meta/Meta; + public static final fun getNumber (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Number; + public static final fun getShort (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Short; + public static final fun getString (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/String; + public static final fun getStringList (Lhep/dataforge/meta/TypedMetaItem;)Ljava/util/List; + public static final fun getValue (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/values/Value; +} + +public final class hep/dataforge/meta/ValueItem : hep/dataforge/meta/TypedMetaItem { + public static final field Companion Lhep/dataforge/meta/ValueItem$Companion; + public fun (Lhep/dataforge/values/Value;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getValue ()Lhep/dataforge/values/Value; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class hep/dataforge/meta/ValueItem$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public abstract interface class hep/dataforge/meta/descriptors/Described { public static final field Companion Lhep/dataforge/meta/descriptors/Described$Companion; public abstract fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; @@ -547,7 +546,7 @@ public final class hep/dataforge/meta/descriptors/Described$Companion { } public final class hep/dataforge/meta/descriptors/DescriptorMetaKt { - public static final fun defaultItem (Lhep/dataforge/meta/descriptors/ItemDescriptor;)Lhep/dataforge/meta/MetaItem; + public static final fun defaultItem (Lhep/dataforge/meta/descriptors/ItemDescriptor;)Lhep/dataforge/meta/TypedMetaItem; public static final fun defaultMeta (Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Laminate; } @@ -577,7 +576,7 @@ public final class hep/dataforge/meta/descriptors/ItemDescriptorKt { public static final fun get (Lhep/dataforge/meta/descriptors/ItemDescriptor;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/descriptors/ItemDescriptor; public static final fun get (Lhep/dataforge/meta/descriptors/ItemDescriptor;Ljava/lang/String;)Lhep/dataforge/meta/descriptors/ItemDescriptor; public static final fun plus (Lhep/dataforge/meta/descriptors/NodeDescriptor;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/descriptors/NodeDescriptor; - public static final fun validateItem (Lhep/dataforge/meta/descriptors/ItemDescriptor;Lhep/dataforge/meta/MetaItem;)Z + public static final fun validateItem (Lhep/dataforge/meta/descriptors/ItemDescriptor;Lhep/dataforge/meta/TypedMetaItem;)Z } public final class hep/dataforge/meta/descriptors/NodeDescriptor : hep/dataforge/meta/descriptors/ItemDescriptor { @@ -634,16 +633,16 @@ public final class hep/dataforge/meta/transformations/KeepTransformationRule : h public fun equals (Ljava/lang/Object;)Z public final fun getSelector ()Lkotlin/jvm/functions/Function1; public fun hashCode ()I - public fun matches (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)Z + public fun matches (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)Z public fun selectItems (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; public fun toString ()Ljava/lang/String; - public fun transformItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;Lhep/dataforge/meta/MutableMeta;)V + public fun transformItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;Lhep/dataforge/meta/MutableMeta;)V } public abstract interface class hep/dataforge/meta/transformations/MetaConverter { public static final field Companion Lhep/dataforge/meta/transformations/MetaConverter$Companion; - public abstract fun itemToObject (Lhep/dataforge/meta/MetaItem;)Ljava/lang/Object; - public abstract fun objectToMetaItem (Ljava/lang/Object;)Lhep/dataforge/meta/MetaItem; + public abstract fun itemToObject (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Object; + public abstract fun objectToMetaItem (Ljava/lang/Object;)Lhep/dataforge/meta/TypedMetaItem; } public final class hep/dataforge/meta/transformations/MetaConverter$Companion { @@ -663,8 +662,8 @@ public final class hep/dataforge/meta/transformations/MetaConverter$Companion { public final class hep/dataforge/meta/transformations/MetaConverterKt { public static final fun metaToObject (Lhep/dataforge/meta/transformations/MetaConverter;Lhep/dataforge/meta/Meta;)Ljava/lang/Object; - public static final fun nullableItemToObject (Lhep/dataforge/meta/transformations/MetaConverter;Lhep/dataforge/meta/MetaItem;)Ljava/lang/Object; - public static final fun nullableObjectToMetaItem (Lhep/dataforge/meta/transformations/MetaConverter;Ljava/lang/Object;)Lhep/dataforge/meta/MetaItem; + public static final fun nullableItemToObject (Lhep/dataforge/meta/transformations/MetaConverter;Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Object; + public static final fun nullableObjectToMetaItem (Lhep/dataforge/meta/transformations/MetaConverter;Ljava/lang/Object;)Lhep/dataforge/meta/TypedMetaItem; public static final fun valueToObject (Lhep/dataforge/meta/transformations/MetaConverter;Lhep/dataforge/values/Value;)Ljava/lang/Object; } @@ -711,10 +710,10 @@ public final class hep/dataforge/meta/transformations/RegexItemTransformationRul public final fun getFrom ()Lkotlin/text/Regex; public final fun getTransform ()Lkotlin/jvm/functions/Function4; public fun hashCode ()I - public fun matches (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)Z + public fun matches (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)Z public fun selectItems (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; public fun toString ()Ljava/lang/String; - public fun transformItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;Lhep/dataforge/meta/MutableMeta;)V + public fun transformItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;Lhep/dataforge/meta/MutableMeta;)V } public final class hep/dataforge/meta/transformations/SingleItemTransformationRule : hep/dataforge/meta/transformations/TransformationRule { @@ -727,16 +726,16 @@ public final class hep/dataforge/meta/transformations/SingleItemTransformationRu public final fun getFrom ()Lhep/dataforge/names/Name; public final fun getTransform ()Lkotlin/jvm/functions/Function3; public fun hashCode ()I - public fun matches (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)Z + public fun matches (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)Z public fun selectItems (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; public fun toString ()Ljava/lang/String; - public fun transformItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;Lhep/dataforge/meta/MutableMeta;)V + public fun transformItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;Lhep/dataforge/meta/MutableMeta;)V } public abstract interface class hep/dataforge/meta/transformations/TransformationRule { - public abstract fun matches (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;)Z + public abstract fun matches (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)Z public abstract fun selectItems (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; - public abstract fun transformItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaItem;Lhep/dataforge/meta/MutableMeta;)V + public abstract fun transformItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;Lhep/dataforge/meta/MutableMeta;)V } public final class hep/dataforge/meta/transformations/TransformationRule$DefaultImpls { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index 3fb557bd..781acb46 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -16,11 +16,11 @@ import kotlin.jvm.Synchronized public data class ItemListener( val owner: Any? = null, - val action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit + val action: (name: Name, oldItem: MetaItem?, newItem: MetaItem?) -> Unit ) public interface ObservableItemProvider : ItemProvider { - public fun onChange(owner: Any?, action: (name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) -> Unit) + public fun onChange(owner: Any?, action: (name: Name, oldItem: MetaItem?, newItem: MetaItem?) -> Unit) public fun removeListener(owner: Any?) } @@ -33,7 +33,7 @@ public class Config() : AbstractMutableMeta(), ObservableItemProvider { private val listeners = HashSet() @Synchronized - private fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) { + private fun itemChanged(name: Name, oldItem: MetaItem?, newItem: MetaItem?) { listeners.forEach { it.action(name, oldItem, newItem) } } @@ -41,7 +41,7 @@ public class Config() : AbstractMutableMeta(), ObservableItemProvider { * Add change listener to this meta. Owner is declared to be able to remove listeners later. Listener without owner could not be removed */ @Synchronized - override fun onChange(owner: Any?, action: (Name, MetaItem<*>?, MetaItem<*>?) -> Unit) { + override fun onChange(owner: Any?, action: (Name, MetaItem?, MetaItem?) -> Unit) { listeners.add(ItemListener(owner, action)) } @@ -53,15 +53,15 @@ public class Config() : AbstractMutableMeta(), ObservableItemProvider { listeners.removeAll { it.owner === owner } } - override fun replaceItem(key: NameToken, oldItem: MetaItem?, newItem: MetaItem?) { + override fun replaceItem(key: NameToken, oldItem: TypedMetaItem?, newItem: TypedMetaItem?) { if (newItem == null) { children.remove(key) - if (oldItem != null && oldItem is MetaItem.NodeItem) { + if (oldItem != null && oldItem is NodeItem) { oldItem.node.removeListener(this) } } else { children[key] = newItem - if (newItem is MetaItem.NodeItem) { + if (newItem is NodeItem) { newItem.node.onChange(this) { name, oldChild, newChild -> itemChanged(key + name, oldChild, newChild) } @@ -92,7 +92,7 @@ public class Config() : AbstractMutableMeta(), ObservableItemProvider { } } -public operator fun Config.get(token: NameToken): MetaItem? = items[token] +public operator fun Config.get(token: NameToken): TypedMetaItem? = items[token] /** * Create a mutable copy of this [Meta]. The copy is created event if initial [Meta] is a [Config]. @@ -102,8 +102,8 @@ public fun Meta.toConfig(): Config = Config().also { builder -> this.items.mapValues { entry -> val item = entry.value builder[entry.key.asName()] = when (item) { - is MetaItem.ValueItem -> item.value - is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.asConfig()) + is ValueItem -> item.value + is NodeItem -> NodeItem(item.node.asConfig()) } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt index 1c8c590b..775bb243 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemDelegate.kt @@ -8,7 +8,7 @@ import kotlin.properties.ReadOnlyProperty /* Meta delegates */ -public typealias ItemDelegate = ReadOnlyProperty?> +public typealias ItemDelegate = ReadOnlyProperty public fun ItemProvider.item(key: Name? = null): ItemDelegate = ReadOnlyProperty { _, property -> get(key ?: property.name.asName()) @@ -40,7 +40,7 @@ public fun ItemDelegate.convert( * A converter with a custom reader transformation */ public fun ItemDelegate.convert( - reader: (MetaItem<*>?) -> R, + reader: (MetaItem?) -> R, ): ReadOnlyProperty = ReadOnlyProperty { thisRef, property -> this@convert.getValue(thisRef, property).let(reader) } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt index 18f0df9a..f3d3884f 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt @@ -1,11 +1,10 @@ package hep.dataforge.meta -import hep.dataforge.meta.MetaItem.NodeItem import hep.dataforge.names.* public fun interface ItemProvider { //getItem used instead of get in order to provide extension freedom - public fun getItem(name: Name): MetaItem<*>? + public fun getItem(name: Name): MetaItem? public companion object { public val EMPTY: ItemProvider = ItemProvider { null } @@ -20,7 +19,7 @@ public fun interface ItemProvider { * * If [name] is empty return current [Meta] as a [NodeItem] */ -public operator fun ItemProvider?.get(name: Name): MetaItem<*>? = this?.getItem(name) +public operator fun ItemProvider?.get(name: Name): MetaItem? = this?.getItem(name) /** * The root node of this item provider if it is present @@ -30,7 +29,7 @@ public val ItemProvider.rootNode: Meta? get() = get(Name.EMPTY).node /** * Parse [Name] from [key] using full name notation and pass it to [Meta.get] */ -public operator fun ItemProvider?.get(key: String): MetaItem<*>? = this?.get(key.toName()) +public operator fun ItemProvider?.get(key: String): MetaItem? = this?.get(key.toName()) /** * Create a provider that uses given provider for default values if those are not found in this provider @@ -43,7 +42,7 @@ public fun ItemProvider.withDefault(default: ItemProvider): ItemProvider = ItemP * Get all items matching given name. The index of the last element, if present is used as a [Regex], * against which indexes of elements are matched. */ -public fun ItemProvider.getIndexed(name: Name): Map> { +public fun ItemProvider.getIndexed(name: Name): Map { val root: Meta = when (name.length) { 0 -> error("Can't use empty name for 'getIndexed'") 1 -> this.rootNode ?: return emptyMap() @@ -60,7 +59,7 @@ public fun ItemProvider.getIndexed(name: Name): Map> { } } -public fun ItemProvider.getIndexed(name: String): Map> = this@getIndexed.getIndexed(name.toName()) +public fun ItemProvider.getIndexed(name: String): Map = this@getIndexed.getIndexed(name.toName()) /** * Return a provider referencing a child node diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt index f41da67e..2c1e290b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt @@ -37,11 +37,11 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String val elementMap = HashMap() - fun MetaItem<*>.toJsonElement(itemDescriptor: ItemDescriptor?, index: String?): JsonElement = when (this) { - is MetaItem.ValueItem -> { + fun MetaItem.toJsonElement(itemDescriptor: ItemDescriptor?, index: String?): JsonElement = when (this) { + is ValueItem -> { value.toJson(itemDescriptor as? ValueDescriptor) } - is MetaItem.NodeItem -> { + is NodeItem -> { node.toJsonWithIndex(itemDescriptor as? NodeDescriptor, index) } } @@ -49,7 +49,7 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String fun addElement(key: String) { val itemDescriptor = descriptor?.items?.get(key) val jsonKey = key.toJsonKey(itemDescriptor) - val items: Map> = getIndexed(key) + val items: Map = getIndexed(key) when (items.size) { 0 -> { //do nothing @@ -96,14 +96,14 @@ public fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value { } } -public fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem = when (this) { +public fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): TypedMetaItem = when (this) { is JsonPrimitive -> { val value = this.toValue(descriptor as? ValueDescriptor) - MetaItem.ValueItem(value) + ValueItem(value) } is JsonObject -> { val meta = JsonMeta(this, descriptor as? NodeDescriptor) - MetaItem.NodeItem(meta) + NodeItem(meta) } is JsonArray -> { if (this.all { it is JsonPrimitive }) { @@ -115,7 +115,7 @@ public fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem< (it as JsonPrimitive).toValue(descriptor as? ValueDescriptor) }.asValue() } - MetaItem.ValueItem(value) + ValueItem(value) } else { //We can't return multiple items therefore we create top level node buildJsonObject { put(JSON_ARRAY_KEY, this@toMetaItem) }.toMetaItem(descriptor) @@ -128,18 +128,18 @@ public fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem< */ public class JsonMeta(private val json: JsonObject, private val descriptor: NodeDescriptor? = null) : MetaBase() { - private fun buildItems(): Map> { - val map = LinkedHashMap>() + private fun buildItems(): Map> { + val map = LinkedHashMap>() json.forEach { (jsonKey, value) -> val key = NameToken(jsonKey) val itemDescriptor = descriptor?.items?.get(jsonKey) when (value) { is JsonPrimitive -> { - map[key] = MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) + map[key] = ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) } is JsonObject -> { - map[key] = MetaItem.NodeItem( + map[key] = NodeItem( JsonMeta( value, itemDescriptor as? NodeDescriptor @@ -153,7 +153,7 @@ public class JsonMeta(private val json: JsonObject, private val descriptor: Node (it as JsonPrimitive).toValue(itemDescriptor as? ValueDescriptor) } ) - map[key] = MetaItem.ValueItem(listValue) + map[key] = ValueItem(listValue) } else value.forEachIndexed { index, jsonElement -> val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: DEFAULT_INDEX_KEY val indexValue: String = (jsonElement as? JsonObject) @@ -168,7 +168,7 @@ public class JsonMeta(private val json: JsonObject, private val descriptor: Node return map } - override val items: Map> by lazy(::buildItems) + override val items: Map> by lazy(::buildItems) public companion object { /** diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt index 5545358c..f1c77006 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt @@ -17,7 +17,7 @@ public class Laminate(layers: List) : MetaBase() { } } - override val items: Map> by lazy { + override val items: Map> by lazy { layers.map { it.items.keys }.flatten().associateWith { key -> layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule) } @@ -40,15 +40,15 @@ public class Laminate(layers: List) : MetaBase() { * * TODO add picture */ - public val replaceRule: (Sequence>) -> MetaItem = { it.first().seal() } + public val replaceRule: (Sequence) -> TypedMetaItem = { it.first().seal() } - private fun Sequence>.merge(): MetaItem { + private fun Sequence.merge(): TypedMetaItem { return when { - all { it is MetaItem.ValueItem } -> //If all items are values, take first + all { it is ValueItem } -> //If all items are values, take first first().seal() - all { it is MetaItem.NodeItem } -> { + all { it is NodeItem } -> { //list nodes in item - val nodes = map { (it as MetaItem.NodeItem).node } + val nodes = map { (it as NodeItem).node } //represent as key->value entries val entries = nodes.flatMap { it.items.entries.asSequence() } //group by keys @@ -57,13 +57,13 @@ public class Laminate(layers: List) : MetaBase() { val items = groups.mapValues { entry -> entry.value.asSequence().map { it.value }.merge() } - MetaItem.NodeItem(SealedMeta(items)) + NodeItem(SealedMeta(items)) } else -> map { when (it) { - is MetaItem.ValueItem -> MetaItem.NodeItem(Meta { Meta.VALUE_KEY put it.value }) - is MetaItem.NodeItem -> it + is ValueItem -> NodeItem(Meta { Meta.VALUE_KEY put it.value }) + is NodeItem -> it } }.merge() } @@ -74,7 +74,7 @@ public class Laminate(layers: List) : MetaBase() { * The values a replaced but meta children are joined * TODO add picture */ - public val mergeRule: (Sequence>) -> MetaItem = { it.merge() } + public val mergeRule: (Sequence) -> TypedMetaItem = { it.merge() } } } @@ -84,7 +84,7 @@ public fun Laminate(vararg layers: Meta?): Laminate = Laminate(layers.filterNotN /** * Performance optimized version of get method */ -public fun Laminate.getFirst(name: Name): MetaItem<*>? { +public fun Laminate.getFirst(name: Name): MetaItem? { layers.forEach { layer -> layer[name]?.let { return it } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 355f5982..f672279e 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -1,7 +1,5 @@ package hep.dataforge.meta -import hep.dataforge.meta.MetaItem.NodeItem -import hep.dataforge.meta.MetaItem.ValueItem import hep.dataforge.names.* import hep.dataforge.values.Value import kotlinx.serialization.Serializable @@ -18,9 +16,9 @@ public interface MetaRepr { } /** - * Generic meta tree representation. Elements are [MetaItem] objects that could be represented by three different entities: - * * [MetaItem.ValueItem] (leaf) - * * [MetaItem.NodeItem] single node + * Generic meta tree representation. Elements are [TypedMetaItem] objects that could be represented by three different entities: + * * [ValueItem] (leaf) + * * [NodeItem] single node * * * Same name siblings are supported via elements with the same [Name] but different queries */ @@ -28,9 +26,9 @@ public interface Meta : MetaRepr, ItemProvider { /** * Top level items of meta tree */ - public val items: Map> + public val items: Map - override fun getItem(name: Name): MetaItem<*>? { + override fun getItem(name: Name): MetaItem? { if (name.isEmpty()) return NodeItem(this) return name.firstOrNull()?.let { token -> val tail = name.cutFirst() @@ -58,12 +56,12 @@ public interface Meta : MetaRepr, ItemProvider { public const val VALUE_KEY: String = "@value" public val EMPTY: Meta = object : MetaBase() { - override val items: Map> = emptyMap() + override val items: Map = emptyMap() } } } -public operator fun Meta.get(token: NameToken): MetaItem<*>? = items.get(token) +public operator fun Meta.get(token: NameToken): MetaItem? = items.get(token) /** * Get a sequence of [Name]-[Value] pairs @@ -78,9 +76,9 @@ public fun Meta.valueSequence(): Sequence> { } /** - * Get a sequence of all [Name]-[MetaItem] pairs for all items including nodes + * Get a sequence of all [Name]-[TypedMetaItem] pairs for all items including nodes */ -public fun Meta.itemSequence(): Sequence>> = sequence { +public fun Meta.itemSequence(): Sequence> = sequence { items.forEach { (key, item) -> yield(key.asName() to item) if (item is NodeItem) { @@ -91,27 +89,27 @@ public fun Meta.itemSequence(): Sequence>> = sequence { } } -public operator fun Meta.iterator(): Iterator>> = itemSequence().iterator() +public operator fun Meta.iterator(): Iterator> = itemSequence().iterator() /** * A meta node that ensures that all of its descendants has at least the same type */ public interface TypedMeta> : Meta { - override val items: Map> + override val items: Map> @Suppress("UNCHECKED_CAST") - override fun getItem(name: Name): MetaItem? = super.getItem(name)?.let { it as MetaItem } + override fun getItem(name: Name): TypedMetaItem? = super.getItem(name)?.let { it as TypedMetaItem } //Typed meta guarantees that all children have M type } /** * The same as [Meta.get], but with specific node type */ -public operator fun > M?.get(name: Name): MetaItem? = this?.getItem(name) +public operator fun > M?.get(name: Name): TypedMetaItem? = this?.getItem(name) -public operator fun > M?.get(key: String): MetaItem? = this[key.toName()] -public operator fun > M?.get(key: NameToken): MetaItem? = this[key.asName()] +public operator fun > M?.get(key: String): TypedMetaItem? = this[key.toName()] +public operator fun > M?.get(key: NameToken): TypedMetaItem? = this[key.asName()] /** * Equals, hashcode and to string for any meta diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index 8ebceaa3..859948c2 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -15,7 +15,7 @@ public class MetaBuilder : AbstractMutableMeta() { override fun wrapNode(meta: Meta): MetaBuilder = if (meta is MetaBuilder) meta else meta.builder() override fun empty(): MetaBuilder = MetaBuilder() - public infix fun String.put(item: MetaItem<*>?) { + public infix fun String.put(item: MetaItem?) { set(this, item) } @@ -126,8 +126,8 @@ public fun Meta.builder(): MetaBuilder { items.mapValues { entry -> val item = entry.value builder[entry.key.asName()] = when (item) { - is MetaItem.ValueItem -> item.value - is MetaItem.NodeItem -> MetaItem.NodeItem(item.node.builder()) + is ValueItem -> item.value + is NodeItem -> NodeItem(item.node.builder()) } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt deleted file mode 100644 index 1649393d..00000000 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt +++ /dev/null @@ -1,88 +0,0 @@ -package hep.dataforge.meta - -import hep.dataforge.meta.MetaItem.NodeItem -import hep.dataforge.meta.MetaItem.ValueItem -import hep.dataforge.values.* -import kotlinx.serialization.Serializable - -/** - * A member of the meta tree. Could be represented as one of following: - * * a [ValueItem] (leaf) - * * a [NodeItem] (node) - */ -@Serializable(MetaItemSerializer::class) -public sealed class MetaItem() { - - abstract override fun equals(other: Any?): Boolean - - abstract override fun hashCode(): Int - - @Serializable(MetaItemSerializer::class) - public class ValueItem(public val value: Value) : MetaItem() { - override fun toString(): String = value.toString() - - override fun equals(other: Any?): Boolean { - return this.value == (other as? ValueItem)?.value - } - - override fun hashCode(): Int { - return value.hashCode() - } - } - - @Serializable(MetaItemSerializer::class) - public class NodeItem(public val node: M) : MetaItem() { - //Fixing serializer for node could cause class cast problems, but it should not since Meta descendants are not serializeable - override fun toString(): String = node.toString() - - override fun equals(other: Any?): Boolean = node == (other as? NodeItem<*>)?.node - - override fun hashCode(): Int = node.hashCode() - } - - public companion object { - public fun of(arg: Any?): MetaItem<*> { - return when (arg) { - null -> ValueItem(Null) - is MetaItem<*> -> arg - is Meta -> NodeItem(arg) - else -> ValueItem(Value.of(arg)) - } - } - } -} - -public fun Value.asMetaItem(): ValueItem = ValueItem(this) -public fun M.asMetaItem(): NodeItem = NodeItem(this) - - -/** - * Unsafe methods to access values and nodes directly from [MetaItem] - */ -public val MetaItem<*>?.value: Value? - get() = (this as? ValueItem)?.value - ?: (this?.node?.get(Meta.VALUE_KEY) as? ValueItem)?.value - -public val MetaItem<*>?.string: String? get() = value?.string -public val MetaItem<*>?.boolean: Boolean? get() = value?.boolean -public val MetaItem<*>?.number: Number? get() = value?.numberOrNull -public val MetaItem<*>?.double: Double? get() = number?.toDouble() -public val MetaItem<*>?.float: Float? get() = number?.toFloat() -public val MetaItem<*>?.int: Int? get() = number?.toInt() -public val MetaItem<*>?.long: Long? get() = number?.toLong() -public val MetaItem<*>?.short: Short? get() = number?.toShort() - -public inline fun > MetaItem<*>?.enum(): E? = if (this is ValueItem && this.value is EnumValue<*>) { - this.value.value as E -} else { - string?.let { enumValueOf(it) } -} - -public val MetaItem<*>.stringList: List? get() = value?.list?.map { it.string } - -public val MetaItem?.node: M? - get() = when (this) { - null -> null - is ValueItem -> null//error("Trying to interpret value meta item as node item") - is NodeItem -> node - } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt index 81730b5d..2dd86633 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt @@ -12,7 +12,7 @@ import kotlinx.serialization.json.JsonDecoder import kotlinx.serialization.json.JsonEncoder import kotlinx.serialization.json.JsonObject -public object MetaItemSerializer : KSerializer> { +public object MetaItemSerializer : KSerializer { @OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class) override val descriptor: SerialDescriptor = buildSerialDescriptor("MetaItem", PolymorphicKind.SEALED) { @@ -20,7 +20,7 @@ public object MetaItemSerializer : KSerializer> { element("value", buildSerialDescriptor("MetaItem.value", SerialKind.CONTEXTUAL)) } - override fun deserialize(decoder: Decoder): MetaItem<*> { + override fun deserialize(decoder: Decoder): MetaItem { decoder.decodeStructure(descriptor) { //Force strict serialization order require(decodeElementIndex(descriptor) == 0) { "Node flag must be first item serialized" } @@ -36,12 +36,12 @@ public object MetaItemSerializer : KSerializer> { } } - override fun serialize(encoder: Encoder, value: MetaItem<*>) { + override fun serialize(encoder: Encoder, value: MetaItem) { encoder.encodeStructure(descriptor) { - encodeBooleanElement(descriptor, 0, value is MetaItem.NodeItem) + encodeBooleanElement(descriptor, 0, value is NodeItem) when (value) { - is MetaItem.ValueItem -> encodeSerializableElement(descriptor, 1, ValueSerializer, value.value) - is MetaItem.NodeItem -> encodeSerializableElement(descriptor, 1, MetaSerializer, value.node) + is ValueItem -> encodeSerializableElement(descriptor, 1, ValueSerializer, value.value) + is NodeItem -> encodeSerializableElement(descriptor, 1, MetaSerializer, value.node) } } } @@ -52,7 +52,7 @@ public object MetaItemSerializer : KSerializer> { */ public object MetaSerializer : KSerializer { - private val mapSerializer: KSerializer>> = MapSerializer( + private val mapSerializer: KSerializer>> = MapSerializer( NameToken, MetaItemSerializer//MetaItem.serializer(MetaSerializer) ) @@ -64,7 +64,7 @@ public object MetaSerializer : KSerializer { JsonObject.serializer().deserialize(decoder).toMeta() } else { object : MetaBase() { - override val items: Map> = mapSerializer.deserialize(decoder) + override val items: Map = mapSerializer.deserialize(decoder) } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt index ebf20f13..1858bd3c 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt @@ -7,10 +7,10 @@ import hep.dataforge.names.NameToken * Meta object with default provider for items not present in the initial meta. */ public class MetaWithDefault(public val meta: Meta, public val default: ItemProvider) : MetaBase() { - override val items: Map> + override val items: Map get() = meta.items - override fun getItem(name: Name): MetaItem<*>? { + override fun getItem(name: Name): MetaItem? { return meta[name] ?: default[name] } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt index 53f98997..63a1cb26 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt @@ -9,14 +9,14 @@ import kotlin.reflect.KProperty /* Read-write delegates */ -public typealias MutableItemDelegate = ReadWriteProperty?> +public typealias MutableItemDelegate = ReadWriteProperty public fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate = object : MutableItemDelegate { - override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*>? { + override fun getValue(thisRef: Any?, property: KProperty<*>): MetaItem? { return get(key ?: property.name.asName()) } - override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>?) { + override fun setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem?) { val name = key ?: property.name.asName() set(name, value) } @@ -25,7 +25,7 @@ public fun MutableItemProvider.item(key: Name? = null): MutableItemDelegate = ob /* Mutable converters */ /** - * A type converter for a mutable [MetaItem] delegate + * A type converter for a mutable [TypedMetaItem] delegate */ public fun MutableItemDelegate.convert( converter: MetaConverter, @@ -55,8 +55,8 @@ public fun MutableItemDelegate.convert( } public fun MutableItemDelegate.convert( - reader: (MetaItem<*>?) -> R, - writer: (R) -> MetaItem<*>?, + reader: (MetaItem?) -> R, + writer: (R) -> MetaItem?, ): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): R = @@ -119,7 +119,7 @@ public fun MutableItemProvider.node(key: Name? = null): ReadWriteProperty> M.node(key: Name? = null): ReadWriteProperty = - item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItem.NodeItem(it) } }) + item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { NodeItem(it) } }) /* Number delegates */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt index 5efa89a3..5ca6334a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt @@ -5,16 +5,16 @@ import hep.dataforge.values.Value import hep.dataforge.values.asValue public interface MutableItemProvider : ItemProvider { - public fun setItem(name: Name, item: MetaItem<*>?) + public fun setItem(name: Name, item: MetaItem?) } -public operator fun MutableItemProvider.set(name: Name, item: MetaItem<*>?): Unit = setItem(name, item) +public operator fun MutableItemProvider.set(name: Name, item: MetaItem?): Unit = setItem(name, item) public operator fun MutableItemProvider.set(name: Name, value: Value?): Unit = set(name, value?.asMetaItem()) public operator fun MutableItemProvider.set(name: Name, meta: Meta?): Unit = set(name, meta?.asMetaItem()) -public operator fun MutableItemProvider.set(key: String, item: MetaItem<*>?): Unit = set(key.toName(), item) +public operator fun MutableItemProvider.set(key: String, item: MetaItem?): Unit = set(key.toName(), item) public operator fun MutableItemProvider.set(key: String, meta: Meta?): Unit = set(key, meta?.asMetaItem()) @@ -34,7 +34,7 @@ public operator fun MutableItemProvider.set(name: Name, value: Any?) { is Number -> set(name, value.asValue()) is String -> set(name, value.asValue()) is Boolean -> set(name, value.asValue()) - is MetaItem<*> -> set(name, value) + is MetaItem -> set(name, value) is Meta -> set(name, value) is MetaRepr -> set(name, value.toMeta()) is Configurable -> set(name, value.config) @@ -56,8 +56,8 @@ public operator fun MutableItemProvider.set(key: String, index: String, value: A public fun MutableItemProvider.setIndexedItems( name: Name, - items: Iterable>, - indexFactory: (MetaItem<*>, index: Int) -> String = { _, index -> index.toString() }, + items: Iterable, + indexFactory: (MetaItem, index: Int) -> String = { _, index -> index.toString() }, ) { val tokens = name.tokens.toMutableList() val last = tokens.last() @@ -73,7 +73,7 @@ public fun MutableItemProvider.setIndexed( metas: Iterable, indexFactory: (Meta, index: Int) -> String = { _, index -> index.toString() }, ) { - setIndexedItems(name, metas.map { MetaItem.NodeItem(it) }) { item, index -> indexFactory(item.node!!, index) } + setIndexedItems(name, metas.map { NodeItem(it) }) { item, index -> indexFactory(item.node!!, index) } } public operator fun MutableItemProvider.set(name: Name, metas: Iterable): Unit = setIndexed(name, metas) @@ -85,11 +85,11 @@ public operator fun MutableItemProvider.set(name: String, metas: Iterable) */ public fun MutableItemProvider.getChild(childName: Name): MutableItemProvider { fun createProvider() = object : MutableItemProvider { - override fun setItem(name: Name, item: MetaItem<*>?) { + override fun setItem(name: Name, item: MetaItem?) { this@getChild.setItem(childName + name, item) } - override fun getItem(name: Name): MetaItem<*>? = this@getChild.getItem(childName + name) + override fun getItem(name: Name): MetaItem? = this@getChild.getItem(childName + name) } return when { @@ -130,9 +130,9 @@ public fun MutableItemProvider.withDefault(default: ItemProvider): MutableItemPr //Optimize for use with empty default this } else object : MutableItemProvider { - override fun setItem(name: Name, item: MetaItem<*>?) { + override fun setItem(name: Name, item: MetaItem?) { this@withDefault.setItem(name, item) } - override fun getItem(name: Name): MetaItem<*>? = this@withDefault.getItem(name) ?: default.getItem(name) + override fun getItem(name: Name): MetaItem? = this@withDefault.getItem(name) ?: default.getItem(name) } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index 23decbff..7f8487f8 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -3,7 +3,7 @@ package hep.dataforge.meta import hep.dataforge.names.* public interface MutableMeta> : TypedMeta, MutableItemProvider { - override val items: Map> + override val items: Map> } /** @@ -12,14 +12,14 @@ public interface MutableMeta> : TypedMeta, MutableItem * Changes in Meta are not thread safe. */ public abstract class AbstractMutableMeta> : AbstractTypedMeta(), MutableMeta { - protected val children: MutableMap> = LinkedHashMap() + protected val children: MutableMap> = LinkedHashMap() - override val items: Map> + override val items: Map> get() = children //protected abstract fun itemChanged(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) - protected open fun replaceItem(key: NameToken, oldItem: MetaItem?, newItem: MetaItem?) { + protected open fun replaceItem(key: NameToken, oldItem: TypedMetaItem?, newItem: TypedMetaItem?) { if (newItem == null) { children.remove(key) } else { @@ -28,10 +28,10 @@ public abstract class AbstractMutableMeta> : AbstractTypedMet //itemChanged(key.asName(), oldItem, newItem) } - private fun wrapItem(item: MetaItem<*>?): MetaItem? = when (item) { + private fun wrapItem(item: MetaItem?): TypedMetaItem? = when (item) { null -> null - is MetaItem.ValueItem -> item - is MetaItem.NodeItem -> MetaItem.NodeItem(wrapNode(item.node)) + is ValueItem -> item + is NodeItem -> NodeItem(wrapNode(item.node)) } /** @@ -44,19 +44,19 @@ public abstract class AbstractMutableMeta> : AbstractTypedMet */ internal abstract fun empty(): M - override fun setItem(name: Name, item: MetaItem<*>?) { + override fun setItem(name: Name, item: MetaItem?) { when (name.length) { 0 -> error("Can't set a meta item for empty name") 1 -> { val token = name.firstOrNull()!! - val oldItem: MetaItem? = getItem(name) + val oldItem: TypedMetaItem? = getItem(name) replaceItem(token, oldItem, wrapItem(item)) } else -> { val token = name.firstOrNull()!! //get existing or create new node. Query is ignored for new node if (items[token] == null) { - replaceItem(token, null, MetaItem.NodeItem(empty())) + replaceItem(token, null, NodeItem(empty())) } items[token]?.node!!.set(name.cutFirst(), item) } @@ -87,7 +87,7 @@ public fun MutableItemProvider.append(name: String, value: Any?): Unit = append( public fun > M.edit(name: Name, builder: M.() -> Unit) { val item = when (val existingItem = get(name)) { null -> empty().also { set(name, it) } - is MetaItem.NodeItem -> existingItem.node + is NodeItem -> existingItem.node else -> error("Can't edit value meta item") } item.apply(builder) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index 222c439a..42a95deb 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -25,19 +25,19 @@ public open class Scheme( internal set - private fun getDefaultItem(name: Name): MetaItem<*>? { + private fun getDefaultItem(name: Name): MetaItem? { return default?.get(name) ?: descriptor?.get(name)?.defaultItem() } /** * Get a property with default */ - override fun getItem(name: Name): MetaItem<*>? = items[name] ?: getDefaultItem(name) + override fun getItem(name: Name): MetaItem? = items[name] ?: getDefaultItem(name) /** * Check if property with given [name] could be assigned to [item] */ - public open fun validateItem(name: Name, item: MetaItem<*>?): Boolean { + public open fun validateItem(name: Name, item: MetaItem?): Boolean { val descriptor = descriptor?.get(name) return descriptor?.validateItem(item) ?: true } @@ -45,7 +45,7 @@ public open class Scheme( /** * Set a configurable property */ - override fun setItem(name: Name, item: MetaItem<*>?) { + override fun setItem(name: Name, item: MetaItem?) { if (validateItem(name, item)) { items[name] = item } else { @@ -60,7 +60,7 @@ public open class Scheme( */ public open val defaultLayer: Meta get() = object : MetaBase() { - override val items: Map> = buildMap { + override val items: Map = buildMap { descriptor?.items?.forEach { (key, itemDescriptor) -> val token = NameToken(key) val name = token.asName() diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt index bcd69eaa..75baa12c 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt @@ -8,7 +8,7 @@ import hep.dataforge.names.NameToken * If the argument is possibly mutable node, it is copied on creation */ public class SealedMeta internal constructor( - override val items: Map>, + override val items: Map>, ) : AbstractTypedMeta() /** @@ -17,7 +17,7 @@ public class SealedMeta internal constructor( public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.mapValues { entry -> entry.value.seal() }) @Suppress("UNCHECKED_CAST") -public fun MetaItem<*>.seal(): MetaItem = when (this) { - is MetaItem.ValueItem -> this - is MetaItem.NodeItem -> MetaItem.NodeItem(node.seal()) +public fun MetaItem.seal(): TypedMetaItem = when (this) { + is ValueItem -> this + is NodeItem -> NodeItem(node.seal()) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt index a4e18069..d6bd4579 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -46,7 +46,7 @@ public fun > Configurable.update( action: C.() -> Unit, ): Configurable = apply { config.update(spec, action) } -public fun MetaItem.withSpec(spec: Specification): T? = +public fun TypedMetaItem.withSpec(spec: Specification): T? = node?.let { spec.write(it) } public fun MutableItemProvider.spec( diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMetaItem.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMetaItem.kt new file mode 100644 index 00000000..94e4ff6a --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMetaItem.kt @@ -0,0 +1,88 @@ +package hep.dataforge.meta + +import hep.dataforge.values.* +import kotlinx.serialization.Serializable + +/** + * A member of the meta tree. Could be represented as one of following: + * * a [ValueItem] (leaf) + * * a [NodeItem] (node) + */ +@Serializable(MetaItemSerializer::class) +public sealed class TypedMetaItem() { + + abstract override fun equals(other: Any?): Boolean + + abstract override fun hashCode(): Int + + public companion object { + public fun of(arg: Any?): MetaItem { + return when (arg) { + null -> ValueItem(Null) + is MetaItem -> arg + is Meta -> NodeItem(arg) + else -> ValueItem(Value.of(arg)) + } + } + } +} + +public typealias MetaItem = TypedMetaItem<*> + +@Serializable(MetaItemSerializer::class) +public class ValueItem(public val value: Value) : TypedMetaItem() { + override fun toString(): String = value.toString() + + override fun equals(other: Any?): Boolean { + return this.value == (other as? ValueItem)?.value + } + + override fun hashCode(): Int { + return value.hashCode() + } +} + +@Serializable(MetaItemSerializer::class) +public class NodeItem(public val node: M) : TypedMetaItem() { + //Fixing serializer for node could cause class cast problems, but it should not since Meta descendants are not serializable + override fun toString(): String = node.toString() + + override fun equals(other: Any?): Boolean = node == (other as? NodeItem<*>)?.node + + override fun hashCode(): Int = node.hashCode() +} + +public fun Value.asMetaItem(): ValueItem = ValueItem(this) +public fun M.asMetaItem(): NodeItem = NodeItem(this) + + +/** + * Unsafe methods to access values and nodes directly from [TypedMetaItem] + */ +public val MetaItem?.value: Value? + get() = (this as? ValueItem)?.value + ?: (this?.node?.get(Meta.VALUE_KEY) as? ValueItem)?.value + +public val MetaItem?.string: String? get() = value?.string +public val MetaItem?.boolean: Boolean? get() = value?.boolean +public val MetaItem?.number: Number? get() = value?.numberOrNull +public val MetaItem?.double: Double? get() = number?.toDouble() +public val MetaItem?.float: Float? get() = number?.toFloat() +public val MetaItem?.int: Int? get() = number?.toInt() +public val MetaItem?.long: Long? get() = number?.toLong() +public val MetaItem?.short: Short? get() = number?.toShort() + +public inline fun > MetaItem?.enum(): E? = if (this is ValueItem && this.value is EnumValue<*>) { + this.value.value as E +} else { + string?.let { enumValueOf(it) } +} + +public val MetaItem.stringList: List? get() = value?.list?.map { it.string } + +public val TypedMetaItem?.node: M? + get() = when (this) { + null -> null + is ValueItem -> null//error("Trying to interpret value meta item as node item") + is NodeItem -> node + } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/DescriptorMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/DescriptorMeta.kt index 9a5b915d..d6cb15cb 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/DescriptorMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/DescriptorMeta.kt @@ -1,16 +1,13 @@ package hep.dataforge.meta.descriptors -import hep.dataforge.meta.Laminate -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaBase -import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.* import hep.dataforge.names.NameToken /** * A [Meta] that is constructed from [NodeDescriptor] */ private class DescriptorMeta(val descriptor: NodeDescriptor) : Meta, MetaBase() { - override val items: Map> + override val items: Map get() = buildMap { descriptor.items.forEach { (token, descriptorItem) -> val item = descriptorItem.defaultItem() @@ -27,22 +24,22 @@ private class DescriptorMeta(val descriptor: NodeDescriptor) : Meta, MetaBase() public fun NodeDescriptor.defaultMeta(): Laminate = Laminate(default, DescriptorMeta(this)) /** - * Build a default [MetaItem.NodeItem] from this node descriptor + * Build a default [NodeItem] from this node descriptor */ -internal fun NodeDescriptor.defaultItem(): MetaItem.NodeItem<*> = - MetaItem.NodeItem(defaultMeta()) +internal fun NodeDescriptor.defaultItem(): NodeItem<*> = + NodeItem(defaultMeta()) /** - * Build a default [MetaItem.ValueItem] from this descriptor + * Build a default [ValueItem] from this descriptor */ -internal fun ValueDescriptor.defaultItem(): MetaItem.ValueItem? { - return MetaItem.ValueItem(default ?: return null) +internal fun ValueDescriptor.defaultItem(): ValueItem? { + return ValueItem(default ?: return null) } /** - * Build a default [MetaItem] from descriptor. + * Build a default [TypedMetaItem] from descriptor. */ -public fun ItemDescriptor.defaultItem(): MetaItem<*>? { +public fun ItemDescriptor.defaultItem(): MetaItem? { return when (this) { is ValueDescriptor -> defaultItem() is NodeDescriptor -> defaultItem() diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt index dd3af2af..03762b29 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt @@ -5,7 +5,7 @@ import hep.dataforge.names.* import hep.dataforge.values.* /** - * A common parent for [ValueDescriptor] and [NodeDescriptor]. Describes a single [MetaItem] or a group of same-name-siblings. + * A common parent for [ValueDescriptor] and [NodeDescriptor]. Describes a single [TypedMetaItem] or a group of same-name-siblings. */ @DFBuilder public sealed class ItemDescriptor(final override val config: Config) : Configurable { @@ -55,7 +55,7 @@ public inline fun ItemDescriptor.attributes(block: Config.() -> Unit) { /** * Check if given item suits the descriptor */ -public fun ItemDescriptor.validateItem(item: MetaItem<*>?): Boolean { +public fun ItemDescriptor.validateItem(item: MetaItem?): Boolean { if (item == null) return !required return when (this) { is ValueDescriptor -> isAllowedValue(item.value ?: return false) @@ -279,7 +279,7 @@ public class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) } }, writer = { - MetaItem.ValueItem(it.asValue()) + ValueItem(it.asValue()) } ) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt index 6949987a..ee30b613 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt @@ -11,30 +11,30 @@ import hep.dataforge.values.Value public fun Meta.toMap(descriptor: NodeDescriptor? = null): Map { return items.entries.associate { (token, item) -> token.toString() to when (item) { - is MetaItem.NodeItem -> item.node.toMap() - is MetaItem.ValueItem -> item.value.value + is NodeItem -> item.node.toMap() + is ValueItem -> item.value.value } } } /** - * Convert map of maps to meta. This method will recognize [MetaItem], [Map] and [List] of all mentioned above as value. + * Convert map of maps to meta. This method will recognize [TypedMetaItem], [Map] and [List] of all mentioned above as value. * All other values will be converted to values. */ @DFExperimental public fun Map.toMeta(descriptor: NodeDescriptor? = null): Meta = Meta { @Suppress("UNCHECKED_CAST") - fun toItem(value: Any?): MetaItem<*> = when (value) { - is MetaItem<*> -> value - is Meta -> MetaItem.NodeItem(value) - is Map<*, *> -> MetaItem.NodeItem((value as Map).toMeta()) - else -> MetaItem.ValueItem(Value.of(value)) + fun toItem(value: Any?): MetaItem = when (value) { + is MetaItem -> value + is Meta -> NodeItem(value) + is Map<*, *> -> NodeItem((value as Map).toMeta()) + else -> ValueItem(Value.of(value)) } entries.forEach { (key, value) -> if (value is List<*>) { val items = value.map { toItem(it) } - if (items.all { it is MetaItem.ValueItem }) { + if (items.all { it is ValueItem }) { set(key, ListValue(items.map { it.value!! })) } else { setIndexedItems(key.toName(), value.map { toItem(it) }) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt index 0e4d6261..9f15a078 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt @@ -4,121 +4,121 @@ import hep.dataforge.meta.* import hep.dataforge.values.* /** - * A converter of generic object to and from [MetaItem] + * A converter of generic object to and from [TypedMetaItem] */ public interface MetaConverter { - public fun itemToObject(item: MetaItem<*>): T - public fun objectToMetaItem(obj: T): MetaItem<*> + public fun itemToObject(item: MetaItem): T + public fun objectToMetaItem(obj: T): MetaItem public companion object { - public val item: MetaConverter> = object : MetaConverter> { - override fun itemToObject(item: MetaItem<*>): MetaItem<*> = item - override fun objectToMetaItem(obj: MetaItem<*>): MetaItem<*> = obj + public val item: MetaConverter = object : MetaConverter { + override fun itemToObject(item: MetaItem): MetaItem = item + override fun objectToMetaItem(obj: MetaItem): MetaItem = obj } public val meta: MetaConverter = object : MetaConverter { - override fun itemToObject(item: MetaItem<*>): Meta = when (item) { - is MetaItem.NodeItem -> item.node - is MetaItem.ValueItem -> item.value.toMeta() + override fun itemToObject(item: MetaItem): Meta = when (item) { + is NodeItem -> item.node + is ValueItem -> item.value.toMeta() } - override fun objectToMetaItem(obj: Meta): MetaItem<*> = MetaItem.NodeItem(obj) + override fun objectToMetaItem(obj: Meta): MetaItem = NodeItem(obj) } public val value: MetaConverter = object : MetaConverter { - override fun itemToObject(item: MetaItem<*>): Value = when (item) { - is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is MetaItem.ValueItem -> item.value + override fun itemToObject(item: MetaItem): Value = when (item) { + is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is ValueItem -> item.value } - override fun objectToMetaItem(obj: Value): MetaItem<*> = MetaItem.ValueItem(obj) + override fun objectToMetaItem(obj: Value): MetaItem = ValueItem(obj) } public val string: MetaConverter = object : MetaConverter { - override fun itemToObject(item: MetaItem<*>): String = when (item) { - is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is MetaItem.ValueItem -> item.value + override fun itemToObject(item: MetaItem): String = when (item) { + is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is ValueItem -> item.value }.string - override fun objectToMetaItem(obj: String): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: String): MetaItem = ValueItem(obj.asValue()) } public val boolean: MetaConverter = object : MetaConverter { - override fun itemToObject(item: MetaItem<*>): Boolean = when (item) { - is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is MetaItem.ValueItem -> item.value + override fun itemToObject(item: MetaItem): Boolean = when (item) { + is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is ValueItem -> item.value }.boolean - override fun objectToMetaItem(obj: Boolean): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Boolean): MetaItem = ValueItem(obj.asValue()) } public val number: MetaConverter = object : MetaConverter { - override fun itemToObject(item: MetaItem<*>): Number = when (item) { - is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is MetaItem.ValueItem -> item.value + override fun itemToObject(item: MetaItem): Number = when (item) { + is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is ValueItem -> item.value }.number - override fun objectToMetaItem(obj: Number): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Number): MetaItem = ValueItem(obj.asValue()) } public val double: MetaConverter = object : MetaConverter { - override fun itemToObject(item: MetaItem<*>): Double = when (item) { - is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is MetaItem.ValueItem -> item.value + override fun itemToObject(item: MetaItem): Double = when (item) { + is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is ValueItem -> item.value }.double - override fun objectToMetaItem(obj: Double): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Double): MetaItem = ValueItem(obj.asValue()) } public val float: MetaConverter = object : MetaConverter { - override fun itemToObject(item: MetaItem<*>): Float = when (item) { - is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is MetaItem.ValueItem -> item.value + override fun itemToObject(item: MetaItem): Float = when (item) { + is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is ValueItem -> item.value }.float - override fun objectToMetaItem(obj: Float): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Float): MetaItem = ValueItem(obj.asValue()) } public val int: MetaConverter = object : MetaConverter { - override fun itemToObject(item: MetaItem<*>): Int = when (item) { - is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is MetaItem.ValueItem -> item.value + override fun itemToObject(item: MetaItem): Int = when (item) { + is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is ValueItem -> item.value }.int - override fun objectToMetaItem(obj: Int): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Int): MetaItem = ValueItem(obj.asValue()) } public val long: MetaConverter = object : MetaConverter { - override fun itemToObject(item: MetaItem<*>): Long = when (item) { - is MetaItem.NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is MetaItem.ValueItem -> item.value + override fun itemToObject(item: MetaItem): Long = when (item) { + is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is ValueItem -> item.value }.long - override fun objectToMetaItem(obj: Long): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Long): MetaItem = ValueItem(obj.asValue()) } public inline fun > enum(): MetaConverter = object : MetaConverter { @Suppress("USELESS_CAST") - override fun itemToObject(item: MetaItem<*>): E = item.enum() as? E ?: error("The Item is not a Enum") + override fun itemToObject(item: MetaItem): E = item.enum() as? E ?: error("The Item is not a Enum") - override fun objectToMetaItem(obj: E): MetaItem<*> = MetaItem.ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: E): MetaItem = ValueItem(obj.asValue()) } public fun valueList(writer: (T) -> Value = {Value.of(it)}, reader: (Value) -> T): MetaConverter> = object : MetaConverter> { - override fun itemToObject(item: MetaItem<*>): List = + override fun itemToObject(item: MetaItem): List = item.value?.list?.map(reader) ?: error("The item is not a value list") - override fun objectToMetaItem(obj: List): MetaItem<*> = - MetaItem.ValueItem(obj.map(writer).asValue()) + override fun objectToMetaItem(obj: List): MetaItem = + ValueItem(obj.map(writer).asValue()) } } } -public fun MetaConverter.nullableItemToObject(item: MetaItem<*>?): T? = item?.let { itemToObject(it) } -public fun MetaConverter.nullableObjectToMetaItem(obj: T?): MetaItem<*>? = obj?.let { objectToMetaItem(it) } +public fun MetaConverter.nullableItemToObject(item: MetaItem?): T? = item?.let { itemToObject(it) } +public fun MetaConverter.nullableObjectToMetaItem(obj: T?): MetaItem? = obj?.let { objectToMetaItem(it) } -public fun MetaConverter.metaToObject(meta: Meta): T = itemToObject(MetaItem.NodeItem(meta)) -public fun MetaConverter.valueToObject(value: Value): T = itemToObject(MetaItem.ValueItem(value)) +public fun MetaConverter.metaToObject(meta: Meta): T = itemToObject(NodeItem(meta)) +public fun MetaConverter.valueToObject(value: Value): T = itemToObject(ValueItem(value)) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt index 7f8e8097..865dd776 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt @@ -11,7 +11,7 @@ public interface TransformationRule { /** * Check if this transformation should be applied to a node with given name and value */ - public fun matches(name: Name, item: MetaItem<*>?): Boolean + public fun matches(name: Name, item: MetaItem?): Boolean /** * Select all items to be transformed. Item could be a value as well as node @@ -24,7 +24,7 @@ public interface TransformationRule { /** * Apply transformation for a single item (Node or Value) to the target */ - public fun > transformItem(name: Name, item: MetaItem<*>?, target: M): Unit + public fun > transformItem(name: Name, item: MetaItem?, target: M): Unit } /** @@ -32,14 +32,14 @@ public interface TransformationRule { */ public data class KeepTransformationRule(val selector: (Name) -> Boolean) : TransformationRule { - override fun matches(name: Name, item: MetaItem<*>?): Boolean { + override fun matches(name: Name, item: MetaItem?): Boolean { return selector(name) } override fun selectItems(meta: Meta): Sequence = meta.itemSequence().map { it.first }.filter(selector) - override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { + override fun > transformItem(name: Name, item: MetaItem?, target: M) { if (selector(name)) target.set(name, item) } } @@ -49,15 +49,15 @@ public data class KeepTransformationRule(val selector: (Name) -> Boolean) : */ public data class SingleItemTransformationRule( val from: Name, - val transform: MutableMeta<*>.(Name, MetaItem<*>?) -> Unit, + val transform: MutableMeta<*>.(Name, MetaItem?) -> Unit, ) : TransformationRule { - override fun matches(name: Name, item: MetaItem<*>?): Boolean { + override fun matches(name: Name, item: MetaItem?): Boolean { return name == from } override fun selectItems(meta: Meta): Sequence = sequenceOf(from) - override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { + override fun > transformItem(name: Name, item: MetaItem?, target: M) { if (name == this.from) { target.transform(name, item) } @@ -66,13 +66,13 @@ public data class SingleItemTransformationRule( public data class RegexItemTransformationRule( val from: Regex, - val transform: MutableMeta<*>.(name: Name, MatchResult, MetaItem<*>?) -> Unit, + val transform: MutableMeta<*>.(name: Name, MatchResult, MetaItem?) -> Unit, ) : TransformationRule { - override fun matches(name: Name, item: MetaItem<*>?): Boolean { + override fun matches(name: Name, item: MetaItem?): Boolean { return from.matches(name.toString()) } - override fun > transformItem(name: Name, item: MetaItem<*>?, target: M) { + override fun > transformItem(name: Name, item: MetaItem?, target: M) { val match = from.matchEntire(name.toString()) if (match != null) { target.transform(name, match, item) @@ -177,7 +177,7 @@ public class MetaTransformationBuilder { /** * Move an item from [from] to [to], optionally applying [operation] it defined */ - public fun move(from: Name, to: Name, operation: (MetaItem<*>?) -> Any? = { it }) { + public fun move(from: Name, to: Name, operation: (MetaItem?) -> Any? = { it }) { transformations.add( SingleItemTransformationRule(from) { _, item -> set(to, operation(item)) diff --git a/dataforge-meta/src/jsMain/kotlin/hep/dataforge/meta/DynamicMeta.kt b/dataforge-meta/src/jsMain/kotlin/hep/dataforge/meta/DynamicMeta.kt index 9d76b606..dadecbfb 100644 --- a/dataforge-meta/src/jsMain/kotlin/hep/dataforge/meta/DynamicMeta.kt +++ b/dataforge-meta/src/jsMain/kotlin/hep/dataforge/meta/DynamicMeta.kt @@ -22,9 +22,9 @@ public fun Value.toDynamic(): dynamic { public fun Meta.toDynamic(): dynamic { if (this is DynamicMeta) return this.obj - fun MetaItem<*>.toDynamic(): dynamic = when (this) { - is MetaItem.ValueItem -> this.value.toDynamic() - is MetaItem.NodeItem -> this.node.toDynamic() + fun MetaItem.toDynamic(): dynamic = when (this) { + is ValueItem -> this.value.toDynamic() + is NodeItem -> this.node.toDynamic() } val res = js("{}") @@ -48,27 +48,27 @@ public class DynamicMeta(internal val obj: dynamic) : MetaBase() { (jsTypeOf(obj) != "object") @Suppress("UNCHECKED_CAST", "USELESS_CAST") - private fun asItem(obj: dynamic): MetaItem? { + private fun asItem(obj: dynamic): TypedMetaItem? { return when { - obj == null -> MetaItem.ValueItem(Null) - isArray(obj) && (obj as Array).all { isPrimitive(it) } -> MetaItem.ValueItem(Value.of(obj as Array)) + obj == null -> ValueItem(Null) + isArray(obj) && (obj as Array).all { isPrimitive(it) } -> ValueItem(Value.of(obj as Array)) else -> when (jsTypeOf(obj)) { - "boolean" -> MetaItem.ValueItem(Value.of(obj as Boolean)) - "number" -> MetaItem.ValueItem(Value.of(obj as Number)) - "string" -> MetaItem.ValueItem(Value.of(obj as String)) - "object" -> MetaItem.NodeItem(DynamicMeta(obj)) + "boolean" -> ValueItem(Value.of(obj as Boolean)) + "number" -> ValueItem(Value.of(obj as Number)) + "string" -> ValueItem(Value.of(obj as String)) + "object" -> NodeItem(DynamicMeta(obj)) else -> null } } } - override val items: Map> - get() = keys().flatMap>> { key -> + override val items: Map> + get() = keys().flatMap>> { key -> val value = obj[key] ?: return@flatMap emptyList() if (isArray(value)) { val array = value as Array return@flatMap if (array.all { isPrimitive(it) }) { - listOf(NameToken(key) to MetaItem.ValueItem(Value.of(array))) + listOf(NameToken(key) to ValueItem(Value.of(array))) } else { array.mapIndexedNotNull { index, it -> val item = asItem(it) ?: return@mapIndexedNotNull null From 76968f07e5d02d40e25bf1b459eb21a73c5b7e25 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 24 Dec 2020 23:17:32 +0300 Subject: [PATCH 22/59] Fix scheme initialization --- .../src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index 42a95deb..6482a139 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -18,7 +18,9 @@ public open class Scheme( public var items: MutableItemProvider = items internal set(value) { //Fix problem with `init` blocks in specifications - field = value.withDefault(field) + field = value.apply { + field.rootNode?.let { update(it) } + } } override var descriptor: NodeDescriptor? = descriptor From 460dc77d51313a4e6ce3d731af540da3c97e9f98 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 25 Dec 2020 16:19:19 +0300 Subject: [PATCH 23/59] Fix MetaItem inference from Any --- build.gradle.kts | 2 +- dataforge-meta/api/dataforge-meta.api | 38 +++++++------- .../kotlin/hep/dataforge/meta/ItemProvider.kt | 7 ++- .../kotlin/hep/dataforge/meta/Meta.kt | 45 ---------------- .../meta/{TypedMetaItem.kt => MetaItem.kt} | 7 +-- .../hep/dataforge/meta/MutableItemProvider.kt | 11 +--- .../kotlin/hep/dataforge/meta/TypedMeta.kt | 51 +++++++++++++++++++ 7 files changed, 84 insertions(+), 77 deletions(-) rename dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/{TypedMetaItem.kt => MetaItem.kt} (92%) create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMeta.kt diff --git a/build.gradle.kts b/build.gradle.kts index aa39db90..65e56e40 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.3.0-dev-SNAPSHOT") +val dataforgeVersion by extra("0.3.0-dev") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index c130e09e..a3c45b6a 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -131,6 +131,7 @@ public final class hep/dataforge/meta/ItemProviderKt { public static final fun getChild (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Lhep/dataforge/meta/ItemProvider; public static final fun getIndexed (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Ljava/util/Map; public static final fun getIndexed (Lhep/dataforge/meta/ItemProvider;Ljava/lang/String;)Ljava/util/Map; + public static final fun getRootItem (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/TypedMetaItem; public static final fun getRootNode (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Meta; public static final fun withDefault (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/ItemProvider; } @@ -252,6 +253,22 @@ public final class hep/dataforge/meta/MetaBuilderKt { public static final fun builder (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MetaBuilder; } +public final class hep/dataforge/meta/MetaItemKt { + public static final fun asMetaItem (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/NodeItem; + public static final fun asMetaItem (Lhep/dataforge/values/Value;)Lhep/dataforge/meta/ValueItem; + public static final fun getBoolean (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Boolean; + public static final fun getDouble (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Double; + public static final fun getFloat (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Float; + public static final fun getInt (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Integer; + public static final fun getLong (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Long; + public static final fun getNode (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/meta/Meta; + public static final fun getNumber (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Number; + public static final fun getShort (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Short; + public static final fun getString (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/String; + public static final fun getStringList (Lhep/dataforge/meta/TypedMetaItem;)Ljava/util/List; + public static final fun getValue (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/values/Value; +} + public final class hep/dataforge/meta/MetaItemSerializer : kotlinx/serialization/KSerializer { public static final field INSTANCE Lhep/dataforge/meta/MetaItemSerializer; public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lhep/dataforge/meta/TypedMetaItem; @@ -263,9 +280,6 @@ public final class hep/dataforge/meta/MetaItemSerializer : kotlinx/serialization public final class hep/dataforge/meta/MetaKt { public static final fun get (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/TypedMetaItem; - public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; - public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/TypedMetaItem; - public static final fun get (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Lhep/dataforge/meta/TypedMetaItem; public static final fun isEmpty (Lhep/dataforge/meta/Meta;)Z public static final fun itemSequence (Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; public static final fun iterator (Lhep/dataforge/meta/Meta;)Ljava/util/Iterator; @@ -508,20 +522,10 @@ public final class hep/dataforge/meta/TypedMetaItem$Companion { public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; } -public final class hep/dataforge/meta/TypedMetaItemKt { - public static final fun asMetaItem (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/NodeItem; - public static final fun asMetaItem (Lhep/dataforge/values/Value;)Lhep/dataforge/meta/ValueItem; - public static final fun getBoolean (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Boolean; - public static final fun getDouble (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Double; - public static final fun getFloat (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Float; - public static final fun getInt (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Integer; - public static final fun getLong (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Long; - public static final fun getNode (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/meta/Meta; - public static final fun getNumber (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Number; - public static final fun getShort (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Short; - public static final fun getString (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/String; - public static final fun getStringList (Lhep/dataforge/meta/TypedMetaItem;)Ljava/util/List; - public static final fun getValue (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/values/Value; +public final class hep/dataforge/meta/TypedMetaKt { + public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; + public static final fun get (Lhep/dataforge/meta/TypedMeta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/TypedMetaItem; + public static final fun get (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Lhep/dataforge/meta/TypedMetaItem; } public final class hep/dataforge/meta/ValueItem : hep/dataforge/meta/TypedMetaItem { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt index f3d3884f..194a3398 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt @@ -21,10 +21,15 @@ public fun interface ItemProvider { */ public operator fun ItemProvider?.get(name: Name): MetaItem? = this?.getItem(name) +/** + * Root item of this provider + */ +public val ItemProvider.rootItem: MetaItem? get() = get(Name.EMPTY) + /** * The root node of this item provider if it is present */ -public val ItemProvider.rootNode: Meta? get() = get(Name.EMPTY).node +public val ItemProvider.rootNode: Meta? get() = rootItem.node /** * Parse [Name] from [key] using full name notation and pass it to [Meta.get] diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index f672279e..95d144b0 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -3,7 +3,6 @@ package hep.dataforge.meta import hep.dataforge.names.* import hep.dataforge.values.Value import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json /** @@ -91,48 +90,4 @@ public fun Meta.itemSequence(): Sequence> = sequence { public operator fun Meta.iterator(): Iterator> = itemSequence().iterator() -/** - * A meta node that ensures that all of its descendants has at least the same type - */ -public interface TypedMeta> : Meta { - override val items: Map> - - @Suppress("UNCHECKED_CAST") - override fun getItem(name: Name): TypedMetaItem? = super.getItem(name)?.let { it as TypedMetaItem } - //Typed meta guarantees that all children have M type -} - -/** - * The same as [Meta.get], but with specific node type - */ -public operator fun > M?.get(name: Name): TypedMetaItem? = this?.getItem(name) - - -public operator fun > M?.get(key: String): TypedMetaItem? = this[key.toName()] -public operator fun > M?.get(key: NameToken): TypedMetaItem? = this[key.asName()] - -/** - * Equals, hashcode and to string for any meta - */ -public abstract class MetaBase : Meta { - - override fun equals(other: Any?): Boolean = if (other is Meta) { - this.items == other.items - } else { - false - } - - override fun hashCode(): Int = items.hashCode() - - override fun toString(): String = Json { - prettyPrint = true - useArrayPolymorphism = true - }.encodeToString(MetaSerializer, this) -} - -/** - * Equals and hash code implementation for meta node - */ -public abstract class AbstractTypedMeta> : TypedMeta, MetaBase() - public fun Meta.isEmpty(): Boolean = this === Meta.EMPTY || this.items.isEmpty() \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMetaItem.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt similarity index 92% rename from dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMetaItem.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt index 94e4ff6a..bfc3a83c 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMetaItem.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt @@ -18,10 +18,11 @@ public sealed class TypedMetaItem() { public companion object { public fun of(arg: Any?): MetaItem { return when (arg) { - null -> ValueItem(Null) + null -> Null.asMetaItem() is MetaItem -> arg - is Meta -> NodeItem(arg) - else -> ValueItem(Value.of(arg)) + is Meta -> arg.asMetaItem() + is ItemProvider -> arg.rootItem ?: Null.asMetaItem() + else -> Value.of(arg).asMetaItem() } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt index 5ca6334a..3625162a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt @@ -2,7 +2,6 @@ package hep.dataforge.meta import hep.dataforge.names.* import hep.dataforge.values.Value -import hep.dataforge.values.asValue public interface MutableItemProvider : ItemProvider { public fun setItem(name: Name, item: MetaItem?) @@ -30,15 +29,7 @@ public inline fun MutableItemProvider.remove(name: String): Unit = remove(name.t public operator fun MutableItemProvider.set(name: Name, value: Any?) { when (value) { null -> remove(name) - is Value -> set(name, value) - is Number -> set(name, value.asValue()) - is String -> set(name, value.asValue()) - is Boolean -> set(name, value.asValue()) - is MetaItem -> set(name, value) - is Meta -> set(name, value) - is MetaRepr -> set(name, value.toMeta()) - is Configurable -> set(name, value.config) - else -> set(name, Value.of(value)) + else -> set(name, MetaItem.of(value)) } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMeta.kt new file mode 100644 index 00000000..786054c1 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/TypedMeta.kt @@ -0,0 +1,51 @@ +package hep.dataforge.meta + +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken +import hep.dataforge.names.toName +import kotlinx.serialization.json.Json + +/** + * A meta node that ensures that all of its descendants has at least the same type + */ +public interface TypedMeta> : Meta { + override val items: Map> + + @Suppress("UNCHECKED_CAST") + override fun getItem(name: Name): TypedMetaItem? = super.getItem(name)?.let { it as TypedMetaItem } + //Typed meta guarantees that all children have M type +} + + +/** + * The same as [Meta.get], but with specific node type + */ +public operator fun > M.get(name: Name): TypedMetaItem? = getItem(name) + + +public operator fun > M.get(key: String): TypedMetaItem? = this[key.toName()] +public operator fun > M.get(key: NameToken): TypedMetaItem? = items[key] + +/** + * Equals, hashcode and to string for any meta + */ +public abstract class MetaBase : Meta { + + override fun equals(other: Any?): Boolean = if (other is Meta) { + this.items == other.items + } else { + false + } + + override fun hashCode(): Int = items.hashCode() + + override fun toString(): String = Json { + prettyPrint = true + useArrayPolymorphism = true + }.encodeToString(MetaSerializer, this) +} + +/** + * Equals and hash code implementation for meta node + */ +public abstract class AbstractTypedMeta> : TypedMeta, MetaBase() From adf65a5bdec332a4a9903c259e7a803af012b574 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 25 Dec 2020 16:43:39 +0300 Subject: [PATCH 24/59] Fix Specification update --- dataforge-meta/api/dataforge-meta.api | 2 +- .../src/commonMain/kotlin/hep/dataforge/meta/Specification.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index a3c45b6a..c1f68f89 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -496,8 +496,8 @@ public final class hep/dataforge/meta/SpecificationKt { public static final fun spec (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun spec$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Scheme;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun spec$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun update (Lhep/dataforge/meta/Config;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public static final fun update (Lhep/dataforge/meta/Configurable;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Configurable; + public static final fun update (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public static final fun withSpec (Lhep/dataforge/meta/TypedMetaItem;Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt index d6bd4579..b15ef355 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -33,9 +33,9 @@ public interface Specification { } /** - * Update a [Config] using given specification + * Update a [MutableItemProvider] using given specification */ -public fun Config.update(spec: Specification, action: T.() -> Unit): T = +public fun MutableItemProvider.update(spec: Specification, action: T.() -> Unit): T = spec.write(this).apply(action) /** From 9c3f9420ea24153872420420408d8b955718efea Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 25 Dec 2020 18:28:23 +0300 Subject: [PATCH 25/59] cleanup specification. again --- .../kotlin/hep/dataforge/meta/Scheme.kt | 27 +++++++----- .../hep/dataforge/meta/Specification.kt | 29 ++++--------- .../kotlin/hep/dataforge/meta/SchemeTest.kt | 32 -------------- .../hep/dataforge/meta/SpecificationTest.kt | 42 +++++++++++++++++++ 4 files changed, 66 insertions(+), 64 deletions(-) delete mode 100644 dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index 6482a139..211b0f97 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -9,13 +9,9 @@ import hep.dataforge.names.asName * A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification]. * Default item provider and [NodeDescriptor] are optional */ -public open class Scheme( - items: MutableItemProvider = Config(), - internal var default: ItemProvider? = null, - descriptor: NodeDescriptor? = null, -) : MutableItemProvider, Described, MetaRepr { +public open class Scheme() : MutableItemProvider, Described, MetaRepr { - public var items: MutableItemProvider = items + public var items: MutableItemProvider = MetaBuilder() internal set(value) { //Fix problem with `init` blocks in specifications field = value.apply { @@ -23,9 +19,21 @@ public open class Scheme( } } - override var descriptor: NodeDescriptor? = descriptor + internal var default: ItemProvider? = null + + final override var descriptor: NodeDescriptor? = null internal set + public constructor( + items: MutableItemProvider, + default: ItemProvider? = null, + descriptor: NodeDescriptor? = null, + ) : this(){ + this.items = items + this.default = default + this.descriptor = descriptor + } + private fun getDefaultItem(name: Name): MetaItem? { return default?.get(name) ?: descriptor?.get(name)?.defaultItem() @@ -114,7 +122,4 @@ public open class SchemeSpec( public fun Meta.asScheme(): Scheme = Scheme().apply { items = this@asScheme.asConfig() -} - -public fun Meta.toScheme(spec: Specification, block: T.() -> Unit = {}): T = - spec.read(this).apply(block) +} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt index b15ef355..5f35f228 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -35,8 +35,9 @@ public interface Specification { /** * Update a [MutableItemProvider] using given specification */ -public fun MutableItemProvider.update(spec: Specification, action: T.() -> Unit): T = +public fun MutableItemProvider.update(spec: Specification, action: T.() -> Unit) { spec.write(this).apply(action) +} /** * Update configuration using given specification @@ -44,38 +45,24 @@ public fun MutableItemProvider.update(spec: Specificat public fun > Configurable.update( spec: S, action: C.() -> Unit, -): Configurable = apply { config.update(spec, action) } +) { + config.update(spec, action) +} -public fun TypedMetaItem.withSpec(spec: Specification): T? = +public fun TypedMetaItem>.withSpec(spec: Specification): T? = node?.let { spec.write(it) } public fun MutableItemProvider.spec( spec: Specification, key: Name? = null, -): ReadWriteProperty = object : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T? { - val name = key ?: property.name.asName() - return get(name).node?.let { spec.read(it) } - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { - val name = key ?: property.name.asName() - set(name, value?.toMeta()?.asMetaItem()) - } -} - -public fun MutableItemProvider.spec( - spec: Specification, - default: T, - key: Name? = null, ): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): T { val name = key ?: property.name.asName() - return get(name).node?.let { spec.read(it) } ?: default + return getChild(name).let { spec.write(it) } } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { val name = key ?: property.name.asName() set(name, value.toMeta().asMetaItem()) } -} \ No newline at end of file +} diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt deleted file mode 100644 index 14085f08..00000000 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -package hep.dataforge.meta - -import kotlin.test.Test -import kotlin.test.assertEquals - - -class SchemeTest{ - @Test - fun testMetaScheme(){ - val styled = Meta { - repeat(10){ - "b.a[$it]" put { - "d" put it - } - } - }.asScheme() - - val meta = styled.toMeta() - - assertEquals(10, meta.valueSequence().count()) - - val bNode = styled.get("b").node - - val aNodes = bNode?.getIndexed("a") - - val allNodes = meta.getIndexed("b.a") - - assertEquals(3, aNodes?.get("3").node["d"].int) - assertEquals(3, allNodes["3"].node["d"].int) - } - -} \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt index 93934154..aa69a9e6 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt @@ -15,6 +15,37 @@ class SpecificationTest { } } + class TestScheme : Scheme() { + var a by int() + var b by string() + + companion object : SchemeSpec(::TestScheme) + } + +// @Test +// fun testMetaScheme(){ +// val styled = Meta { +// repeat(10){ +// "b.a[$it]" put { +// "d" put it +// } +// } +// }.asScheme() +// +// val meta = styled.toMeta() +// +// assertEquals(10, meta.valueSequence().count()) +// +// val bNode = styled["b"].node +// +// val aNodes = bNode?.getIndexed("a") +// +// val allNodes = meta.getIndexed("b.a") +// +// assertEquals(3, aNodes?.get("3").node["d"].int) +// assertEquals(3, allNodes["3"].node["d"].int) +// } + @Test fun testSpecific() { @@ -23,4 +54,15 @@ class SpecificationTest { } assertEquals(emptyList(), testObject.list) } + + @Test + fun testChildModification() { + val config = Config() + val child = config.getChild("child") + val scheme = TestScheme.write(child) + scheme.a = 22 + scheme.b = "test" + assertEquals(22,config["child.a"].int) + assertEquals("test",config["child.b"].string) + } } \ No newline at end of file From 81375d4644ed94f716b7a51c31ef7f9f9713786a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 25 Dec 2020 19:27:58 +0300 Subject: [PATCH 26/59] Separate ReadOnlySpecification. Cleanup Scheme inflation --- dataforge-meta/api/dataforge-meta.api | 39 +++++++------- .../hep/dataforge/meta/MutableItemProvider.kt | 4 +- .../kotlin/hep/dataforge/meta/Scheme.kt | 51 ++++++++----------- .../hep/dataforge/meta/Specification.kt | 27 ++++++---- .../hep/dataforge/meta/SpecificationTest.kt | 40 ++++++++++----- 5 files changed, 86 insertions(+), 75 deletions(-) diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index c1f68f89..0f94e3bd 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -431,16 +431,24 @@ public abstract interface class hep/dataforge/meta/ObservableItemProvider : hep/ public abstract fun removeListener (Ljava/lang/Object;)V } +public abstract interface class hep/dataforge/meta/ReadOnlySpecification { + public abstract fun empty ()Lhep/dataforge/meta/ItemProvider; + public abstract fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/ItemProvider; + public abstract fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/ItemProvider; +} + +public final class hep/dataforge/meta/ReadOnlySpecification$DefaultImpls { + public static fun invoke (Lhep/dataforge/meta/ReadOnlySpecification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/ItemProvider; +} + public class hep/dataforge/meta/Scheme : hep/dataforge/meta/MetaRepr, hep/dataforge/meta/MutableItemProvider, hep/dataforge/meta/descriptors/Described { public fun ()V - public fun (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)V - public synthetic fun (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getDefaultLayer ()Lhep/dataforge/meta/Meta; public synthetic fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; - public fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; + public final fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; - public final fun getItems ()Lhep/dataforge/meta/MutableItemProvider; public final fun isEmpty ()Z + public final fun setDescriptor (Lhep/dataforge/meta/descriptors/NodeDescriptor;)V public fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)V public fun toMeta ()Lhep/dataforge/meta/Laminate; public synthetic fun toMeta ()Lhep/dataforge/meta/Meta; @@ -448,22 +456,21 @@ public class hep/dataforge/meta/Scheme : hep/dataforge/meta/MetaRepr, hep/datafo } public final class hep/dataforge/meta/SchemeKt { - public static final fun asScheme (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/Scheme; + public static final fun inflate (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Scheme; + public static synthetic fun inflate$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/Scheme; public static final fun invoke (Lhep/dataforge/meta/Scheme;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Scheme; - public static final fun toScheme (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; - public static synthetic fun toScheme$default (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/meta/MutableItemProvider; } public class hep/dataforge/meta/SchemeSpec : hep/dataforge/meta/Specification, hep/dataforge/meta/descriptors/Described { public fun (Lkotlin/jvm/functions/Function0;)V - public fun (Lkotlin/jvm/functions/Function3;)V - public synthetic fun empty ()Lhep/dataforge/meta/MutableItemProvider; + public synthetic fun empty ()Lhep/dataforge/meta/ItemProvider; public fun empty ()Lhep/dataforge/meta/Scheme; public synthetic fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; public fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; + public synthetic fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/ItemProvider; public synthetic fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public final fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Scheme; - public synthetic fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; + public synthetic fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/ItemProvider; public fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; public synthetic fun write (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; public fun write (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; @@ -478,26 +485,20 @@ public final class hep/dataforge/meta/SealedMetaKt { public static final fun seal (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/meta/TypedMetaItem; } -public abstract interface class hep/dataforge/meta/Specification { - public abstract fun empty ()Lhep/dataforge/meta/MutableItemProvider; - public abstract fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; - public abstract fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; +public abstract interface class hep/dataforge/meta/Specification : hep/dataforge/meta/ReadOnlySpecification { public abstract fun write (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MutableItemProvider; } public final class hep/dataforge/meta/Specification$DefaultImpls { - public static fun empty (Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; public static fun invoke (Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public static synthetic fun write$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;ILjava/lang/Object;)Lhep/dataforge/meta/MutableItemProvider; } public final class hep/dataforge/meta/SpecificationKt { - public static final fun spec (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Scheme;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static final fun spec (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun spec$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/Scheme;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; public static synthetic fun spec$default (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lhep/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun update (Lhep/dataforge/meta/Configurable;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Configurable; - public static final fun update (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; + public static final fun update (Lhep/dataforge/meta/Configurable;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)V + public static final fun update (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)V public static final fun withSpec (Lhep/dataforge/meta/TypedMetaItem;Lhep/dataforge/meta/Specification;)Lhep/dataforge/meta/MutableItemProvider; } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt index 3625162a..4216f7a1 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt @@ -116,8 +116,8 @@ public fun MutableItemProvider.editChild(name: Name, builder: MutableItemProvide * Create a mutable item provider that uses given provider for default values if those are not found in this provider. * Changes are propagated only to this provider. */ -public fun MutableItemProvider.withDefault(default: ItemProvider): MutableItemProvider = - if (default is Meta && default.isEmpty()) { +public fun MutableItemProvider.withDefault(default: ItemProvider?): MutableItemProvider = + if (default == null || (default is Meta && default.isEmpty())) { //Optimize for use with empty default this } else object : MutableItemProvider { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index 211b0f97..fcaf5f15 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -11,30 +11,24 @@ import hep.dataforge.names.asName */ public open class Scheme() : MutableItemProvider, Described, MetaRepr { - public var items: MutableItemProvider = MetaBuilder() - internal set(value) { - //Fix problem with `init` blocks in specifications - field = value.apply { - field.rootNode?.let { update(it) } - } - } + private var items: MutableItemProvider = MetaBuilder() - internal var default: ItemProvider? = null + private var default: ItemProvider? = null final override var descriptor: NodeDescriptor? = null - internal set - public constructor( + internal fun inflate( items: MutableItemProvider, default: ItemProvider? = null, descriptor: NodeDescriptor? = null, - ) : this(){ + ) { + //use properties in the init block as default + this.default = items.withDefault(default) + //reset values, defaults are already saved this.items = items - this.default = default this.descriptor = descriptor } - private fun getDefaultItem(name: Name): MetaItem? { return default?.get(name) ?: descriptor?.get(name)?.defaultItem() } @@ -87,6 +81,16 @@ public open class Scheme() : MutableItemProvider, Described, MetaRepr { public fun isEmpty(): Boolean = toMeta().isEmpty() } + +public fun > S.inflate( + items: MutableItemProvider, + default: ItemProvider? = null, + descriptor: NodeDescriptor? = null, +): T = empty().apply { + inflate(items, default, descriptor) +} + + /** * A shortcut to edit a [Scheme] object in-place */ @@ -95,31 +99,20 @@ public inline operator fun T.invoke(block: T.() -> Unit): T = apply /** * A specification for simplified generation of wrappers */ -public open class SchemeSpec( - private val builder: (target: MutableItemProvider, defaultProvider: ItemProvider, descriptor: NodeDescriptor?) -> T, +public open class SchemeSpec( + private val builder: () -> T, ) : Specification, Described { - public constructor(emptyBuilder: () -> T) : this({ target: MutableItemProvider, defaultProvider: ItemProvider, descriptor: NodeDescriptor? -> - emptyBuilder().apply { - this.items = target - this.default = defaultProvider - this.descriptor = descriptor - } - }) + override fun empty(): T = builder() - override fun read(items: ItemProvider): T = - builder(Config(), items, descriptor) + override fun read(items: ItemProvider): T = inflate(Config(), items, descriptor) override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): T = - builder(target, defaultProvider, descriptor) + inflate(target, defaultProvider, descriptor) //TODO Generate descriptor from Scheme class override val descriptor: NodeDescriptor? get() = null @Suppress("OVERRIDE_BY_INLINE") final override inline operator fun invoke(action: T.() -> Unit): T = empty().apply(action) -} - -public fun Meta.asScheme(): Scheme = Scheme().apply { - items = this@asScheme.asConfig() } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt index 5f35f228..e6edd22e 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Specification.kt @@ -5,26 +5,18 @@ import hep.dataforge.names.asName import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty -/** - * Allows to apply custom configuration in a type safe way to simple untyped configuration. - * By convention [Scheme] companion should inherit this class - * - */ -public interface Specification { +public interface ReadOnlySpecification { + /** * Read generic read-only meta with this [Specification] producing instance of desired type. */ public fun read(items: ItemProvider): T - /** - * Wrap [MutableItemProvider], using it as inner storage (changes to [Specification] are reflected on [MutableItemProvider] - */ - public fun write(target: MutableItemProvider, defaultProvider: ItemProvider = ItemProvider.EMPTY): T /** * Generate an empty object */ - public fun empty(): T = read(Meta.EMPTY) + public fun empty(): T /** * A convenience method to use specifications in builders @@ -32,6 +24,19 @@ public interface Specification { public operator fun invoke(action: T.() -> Unit): T = empty().apply(action) } + +/** + * Allows to apply custom configuration in a type safe way to simple untyped configuration. + * By convention [Scheme] companion should inherit this class + * + */ +public interface Specification: ReadOnlySpecification { + /** + * Wrap [MutableItemProvider], using it as inner storage (changes to [Specification] are reflected on [MutableItemProvider] + */ + public fun write(target: MutableItemProvider, defaultProvider: ItemProvider = ItemProvider.EMPTY): T +} + /** * Update a [MutableItemProvider] using given specification */ diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt index aa69a9e6..0f826c2c 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt @@ -4,22 +4,22 @@ import kotlin.test.Test import kotlin.test.assertEquals class SpecificationTest { - class TestStyled(target: MutableItemProvider, default: ItemProvider?) : Scheme(target, default) { + class TestScheme : Scheme() { var list by numberList(1, 2, 3) - companion object : Specification { - override fun read(items: ItemProvider): TestStyled = TestStyled(Config(), items) - - override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): TestStyled = - TestStyled(target, defaultProvider) - } - } - - class TestScheme : Scheme() { var a by int() var b by string() - companion object : SchemeSpec(::TestScheme) + companion object : Specification { + override fun empty(): TestScheme = TestScheme() + + override fun read(items: ItemProvider): TestScheme = + inflate(Config(), items) + + override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): TestScheme = + inflate(target, defaultProvider) + + } } // @Test @@ -49,7 +49,7 @@ class SpecificationTest { @Test fun testSpecific() { - val testObject = TestStyled { + val testObject = TestScheme { list = emptyList() } assertEquals(emptyList(), testObject.list) @@ -62,7 +62,19 @@ class SpecificationTest { val scheme = TestScheme.write(child) scheme.a = 22 scheme.b = "test" - assertEquals(22,config["child.a"].int) - assertEquals("test",config["child.b"].string) + assertEquals(22, config["child.a"].int) + assertEquals("test", config["child.b"].string) + } + + @Test + fun testChildUpdate() { + val config = Config() + val child = config.getChild("child") + val scheme = child.update(TestScheme) { + a = 22 + b = "test" + } + assertEquals(22, config["child.a"].int) + assertEquals("test", config["child.b"].string) } } \ No newline at end of file From 7a9f7da7f289a343283120733e000234b3b7c4bc Mon Sep 17 00:00:00 2001 From: darksnake Date: Sat, 26 Dec 2020 10:35:24 +0300 Subject: [PATCH 27/59] Scheme empty check moved to extension --- dataforge-meta/api/dataforge-meta.api | 2 +- .../src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 0f94e3bd..2b0ea50f 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -447,7 +447,6 @@ public class hep/dataforge/meta/Scheme : hep/dataforge/meta/MetaRepr, hep/datafo public synthetic fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; public final fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; - public final fun isEmpty ()Z public final fun setDescriptor (Lhep/dataforge/meta/descriptors/NodeDescriptor;)V public fun setItem (Lhep/dataforge/names/Name;Lhep/dataforge/meta/TypedMetaItem;)V public fun toMeta ()Lhep/dataforge/meta/Laminate; @@ -459,6 +458,7 @@ public final class hep/dataforge/meta/SchemeKt { public static final fun inflate (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Scheme; public static synthetic fun inflate$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/Scheme; public static final fun invoke (Lhep/dataforge/meta/Scheme;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Scheme; + public static final fun isEmpty (Lhep/dataforge/meta/Scheme;)Z } public class hep/dataforge/meta/SchemeSpec : hep/dataforge/meta/Specification, hep/dataforge/meta/descriptors/Described { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index fcaf5f15..8875e762 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -77,10 +77,12 @@ public open class Scheme() : MutableItemProvider, Described, MetaRepr { } override fun toMeta(): Laminate = Laminate(items[Name.EMPTY].node, defaultLayer) - - public fun isEmpty(): Boolean = toMeta().isEmpty() } +/** + * The scheme is considered empty only if its root item does not exist. + */ +public fun Scheme.isEmpty(): Boolean = rootItem == null public fun > S.inflate( items: MutableItemProvider, From f946777f40c67feb53f5a8ac2c13461d6cf687bc Mon Sep 17 00:00:00 2001 From: darksnake Date: Sat, 26 Dec 2020 13:34:53 +0300 Subject: [PATCH 28/59] Add retarget method to change the target of a Scheme --- dataforge-meta/api/dataforge-meta.api | 5 +++-- .../kotlin/hep/dataforge/meta/Scheme.kt | 19 ++++++++++++++----- .../hep/dataforge/meta/SpecificationTest.kt | 4 ++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 2b0ea50f..06b46d01 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -455,10 +455,11 @@ public class hep/dataforge/meta/Scheme : hep/dataforge/meta/MetaRepr, hep/datafo } public final class hep/dataforge/meta/SchemeKt { - public static final fun inflate (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Scheme; - public static synthetic fun inflate$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/Scheme; public static final fun invoke (Lhep/dataforge/meta/Scheme;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Scheme; public static final fun isEmpty (Lhep/dataforge/meta/Scheme;)Z + public static final fun retarget (Lhep/dataforge/meta/Scheme;Lhep/dataforge/meta/MutableItemProvider;)Lhep/dataforge/meta/Scheme; + public static final fun wrap (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;)Lhep/dataforge/meta/Scheme; + public static synthetic fun wrap$default (Lhep/dataforge/meta/Specification;Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/meta/descriptors/NodeDescriptor;ILjava/lang/Object;)Lhep/dataforge/meta/Scheme; } public class hep/dataforge/meta/SchemeSpec : hep/dataforge/meta/Specification, hep/dataforge/meta/descriptors/Described { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index 8875e762..b2a82ca7 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -17,7 +17,7 @@ public open class Scheme() : MutableItemProvider, Described, MetaRepr { final override var descriptor: NodeDescriptor? = null - internal fun inflate( + internal fun wrap( items: MutableItemProvider, default: ItemProvider? = null, descriptor: NodeDescriptor? = null, @@ -84,14 +84,23 @@ public open class Scheme() : MutableItemProvider, Described, MetaRepr { */ public fun Scheme.isEmpty(): Boolean = rootItem == null -public fun > S.inflate( +/** + * Create a new empty [Scheme] object (including defaults) and inflate it around existing [MutableItemProvider]. + * Items already present in the scheme are used as defaults. + */ +public fun > S.wrap( items: MutableItemProvider, default: ItemProvider? = null, descriptor: NodeDescriptor? = null, ): T = empty().apply { - inflate(items, default, descriptor) + wrap(items, default, descriptor) } +/** + * Relocate scheme target onto given [MutableItemProvider]. Old provider does not get updates anymore. + * Current state of the scheme used as a default. + */ +public fun T.retarget(provider: MutableItemProvider) :T = apply { wrap(provider) } /** * A shortcut to edit a [Scheme] object in-place @@ -107,10 +116,10 @@ public open class SchemeSpec( override fun empty(): T = builder() - override fun read(items: ItemProvider): T = inflate(Config(), items, descriptor) + override fun read(items: ItemProvider): T = wrap(Config(), items, descriptor) override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): T = - inflate(target, defaultProvider, descriptor) + wrap(target, defaultProvider, descriptor) //TODO Generate descriptor from Scheme class override val descriptor: NodeDescriptor? get() = null diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt index 0f826c2c..ddf06c1b 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt @@ -14,10 +14,10 @@ class SpecificationTest { override fun empty(): TestScheme = TestScheme() override fun read(items: ItemProvider): TestScheme = - inflate(Config(), items) + wrap(Config(), items) override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): TestScheme = - inflate(target, defaultProvider) + wrap(target, defaultProvider) } } From 027d5ed923a093e152576df4fe1257e68a67ccdd Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 27 Dec 2020 17:40:34 +0300 Subject: [PATCH 29/59] Fix scheme wrapping --- .../kotlin/hep/dataforge/meta/Scheme.kt | 4 +-- .../kotlin/hep/dataforge/meta/SchemeTest.kt | 23 ++++++++++++++++ .../hep/dataforge/meta/SpecificationTest.kt | 27 ++++++++++--------- .../hep/dataforge/meta/DynamicMetaTest.kt | 6 ----- 4 files changed, 39 insertions(+), 21 deletions(-) create mode 100644 dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt index b2a82ca7..1f1b3b4d 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Scheme.kt @@ -11,7 +11,7 @@ import hep.dataforge.names.asName */ public open class Scheme() : MutableItemProvider, Described, MetaRepr { - private var items: MutableItemProvider = MetaBuilder() + private var items: MutableItemProvider = Config() private var default: ItemProvider? = null @@ -23,7 +23,7 @@ public open class Scheme() : MutableItemProvider, Described, MetaRepr { descriptor: NodeDescriptor? = null, ) { //use properties in the init block as default - this.default = items.withDefault(default) + this.default = this.items.withDefault(default) //reset values, defaults are already saved this.items = items this.descriptor = descriptor diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt new file mode 100644 index 00000000..2dfd746c --- /dev/null +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SchemeTest.kt @@ -0,0 +1,23 @@ +package hep.dataforge.meta + +import kotlin.test.Test +import kotlin.test.assertEquals + +class SchemeTest { + @Test + fun testSchemeWrappingBeforeEdit(){ + val config = Config() + val scheme = TestScheme.wrap(config) + scheme.a = 29 + assertEquals(29, config["a"].int) + } + + @Test + fun testSchemeWrappingAfterEdit(){ + val scheme = TestScheme.empty() + scheme.a = 29 + val config = Config() + scheme.retarget(config) + assertEquals(29, scheme.a) + } +} \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt index ddf06c1b..cd8b2f82 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/SpecificationTest.kt @@ -3,24 +3,25 @@ package hep.dataforge.meta import kotlin.test.Test import kotlin.test.assertEquals -class SpecificationTest { - class TestScheme : Scheme() { - var list by numberList(1, 2, 3) +internal class TestScheme : Scheme() { + var list by numberList(1, 2, 3) - var a by int() - var b by string() + var a by int() + var b by string() - companion object : Specification { - override fun empty(): TestScheme = TestScheme() + companion object : Specification { + override fun empty(): TestScheme = TestScheme() - override fun read(items: ItemProvider): TestScheme = - wrap(Config(), items) + override fun read(items: ItemProvider): TestScheme = + wrap(Config(), items) - override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): TestScheme = - wrap(target, defaultProvider) + override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): TestScheme = + wrap(target, defaultProvider) - } } +} + +class SpecificationTest { // @Test // fun testMetaScheme(){ @@ -70,7 +71,7 @@ class SpecificationTest { fun testChildUpdate() { val config = Config() val child = config.getChild("child") - val scheme = child.update(TestScheme) { + child.update(TestScheme) { a = 22 b = "test" } diff --git a/dataforge-meta/src/jsTest/kotlin/hep/dataforge/meta/DynamicMetaTest.kt b/dataforge-meta/src/jsTest/kotlin/hep/dataforge/meta/DynamicMetaTest.kt index a2dc2de9..1fabf061 100644 --- a/dataforge-meta/src/jsTest/kotlin/hep/dataforge/meta/DynamicMetaTest.kt +++ b/dataforge-meta/src/jsTest/kotlin/hep/dataforge/meta/DynamicMetaTest.kt @@ -38,17 +38,11 @@ class DynamicMetaTest { } val dynamic = meta.toDynamic() - assertEquals(2,dynamic.array[1]) - assertEquals(22, dynamic.a) - val keys = js("Object.keys(dynamic)") as Array - assertTrue { keys.contains("ob") } - assertEquals(18, dynamic.ob.childNode) - assertEquals(meta, DynamicMeta(dynamic)) } From 030f3ed6fecb5abbc6c07f631e59cfdea83c0294 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 28 Dec 2020 21:05:27 +0300 Subject: [PATCH 30/59] Refactor context factories --- CHANGELOG.md | 1 + dataforge-context/api/dataforge-context.api | 6 ++---- .../kotlin/hep/dataforge/context/Context.kt | 3 +-- .../hep/dataforge/context/ContextBuilder.kt | 19 ++++++++--------- .../hep/dataforge/context/PluginFactory.kt | 14 +++++++++++++ .../hep/dataforge/context/PluginManager.kt | 15 ++----------- .../hep/dataforge/context/ContextTest.kt | 6 +++--- dataforge-meta/api/dataforge-meta.api | 12 ----------- .../kotlin/hep/dataforge/meta/ItemProvider.kt | 12 +++++++---- .../hep/dataforge/meta/MetaWithDefault.kt | 18 ---------------- .../workspace/DataPropagationTest.kt | 5 ++--- .../workspace/SimpleWorkspaceTest.kt | 21 ++++++++++++------- 12 files changed, 56 insertions(+), 76 deletions(-) create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt delete mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index a586067c..a485b761 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - \[Major breaking change\] Schemes and configurables us `MutableItemProvider` instead of `Config` - \[Major breaking change\] `MetaItem` renamed to `TypedMetaItem` and `MetaItem` is now an alias for `TypedMetaItem<*>` - \[Major breaking change\] Moved `NodeItem` and `ValueItem` to a top level +- Plugins are removed from Context constructor and added lazily in ContextBuilder ### Deprecated diff --git a/dataforge-context/api/dataforge-context.api b/dataforge-context/api/dataforge-context.api index f433949c..b8c52285 100644 --- a/dataforge-context/api/dataforge-context.api +++ b/dataforge-context/api/dataforge-context.api @@ -38,8 +38,7 @@ public final class hep/dataforge/context/ClassLoaderPluginKt { public class hep/dataforge/context/Context : hep/dataforge/context/Named, hep/dataforge/meta/MetaRepr, hep/dataforge/provider/Provider, kotlinx/coroutines/CoroutineScope { public static final field Companion Lhep/dataforge/context/Context$Companion; public static final field PROPERTY_TARGET Ljava/lang/String; - public fun (Lhep/dataforge/names/Name;Lhep/dataforge/context/Context;Lhep/dataforge/meta/Meta;Ljava/util/Set;)V - public synthetic fun (Lhep/dataforge/names/Name;Lhep/dataforge/context/Context;Lhep/dataforge/meta/Meta;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lhep/dataforge/names/Name;Lhep/dataforge/context/Context;Lhep/dataforge/meta/Meta;)V public fun close ()V public fun content (Ljava/lang/String;)Ljava/util/Map; public final fun content (Ljava/lang/String;Z)Ljava/util/Map; @@ -65,7 +64,6 @@ public final class hep/dataforge/context/ContextBuilder { public synthetic fun (Lhep/dataforge/context/Context;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun build ()Lhep/dataforge/context/Context; public final fun getName ()Ljava/lang/String; - public final fun plugin (Lhep/dataforge/context/Plugin;)V public final fun plugin (Lhep/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;)V public final fun plugin (Lhep/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;)V public final fun plugin (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V @@ -147,7 +145,7 @@ public final class hep/dataforge/context/PluginFactory$Companion { } public final class hep/dataforge/context/PluginManager : hep/dataforge/context/ContextAware, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { - public fun (Lhep/dataforge/context/Context;Ljava/util/Set;)V + public fun (Lhep/dataforge/context/Context;)V public final fun fetch (Lhep/dataforge/context/PluginFactory;ZLhep/dataforge/meta/Meta;)Lhep/dataforge/context/Plugin; public final fun fetch (Lhep/dataforge/context/PluginFactory;ZLkotlin/jvm/functions/Function1;)Lhep/dataforge/context/Plugin; public static synthetic fun fetch$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginFactory;ZLhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/context/Plugin; diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index 9ea626e9..3ac2f7a3 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -25,7 +25,6 @@ public open class Context( final override val name: Name, public val parent: Context?, meta: Meta, - plugins: Set = emptySet(), ) : Named, MetaRepr, Provider, CoroutineScope { /** @@ -40,7 +39,7 @@ public open class Context( /** * A [PluginManager] for current context */ - public val plugins: PluginManager by lazy { PluginManager(this, plugins)} + public val plugins: PluginManager by lazy { PluginManager(this)} override val defaultTarget: String get() = Plugin.TARGET diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt index b742474c..69a68026 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt @@ -8,17 +8,13 @@ import hep.dataforge.names.toName */ @DFBuilder public class ContextBuilder(private val parent: Context = Global, public var name: String = "@anonymous") { - private val plugins = HashSet() + private val factories = HashMap, Meta>() private var meta = MetaBuilder() public fun properties(action: MetaBuilder.() -> Unit) { meta.action() } - public fun plugin(plugin: Plugin) { - plugins.add(plugin) - } - @OptIn(DFExperimental::class) private fun findPluginFactory(tag: PluginTag): PluginFactory<*> = parent.gatherInSequence>(PluginFactory.TYPE).values @@ -26,12 +22,11 @@ public class ContextBuilder(private val parent: Context = Global, public var nam public fun plugin(tag: PluginTag, metaBuilder: MetaBuilder.() -> Unit = {}) { val factory = findPluginFactory(tag) - val plugin = factory.invoke(Meta(metaBuilder), parent) - plugins.add(plugin) + factories[factory] = Meta(metaBuilder) } - public fun plugin(builder: PluginFactory<*>, action: MetaBuilder.() -> Unit = {}) { - plugins.add(builder.invoke(Meta(action))) + public fun plugin(factory: PluginFactory<*>, metaBuilder: MetaBuilder.() -> Unit = {}) { + factories[factory] = Meta(metaBuilder) } public fun plugin(name: String, group: String = "", version: String = "", action: MetaBuilder.() -> Unit = {}) { @@ -39,6 +34,10 @@ public class ContextBuilder(private val parent: Context = Global, public var nam } public fun build(): Context { - return Context(name.toName(), parent, meta.seal(), plugins) + return Context(name.toName(), parent, meta.seal()).apply { + factories.forEach { (factory, meta) -> + plugins.load(factory, meta) + } + } } } \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt new file mode 100644 index 00000000..5985a1e7 --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt @@ -0,0 +1,14 @@ +package hep.dataforge.context + +import hep.dataforge.provider.Type +import kotlin.reflect.KClass + +@Type(PluginFactory.TYPE) +public interface PluginFactory : Factory { + public val tag: PluginTag + public val type: KClass + + public companion object { + public const val TYPE: String = "pluginFactory" + } +} diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt index 3e9def85..6ab0f5d9 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt @@ -2,34 +2,23 @@ package hep.dataforge.context import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder -import hep.dataforge.provider.Type import kotlin.reflect.KClass -@Type(PluginFactory.TYPE) -public interface PluginFactory : Factory { - public val tag: PluginTag - public val type: KClass - - public companion object { - public const val TYPE: String = "pluginFactory" - } -} - /** * The manager for plugin system. Should monitor plugin dependencies and locks. * * @property context A context for this plugin manager * @author Alexander Nozik */ -public class PluginManager(override val context: Context, plugins: Set) : ContextAware, Iterable { +public class PluginManager(override val context: Context) : ContextAware, Iterable { //TODO refactor to read-only container /** * A set of loaded plugins */ - private val plugins: HashSet = HashSet(plugins) + private val plugins: HashSet = HashSet() init { plugins.forEach { it.attach(context) } diff --git a/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt b/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt index 58647400..3d15e8b3 100644 --- a/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt +++ b/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt @@ -17,13 +17,13 @@ class ContextTest { else -> emptyMap() } } + } @Test fun testPluginManager() { - val context = Global.context("test"){ - plugin(DummyPlugin()) - } + val context = Global.context("test") + context.plugins.load(DummyPlugin()) //Global.plugins.load(DummyPlugin()) val members = context.gather("test") assertEquals(3, members.count()) diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 06b46d01..3e85187f 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -299,18 +299,6 @@ public final class hep/dataforge/meta/MetaSerializer : kotlinx/serialization/KSe public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } -public final class hep/dataforge/meta/MetaWithDefault : hep/dataforge/meta/MetaBase { - public fun (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/ItemProvider;)V - public final fun getDefault ()Lhep/dataforge/meta/ItemProvider; - public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; - public fun getItems ()Ljava/util/Map; - public final fun getMeta ()Lhep/dataforge/meta/Meta; -} - -public final class hep/dataforge/meta/MetaWithDefaultKt { - public static final fun withDefault (Lhep/dataforge/meta/Meta;Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/MetaWithDefault; -} - public final class hep/dataforge/meta/MutableItemDelegateKt { public static final fun boolean (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; public static final fun boolean (Lhep/dataforge/meta/MutableItemProvider;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadWriteProperty; diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt index 194a3398..7811e308 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt @@ -24,7 +24,7 @@ public operator fun ItemProvider?.get(name: Name): MetaItem? = this?.getItem(nam /** * Root item of this provider */ -public val ItemProvider.rootItem: MetaItem? get() = get(Name.EMPTY) +public val ItemProvider.rootItem: MetaItem? get() = get(Name.EMPTY) /** * The root node of this item provider if it is present @@ -39,8 +39,12 @@ public operator fun ItemProvider?.get(key: String): MetaItem? = this?.get(key.to /** * Create a provider that uses given provider for default values if those are not found in this provider */ -public fun ItemProvider.withDefault(default: ItemProvider): ItemProvider = ItemProvider { - this[it] ?: default[it] +public fun ItemProvider.withDefault(default: ItemProvider?): ItemProvider = if (default == null) { + this +} else { + ItemProvider { + this[it] ?: default[it] + } } /** @@ -71,7 +75,7 @@ public fun ItemProvider.getIndexed(name: String): Map = this@ */ public fun ItemProvider.getChild(childName: Name): ItemProvider = get(childName).node ?: ItemProvider.EMPTY -public fun ItemProvider.getChild(childName: String): ItemProvider = getChild(childName.toName()) +public fun ItemProvider.getChild(childName: String): ItemProvider = getChild(childName.toName()) ///** // * Get all items matching given name. diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt deleted file mode 100644 index 1858bd3c..00000000 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaWithDefault.kt +++ /dev/null @@ -1,18 +0,0 @@ -package hep.dataforge.meta - -import hep.dataforge.names.Name -import hep.dataforge.names.NameToken - -/** - * Meta object with default provider for items not present in the initial meta. - */ -public class MetaWithDefault(public val meta: Meta, public val default: ItemProvider) : MetaBase() { - override val items: Map - get() = meta.items - - override fun getItem(name: Name): MetaItem? { - return meta[name] ?: default[name] - } -} - -public fun Meta.withDefault(default: ItemProvider): MetaWithDefault = MetaWithDefault(this, default) \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index 083d3f57..784a7441 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -56,8 +56,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { override val type: KClass = DataPropagationTestPlugin::class - override fun invoke(meta: Meta, context: Context): DataPropagationTestPlugin = - DataPropagationTestPlugin(meta) + override fun invoke(meta: Meta, context: Context): DataPropagationTestPlugin = DataPropagationTestPlugin() override val tag: PluginTag = PluginTag("Test") } @@ -66,7 +65,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { class DataPropagationTest { val testWorkspace = Workspace { context { - plugin(DataPropagationTestPlugin()) + plugin(DataPropagationTestPlugin) } data { repeat(100) { diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index e97f4be8..34bfd734 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -1,17 +1,23 @@ package hep.dataforge.workspace -import hep.dataforge.context.PluginTag -import hep.dataforge.context.logger +import hep.dataforge.context.* import hep.dataforge.data.* -import hep.dataforge.meta.boolean -import hep.dataforge.meta.builder -import hep.dataforge.meta.get -import hep.dataforge.meta.int +import hep.dataforge.meta.* import hep.dataforge.names.plus +import kotlin.reflect.KClass import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue +/** + * Make a fake-factory for a one single plugin. Useful for unique or test plugins + */ +public inline fun P.toFactory(): PluginFactory

= object : PluginFactory

{ + override fun invoke(meta: Meta, context: Context): P = this@toFactory + + override val tag: PluginTag = this@toFactory.tag + override val type: KClass = P::class +} class SimpleWorkspaceTest { val testPlugin = object : WorkspacePlugin() { @@ -23,11 +29,12 @@ class SimpleWorkspaceTest { } } } + val testPluginFactory = testPlugin.toFactory() val workspace = Workspace { context { - plugin(testPlugin) + plugin(testPluginFactory) } data { From 8b1d5eb69edf3e97d0987a5b470c9c75529da592 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 29 Dec 2020 22:19:11 +0300 Subject: [PATCH 31/59] Task and data input validation moved to JVM --- dataforge-context/api/dataforge-context.api | 4 - .../kotlin/hep/dataforge/context/Plugin.kt | 2 +- .../hep/dataforge/context/PluginFactory.kt | 2 +- .../kotlin/hep/dataforge/provider/dfType.kt | 1 + dataforge-data/api/dataforge-data.api | 89 +++++---- .../kotlin/hep/dataforge/data/Data.kt | 48 ++--- .../kotlin/hep/dataforge/data/DataNode.kt | 176 ++---------------- .../hep/dataforge/data/DataTreeBuilder.kt | 164 ++++++++++++++++ .../kotlin/hep/dataforge/data/Goal.kt | 49 ++--- .../kotlin/hep/dataforge/data/MapAction.kt | 47 +++-- .../kotlin/hep/dataforge/data/ReduceAction.kt | 30 ++- .../kotlin/hep/dataforge/data/SplitAction.kt | 25 +-- .../kotlin/hep/dataforge/data/dataCast.kt | 73 -------- .../kotlin/hep/dataforge/data/dataJS.kt | 14 -- .../kotlin/hep/dataforge/data/dataJVM.kt | 70 ++++++- .../kotlin/hep/dataforge/data/dataNative.kt | 14 -- .../kotlin/hep/dataforge/io/EnvelopeFormat.kt | 2 +- .../kotlin/hep/dataforge/io/IOFormat.kt | 2 +- .../kotlin/hep/dataforge/io/MetaFormat.kt | 2 +- dataforge-meta/api/dataforge-meta.api | 4 + .../kotlin/hep/dataforge/type}/Type.kt | 2 +- .../api/dataforge-workspace.api | 4 +- .../kotlin/hep/dataforge/workspace/Task.kt | 2 +- .../hep/dataforge/workspace/Workspace.kt | 9 +- .../hep/dataforge/workspace/TaskBuilder.kt | 64 +++---- .../dataforge/workspace/WorkspaceBuilder.kt | 9 +- .../dataforge/workspace/WorkspacePlugin.kt | 0 27 files changed, 429 insertions(+), 479 deletions(-) create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt delete mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt delete mode 100644 dataforge-data/src/jsMain/kotlin/hep/dataforge/data/dataJS.kt delete mode 100644 dataforge-data/src/nativeMain/kotlin/hep/dataforge/data/dataNative.kt rename {dataforge-context/src/commonMain/kotlin/hep/dataforge/provider => dataforge-meta/src/commonMain/kotlin/hep/dataforge/type}/Type.kt (90%) rename dataforge-workspace/src/{commonMain => jvmMain}/kotlin/hep/dataforge/workspace/TaskBuilder.kt (81%) rename dataforge-workspace/src/{commonMain => jvmMain}/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt (93%) rename dataforge-workspace/src/{commonMain => jvmMain}/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt (100%) diff --git a/dataforge-context/api/dataforge-context.api b/dataforge-context/api/dataforge-context.api index b8c52285..1831f5ce 100644 --- a/dataforge-context/api/dataforge-context.api +++ b/dataforge-context/api/dataforge-context.api @@ -326,7 +326,3 @@ public final class hep/dataforge/provider/ProviderKt { public static final fun top (Lhep/dataforge/provider/Provider;Ljava/lang/String;Lkotlin/reflect/KClass;)Ljava/util/Map; } -public abstract interface annotation class hep/dataforge/provider/Type : java/lang/annotation/Annotation { - public abstract fun id ()Ljava/lang/String; -} - diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt index ff0e3a0c..146f0e79 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt @@ -6,7 +6,7 @@ import hep.dataforge.meta.MetaRepr import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.provider.Provider -import hep.dataforge.provider.Type +import hep.dataforge.type.Type /** * The interface to define a Context plugin. A plugin stores all runtime features of a context. diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt index 5985a1e7..f5540b8c 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt @@ -1,6 +1,6 @@ package hep.dataforge.context -import hep.dataforge.provider.Type +import hep.dataforge.type.Type import kotlin.reflect.KClass @Type(PluginFactory.TYPE) diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt index 9c08d6a8..b7b31b5e 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt @@ -4,6 +4,7 @@ import hep.dataforge.context.Context import hep.dataforge.context.gather import hep.dataforge.meta.DFExperimental import hep.dataforge.names.Name +import hep.dataforge.type.Type import kotlin.reflect.KClass import kotlin.reflect.full.findAnnotation diff --git a/dataforge-data/api/dataforge-data.api b/dataforge-data/api/dataforge-data.api index 06e99242..7b82acc4 100644 --- a/dataforge-data/api/dataforge-data.api +++ b/dataforge-data/api/dataforge-data.api @@ -26,6 +26,24 @@ public final class hep/dataforge/data/ActionKt { public static final fun then (Lhep/dataforge/data/Action;Lhep/dataforge/data/Action;)Lhep/dataforge/data/Action; } +public final class hep/dataforge/data/ComputationData : hep/dataforge/data/ComputationGoal, hep/dataforge/data/Data { + public fun (Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;)V + public synthetic fun (Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getMeta ()Lhep/dataforge/meta/Meta; + public fun getType ()Lkotlin/reflect/KClass; + public fun toMeta ()Lhep/dataforge/meta/Meta; +} + +public class hep/dataforge/data/ComputationGoal : hep/dataforge/data/Goal { + public fun (Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;)V + public synthetic fun (Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getBlock ()Lkotlin/jvm/functions/Function2; + public fun getDependencies ()Ljava/util/Collection; + public final fun getResult ()Lkotlinx/coroutines/Deferred; + public fun reset ()V + public fun startAsync (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Deferred; +} + public final class hep/dataforge/data/CoroutineMonitor : kotlin/coroutines/CoroutineContext$Element { public static final field Companion Lhep/dataforge/data/CoroutineMonitor$Companion; public fun ()V @@ -79,14 +97,6 @@ public final class hep/dataforge/data/Data$DefaultImpls { public static fun toMeta (Lhep/dataforge/data/Data;)Lhep/dataforge/meta/Meta; } -public final class hep/dataforge/data/DataCastKt { - public static final fun canCast (Lhep/dataforge/data/DataItem;Lkotlin/reflect/KClass;)Z - public static final fun cast (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;)Lhep/dataforge/data/Data; - public static final fun cast (Lhep/dataforge/data/DataNode;Lkotlin/reflect/KClass;)Lhep/dataforge/data/DataNode; - public static final fun ensureType (Lhep/dataforge/data/DataNode;Lkotlin/reflect/KClass;)V - public static final fun upcast (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;)Lhep/dataforge/data/Data; -} - public final class hep/dataforge/data/DataFilter : hep/dataforge/meta/Scheme { public static final field Companion Lhep/dataforge/data/DataFilter$Companion; public fun ()V @@ -129,17 +139,22 @@ public final class hep/dataforge/data/DataItem$Node : hep/dataforge/data/DataIte } public final class hep/dataforge/data/DataJVMKt { + public static final fun canCast (Lhep/dataforge/data/DataItem;Lkotlin/reflect/KClass;)Z + public static final fun cast (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;)Lhep/dataforge/data/Data; + public static final fun cast (Lhep/dataforge/data/DataNode;Lkotlin/reflect/KClass;)Lhep/dataforge/data/DataNode; + public static final fun ensureType (Lhep/dataforge/data/DataNode;Lkotlin/reflect/KClass;)V public static final fun filterIsInstance (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;)Lhep/dataforge/data/Data; public static final fun filterIsInstance (Lhep/dataforge/data/DataItem;Lkotlin/reflect/KClass;)Lhep/dataforge/data/DataItem; public static final fun filterIsInstance (Lhep/dataforge/data/DataNode;Lkotlin/reflect/KClass;)Lhep/dataforge/data/DataNode; public static final fun get (Lhep/dataforge/data/Data;)Ljava/lang/Object; + public static final fun upcast (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;)Lhep/dataforge/data/Data; } public final class hep/dataforge/data/DataKt { public static final fun map (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/data/Data; public static synthetic fun map$default (Lhep/dataforge/data/Data;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/data/Data; - public static final fun reduce (Ljava/util/Map;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/data/DynamicData; - public static synthetic fun reduce$default (Ljava/util/Map;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/data/DynamicData; + public static final fun reduce (Ljava/util/Map;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;)Lhep/dataforge/data/ComputationData; + public static synthetic fun reduce$default (Ljava/util/Map;Lkotlin/reflect/KClass;Lkotlin/coroutines/CoroutineContext;Lhep/dataforge/meta/Meta;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lhep/dataforge/data/ComputationData; } public abstract interface class hep/dataforge/data/DataNode : hep/dataforge/meta/MetaRepr { @@ -164,27 +179,16 @@ public final class hep/dataforge/data/DataNode$DefaultImpls { } public final class hep/dataforge/data/DataNodeKt { - public static final fun asSequence (Lhep/dataforge/data/DataNode;)Lkotlin/sequences/Sequence; - public static final fun builder (Lhep/dataforge/data/DataNode;)Lhep/dataforge/data/DataTreeBuilder; public static final fun dataSequence (Lhep/dataforge/data/DataNode;)Lkotlin/sequences/Sequence; - public static final fun datum (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Lhep/dataforge/data/Data;)V - public static final fun datum (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Lhep/dataforge/data/Data;)V public static final fun filter (Lhep/dataforge/data/DataNode;Lkotlin/jvm/functions/Function2;)Lhep/dataforge/data/DataNode; public static final fun first (Lhep/dataforge/data/DataNode;)Lhep/dataforge/data/Data; public static final fun get (Lhep/dataforge/data/DataNode;Lhep/dataforge/names/Name;)Lhep/dataforge/data/DataItem; public static final fun get (Lhep/dataforge/data/DataNode;Ljava/lang/String;)Lhep/dataforge/data/DataItem; public static final fun getData (Lhep/dataforge/data/DataItem;)Lhep/dataforge/data/Data; public static final fun getNode (Lhep/dataforge/data/DataItem;)Lhep/dataforge/data/DataNode; + public static final fun itemSequence (Lhep/dataforge/data/DataNode;)Lkotlin/sequences/Sequence; public static final fun iterator (Lhep/dataforge/data/DataNode;)Ljava/util/Iterator; public static final fun join (Lhep/dataforge/data/DataNode;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun node (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Lhep/dataforge/data/DataNode;)V - public static final fun node (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Lhep/dataforge/data/DataNode;)V - public static final fun static (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lhep/dataforge/meta/Meta;)V - public static final fun static (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V - public static final fun static (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun static$default (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)V - public static synthetic fun static$default (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V - public static synthetic fun static$default (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V } public final class hep/dataforge/data/DataTree : hep/dataforge/data/DataNode { @@ -214,6 +218,20 @@ public final class hep/dataforge/data/DataTreeBuilder { public final fun update (Lhep/dataforge/data/DataNode;)V } +public final class hep/dataforge/data/DataTreeBuilderKt { + public static final fun builder (Lhep/dataforge/data/DataNode;)Lhep/dataforge/data/DataTreeBuilder; + public static final fun datum (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Lhep/dataforge/data/Data;)V + public static final fun datum (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Lhep/dataforge/data/Data;)V + public static final fun node (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Lhep/dataforge/data/DataNode;)V + public static final fun node (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Lhep/dataforge/data/DataNode;)V + public static final fun static (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lhep/dataforge/meta/Meta;)V + public static final fun static (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V + public static final fun static (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V + public static synthetic fun static$default (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)V + public static synthetic fun static$default (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public static synthetic fun static$default (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V +} + public final class hep/dataforge/data/Dependencies : kotlin/coroutines/CoroutineContext$Element { public static final field Companion Lhep/dataforge/data/Dependencies$Companion; public fun (Ljava/util/Collection;)V @@ -228,24 +246,6 @@ public final class hep/dataforge/data/Dependencies : kotlin/coroutines/Coroutine public final class hep/dataforge/data/Dependencies$Companion : kotlin/coroutines/CoroutineContext$Key { } -public final class hep/dataforge/data/DynamicData : hep/dataforge/data/DynamicGoal, hep/dataforge/data/Data { - public fun (Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;)V - public synthetic fun (Lkotlin/reflect/KClass;Lhep/dataforge/meta/Meta;Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun getMeta ()Lhep/dataforge/meta/Meta; - public fun getType ()Lkotlin/reflect/KClass; - public fun toMeta ()Lhep/dataforge/meta/Meta; -} - -public class hep/dataforge/data/DynamicGoal : hep/dataforge/data/Goal { - public fun (Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;)V - public synthetic fun (Lkotlin/coroutines/CoroutineContext;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getBlock ()Lkotlin/jvm/functions/Function2; - public fun getDependencies ()Ljava/util/Collection; - public final fun getResult ()Lkotlinx/coroutines/Deferred; - public fun reset ()V - public fun startAsync (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Deferred; -} - public final class hep/dataforge/data/FragmentRule { public field result Lkotlin/jvm/functions/Function2; public fun (Lhep/dataforge/names/Name;Lhep/dataforge/meta/MetaBuilder;)V @@ -302,8 +302,7 @@ public final class hep/dataforge/data/JoinGroup { } public final class hep/dataforge/data/MapAction : hep/dataforge/data/Action { - public fun (Lkotlin/reflect/KClass;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V - public final fun getInputType ()Lkotlin/reflect/KClass; + public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V public final fun getOutputType ()Lkotlin/reflect/KClass; public fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode; public fun isTerminal ()Z @@ -335,8 +334,7 @@ public final class hep/dataforge/data/NamedData : hep/dataforge/data/Data { } public final class hep/dataforge/data/ReduceAction : hep/dataforge/data/Action { - public fun (Lkotlin/reflect/KClass;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V - public final fun getInputType ()Lkotlin/reflect/KClass; + public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V public final fun getOutputType ()Lkotlin/reflect/KClass; public fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode; public fun isTerminal ()Z @@ -357,8 +355,7 @@ public final class hep/dataforge/data/ReduceGroupBuilder { } public final class hep/dataforge/data/SplitAction : hep/dataforge/data/Action { - public fun (Lkotlin/reflect/KClass;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V - public final fun getInputType ()Lkotlin/reflect/KClass; + public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V public final fun getOutputType ()Lkotlin/reflect/KClass; public fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode; public fun isTerminal ()Z diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index c573aad8..43bbc5f2 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -3,6 +3,7 @@ package hep.dataforge.data import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.isEmpty +import hep.dataforge.type.Type import kotlinx.coroutines.CoroutineScope import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -11,7 +12,8 @@ import kotlin.reflect.KClass /** * A data element characterized by its meta */ -public interface Data : Goal, MetaRepr{ +@Type(Data.TYPE) +public interface Data : Goal, MetaRepr { /** * Type marker for the data. The type is known before the calculation takes place so it could be checked. */ @@ -22,9 +24,9 @@ public interface Data : Goal, MetaRepr{ */ public val meta: Meta - override fun toMeta(): Meta = Meta { - "type" put (type.simpleName?:"undefined") - if(!meta.isEmpty()) { + override fun toMeta(): Meta = Meta { + "type" put (type.simpleName ?: "undefined") + if (!meta.isEmpty()) { "meta" put meta } } @@ -37,14 +39,14 @@ public interface Data : Goal, MetaRepr{ meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), - block: suspend CoroutineScope.() -> T - ): Data = DynamicData(type, meta, context, dependencies, block) + block: suspend CoroutineScope.() -> T, + ): Data = ComputationData(type, meta, context, dependencies, block) public inline operator fun invoke( meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), - noinline block: suspend CoroutineScope.() -> T + noinline block: suspend CoroutineScope.() -> T, ): Data = invoke(T::class, meta, context, dependencies, block) public operator fun invoke( @@ -53,7 +55,7 @@ public interface Data : Goal, MetaRepr{ meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), - block: suspend CoroutineScope.() -> T + block: suspend CoroutineScope.() -> T, ): Data = NamedData(name, invoke(type, meta, context, dependencies, block)) public inline operator fun invoke( @@ -61,7 +63,7 @@ public interface Data : Goal, MetaRepr{ meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), - noinline block: suspend CoroutineScope.() -> T + noinline block: suspend CoroutineScope.() -> T, ): Data = invoke(name, T::class, meta, context, dependencies, block) @@ -71,17 +73,17 @@ public interface Data : Goal, MetaRepr{ } -public class DynamicData( +public class ComputationData( override val type: KClass, override val meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), - block: suspend CoroutineScope.() -> T -) : Data, DynamicGoal(context, dependencies, block) + block: suspend CoroutineScope.() -> T, +) : Data, ComputationGoal(context, dependencies, block) public class StaticData( value: T, - override val meta: Meta = Meta.EMPTY + override val meta: Meta = Meta.EMPTY, ) : Data, StaticGoal(value) { override val type: KClass get() = value::class } @@ -92,8 +94,8 @@ public fun Data.map( outputType: KClass, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, - block: suspend CoroutineScope.(T) -> R -): Data = DynamicData(outputType, meta, coroutineContext, listOf(this)) { + block: suspend CoroutineScope.(T) -> R, +): Data = ComputationData(outputType, meta, coroutineContext, listOf(this)) { block(await()) } @@ -104,8 +106,8 @@ public fun Data.map( public inline fun Data.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, - noinline block: suspend CoroutineScope.(T) -> R -): Data = DynamicData(R::class, meta, coroutineContext, listOf(this)) { + noinline block: suspend CoroutineScope.(T) -> R, +): Data = ComputationData(R::class, meta, coroutineContext, listOf(this)) { block(await()) } @@ -115,8 +117,8 @@ public inline fun Data.map( public inline fun Collection>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta, - noinline block: suspend CoroutineScope.(Collection) -> R -): Data = DynamicData( + noinline block: suspend CoroutineScope.(Collection) -> R, +): Data = ComputationData( R::class, meta, coroutineContext, @@ -129,8 +131,8 @@ public fun Map>.reduce( outputType: KClass, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta, - block: suspend CoroutineScope.(Map) -> R -): DynamicData = DynamicData( + block: suspend CoroutineScope.(Map) -> R, +): ComputationData = ComputationData( outputType, meta, coroutineContext, @@ -149,8 +151,8 @@ public fun Map>.reduce( public inline fun Map>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta, - noinline block: suspend CoroutineScope.(Map) -> R -): DynamicData = DynamicData( + noinline block: suspend CoroutineScope.(Map) -> R, +): ComputationData = ComputationData( R::class, meta, coroutineContext, diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt index 23fea9ef..e8625f65 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt @@ -2,13 +2,13 @@ package hep.dataforge.data import hep.dataforge.meta.* import hep.dataforge.names.* +import hep.dataforge.type.Type import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlin.collections.component1 import kotlin.collections.component2 -import kotlin.collections.set import kotlin.reflect.KClass public sealed class DataItem : MetaRepr { @@ -36,6 +36,7 @@ public sealed class DataItem : MetaRepr { /** * A tree-like data structure grouped into the node. All data inside the node must inherit its type */ +@Type(DataNode.TYPE) public interface DataNode : MetaRepr { /** @@ -49,6 +50,7 @@ public interface DataNode : MetaRepr { override fun toMeta(): Meta = Meta { "type" put (type.simpleName ?: "undefined") + "meta" put meta "items" put { this@DataNode.items.forEach { it.key.toString() put it.value.toMeta() @@ -64,7 +66,7 @@ public interface DataNode : MetaRepr { items.values.forEach { when (it) { is DataItem.Node<*> -> it.node.run { startAll() } - is DataItem.Leaf<*> -> it.data.run { startAsync() } + is DataItem.Leaf<*> -> it.data.run { startAsync(this@launch) } } } } @@ -98,11 +100,11 @@ public operator fun DataNode.get(name: String): DataItem? = get( /** * Sequence of all children including nodes */ -public fun DataNode.asSequence(): Sequence>> = sequence { +public fun DataNode.itemSequence(): Sequence>> = sequence { items.forEach { (head, item) -> yield(head.asName() to item) if (item is DataItem.Node) { - val subSequence = item.node.asSequence() + val subSequence = item.node.itemSequence() .map { (name, data) -> (head.asName() + name) to data } yieldAll(subSequence) } @@ -125,166 +127,6 @@ public fun DataNode.dataSequence(): Sequence>> = } } -public operator fun DataNode.iterator(): Iterator>> = asSequence().iterator() - -public class DataTree internal constructor( - override val type: KClass, - override val items: Map>, - override val meta: Meta -) : DataNode - -private sealed class DataTreeBuilderItem { - class Node(val tree: DataTreeBuilder) : DataTreeBuilderItem() - class Leaf(val value: Data) : DataTreeBuilderItem() -} - -/** - * A builder for a DataTree. - */ -@DFBuilder -public class DataTreeBuilder(public val type: KClass) { - private val map = HashMap>() - - private var meta = MetaBuilder() - - public operator fun set(token: NameToken, node: DataTreeBuilder) { - if (map.containsKey(token)) error("Tree entry with name $token is not empty") - map[token] = DataTreeBuilderItem.Node(node) - } - - public operator fun set(token: NameToken, data: Data) { - if (map.containsKey(token)) error("Tree entry with name $token is not empty") - map[token] = DataTreeBuilderItem.Leaf(data) - } - - private fun buildNode(token: NameToken): DataTreeBuilder { - return if (!map.containsKey(token)) { - DataTreeBuilder(type).also { map[token] = DataTreeBuilderItem.Node(it) } - } else { - (map[token] as? DataTreeBuilderItem.Node ?: error("The node with name $token is occupied by leaf")).tree - } - } - - private fun buildNode(name: Name): DataTreeBuilder { - return when (name.length) { - 0 -> this - 1 -> buildNode(name.firstOrNull()!!) - else -> buildNode(name.firstOrNull()!!).buildNode(name.cutFirst()) - } - } - - public operator fun set(name: Name, data: Data) { - when (name.length) { - 0 -> error("Can't add data with empty name") - 1 -> set(name.firstOrNull()!!, data) - 2 -> buildNode(name.cutLast())[name.lastOrNull()!!] = data - } - } - - public operator fun set(name: Name, node: DataTreeBuilder) { - when (name.length) { - 0 -> error("Can't add data with empty name") - 1 -> set(name.firstOrNull()!!, node) - 2 -> buildNode(name.cutLast())[name.lastOrNull()!!] = node - } - } - - public operator fun set(name: Name, node: DataNode): Unit = set(name, node.builder()) - - public operator fun set(name: Name, item: DataItem): Unit = when (item) { - is DataItem.Node -> set(name, item.node.builder()) - is DataItem.Leaf -> set(name, item.data) - } - - /** - * Append data to node - */ - public infix fun String.put(data: Data): Unit = set(toName(), data) - - /** - * Append node - */ - public infix fun String.put(node: DataNode): Unit = set(toName(), node) - - public infix fun String.put(item: DataItem): Unit = set(toName(), item) - - /** - * Build and append node - */ - public infix fun String.put(block: DataTreeBuilder.() -> Unit): Unit = set(toName(), DataTreeBuilder(type).apply(block)) - - - /** - * Update data with given node data and meta with node meta. - */ - public fun update(node: DataNode) { - node.dataSequence().forEach { - //TODO check if the place is occupied - this[it.first] = it.second - } - meta.update(node.meta) - } - - public fun meta(block: MetaBuilder.() -> Unit): MetaBuilder = meta.apply(block) - - public fun meta(meta: Meta) { - this.meta = meta.builder() - } - - public fun build(): DataTree { - val resMap = map.mapValues { (_, value) -> - when (value) { - is DataTreeBuilderItem.Leaf -> DataItem.Leaf(value.value) - is DataTreeBuilderItem.Node -> DataItem.Node(value.tree.build()) - } - } - return DataTree(type, resMap, meta.seal()) - } -} - -public fun DataTreeBuilder.datum(name: Name, data: Data) { - this[name] = data -} - -public fun DataTreeBuilder.datum(name: String, data: Data) { - this[name.toName()] = data -} - -public fun DataTreeBuilder.static(name: Name, data: T, meta: Meta = Meta.EMPTY) { - this[name] = Data.static(data, meta) -} - -public fun DataTreeBuilder.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { - this[name] = Data.static(data, Meta(block)) -} - -public fun DataTreeBuilder.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { - this[name.toName()] = Data.static(data, Meta(block)) -} - -public fun DataTreeBuilder.node(name: Name, node: DataNode) { - this[name] = node -} - -public fun DataTreeBuilder.node(name: String, node: DataNode) { - this[name.toName()] = node -} - -public inline fun DataTreeBuilder.node(name: Name, noinline block: DataTreeBuilder.() -> Unit) { - this[name] = DataNode(T::class, block) -} - -public inline fun DataTreeBuilder.node(name: String, noinline block: DataTreeBuilder.() -> Unit) { - this[name.toName()] = DataNode(T::class, block) -} - -/** - * Generate a mutable builder from this node. Node content is not changed - */ -public fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder(type).apply { - dataSequence().forEach { (name, data) -> this[name] = data } -} - public fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataNode.invoke(type) { dataSequence().forEach { (name, data) -> if (predicate(name, data)) { @@ -293,4 +135,8 @@ public fun DataNode.filter(predicate: (Name, Data) -> Boolean): } } -public fun DataNode.first(): Data? = dataSequence().first().second \ No newline at end of file +public fun DataNode.first(): Data? = dataSequence().firstOrNull()?.second + + +public operator fun DataNode.iterator(): Iterator>> = itemSequence().iterator() + diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt new file mode 100644 index 00000000..238e862b --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt @@ -0,0 +1,164 @@ +package hep.dataforge.data + +import hep.dataforge.meta.* +import hep.dataforge.names.* +import kotlin.reflect.KClass + +public class DataTree internal constructor( + override val type: KClass, + override val items: Map>, + override val meta: Meta +) : DataNode + +private sealed class DataTreeBuilderItem { + class Node(val tree: DataTreeBuilder) : DataTreeBuilderItem() + class Leaf(val value: Data) : DataTreeBuilderItem() +} + +/** + * A builder for a DataTree. + */ +@DFBuilder +public class DataTreeBuilder(public val type: KClass) { + private val map = HashMap>() + + private var meta = MetaBuilder() + + public operator fun set(token: NameToken, node: DataTreeBuilder) { + if (map.containsKey(token)) error("Tree entry with name $token is not empty") + map[token] = DataTreeBuilderItem.Node(node) + } + + public operator fun set(token: NameToken, data: Data) { + if (map.containsKey(token)) error("Tree entry with name $token is not empty") + map[token] = DataTreeBuilderItem.Leaf(data) + } + + private fun buildNode(token: NameToken): DataTreeBuilder { + return if (!map.containsKey(token)) { + DataTreeBuilder(type).also { map[token] = DataTreeBuilderItem.Node(it) } + } else { + (map[token] as? DataTreeBuilderItem.Node ?: error("The node with name $token is occupied by leaf")).tree + } + } + + private fun buildNode(name: Name): DataTreeBuilder { + return when (name.length) { + 0 -> this + 1 -> buildNode(name.firstOrNull()!!) + else -> buildNode(name.firstOrNull()!!).buildNode(name.cutFirst()) + } + } + + public operator fun set(name: Name, data: Data) { + when (name.length) { + 0 -> error("Can't add data with empty name") + 1 -> set(name.firstOrNull()!!, data) + 2 -> buildNode(name.cutLast())[name.lastOrNull()!!] = data + } + } + + public operator fun set(name: Name, node: DataTreeBuilder) { + when (name.length) { + 0 -> error("Can't add data with empty name") + 1 -> set(name.firstOrNull()!!, node) + 2 -> buildNode(name.cutLast())[name.lastOrNull()!!] = node + } + } + + public operator fun set(name: Name, node: DataNode): Unit = set(name, node.builder()) + + public operator fun set(name: Name, item: DataItem): Unit = when (item) { + is DataItem.Node -> set(name, item.node.builder()) + is DataItem.Leaf -> set(name, item.data) + } + + /** + * Append data to node + */ + public infix fun String.put(data: Data): Unit = set(toName(), data) + + /** + * Append node + */ + public infix fun String.put(node: DataNode): Unit = set(toName(), node) + + public infix fun String.put(item: DataItem): Unit = set(toName(), item) + + /** + * Build and append node + */ + public infix fun String.put(block: DataTreeBuilder.() -> Unit): Unit = set(toName(), DataTreeBuilder(type).apply(block)) + + + /** + * Update data with given node data and meta with node meta. + */ + public fun update(node: DataNode) { + node.dataSequence().forEach { + //TODO check if the place is occupied + this[it.first] = it.second + } + meta.update(node.meta) + } + + public fun meta(block: MetaBuilder.() -> Unit): MetaBuilder = meta.apply(block) + + public fun meta(meta: Meta) { + this.meta = meta.builder() + } + + public fun build(): DataTree { + val resMap = map.mapValues { (_, value) -> + when (value) { + is DataTreeBuilderItem.Leaf -> DataItem.Leaf(value.value) + is DataTreeBuilderItem.Node -> DataItem.Node(value.tree.build()) + } + } + return DataTree(type, resMap, meta.seal()) + } +} + + +public fun DataTreeBuilder.datum(name: Name, data: Data) { + this[name] = data +} + +public fun DataTreeBuilder.datum(name: String, data: Data) { + this[name.toName()] = data +} + +public fun DataTreeBuilder.static(name: Name, data: T, meta: Meta = Meta.EMPTY) { + this[name] = Data.static(data, meta) +} + +public fun DataTreeBuilder.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { + this[name] = Data.static(data, Meta(block)) +} + +public fun DataTreeBuilder.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { + this[name.toName()] = Data.static(data, Meta(block)) +} + +public fun DataTreeBuilder.node(name: Name, node: DataNode) { + this[name] = node +} + +public fun DataTreeBuilder.node(name: String, node: DataNode) { + this[name.toName()] = node +} + +public inline fun DataTreeBuilder.node(name: Name, noinline block: DataTreeBuilder.() -> Unit) { + this[name] = DataNode(T::class, block) +} + +public inline fun DataTreeBuilder.node(name: String, noinline block: DataTreeBuilder.() -> Unit) { + this[name.toName()] = DataNode(T::class, block) +} + +/** + * Generate a mutable builder from this node. Node content is not changed + */ +public fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder(type).apply { + dataSequence().forEach { (name, data) -> this[name] = data } +} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt index e5050d22..00555a57 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt @@ -5,8 +5,12 @@ import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +/** + * Lazy computation result with its dependencies to allowing to stat computing dependencies ahead of time + */ public interface Goal { public val dependencies: Collection> + /** * Returns current running coroutine if the goal is started */ @@ -16,7 +20,7 @@ public interface Goal { * Get ongoing computation or start a new one. * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. */ - public fun CoroutineScope.startAsync(): Deferred + public fun startAsync(coroutineScope: CoroutineScope): Deferred /** * Reset the computation @@ -26,7 +30,7 @@ public interface Goal { public companion object } -public suspend fun Goal.await(): T = coroutineScope { startAsync().await() } +public suspend fun Goal.await(): T = coroutineScope { startAsync(this).await() } public val Goal<*>.isComplete: Boolean get() = result?.isCompleted ?: false @@ -34,17 +38,17 @@ public open class StaticGoal(public val value: T) : Goal { override val dependencies: Collection> get() = emptyList() override val result: Deferred = CompletableDeferred(value) - override fun CoroutineScope.startAsync(): Deferred = result + override fun startAsync(coroutineScope: CoroutineScope): Deferred = result override fun reset() { //doNothing } } -public open class DynamicGoal( +public open class ComputationGoal( private val coroutineContext: CoroutineContext = EmptyCoroutineContext, override val dependencies: Collection> = emptyList(), - public val block: suspend CoroutineScope.() -> T + public val block: suspend CoroutineScope.() -> T, ) : Goal { final override var result: Deferred? = null @@ -55,19 +59,20 @@ public open class DynamicGoal( * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. */ @DFExperimental - override fun CoroutineScope.startAsync(): Deferred { - val startedDependencies = this@DynamicGoal.dependencies.map { goal -> - goal.run { startAsync() } + override fun startAsync(coroutineScope: CoroutineScope): Deferred { + val startedDependencies = this.dependencies.map { goal -> + goal.run { startAsync(coroutineScope) } } - return result - ?: async(this@DynamicGoal.coroutineContext + CoroutineMonitor() + Dependencies(startedDependencies)) { - startedDependencies.forEach { deferred -> - deferred.invokeOnCompletion { error -> - if (error != null) cancel(CancellationException("Dependency $deferred failed with error: ${error.message}")) - } + return result ?: coroutineScope.async( + this.coroutineContext + CoroutineMonitor() + Dependencies(startedDependencies) + ) { + startedDependencies.forEach { deferred -> + deferred.invokeOnCompletion { error -> + if (error != null) this.cancel(CancellationException("Dependency $deferred failed with error: ${error.message}")) } - block() - }.also { result = it } + } + block() + }.also { result = it } } /** @@ -84,8 +89,8 @@ public open class DynamicGoal( */ public fun Goal.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.(T) -> R -): Goal = DynamicGoal(coroutineContext, listOf(this)) { + block: suspend CoroutineScope.(T) -> R, +): Goal = ComputationGoal(coroutineContext, listOf(this)) { block(await()) } @@ -94,8 +99,8 @@ public fun Goal.map( */ public fun Collection>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.(Collection) -> R -): Goal = DynamicGoal(coroutineContext, this) { + block: suspend CoroutineScope.(Collection) -> R, +): Goal = ComputationGoal(coroutineContext, this) { block(map { run { it.await() } }) } @@ -107,8 +112,8 @@ public fun Collection>.reduce( */ public fun Map>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.(Map) -> R -): Goal = DynamicGoal(coroutineContext, this.values) { + block: suspend CoroutineScope.(Map) -> R, +): Goal = ComputationGoal(coroutineContext, this.values) { block(mapValues { it.value.await() }) } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt index e5504530..c1ba2074 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt @@ -30,46 +30,41 @@ public class MapActionBuilder(public var name: Name, public var meta: Meta public class MapAction( - public val inputType: KClass, public val outputType: KClass, private val block: MapActionBuilder.() -> Unit ) : Action { - override fun invoke(node: DataNode, meta: Meta): DataNode { - node.ensureType(inputType) + override fun invoke(node: DataNode, meta: Meta): DataNode = DataNode(outputType) { + node.dataSequence().forEach { (name, data) -> + /* + * Creating a new environment for action using **old** name, old meta and task meta + */ + val env = ActionEnv(name, data.meta, meta) - return DataNode.invoke(outputType) { - node.dataSequence().forEach { (name, data) -> - /* - * Creating a new environment for action using **old** name, old meta and task meta - */ - val env = ActionEnv(name, data.meta, meta) + //applying transformation from builder + val builder = MapActionBuilder( + name, + data.meta.builder(), // using data meta + meta + ).apply(block) - //applying transformation from builder - val builder = MapActionBuilder( - name, - data.meta.builder(), // using data meta - meta - ).apply(block) + //getting new name + val newName = builder.name - //getting new name - val newName = builder.name + //getting new meta + val newMeta = builder.meta.seal() - //getting new meta - val newMeta = builder.meta.seal() - - val newData = data.map(outputType, meta = newMeta) { builder.result(env, it) } - //setting the data node - this[newName] = newData - } + val newData = data.map(outputType, meta = newMeta) { builder.result(env, it) } + //setting the data node + this[newName] = newData } } } -public inline fun DataNode.map( +public inline fun DataNode.map( meta: Meta, noinline action: MapActionBuilder.() -> Unit -): DataNode = MapAction(T::class, R::class, action).invoke(this, meta) +): DataNode = MapAction(R::class, action).invoke(this, meta) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt index a0227af9..d9d9dec2 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt @@ -72,35 +72,31 @@ public class ReduceGroupBuilder(public val actionMeta: Meta) { * The same rules as for KPipe */ public class ReduceAction( - public val inputType: KClass, public val outputType: KClass, private val action: ReduceGroupBuilder.() -> Unit ) : Action { - override fun invoke(node: DataNode, meta: Meta): DataNode { - node.ensureType(inputType) - return DataNode.invoke(outputType) { - ReduceGroupBuilder(meta).apply(action).buildGroups(node).forEach { group -> + override fun invoke(node: DataNode, meta: Meta): DataNode = DataNode(outputType) { + ReduceGroupBuilder(meta).apply(action).buildGroups(node).forEach { group -> - //val laminate = Laminate(group.meta, meta) + //val laminate = Laminate(group.meta, meta) - val dataMap = group.node.dataSequence().associate { it } + val dataMap = group.node.dataSequence().associate { it } - val groupName: String = group.name + val groupName: String = group.name - val groupMeta = group.meta + val groupMeta = group.meta - val env = ActionEnv(groupName.toName(), groupMeta, meta) + val env = ActionEnv(groupName.toName(), groupMeta, meta) - val res: DynamicData = dataMap.reduce( - outputType, - meta = groupMeta - ) { group.result.invoke(env, it) } - - set(env.name, res) - } + val res: ComputationData = dataMap.reduce( + outputType, + meta = groupMeta + ) { group.result.invoke(env, it) } + set(env.name, res) } + } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt index 731a9403..70fbec96 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt @@ -33,31 +33,26 @@ public class SplitBuilder(public val name: Name, public val me } public class SplitAction( - public val inputType: KClass, public val outputType: KClass, private val action: SplitBuilder.() -> Unit ) : Action { - override fun invoke(node: DataNode, meta: Meta): DataNode { - node.ensureType(inputType) + override fun invoke(node: DataNode, meta: Meta): DataNode = DataNode(outputType) { + node.dataSequence().forEach { (name, data) -> - return DataNode.invoke(outputType) { - node.dataSequence().forEach { (name, data) -> + val laminate = Laminate(data.meta, meta) - val laminate = Laminate(data.meta, meta) - - val split = SplitBuilder(name, data.meta).apply(action) + val split = SplitBuilder(name, data.meta).apply(action) - // apply individual fragment rules to result - split.fragments.forEach { (fragmentName, rule) -> - val env = FragmentRule(fragmentName, laminate.builder()) + // apply individual fragment rules to result + split.fragments.forEach { (fragmentName, rule) -> + val env = FragmentRule(fragmentName, laminate.builder()) - rule(env) + rule(env) - val res = data.map(outputType, meta = env.meta) { env.result(it) } - set(env.name, res) - } + val res = data.map(outputType, meta = env.meta) { env.result(it) } + set(env.name, res) } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt deleted file mode 100644 index 26563bc8..00000000 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt +++ /dev/null @@ -1,73 +0,0 @@ -package hep.dataforge.data - -import hep.dataforge.meta.Meta -import hep.dataforge.names.NameToken -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlin.reflect.KClass - -public fun Data.upcast(type: KClass): Data { - return object : Data by this { - override val type: KClass = type - } -} - -/** - * Safe upcast a [Data] to a supertype - */ -public inline fun Data.upcast(): Data = upcast(R::class) - -/** - * Check if node could be safely cast to given class - */ -internal expect fun DataNode<*>.canCast(type: KClass): Boolean - -/** - * Check if data could be safely cast to given class - */ -internal expect fun Data<*>.canCast(type: KClass): Boolean - -public fun DataItem<*>.canCast(type: KClass): Boolean = when (this) { - is DataItem.Node -> node.canCast(type) - is DataItem.Leaf -> data.canCast(type) -} - -/** - * Unsafe cast of data node - */ -@Suppress("UNCHECKED_CAST") -public fun Data<*>.cast(type: KClass): Data { - return object : Data { - override val meta: Meta get() = this@cast.meta - override val dependencies: Collection> get() = this@cast.dependencies - override val result: Deferred? get() = this@cast.result as Deferred - override fun CoroutineScope.startAsync(): Deferred = this@cast.run { startAsync() as Deferred } - override fun reset() = this@cast.reset() - override val type: KClass = type - } -} - -public inline fun Data<*>.cast(): Data = cast(R::class) - -@Suppress("UNCHECKED_CAST") -public fun DataNode<*>.cast(type: KClass): DataNode { - return object : DataNode { - override val meta: Meta get() = this@cast.meta - override val type: KClass = type - override val items: Map> get() = this@cast.items as Map> - } -} - -public inline fun DataNode<*>.cast(): DataNode = cast(R::class) - -/** - * Check that node is compatible with given type meaning that each element could be cast to the type - */ -public fun DataNode<*>.ensureType(type: KClass) { - if (!canCast(type)) { - error("$type expected, but $type received") - } -} - - -//expect fun DataNode.cast(type: KClass): DataNode \ No newline at end of file diff --git a/dataforge-data/src/jsMain/kotlin/hep/dataforge/data/dataJS.kt b/dataforge-data/src/jsMain/kotlin/hep/dataforge/data/dataJS.kt deleted file mode 100644 index 0257b85a..00000000 --- a/dataforge-data/src/jsMain/kotlin/hep/dataforge/data/dataJS.kt +++ /dev/null @@ -1,14 +0,0 @@ -package hep.dataforge.data - -import kotlin.reflect.KClass - -/** - * Check that node is compatible with given type meaning that each element could be cast to the type - */ -internal actual fun DataNode<*>.canCast(type: KClass): Boolean { - return this.type == type -} - -internal actual fun Data<*>.canCast(type: KClass): Boolean { - return this.type == type -} diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt index 6f758dae..877b6dbc 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt @@ -1,5 +1,9 @@ package hep.dataforge.data +import hep.dataforge.meta.Meta +import hep.dataforge.names.NameToken +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf @@ -12,12 +16,72 @@ public fun Data.get(): T = runBlocking { await() } /** * Check that node is compatible with given type meaning that each element could be cast to the type */ -internal actual fun DataNode<*>.canCast(type: KClass): Boolean = +internal fun DataNode<*>.canCast(type: KClass): Boolean = type.isSubclassOf(this.type) - -internal actual fun Data<*>.canCast(type: KClass): Boolean = +/** + * Check if data could be safely cast to given class + */ +internal fun Data<*>.canCast(type: KClass): Boolean = this.type.isSubclassOf(type) + +public fun Data.upcast(type: KClass): Data { + return object : Data by this { + override val type: KClass = type + } +} + +/** + * Safe upcast a [Data] to a supertype + */ +public inline fun Data.upcast(): Data = upcast(R::class) + +public fun DataItem<*>.canCast(type: KClass): Boolean = when (this) { + is DataItem.Node -> node.canCast(type) + is DataItem.Leaf -> data.canCast(type) +} + +/** + * Unsafe cast of data node + */ +@Suppress("UNCHECKED_CAST") +public fun Data<*>.cast(type: KClass): Data { + if(!canCast(type)) error("Can't cast ${this.type} to $type") + return object : Data { + override val meta: Meta get() = this@cast.meta + override val dependencies: Collection> get() = this@cast.dependencies + override val result: Deferred? get() = this@cast.result as Deferred? + override fun startAsync(coroutineScope: CoroutineScope): Deferred = this@cast.run { + startAsync(coroutineScope) as Deferred + } + + override fun reset() = this@cast.reset() + override val type: KClass = type + } +} + +public inline fun Data<*>.cast(): Data = cast(R::class) + +@Suppress("UNCHECKED_CAST") +public fun DataNode<*>.cast(type: KClass): DataNode { + return object : DataNode { + override val meta: Meta get() = this@cast.meta + override val type: KClass = type + override val items: Map> get() = this@cast.items as Map> + } +} + +public inline fun DataNode<*>.cast(): DataNode = cast(R::class) + +/** + * Check that node is compatible with given type meaning that each element could be cast to the type + */ +public fun DataNode<*>.ensureType(type: KClass) { + if (!canCast(type)) { + error("$type expected, but $type received") + } +} + /** * Cast the node to given type if the cast is possible or return null */ diff --git a/dataforge-data/src/nativeMain/kotlin/hep/dataforge/data/dataNative.kt b/dataforge-data/src/nativeMain/kotlin/hep/dataforge/data/dataNative.kt deleted file mode 100644 index fe770f88..00000000 --- a/dataforge-data/src/nativeMain/kotlin/hep/dataforge/data/dataNative.kt +++ /dev/null @@ -1,14 +0,0 @@ -package hep.dataforge.data - -import kotlin.reflect.KClass - -/** - * Check that node is compatible with given type meaning that each element could be cast to the type - */ -internal actual fun DataNode<*>.canCast(type: KClass): Boolean { - return this.type == type -} - -internal actual fun Data<*>.canCast(type: KClass): Boolean { - return this.type == type -} \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt index b02052c1..06cc9374 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt @@ -5,7 +5,7 @@ import hep.dataforge.io.EnvelopeFormatFactory.Companion.ENVELOPE_FORMAT_TYPE import hep.dataforge.meta.Meta import hep.dataforge.names.Name import hep.dataforge.names.asName -import hep.dataforge.provider.Type +import hep.dataforge.type.Type import kotlinx.io.Input import kotlinx.io.Output import kotlin.reflect.KClass diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt index aac83ae6..1fd845ea 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt @@ -10,7 +10,7 @@ import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.ValueItem import hep.dataforge.names.Name import hep.dataforge.names.asName -import hep.dataforge.provider.Type +import hep.dataforge.type.Type import hep.dataforge.values.Value import kotlinx.io.* import kotlinx.io.buffer.Buffer diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt index f3e26d2a..0fa51555 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt @@ -7,7 +7,7 @@ import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.plus -import hep.dataforge.provider.Type +import hep.dataforge.type.Type import kotlinx.io.ByteArrayInput import kotlinx.io.Input import kotlinx.io.Output diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 3e85187f..113bf7e1 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -812,6 +812,10 @@ public final class hep/dataforge/names/NameTokenKt { public static final fun withIndex (Lhep/dataforge/names/NameToken;Ljava/lang/String;)Lhep/dataforge/names/NameToken; } +public abstract interface annotation class hep/dataforge/type/Type : java/lang/annotation/Annotation { + public abstract fun id ()Ljava/lang/String; +} + public final class hep/dataforge/values/DoubleArrayValue : hep/dataforge/values/Value, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { public fun ([D)V public fun equals (Ljava/lang/Object;)Z diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Type.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/type/Type.kt similarity index 90% rename from dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Type.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/type/Type.kt index e07cfd10..34c2a113 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Type.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/type/Type.kt @@ -1,4 +1,4 @@ -package hep.dataforge.provider +package hep.dataforge.type /** * A text label for internal DataForge type classification. Alternative for mime container type. diff --git a/dataforge-workspace/api/dataforge-workspace.api b/dataforge-workspace/api/dataforge-workspace.api index c599d391..d510660b 100644 --- a/dataforge-workspace/api/dataforge-workspace.api +++ b/dataforge-workspace/api/dataforge-workspace.api @@ -210,8 +210,6 @@ public abstract interface class hep/dataforge/workspace/Workspace : hep/dataforg public final class hep/dataforge/workspace/Workspace$Companion { public static final field TYPE Ljava/lang/String; - public final fun invoke (Lhep/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/workspace/SimpleWorkspace; - public static synthetic fun invoke$default (Lhep/dataforge/workspace/Workspace$Companion;Lhep/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/workspace/SimpleWorkspace; } public final class hep/dataforge/workspace/Workspace$DefaultImpls { @@ -235,6 +233,8 @@ public abstract interface class hep/dataforge/workspace/WorkspaceBuilder { } public final class hep/dataforge/workspace/WorkspaceBuilderKt { + public static final fun Workspace (Lhep/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/workspace/Workspace; + public static synthetic fun Workspace$default (Lhep/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/workspace/Workspace; public static final fun context (Lhep/dataforge/workspace/WorkspaceBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V public static synthetic fun context$default (Lhep/dataforge/workspace/WorkspaceBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static final fun rawData (Lhep/dataforge/workspace/WorkspaceBuilder;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/data/DataNode; diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt index 6792adfa..9534c7ec 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt @@ -4,7 +4,7 @@ import hep.dataforge.context.Named import hep.dataforge.data.DataNode import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.Described -import hep.dataforge.provider.Type +import hep.dataforge.type.Type import hep.dataforge.workspace.Task.Companion.TYPE import kotlin.reflect.KClass diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt index b9aaad18..589dce0a 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt @@ -1,8 +1,6 @@ package hep.dataforge.workspace -import hep.dataforge.context.Context import hep.dataforge.context.ContextAware -import hep.dataforge.context.Global import hep.dataforge.data.Data import hep.dataforge.data.DataNode import hep.dataforge.data.dataSequence @@ -11,7 +9,7 @@ import hep.dataforge.meta.MetaBuilder import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.provider.Provider -import hep.dataforge.provider.Type +import hep.dataforge.type.Type @Type(Workspace.TYPE) @@ -52,11 +50,6 @@ public interface Workspace : ContextAware, Provider { public companion object { public const val TYPE: String = "workspace" - public operator fun invoke( - parent: Context = Global, - block: SimpleWorkspaceBuilder.() -> Unit, - ): SimpleWorkspace = - SimpleWorkspaceBuilder(parent).apply(block).build() } } diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt similarity index 81% rename from dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt rename to dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index f25faa97..3117473f 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -11,7 +11,6 @@ import hep.dataforge.meta.string import hep.dataforge.names.Name import hep.dataforge.names.isEmpty import hep.dataforge.names.toName -import kotlin.jvm.JvmName import kotlin.reflect.KClass @DFBuilder @@ -28,7 +27,7 @@ public class TaskBuilder(public val name: Name, public val type: KClass private inner class DataTransformation( val from: String = "", val to: String = "", - val transform: (Context, TaskModel, DataNode) -> DataNode + val transform: (Context, TaskModel, DataNode) -> DataNode, ) { operator fun invoke(workspace: Workspace, model: TaskModel, node: DataNode): DataNode? { val localData = if (from.isEmpty()) { @@ -55,7 +54,7 @@ public class TaskBuilder(public val name: Name, public val type: KClass public fun transform( from: String = "", to: String = "", - block: TaskEnv.(DataNode<*>) -> DataNode + block: TaskEnv.(DataNode<*>) -> DataNode, ) { dataTransforms += DataTransformation(from, to) { context, model, data -> val env = TaskEnv(Name.EMPTY, model.meta, context, data) @@ -67,7 +66,7 @@ public class TaskBuilder(public val name: Name, public val type: KClass inputType: KClass, from: String = "", to: String = "", - block: TaskEnv.(DataNode) -> DataNode + block: TaskEnv.(DataNode) -> DataNode, ) { dataTransforms += DataTransformation(from, to) { context, model, data -> data.ensureType(inputType) @@ -79,7 +78,7 @@ public class TaskBuilder(public val name: Name, public val type: KClass public inline fun transform( from: String = "", to: String = "", - noinline block: TaskEnv.(DataNode) -> DataNode + noinline block: TaskEnv.(DataNode) -> DataNode, ) { transform(T::class, from, to, block) } @@ -90,14 +89,19 @@ public class TaskBuilder(public val name: Name, public val type: KClass public inline fun action( from: String = "", to: String = "", - crossinline block: TaskEnv.() -> Action + crossinline block: TaskEnv.() -> Action, ) { transform(from, to) { data: DataNode -> block().invoke(data, meta) } } - public class TaskEnv(public val name: Name, public val meta: Meta, public val context: Context, public val data: DataNode) { + public class TaskEnv( + public val name: Name, + public val meta: Meta, + public val context: Context, + public val data: DataNode, + ) { public operator fun DirectTaskDependency.invoke(): DataNode = if (placement.isEmpty()) { data.cast(task.type) } else { @@ -112,14 +116,11 @@ public class TaskBuilder(public val name: Name, public val type: KClass public inline fun mapAction( from: String = "", to: String = "", - crossinline block: MapActionBuilder.(TaskEnv) -> Unit + crossinline block: MapActionBuilder.(TaskEnv) -> Unit, ) { action(from, to) { val env = this - MapAction( - inputType = T::class, - outputType = type - ) { + MapAction(type) { block(env) } } @@ -131,13 +132,10 @@ public class TaskBuilder(public val name: Name, public val type: KClass public inline fun map( from: String = "", to: String = "", - crossinline block: suspend TaskEnv.(T) -> R + crossinline block: suspend TaskEnv.(T) -> R, ) { action(from, to) { - MapAction( - inputType = T::class, - outputType = type - ) { + MapAction(type) { //TODO automatically append task meta result = { data -> block(data) @@ -152,14 +150,11 @@ public class TaskBuilder(public val name: Name, public val type: KClass public inline fun reduceByGroup( from: String = "", to: String = "", - crossinline block: ReduceGroupBuilder.(TaskEnv) -> Unit //TODO needs KEEP-176 + crossinline block: ReduceGroupBuilder.(TaskEnv) -> Unit, //TODO needs KEEP-176 ) { action(from, to) { val env = this - ReduceAction( - inputType = T::class, - outputType = type - ) { block(env) } + ReduceAction(type) { block(env) } } } @@ -169,20 +164,16 @@ public class TaskBuilder(public val name: Name, public val type: KClass public inline fun reduce( from: String = "", to: String = "", - crossinline block: suspend TaskEnv.(Map) -> R + crossinline block: suspend TaskEnv.(Map) -> R, ) { action(from, to) { - ReduceAction( - inputType = T::class, - outputType = type, - action = { - result( - actionMeta[TaskModel.MODEL_TARGET_KEY]?.string ?: "@anonymous" - ) { data -> - block(data) - } + ReduceAction(type) { + result( + actionMeta[TaskModel.MODEL_TARGET_KEY]?.string ?: "@anonymous" + ) { data -> + block(data) } - ) + } } } @@ -192,14 +183,11 @@ public class TaskBuilder(public val name: Name, public val type: KClass public inline fun split( from: String = "", to: String = "", - crossinline block: SplitBuilder.(TaskEnv) -> Unit //TODO needs KEEP-176 + crossinline block: SplitBuilder.(TaskEnv) -> Unit, //TODO needs KEEP-176 ) { action(from, to) { val env = this - SplitAction( - inputType = T::class, - outputType = type - ) { block(env) } + SplitAction(type) { block(this, env) } } } diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt similarity index 93% rename from dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt rename to dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt index b7f74079..1b2b0cdb 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -2,13 +2,13 @@ package hep.dataforge.workspace import hep.dataforge.context.Context import hep.dataforge.context.ContextBuilder +import hep.dataforge.context.Global import hep.dataforge.data.DataNode import hep.dataforge.data.DataTreeBuilder import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.isEmpty import hep.dataforge.names.toName -import kotlin.jvm.JvmName import kotlin.reflect.KClass @DFBuilder @@ -95,4 +95,9 @@ public class SimpleWorkspaceBuilder(override val parentContext: Context) : Works override fun build(): SimpleWorkspace { return SimpleWorkspace(context, data.build(), targets, tasks) } -} \ No newline at end of file +} + +public fun Workspace( + parent: Context = Global, + block: SimpleWorkspaceBuilder.() -> Unit, +): Workspace = SimpleWorkspaceBuilder(parent).apply(block).build() \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt similarity index 100% rename from dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt rename to dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt From 13c0d189bb5c644aad0ea1c3a653f5be9881e95f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 30 Dec 2020 09:56:49 +0300 Subject: [PATCH 32/59] Move DataNode builders --- build.gradle.kts | 4 ++ dataforge-data/api/dataforge-data.api | 10 +--- .../kotlin/hep/dataforge/data/DataNode.kt | 46 +++++++++---------- .../hep/dataforge/data/DataTreeBuilder.kt | 12 ++++- .../kotlin/hep/dataforge/data/MapAction.kt | 4 +- .../kotlin/hep/dataforge/data/ReduceAction.kt | 4 +- .../kotlin/hep/dataforge/data/SplitAction.kt | 4 +- .../hep/dataforge/data/DataTreeBuilderTest.kt | 4 +- .../hep/dataforge/workspace/Dependency.kt | 7 +-- .../dataforge/workspace/WorkspaceBuilder.kt | 1 - .../hep/dataforge/workspace/fileData.kt | 19 ++++---- .../workspace/DataPropagationTest.kt | 6 +-- .../hep/dataforge/workspace/FileDataTest.kt | 2 +- .../workspace/SimpleWorkspaceTest.kt | 4 +- 14 files changed, 68 insertions(+), 59 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 65e56e40..b13264c5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,6 +19,10 @@ allprojects { } } +apiValidation{ + validationDisabled = true +} + subprojects { apply(plugin = "ru.mipt.npm.publish") } diff --git a/dataforge-data/api/dataforge-data.api b/dataforge-data/api/dataforge-data.api index 7b82acc4..709a8038 100644 --- a/dataforge-data/api/dataforge-data.api +++ b/dataforge-data/api/dataforge-data.api @@ -163,18 +163,15 @@ public abstract interface class hep/dataforge/data/DataNode : hep/dataforge/meta public abstract fun getItems ()Ljava/util/Map; public abstract fun getMeta ()Lhep/dataforge/meta/Meta; public abstract fun getType ()Lkotlin/reflect/KClass; - public abstract fun startAll (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job; public abstract fun toMeta ()Lhep/dataforge/meta/Meta; } public final class hep/dataforge/data/DataNode$Companion { public static final field TYPE Ljava/lang/String; public final fun builder (Lkotlin/reflect/KClass;)Lhep/dataforge/data/DataTreeBuilder; - public final fun invoke (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/data/DataTree; } public final class hep/dataforge/data/DataNode$DefaultImpls { - public static fun startAll (Lhep/dataforge/data/DataNode;Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job; public static fun toMeta (Lhep/dataforge/data/DataNode;)Lhep/dataforge/meta/Meta; } @@ -189,13 +186,13 @@ public final class hep/dataforge/data/DataNodeKt { public static final fun itemSequence (Lhep/dataforge/data/DataNode;)Lkotlin/sequences/Sequence; public static final fun iterator (Lhep/dataforge/data/DataNode;)Ljava/util/Iterator; public static final fun join (Lhep/dataforge/data/DataNode;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun startAll (Lhep/dataforge/data/DataNode;Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job; } public final class hep/dataforge/data/DataTree : hep/dataforge/data/DataNode { public fun getItems ()Ljava/util/Map; public fun getMeta ()Lhep/dataforge/meta/Meta; public fun getType ()Lkotlin/reflect/KClass; - public fun startAll (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job; public fun toMeta ()Lhep/dataforge/meta/Meta; } @@ -219,6 +216,7 @@ public final class hep/dataforge/data/DataTreeBuilder { } public final class hep/dataforge/data/DataTreeBuilderKt { + public static final fun DataTree (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/data/DataTree; public static final fun builder (Lhep/dataforge/data/DataNode;)Lhep/dataforge/data/DataTreeBuilder; public static final fun datum (Lhep/dataforge/data/DataTreeBuilder;Lhep/dataforge/names/Name;Lhep/dataforge/data/Data;)V public static final fun datum (Lhep/dataforge/data/DataTreeBuilder;Ljava/lang/String;Lhep/dataforge/data/Data;)V @@ -303,7 +301,6 @@ public final class hep/dataforge/data/JoinGroup { public final class hep/dataforge/data/MapAction : hep/dataforge/data/Action { public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V - public final fun getOutputType ()Lkotlin/reflect/KClass; public fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode; public fun isTerminal ()Z } @@ -335,7 +332,6 @@ public final class hep/dataforge/data/NamedData : hep/dataforge/data/Data { public final class hep/dataforge/data/ReduceAction : hep/dataforge/data/Action { public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V - public final fun getOutputType ()Lkotlin/reflect/KClass; public fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode; public fun isTerminal ()Z } @@ -356,7 +352,6 @@ public final class hep/dataforge/data/ReduceGroupBuilder { public final class hep/dataforge/data/SplitAction : hep/dataforge/data/Action { public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V - public final fun getOutputType ()Lkotlin/reflect/KClass; public fun invoke (Lhep/dataforge/data/DataNode;Lhep/dataforge/meta/Meta;)Lhep/dataforge/data/DataNode; public fun isTerminal ()Z } @@ -391,7 +386,6 @@ public final class hep/dataforge/data/TypeFilteredDataNode : hep/dataforge/data/ public fun getMeta ()Lhep/dataforge/meta/Meta; public final fun getOrigin ()Lhep/dataforge/data/DataNode; public fun getType ()Lkotlin/reflect/KClass; - public fun startAll (Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job; public fun toMeta ()Lhep/dataforge/meta/Meta; } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt index e8625f65..a1e8be73 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt @@ -44,8 +44,14 @@ public interface DataNode : MetaRepr { */ public val type: KClass + /** + * Children items of this data node + */ public val items: Map> + /** + * Meta for this node + */ public val meta: Meta override fun toMeta(): Meta = Meta { @@ -58,39 +64,33 @@ public interface DataNode : MetaRepr { } } - /** - * Start computation for all goals in data node and return a job for the whole node - */ - @Suppress("DeferredResultUnused") - public fun CoroutineScope.startAll(): Job = launch { - items.values.forEach { - when (it) { - is DataItem.Node<*> -> it.node.run { startAll() } - is DataItem.Leaf<*> -> it.data.run { startAsync(this@launch) } - } - } - } - public companion object { public const val TYPE: String = "dataNode" - public operator fun invoke(type: KClass, block: DataTreeBuilder.() -> Unit): DataTree = - DataTreeBuilder(type).apply(block).build() - - public inline operator fun invoke(noinline block: DataTreeBuilder.() -> Unit): DataTree = - DataTreeBuilder(T::class).apply(block).build() - public fun builder(type: KClass): DataTreeBuilder = DataTreeBuilder(type) } } -public suspend fun DataNode.join(): Unit = coroutineScope { startAll().join() } +/** + * Start computation for all goals in data node and return a job for the whole node + */ +@Suppress("DeferredResultUnused") +public fun DataNode.startAll(coroutineScope: CoroutineScope): Job = coroutineScope.launch { + items.values.forEach { + when (it) { + is DataItem.Node<*> -> it.node.run { startAll(this@launch) } + is DataItem.Leaf<*> -> it.data.run { this.startAsync(this@launch) } + } + } +} + +public suspend fun DataNode.join(): Unit = coroutineScope { startAll(this).join() } public val DataItem?.node: DataNode? get() = (this as? DataItem.Node)?.node public val DataItem?.data: Data? get() = (this as? DataItem.Leaf)?.data public operator fun DataNode.get(name: Name): DataItem? = when (name.length) { - 0 -> error("Empty name") + 0 -> DataItem.Node(this) 1 -> items[name.firstOrNull()] else -> get(name.firstOrNull()!!.asName()).node?.get(name.cutFirst()) } @@ -127,7 +127,8 @@ public fun DataNode.dataSequence(): Sequence>> = } } -public fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataNode.invoke(type) { +@DFExperimental +public fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataTree(type) { dataSequence().forEach { (name, data) -> if (predicate(name, data)) { this[name] = data @@ -137,6 +138,5 @@ public fun DataNode.filter(predicate: (Name, Data) -> Boolean): public fun DataNode.first(): Data? = dataSequence().firstOrNull()?.second - public operator fun DataNode.iterator(): Iterator>> = itemSequence().iterator() diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt index 238e862b..7529abe9 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt @@ -119,6 +119,14 @@ public class DataTreeBuilder(public val type: KClass) { } } +@Suppress("FunctionName") +public fun DataTree(type: KClass, block: DataTreeBuilder.() -> Unit): DataTree = + DataTreeBuilder(type).apply(block).build() + +@Suppress("FunctionName") +public inline fun DataTree(noinline block: DataTreeBuilder.() -> Unit): DataTree = + DataTreeBuilder(T::class).apply(block).build() + public fun DataTreeBuilder.datum(name: Name, data: Data) { this[name] = data @@ -149,11 +157,11 @@ public fun DataTreeBuilder.node(name: String, node: DataNode) { } public inline fun DataTreeBuilder.node(name: Name, noinline block: DataTreeBuilder.() -> Unit) { - this[name] = DataNode(T::class, block) + this[name] = DataTree(T::class, block) } public inline fun DataTreeBuilder.node(name: String, noinline block: DataTreeBuilder.() -> Unit) { - this[name.toName()] = DataNode(T::class, block) + this[name.toName()] = DataTree(T::class, block) } /** diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt index c1ba2074..05ba5056 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt @@ -30,11 +30,11 @@ public class MapActionBuilder(public var name: Name, public var meta: Meta public class MapAction( - public val outputType: KClass, + private val outputType: KClass, private val block: MapActionBuilder.() -> Unit ) : Action { - override fun invoke(node: DataNode, meta: Meta): DataNode = DataNode(outputType) { + override fun invoke(node: DataNode, meta: Meta): DataNode = DataTree(outputType) { node.dataSequence().forEach { (name, data) -> /* * Creating a new environment for action using **old** name, old meta and task meta diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt index d9d9dec2..d92a87cf 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt @@ -72,11 +72,11 @@ public class ReduceGroupBuilder(public val actionMeta: Meta) { * The same rules as for KPipe */ public class ReduceAction( - public val outputType: KClass, + private val outputType: KClass, private val action: ReduceGroupBuilder.() -> Unit ) : Action { - override fun invoke(node: DataNode, meta: Meta): DataNode = DataNode(outputType) { + override fun invoke(node: DataNode, meta: Meta): DataNode = DataTree(outputType) { ReduceGroupBuilder(meta).apply(action).buildGroups(node).forEach { group -> //val laminate = Laminate(group.meta, meta) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt index 70fbec96..a4390849 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt @@ -33,11 +33,11 @@ public class SplitBuilder(public val name: Name, public val me } public class SplitAction( - public val outputType: KClass, + private val outputType: KClass, private val action: SplitBuilder.() -> Unit ) : Action { - override fun invoke(node: DataNode, meta: Meta): DataNode = DataNode(outputType) { + override fun invoke(node: DataNode, meta: Meta): DataNode = DataTree(outputType) { node.dataSequence().forEach { (name, data) -> val laminate = Laminate(data.meta, meta) diff --git a/dataforge-data/src/commonTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/commonTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt index 32bf1760..ca7a1d8f 100644 --- a/dataforge-data/src/commonTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt +++ b/dataforge-data/src/commonTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt @@ -7,14 +7,14 @@ import kotlin.test.assertTrue internal class DataTreeBuilderTest{ @Test fun testDataUpdate(){ - val updateData = DataNode{ + val updateData = DataTree{ "update" put { "a" put Data.static("a") "b" put Data.static("b") } } - val node = DataNode{ + val node = DataTree{ node("primary"){ static("a","a") static("b","b") diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt index e58a988b..f6961866 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt @@ -2,6 +2,7 @@ package hep.dataforge.workspace import hep.dataforge.data.DataFilter import hep.dataforge.data.DataNode +import hep.dataforge.data.DataTree import hep.dataforge.data.filter import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder @@ -24,7 +25,7 @@ public class DataDependency(private val filter: DataFilter, private val placemen return if (placement.isEmpty()) { result } else { - DataNode.invoke(Any::class) { this[placement] = result } + DataTree(Any::class) { this[placement] = result } } } @@ -38,7 +39,7 @@ public class AllDataDependency(private val placement: Name = Name.EMPTY) : Depen override fun apply(workspace: Workspace): DataNode = if (placement.isEmpty()) { workspace.data } else { - DataNode.invoke(Any::class) { this[placement] = workspace.data } + DataTree(Any::class) { this[placement] = workspace.data } } override fun toMeta(): MetaBuilder = Meta { @@ -65,7 +66,7 @@ public abstract class TaskDependency( return if (placement.isEmpty()) { result } else { - DataNode(task.type) { this[placement] = result } + DataTree(task.type) { this[placement] = result } } } diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt index 1b2b0cdb..1289516f 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -22,7 +22,6 @@ public interface WorkspaceBuilder { public fun build(): Workspace } - /** * Set the context for future workspcace */ diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt index 8dfcc63d..2cf203d5 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt @@ -37,7 +37,7 @@ private fun newZFS(path: Path): FileSystem { public fun IOPlugin.readDataFile( path: Path, type: KClass, - formatResolver: FileFormatResolver + formatResolver: FileFormatResolver, ): Data { val envelope = readEnvelopeFile(path, true) ?: error("Can't read data from $path") val format = formatResolver(path, envelope.meta) @@ -57,7 +57,7 @@ public inline fun IOPlugin.readDataFile(path: Path): Data = public fun DataTreeBuilder.file( plugin: IOPlugin, path: Path, - formatResolver: FileFormatResolver + formatResolver: FileFormatResolver, ) { //If path is a single file or a special directory, read it as single datum if (!Files.isDirectory(path) || Files.list(path).allMatch { it.fileName.toString().startsWith("@") }) { @@ -85,7 +85,7 @@ public fun DataTreeBuilder.file( public fun IOPlugin.readDataDirectory( path: Path, type: KClass, - formatResolver: FileFormatResolver + formatResolver: FileFormatResolver, ): DataNode { //read zipped data node if (path.fileName != null && path.fileName.toString().endsWith(".zip")) { @@ -94,7 +94,7 @@ public fun IOPlugin.readDataDirectory( return readDataDirectory(fs.rootDirectories.first(), type, formatResolver) } if (!Files.isDirectory(path)) error("Provided path $path is not a directory") - return DataNode(type) { + return DataTree(type) { Files.list(path).forEach { path -> val fileName = path.fileName.toString() if (fileName.startsWith(IOPlugin.META_FILE_NAME)) { @@ -121,7 +121,7 @@ public suspend fun IOPlugin.writeDataDirectory( node: DataNode, format: IOFormat, envelopeFormat: EnvelopeFormat? = null, - metaFormat: MetaFormatFactory? = null + metaFormat: MetaFormatFactory? = null, ) { withContext(Dispatchers.IO) { if (!Files.exists(path)) { @@ -156,7 +156,7 @@ private suspend fun ZipOutputStream.writeNode( name: String, item: DataItem, dataFormat: IOFormat, - envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat + envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat, ) { withContext(Dispatchers.IO) { when (item) { @@ -187,7 +187,7 @@ suspend fun IOPlugin.writeZip( path: Path, node: DataNode, format: IOFormat, - envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat + envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat, ) { withContext(Dispatchers.IO) { val actualFile = if (path.toString().endsWith(".zip")) { @@ -195,7 +195,10 @@ suspend fun IOPlugin.writeZip( } else { path.resolveSibling(path.fileName.toString() + ".zip") } - val fos = Files.newOutputStream(actualFile, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING) + val fos = Files.newOutputStream(actualFile, + StandardOpenOption.WRITE, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING) val zos = ZipOutputStream(fos) zos.use { it.writeNode("", DataItem.Node(node), format, envelopeFormat) diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index 784a7441..6e0002ed 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -19,7 +19,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { allData() } transform { data -> - return@transform DataNode { + DataTree { val result = data.dataSequence().map { it.second.get() }.reduce { acc, pair -> acc + pair } set("result".asName(), Data { result }) } @@ -32,7 +32,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { data("myData\\[12\\]") } transform { data -> - return@transform DataNode { + DataTree { val result = data.dataSequence().map { it.second.get() }.reduce { acc, pair -> acc + pair } set("result".asName(), Data { result }) } @@ -44,7 +44,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { data(pattern = "myData.*") } transform { data -> - return@transform DataNode { + DataTree{ val result = data.dataSequence().map { it.second.get() }.reduce { acc, pair -> acc + pair } set("result".asName(), Data { result }) } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt index 7357c5c5..66646359 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt @@ -17,7 +17,7 @@ import kotlin.test.assertEquals class FileDataTest { - val dataNode = DataNode { + val dataNode = DataTree { node("dir") { static("a", "Some string") { "content" put "Some string" diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index 34bfd734..c894f6a0 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -74,10 +74,10 @@ class SimpleWorkspaceTest { val squareDep = dependsOn(square, placement = "square") val linearDep = dependsOn(linear, placement = "linear") } - transform { data -> + transform { data -> val squareNode = data["square"].node!!.cast()//squareDep() val linearNode = data["linear"].node!!.cast()//linearDep() - return@transform DataNode(Int::class) { + DataTree { squareNode.dataSequence().forEach { (name, _) -> val newData = Data { val squareValue = squareNode[name].data!!.get() From 9ed4245d84f7d76b7ce7f0290276a22bb91aeb6b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 10 Jan 2021 14:32:52 +0300 Subject: [PATCH 33/59] WIP full data refactor --- CHANGELOG.md | 1 + .../hep/dataforge/context/AbstractPlugin.kt | 9 +- .../kotlin/hep/dataforge/context/Context.kt | 8 +- .../kotlin/hep/dataforge/context/Plugin.kt | 5 +- .../hep/dataforge/context/PluginFactory.kt | 2 +- .../hep/dataforge/context/PluginManager.kt | 8 +- .../kotlin/hep/dataforge/context/logging.kt | 1 + .../kotlin/hep/dataforge/context/resolve.kt | 5 +- .../kotlin/hep/dataforge/provider/dfType.kt | 2 +- dataforge-data/build.gradle.kts | 2 +- .../kotlin/hep/dataforge/data/Action.kt | 22 +- .../hep/dataforge/data/CachingAction.kt | 48 +++++ .../kotlin/hep/dataforge/data/Data.kt | 98 +++++---- .../kotlin/hep/dataforge/data/DataFilter.kt | 53 ----- .../kotlin/hep/dataforge/data/DataNode.kt | 142 ------------- .../kotlin/hep/dataforge/data/DataSet.kt | 121 +++++++++++ .../hep/dataforge/data/DataSetBuilder.kt | 71 +++++++ .../kotlin/hep/dataforge/data/DataTree.kt | 106 ++++++++++ .../hep/dataforge/data/DataTreeBuilder.kt | 172 ---------------- .../kotlin/hep/dataforge/data/Goal.kt | 40 ++-- .../kotlin/hep/dataforge/data/GroupRule.kt | 56 +++--- .../kotlin/hep/dataforge/data/MapAction.kt | 45 +++-- .../hep/dataforge/data/MutableDataTree.kt | 159 +++++++++++++++ .../kotlin/hep/dataforge/data/ReduceAction.kt | 84 ++++---- .../kotlin/hep/dataforge/data/SplitAction.kt | 53 +++-- .../hep/dataforge/data/StaticDataTree.kt | 79 ++++++++ .../kotlin/hep/dataforge/data/dataSetMeta.kt | 20 ++ .../dataforge/data/TypeFilteredDataNode.kt | 27 --- .../kotlin/hep/dataforge/data/dataJVM.kt | 128 ++++-------- .../hep/dataforge/data/filterIsInstance.kt | 29 +++ .../hep/dataforge/data/DataTreeBuilderTest.kt | 18 +- .../hep/dataforge/io/yaml/YamlMetaFormat.kt | 6 +- .../hep/dataforge/io/BinaryMetaFormat.kt | 30 +-- .../kotlin/hep/dataforge/io/EnvelopeFormat.kt | 9 +- .../kotlin/hep/dataforge/io/IOFormat.kt | 30 +-- .../kotlin/hep/dataforge/io/JsonMetaFormat.kt | 4 + .../kotlin/hep/dataforge/io/MetaFormat.kt | 8 +- .../jvmMain/kotlin/hep/dataforge/io/fileIO.kt | 19 +- .../kotlin/hep/dataforge/meta/Config.kt | 8 +- .../kotlin/hep/dataforge/meta/ItemProvider.kt | 2 +- .../kotlin/hep/dataforge/meta/JsonMeta.kt | 16 +- .../kotlin/hep/dataforge/meta/Laminate.kt | 12 +- .../kotlin/hep/dataforge/meta/Meta.kt | 12 +- .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 6 +- .../kotlin/hep/dataforge/meta/MetaItem.kt | 35 ++-- .../hep/dataforge/meta/MetaSerializer.kt | 6 +- .../hep/dataforge/meta/MutableItemDelegate.kt | 2 +- .../hep/dataforge/meta/MutableItemProvider.kt | 2 +- .../kotlin/hep/dataforge/meta/MutableMeta.kt | 8 +- .../kotlin/hep/dataforge/meta/SealedMeta.kt | 4 +- .../meta/descriptors/DescriptorMeta.kt | 12 +- .../meta/descriptors/ItemDescriptor.kt | 2 +- .../kotlin/hep/dataforge/meta/mapMeta.kt | 12 +- .../meta/transformations/MetaConverter.kt | 62 +++--- .../kotlin/hep/dataforge/misc}/Named.kt | 2 +- .../hep/dataforge/{type => misc}/Type.kt | 2 +- .../kotlin/hep/dataforge/names/Name.kt | 17 +- .../kotlin/hep/dataforge/meta/DynamicMeta.kt | 18 +- .../hep/dataforge/workspace/DataPlacement.kt | 96 +++++++++ .../hep/dataforge/workspace/Dependency.kt | 80 +++----- .../hep/dataforge/workspace/GenericTask.kt | 12 +- .../dataforge/workspace/SimpleWorkspace.kt | 4 +- .../kotlin/hep/dataforge/workspace/Task.kt | 18 +- .../hep/dataforge/workspace/TaskModel.kt | 77 ++++--- .../hep/dataforge/workspace/Workspace.kt | 23 +-- .../hep/dataforge/workspace/envelopeData.kt | 8 +- .../hep/dataforge/workspace/TaskBuilder.kt | 188 ++++++++---------- .../dataforge/workspace/WorkspaceBuilder.kt | 35 ++-- .../hep/dataforge/workspace/fileData.kt | 79 ++++---- .../workspace/DataPropagationTest.kt | 32 +-- .../hep/dataforge/workspace/FileDataTest.kt | 17 +- .../workspace/SimpleWorkspaceTest.kt | 42 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 4 +- 74 files changed, 1516 insertions(+), 1161 deletions(-) create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt delete mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt delete mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt delete mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt delete mode 100644 dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/TypeFilteredDataNode.kt create mode 100644 dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/filterIsInstance.kt rename dataforge-data/src/{commonTest => jvmTest}/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt (55%) rename {dataforge-context/src/commonMain/kotlin/hep/dataforge/context => dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc}/Named.kt (97%) rename dataforge-meta/src/commonMain/kotlin/hep/dataforge/{type => misc}/Type.kt (91%) create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index a485b761..92ac8df8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - \[Major breaking change\] `MetaItem` renamed to `TypedMetaItem` and `MetaItem` is now an alias for `TypedMetaItem<*>` - \[Major breaking change\] Moved `NodeItem` and `ValueItem` to a top level - Plugins are removed from Context constructor and added lazily in ContextBuilder +- \[Major breaking change\] Full refactor of DataTree/DataSource ### Deprecated diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt index f64c1042..a7a310cb 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/AbstractPlugin.kt @@ -1,6 +1,7 @@ package hep.dataforge.context import hep.dataforge.meta.Meta +import hep.dataforge.misc.Named import hep.dataforge.names.Name import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KClass @@ -8,7 +9,7 @@ import kotlin.reflect.KProperty public abstract class AbstractPlugin(override val meta: Meta = Meta.EMPTY) : Plugin { private var _context: Context? = null - private val dependencies = ArrayList>() + private val dependencies = HashMap, Meta>() override val context: Context get() = _context ?: error("Plugin $tag is not attached") @@ -21,13 +22,13 @@ public abstract class AbstractPlugin(override val meta: Meta = Meta.EMPTY) : Plu this._context = null } - final override fun dependsOn(): List> = dependencies + final override fun dependsOn(): Map, Meta> = dependencies /** * Register plugin dependency and return a delegate which provides lazily initialized reference to dependent plugin */ - protected fun

require(factory: PluginFactory

): ReadOnlyProperty { - dependencies.add(factory) + protected fun

require(factory: PluginFactory

, meta: Meta = Meta.EMPTY): ReadOnlyProperty { + dependencies[factory] = meta return PluginDependencyDelegate(factory.type) } } diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index 3ac2f7a3..b701d3d6 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.Laminate import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.itemSequence +import hep.dataforge.misc.Named import hep.dataforge.names.Name import hep.dataforge.provider.Provider import kotlinx.coroutines.CoroutineScope @@ -21,7 +22,7 @@ import kotlin.coroutines.CoroutineContext * be overridden by plugin implementation. * */ -public open class Context( +public open class Context internal constructor( final override val name: Name, public val parent: Context?, meta: Meta, @@ -39,7 +40,7 @@ public open class Context( /** * A [PluginManager] for current context */ - public val plugins: PluginManager by lazy { PluginManager(this)} + public val plugins: PluginManager by lazy { PluginManager(this) } override val defaultTarget: String get() = Plugin.TARGET @@ -86,6 +87,9 @@ public open class Context( } } +public fun Context(name: String, parent: Context = Global, block: ContextBuilder.() -> Unit = {}): Context = + Global.context(name, parent, block) + /** * The interface for something that encapsulated in context * diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt index 146f0e79..3d4aea3d 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt @@ -3,10 +3,11 @@ package hep.dataforge.context import hep.dataforge.context.Plugin.Companion.TARGET import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr +import hep.dataforge.misc.Named +import hep.dataforge.misc.Type import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.provider.Provider -import hep.dataforge.type.Type /** * The interface to define a Context plugin. A plugin stores all runtime features of a context. @@ -37,7 +38,7 @@ public interface Plugin : Named, ContextAware, Provider, MetaRepr { * dependencies must be initialized and enabled in the Context before this * plugin is enabled. */ - public fun dependsOn(): Collection> + public fun dependsOn(): Map, Meta> /** * Start this plugin and attach registration info to the context. This method diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt index f5540b8c..18e40fed 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginFactory.kt @@ -1,6 +1,6 @@ package hep.dataforge.context -import hep.dataforge.type.Type +import hep.dataforge.misc.Type import kotlin.reflect.KClass @Type(PluginFactory.TYPE) diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt index 6ab0f5d9..4854b328 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt @@ -89,8 +89,8 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab if (get(plugin::class, plugin.tag, recursive = false) != null) { error("Plugin with tag ${plugin.tag} already exists in ${context.name}") } else { - for (tag in plugin.dependsOn()) { - fetch(tag, true) + for ((factory, meta) in plugin.dependsOn()) { + fetch(factory, meta, true) } logger.info { "Loading plugin ${plugin.name} into ${context.name}" } @@ -123,7 +123,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab /** * Get an existing plugin with given meta or load new one using provided factory */ - public fun fetch(factory: PluginFactory, recursive: Boolean = true, meta: Meta = Meta.EMPTY): T { + public fun fetch(factory: PluginFactory, meta: Meta = Meta.EMPTY, recursive: Boolean = true): T { val loaded = get(factory.type, factory.tag, recursive) return when { loaded == null -> load(factory(meta, context)) @@ -136,7 +136,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab factory: PluginFactory, recursive: Boolean = true, metaBuilder: MetaBuilder.() -> Unit, - ): T = fetch(factory, recursive, Meta(metaBuilder)) + ): T = fetch(factory, Meta(metaBuilder), recursive) override fun iterator(): Iterator = plugins.iterator() diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/logging.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/logging.kt index 87ad9dbe..c43f7ea2 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/logging.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/logging.kt @@ -1,5 +1,6 @@ package hep.dataforge.context +import hep.dataforge.misc.Named import hep.dataforge.provider.Path import mu.KLogger import mu.KotlinLogging diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt index 811220af..bfc4e22d 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt @@ -48,8 +48,9 @@ public fun Context.gather( putAll(top(target, type)) plugins.forEach { plugin -> plugin.top(target, type).forEach { (name, value) -> - if (containsKey(name)) error("Name conflict during gather. An item with name $name could not be gathered from $plugin because key is already present.") - put(plugin.name + name, value) + val itemName = plugin.name + name + if (containsKey(itemName)) error("Name conflict during gather. An item with name $name could not be gathered from $plugin because key is already present.") + put(itemName, value) } } if (inherit) { diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt index b7b31b5e..65817b64 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt @@ -3,8 +3,8 @@ package hep.dataforge.provider import hep.dataforge.context.Context import hep.dataforge.context.gather import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.Type import hep.dataforge.names.Name -import hep.dataforge.type.Type import kotlin.reflect.KClass import kotlin.reflect.full.findAnnotation diff --git a/dataforge-data/build.gradle.kts b/dataforge-data/build.gradle.kts index 436d9428..f28b10e5 100644 --- a/dataforge-data/build.gradle.kts +++ b/dataforge-data/build.gradle.kts @@ -20,4 +20,4 @@ kotlin { } } } -} \ No newline at end of file +} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt index d747587e..514efe08 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt @@ -1,21 +1,22 @@ package hep.dataforge.data import hep.dataforge.meta.Meta +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.* /** - * A simple data transformation on a data node + * A simple data transformation on a data node. Actions should avoid doing actual dependency evaluation in [run]. */ public interface Action { /** * Transform the data in the node, producing a new node. By default it is assumed that all calculations are lazy - * so not actual computation is started at this moment + * so not actual computation is started at this moment. + * + * [scope] context used to compute the initial result, also it is used for updates propagation */ - public operator fun invoke(node: DataNode, meta: Meta): DataNode + public suspend fun run(set: DataSet, meta: Meta, scope: CoroutineScope): DataSet - /** - * Terminal action is the one that could not be invoked lazily and requires some kind of blocking computation to invoke - */ - public val isTerminal: Boolean get() = false + public companion object } /** @@ -24,12 +25,9 @@ public interface Action { public infix fun Action.then(action: Action): Action { // TODO introduce composite action and add optimize by adding action to the list return object : Action { - override fun invoke(node: DataNode, meta: Meta): DataNode { - return action(this@then.invoke(node, meta), meta) + override suspend fun run(set: DataSet, meta: Meta, scope: CoroutineScope): DataSet { + return action.run(this@then.run(set, meta, scope), meta, scope) } - - override val isTerminal: Boolean - get() = this@then.isTerminal || action.isTerminal } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt new file mode 100644 index 00000000..52afc8bf --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt @@ -0,0 +1,48 @@ +package hep.dataforge.data + +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name +import hep.dataforge.names.startsWith +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlin.reflect.KType + +/** + * Remove all values with keys starting with [name] + */ +internal fun MutableMap.removeWhatStartsWith(name: Name) { + val toRemove = keys.filter { it.startsWith(name) } + toRemove.forEach(::remove) +} + +/** + * An action that caches results on-demand and recalculates them on source push + */ +public abstract class CachingAction( + public val outputType: KType, +) : Action { + + protected abstract fun CoroutineScope.transform( + set: DataSet, + meta: Meta, + key: Name = Name.EMPTY, + ): Flow> + + override suspend fun run( + set: DataSet, + meta: Meta, + scope: CoroutineScope, + ): DataSet = DataTree.dynamic(outputType, scope) { + collectFrom(scope.transform(set, meta)) + scope.let { + set.updates.collect { + //clear old nodes + remove(it) + //collect new items + collectFrom(scope.transform(set, meta, it)) + //FIXME if the target is data, updates are fired twice + } + } + } +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 43bbc5f2..259c69e3 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -3,8 +3,10 @@ package hep.dataforge.data import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.isEmpty -import hep.dataforge.type.Type -import kotlinx.coroutines.CoroutineScope +import hep.dataforge.misc.Named +import hep.dataforge.misc.Type +import hep.dataforge.names.Name +import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.reflect.KClass @@ -34,52 +36,34 @@ public interface Data : Goal, MetaRepr { public companion object { public const val TYPE: String = "data" - public operator fun invoke( - type: KClass, + public fun static( + value: T, meta: Meta = Meta.EMPTY, - context: CoroutineContext = EmptyCoroutineContext, - dependencies: Collection> = emptyList(), - block: suspend CoroutineScope.() -> T, - ): Data = ComputationData(type, meta, context, dependencies, block) + ): Data = StaticData(value, meta) - public inline operator fun invoke( - meta: Meta = Meta.EMPTY, - context: CoroutineContext = EmptyCoroutineContext, - dependencies: Collection> = emptyList(), - noinline block: suspend CoroutineScope.() -> T, - ): Data = invoke(T::class, meta, context, dependencies, block) - - public operator fun invoke( - name: String, - type: KClass, - meta: Meta = Meta.EMPTY, - context: CoroutineContext = EmptyCoroutineContext, - dependencies: Collection> = emptyList(), - block: suspend CoroutineScope.() -> T, - ): Data = NamedData(name, invoke(type, meta, context, dependencies, block)) - - public inline operator fun invoke( - name: String, - meta: Meta = Meta.EMPTY, - context: CoroutineContext = EmptyCoroutineContext, - dependencies: Collection> = emptyList(), - noinline block: suspend CoroutineScope.() -> T, - ): Data = - invoke(name, T::class, meta, context, dependencies, block) - - public fun static(value: T, meta: Meta = Meta.EMPTY): Data = - StaticData(value, meta) + /** + * An empty data containing only meta + */ + public fun empty(meta: Meta): Data = object : Data { + override val type: KClass = Nothing::class + override val meta: Meta = meta + override val dependencies: Collection> = emptyList() + override val deferred: Deferred get() = GlobalScope.async(start = CoroutineStart.LAZY) { + error("The Data is empty and could not be computed") + } + override fun async(coroutineScope: CoroutineScope): Deferred = deferred + override fun reset() {} + } } } - -public class ComputationData( +public class LazyData( override val type: KClass, override val meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), block: suspend CoroutineScope.() -> T, -) : Data, ComputationGoal(context, dependencies, block) +) : Data, LazyGoal(context, dependencies, block) public class StaticData( value: T, @@ -88,14 +72,40 @@ public class StaticData( override val type: KClass get() = value::class } -public class NamedData(public val name: String, data: Data) : Data by data +@Suppress("FunctionName") +public fun Data( + type: KClass, + meta: Meta = Meta.EMPTY, + context: CoroutineContext = EmptyCoroutineContext, + dependencies: Collection> = emptyList(), + block: suspend CoroutineScope.() -> T, +): Data = LazyData(type, meta, context, dependencies, block) + +@Suppress("FunctionName") +public inline fun Data( + meta: Meta = Meta.EMPTY, + context: CoroutineContext = EmptyCoroutineContext, + dependencies: Collection> = emptyList(), + noinline block: suspend CoroutineScope.() -> T, +): Data = Data(T::class, meta, context, dependencies, block) + +public class NamedData internal constructor( + override val name: Name, + public val data: Data, +) : Data by data, Named + +public fun Data.named(name: Name): NamedData = if (this is NamedData) { + NamedData(name, this.data) +} else { + NamedData(name, this) +} public fun Data.map( outputType: KClass, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, block: suspend CoroutineScope.(T) -> R, -): Data = ComputationData(outputType, meta, coroutineContext, listOf(this)) { +): Data = LazyData(outputType, meta, coroutineContext, listOf(this)) { block(await()) } @@ -107,7 +117,7 @@ public inline fun Data.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, noinline block: suspend CoroutineScope.(T) -> R, -): Data = ComputationData(R::class, meta, coroutineContext, listOf(this)) { +): Data = LazyData(R::class, meta, coroutineContext, listOf(this)) { block(await()) } @@ -118,7 +128,7 @@ public inline fun Collection>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta, noinline block: suspend CoroutineScope.(Collection) -> R, -): Data = ComputationData( +): Data = LazyData( R::class, meta, coroutineContext, @@ -132,7 +142,7 @@ public fun Map>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta, block: suspend CoroutineScope.(Map) -> R, -): ComputationData = ComputationData( +): LazyData = LazyData( outputType, meta, coroutineContext, @@ -152,7 +162,7 @@ public inline fun Map>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta, noinline block: suspend CoroutineScope.(Map) -> R, -): ComputationData = ComputationData( +): LazyData = LazyData( R::class, meta, coroutineContext, diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt deleted file mode 100644 index df19acd0..00000000 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt +++ /dev/null @@ -1,53 +0,0 @@ -package hep.dataforge.data - -import hep.dataforge.meta.* -import hep.dataforge.names.toName - - -public class DataFilter : Scheme() { - /** - * A source node for the filter - */ - public var from: String? by string() - /** - * A target placement for the filtered node - */ - public var to: String? by string() - /** - * A regular expression pattern for the filter - */ - public var pattern: String by string(".*") -// val prefix by string() -// val suffix by string() - - public companion object : SchemeSpec(::DataFilter) -} - -/** - * Apply meta-based filter to given data node - */ -public fun DataNode.filter(filter: DataFilter): DataNode { - val sourceNode = filter.from?.let { get(it.toName()).node } ?: this@filter - val regex = filter.pattern.toRegex() - val targetNode = DataTreeBuilder(type).apply { - sourceNode.dataSequence().forEach { (name, data) -> - if (name.toString().matches(regex)) { - this[name] = data - } - } - } - return filter.to?.let { - DataTreeBuilder(type).apply { this[it.toName()] = targetNode }.build() - } ?: targetNode.build() -} - -/** - * Filter data using [DataFilter] specification - */ -public fun DataNode.filter(filter: Meta): DataNode = filter(DataFilter.read(filter)) - -/** - * Filter data using [DataFilter] builder - */ -public fun DataNode.filter(filterBuilder: DataFilter.() -> Unit): DataNode = - filter(DataFilter(filterBuilder)) \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt deleted file mode 100644 index a1e8be73..00000000 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt +++ /dev/null @@ -1,142 +0,0 @@ -package hep.dataforge.data - -import hep.dataforge.meta.* -import hep.dataforge.names.* -import hep.dataforge.type.Type -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import kotlin.collections.component1 -import kotlin.collections.component2 -import kotlin.reflect.KClass - -public sealed class DataItem : MetaRepr { - public abstract val type: KClass - - public abstract val meta: Meta - - public class Node(public val node: DataNode) : DataItem() { - override val type: KClass get() = node.type - - override fun toMeta(): Meta = node.toMeta() - - override val meta: Meta get() = node.meta - } - - public class Leaf(public val data: Data) : DataItem() { - override val type: KClass get() = data.type - - override fun toMeta(): Meta = data.toMeta() - - override val meta: Meta get() = data.meta - } -} - -/** - * A tree-like data structure grouped into the node. All data inside the node must inherit its type - */ -@Type(DataNode.TYPE) -public interface DataNode : MetaRepr { - - /** - * The minimal common ancestor to all data in the node - */ - public val type: KClass - - /** - * Children items of this data node - */ - public val items: Map> - - /** - * Meta for this node - */ - public val meta: Meta - - override fun toMeta(): Meta = Meta { - "type" put (type.simpleName ?: "undefined") - "meta" put meta - "items" put { - this@DataNode.items.forEach { - it.key.toString() put it.value.toMeta() - } - } - } - - public companion object { - public const val TYPE: String = "dataNode" - - public fun builder(type: KClass): DataTreeBuilder = DataTreeBuilder(type) - } -} - -/** - * Start computation for all goals in data node and return a job for the whole node - */ -@Suppress("DeferredResultUnused") -public fun DataNode.startAll(coroutineScope: CoroutineScope): Job = coroutineScope.launch { - items.values.forEach { - when (it) { - is DataItem.Node<*> -> it.node.run { startAll(this@launch) } - is DataItem.Leaf<*> -> it.data.run { this.startAsync(this@launch) } - } - } -} - -public suspend fun DataNode.join(): Unit = coroutineScope { startAll(this).join() } - -public val DataItem?.node: DataNode? get() = (this as? DataItem.Node)?.node -public val DataItem?.data: Data? get() = (this as? DataItem.Leaf)?.data - -public operator fun DataNode.get(name: Name): DataItem? = when (name.length) { - 0 -> DataItem.Node(this) - 1 -> items[name.firstOrNull()] - else -> get(name.firstOrNull()!!.asName()).node?.get(name.cutFirst()) -} - -public operator fun DataNode.get(name: String): DataItem? = get(name.toName()) - -/** - * Sequence of all children including nodes - */ -public fun DataNode.itemSequence(): Sequence>> = sequence { - items.forEach { (head, item) -> - yield(head.asName() to item) - if (item is DataItem.Node) { - val subSequence = item.node.itemSequence() - .map { (name, data) -> (head.asName() + name) to data } - yieldAll(subSequence) - } - } -} - -/** - * Sequence of data entries - */ -public fun DataNode.dataSequence(): Sequence>> = sequence { - items.forEach { (head, item) -> - when (item) { - is DataItem.Leaf -> yield(head.asName() to item.data) - is DataItem.Node -> { - val subSequence = item.node.dataSequence() - .map { (name, data) -> (head.asName() + name) to data } - yieldAll(subSequence) - } - } - } -} - -@DFExperimental -public fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataTree(type) { - dataSequence().forEach { (name, data) -> - if (predicate(name, data)) { - this[name] = data - } - } -} - -public fun DataNode.first(): Data? = dataSequence().firstOrNull()?.second - -public operator fun DataNode.iterator(): Iterator>> = itemSequence().iterator() - diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt new file mode 100644 index 00000000..d14dec4d --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt @@ -0,0 +1,121 @@ +package hep.dataforge.data + +import hep.dataforge.meta.DFExperimental +import hep.dataforge.names.* +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.reflect.KClass + +public interface DataSet { + + /** + * The minimal common ancestor to all data in the node + */ + public val dataType: KClass + + /** + * Traverse this provider or its child. The order is not guaranteed. + * [root] points to a root name for traversal. If it is empty, traverse this source, if it points to a [Data], + * return flow, that contains single [Data], if it points to a node with children, return children. + */ + public fun flow(): Flow> + + /** + * Get data with given name. + */ + public suspend fun getData(name: Name): Data? + + /** + * Get a snapshot of names of children of given node. Empty if node does not exist or is a leaf. + * + * By default traverses the whole tree. Could be optimized in descendants + */ + public suspend fun listChildren(prefix: Name = Name.EMPTY): List = + flow().map { it.name }.filter { it.startsWith(prefix) && (it.length == prefix.length + 1) }.toList() + + /** + * A flow of updated item names. Updates are propagated in a form of [Flow] of names of updated nodes. + * Those can include new data items and replacement of existing ones. The replaced items could update existing data content + * and replace it completely, so they should be pulled again. + * + */ + public val updates: Flow + + public companion object { + public val META_KEY: Name = "@meta".asName() + } +} + +/** + * A stateless filtered [DataSet] + */ +@DFExperimental +public fun DataSet.filter( + predicate: suspend (Name, Data) -> Boolean, +): DataSet = object : DataSet { + override val dataType: KClass get() = this@filter.dataType + + override fun flow(): Flow> = + this@filter.flow().filter { predicate(it.name, it.data) } + + override suspend fun getData(name: Name): Data? = this@filter.getData(name)?.takeIf { + predicate(name, it) + } + + override val updates: Flow = this@filter.updates.filter flowFilter@{ name -> + val theData = this@filter.getData(name) ?: return@flowFilter false + predicate(name, theData) + } +} + +/** + * Flow all data nodes with names starting with [branchName] + */ +public fun DataSet.flowChildren(branchName: Name): Flow> = this@flowChildren.flow().filter { + it.name.startsWith(branchName) +} + +/** + * Get a subset of data starting with a given [branchName] + */ +public fun DataSet.branch(branchName: Name): DataSet = if (branchName.isEmpty()) this +else object : DataSet { + override val dataType: KClass get() = this@branch.dataType + + override fun flow(): Flow> = this@branch.flow().mapNotNull { + it.name.removeHeadOrNull(branchName)?.let { name -> + it.data.named(name) + } + } + + override suspend fun getData(name: Name): Data? = this@branch.getData(branchName + name) + + override val updates: Flow get() = this@branch.updates.mapNotNull { it.removeHeadOrNull(branchName) } +} + +/** + * Generate a wrapper data set with a given name prefix appended to all names + */ +public fun DataSet.withNamePrefix(prefix: Name): DataSet = if (prefix.isEmpty()) this +else object : DataSet { + override val dataType: KClass get() = this@withNamePrefix.dataType + + override fun flow(): Flow> = this@withNamePrefix.flow().map { it.data.named(prefix + it.name) } + + override suspend fun getData(name: Name): Data? = + name.removeHeadOrNull(name)?.let { this@withNamePrefix.getData(it) } + + override val updates: Flow get() = this@withNamePrefix.updates.map { prefix + it } + +} + +/** + * Start computation for all goals in data node and return a job for the whole node + */ +public fun DataSet.startAll(coroutineScope: CoroutineScope): Job = coroutineScope.launch { + flow().map { + it.launch(this@launch) + }.toList().joinAll() +} + +public suspend fun DataSet.join(): Unit = coroutineScope { startAll(this).join() } \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt new file mode 100644 index 00000000..2ddc3106 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt @@ -0,0 +1,71 @@ +package hep.dataforge.data + +import hep.dataforge.meta.DFExperimental +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import hep.dataforge.names.Name +import hep.dataforge.names.toName +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.collect + +public interface DataSetBuilder { + public fun remove(name: Name) + + public operator fun set(name: Name, data: Data?) + + public suspend fun set(name: Name, dataSet: DataSet) + + public operator fun set(name: Name, block: DataSetBuilder.() -> Unit) + + /** + * Append data to node + */ + public infix fun String.put(data: Data): Unit = set(toName(), data) + + /** + * Append node + */ + public suspend infix fun String.put(tree: DataSet): Unit = set(toName(), tree) + + /** + * Build and append node + */ + public infix fun String.put(block: DataSetBuilder.() -> Unit): Unit = set(toName(), block) + +} + + +public operator fun DataSetBuilder.set(name: String, data: Data) { + this@set[name.toName()] = data +} + +public fun DataSetBuilder.data(name: Name, data: T, meta: Meta = Meta.EMPTY) { + set(name, Data.static(data, meta)) +} + +public fun DataSetBuilder.data(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { + set(name, Data.static(data, Meta(block))) +} + +public fun DataSetBuilder.data(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { + set(name.toName(), Data.static(data, Meta(block))) +} + +public suspend fun DataSetBuilder.set(name: String, set: DataSet) { + this.set(name.toName(), set) +} + +public operator fun DataSetBuilder.set(name: String, block: DataSetBuilder.() -> Unit): Unit = + this@set.set(name.toName(), block) + + +/** + * Update data with given node data and meta with node meta. + */ +@DFExperimental +public suspend fun DataSetBuilder.update(tree: DataSet): Unit = coroutineScope{ + tree.flow().collect { + //TODO check if the place is occupied + set(it.name, it.data) + } +} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt new file mode 100644 index 00000000..0cb383b0 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt @@ -0,0 +1,106 @@ +package hep.dataforge.data + +import hep.dataforge.meta.* +import hep.dataforge.misc.Type +import hep.dataforge.names.* +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.reflect.KClass + +public sealed class DataTreeItem { + public class Node(public val tree: DataTree) : DataTreeItem() + public class Leaf(public val data: Data) : DataTreeItem() +} + +public val DataTreeItem.type: KClass + get() = when (this) { + is DataTreeItem.Node -> tree.dataType + is DataTreeItem.Leaf -> data.type + } + +/** + * A tree-like [DataSet] grouped into the node. All data inside the node must inherit its type + */ +@Type(DataTree.TYPE) +public interface DataTree : DataSet { + + /** + * Children items of this [DataTree] provided asynchronously + */ + public suspend fun items(): Map> + +// override fun flow(): Flow> = flow flowBuilder@{ +// val item = getItem(root) ?: return@flowBuilder +// when (item) { +// is DataTreeItem.Leaf -> emit(item.data.named(root)) +// is DataTreeItem.Node -> item.tree.items().forEach { (token, childItem: DataTreeItem) -> +// when (childItem) { +// is DataTreeItem.Leaf -> emit(childItem.data.named(root + token)) +// is DataTreeItem.Node -> emitAll(childItem.tree.flow().map { it.named(root + token + it.name) }) +// } +// } +// } +// } + + override fun flow(): Flow> = flow { + items().forEach { (token, childItem: DataTreeItem) -> + when (childItem) { + is DataTreeItem.Leaf -> emit(childItem.data.named(token.asName())) + is DataTreeItem.Node -> emitAll(childItem.tree.flow().map { it.named(token + it.name) }) + } + } + } + + override suspend fun listChildren(prefix: Name): List = + getItem(prefix).tree?.items()?.keys?.map { prefix + it } ?: emptyList() + + override suspend fun getData(name: Name): Data? = when (name.length) { + 0 -> null + 1 -> items()[name.firstOrNull()!!].data + else -> items()[name.firstOrNull()!!].tree?.getData(name.cutFirst()) + } + + public companion object { + public const val TYPE: String = "dataTree" + } +} + +/** + * Get a [DataTreeItem] with given [name] or null if the item does not exist + */ +public tailrec suspend fun DataTree.getItem(name: Name): DataTreeItem? = when (name.length) { + 0 -> DataTreeItem.Node(this) + 1 -> items()[name.firstOrNull()] + else -> items()[name.firstOrNull()!!].tree?.getItem(name.cutFirst()) +} + +public val DataTreeItem?.tree: DataTree? get() = (this as? DataTreeItem.Node)?.tree +public val DataTreeItem?.data: Data? get() = (this as? DataTreeItem.Leaf)?.data + +/** + * Flow of all children including nodes + */ +public fun DataTree.itemFlow(): Flow>> = flow { + items().forEach { (head, item) -> + emit(head.asName() to item) + if (item is DataTreeItem.Node) { + val subSequence = item.tree.itemFlow() + .map { (name, data) -> (head.asName() + name) to data } + emitAll(subSequence) + } + } +} + +/** + * Get a branch of this [DataTree] with a given [branchName]. + * The difference from similar method for [DataSet] is that internal logic is more simple and the return value is a [DataTree] + */ +public fun DataTree.branch(branchName: Name): DataTree = object : DataTree { + override val dataType: KClass get() = this@branch.dataType + + override val updates: Flow = this@branch.updates.mapNotNull { it.removeHeadOrNull(branchName) } + + override suspend fun items(): Map> = getItem(branchName).tree?.items() ?: emptyMap() +} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt deleted file mode 100644 index 7529abe9..00000000 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTreeBuilder.kt +++ /dev/null @@ -1,172 +0,0 @@ -package hep.dataforge.data - -import hep.dataforge.meta.* -import hep.dataforge.names.* -import kotlin.reflect.KClass - -public class DataTree internal constructor( - override val type: KClass, - override val items: Map>, - override val meta: Meta -) : DataNode - -private sealed class DataTreeBuilderItem { - class Node(val tree: DataTreeBuilder) : DataTreeBuilderItem() - class Leaf(val value: Data) : DataTreeBuilderItem() -} - -/** - * A builder for a DataTree. - */ -@DFBuilder -public class DataTreeBuilder(public val type: KClass) { - private val map = HashMap>() - - private var meta = MetaBuilder() - - public operator fun set(token: NameToken, node: DataTreeBuilder) { - if (map.containsKey(token)) error("Tree entry with name $token is not empty") - map[token] = DataTreeBuilderItem.Node(node) - } - - public operator fun set(token: NameToken, data: Data) { - if (map.containsKey(token)) error("Tree entry with name $token is not empty") - map[token] = DataTreeBuilderItem.Leaf(data) - } - - private fun buildNode(token: NameToken): DataTreeBuilder { - return if (!map.containsKey(token)) { - DataTreeBuilder(type).also { map[token] = DataTreeBuilderItem.Node(it) } - } else { - (map[token] as? DataTreeBuilderItem.Node ?: error("The node with name $token is occupied by leaf")).tree - } - } - - private fun buildNode(name: Name): DataTreeBuilder { - return when (name.length) { - 0 -> this - 1 -> buildNode(name.firstOrNull()!!) - else -> buildNode(name.firstOrNull()!!).buildNode(name.cutFirst()) - } - } - - public operator fun set(name: Name, data: Data) { - when (name.length) { - 0 -> error("Can't add data with empty name") - 1 -> set(name.firstOrNull()!!, data) - 2 -> buildNode(name.cutLast())[name.lastOrNull()!!] = data - } - } - - public operator fun set(name: Name, node: DataTreeBuilder) { - when (name.length) { - 0 -> error("Can't add data with empty name") - 1 -> set(name.firstOrNull()!!, node) - 2 -> buildNode(name.cutLast())[name.lastOrNull()!!] = node - } - } - - public operator fun set(name: Name, node: DataNode): Unit = set(name, node.builder()) - - public operator fun set(name: Name, item: DataItem): Unit = when (item) { - is DataItem.Node -> set(name, item.node.builder()) - is DataItem.Leaf -> set(name, item.data) - } - - /** - * Append data to node - */ - public infix fun String.put(data: Data): Unit = set(toName(), data) - - /** - * Append node - */ - public infix fun String.put(node: DataNode): Unit = set(toName(), node) - - public infix fun String.put(item: DataItem): Unit = set(toName(), item) - - /** - * Build and append node - */ - public infix fun String.put(block: DataTreeBuilder.() -> Unit): Unit = set(toName(), DataTreeBuilder(type).apply(block)) - - - /** - * Update data with given node data and meta with node meta. - */ - public fun update(node: DataNode) { - node.dataSequence().forEach { - //TODO check if the place is occupied - this[it.first] = it.second - } - meta.update(node.meta) - } - - public fun meta(block: MetaBuilder.() -> Unit): MetaBuilder = meta.apply(block) - - public fun meta(meta: Meta) { - this.meta = meta.builder() - } - - public fun build(): DataTree { - val resMap = map.mapValues { (_, value) -> - when (value) { - is DataTreeBuilderItem.Leaf -> DataItem.Leaf(value.value) - is DataTreeBuilderItem.Node -> DataItem.Node(value.tree.build()) - } - } - return DataTree(type, resMap, meta.seal()) - } -} - -@Suppress("FunctionName") -public fun DataTree(type: KClass, block: DataTreeBuilder.() -> Unit): DataTree = - DataTreeBuilder(type).apply(block).build() - -@Suppress("FunctionName") -public inline fun DataTree(noinline block: DataTreeBuilder.() -> Unit): DataTree = - DataTreeBuilder(T::class).apply(block).build() - - -public fun DataTreeBuilder.datum(name: Name, data: Data) { - this[name] = data -} - -public fun DataTreeBuilder.datum(name: String, data: Data) { - this[name.toName()] = data -} - -public fun DataTreeBuilder.static(name: Name, data: T, meta: Meta = Meta.EMPTY) { - this[name] = Data.static(data, meta) -} - -public fun DataTreeBuilder.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { - this[name] = Data.static(data, Meta(block)) -} - -public fun DataTreeBuilder.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { - this[name.toName()] = Data.static(data, Meta(block)) -} - -public fun DataTreeBuilder.node(name: Name, node: DataNode) { - this[name] = node -} - -public fun DataTreeBuilder.node(name: String, node: DataNode) { - this[name.toName()] = node -} - -public inline fun DataTreeBuilder.node(name: Name, noinline block: DataTreeBuilder.() -> Unit) { - this[name] = DataTree(T::class, block) -} - -public inline fun DataTreeBuilder.node(name: String, noinline block: DataTreeBuilder.() -> Unit) { - this[name.toName()] = DataTree(T::class, block) -} - -/** - * Generate a mutable builder from this node. Node content is not changed - */ -public fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder(type).apply { - dataSequence().forEach { (name, data) -> this[name] = data } -} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt index 00555a57..3bde5971 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt @@ -12,15 +12,17 @@ public interface Goal { public val dependencies: Collection> /** - * Returns current running coroutine if the goal is started + * Returns current running coroutine if the goal is started. Null if the computation is not started. */ - public val result: Deferred? + public val deferred: Deferred? /** * Get ongoing computation or start a new one. * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. + * + * If the computation is already running, the scope is not used. */ - public fun startAsync(coroutineScope: CoroutineScope): Deferred + public fun async(coroutineScope: CoroutineScope): Deferred /** * Reset the computation @@ -30,28 +32,30 @@ public interface Goal { public companion object } -public suspend fun Goal.await(): T = coroutineScope { startAsync(this).await() } +public fun Goal<*>.launch(coroutineScope: CoroutineScope): Job = async(coroutineScope) -public val Goal<*>.isComplete: Boolean get() = result?.isCompleted ?: false +public suspend fun Goal.await(): T = coroutineScope { async(this).await() } + +public val Goal<*>.isComplete: Boolean get() = deferred?.isCompleted ?: false public open class StaticGoal(public val value: T) : Goal { override val dependencies: Collection> get() = emptyList() - override val result: Deferred = CompletableDeferred(value) + override val deferred: Deferred = CompletableDeferred(value) - override fun startAsync(coroutineScope: CoroutineScope): Deferred = result + override fun async(coroutineScope: CoroutineScope): Deferred = deferred override fun reset() { //doNothing } } -public open class ComputationGoal( +public open class LazyGoal( private val coroutineContext: CoroutineContext = EmptyCoroutineContext, override val dependencies: Collection> = emptyList(), public val block: suspend CoroutineScope.() -> T, ) : Goal { - final override var result: Deferred? = null + final override var deferred: Deferred? = null private set /** @@ -59,11 +63,11 @@ public open class ComputationGoal( * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. */ @DFExperimental - override fun startAsync(coroutineScope: CoroutineScope): Deferred { + override fun async(coroutineScope: CoroutineScope): Deferred { val startedDependencies = this.dependencies.map { goal -> - goal.run { startAsync(coroutineScope) } + goal.run { async(coroutineScope) } } - return result ?: coroutineScope.async( + return deferred ?: coroutineScope.async( this.coroutineContext + CoroutineMonitor() + Dependencies(startedDependencies) ) { startedDependencies.forEach { deferred -> @@ -72,15 +76,15 @@ public open class ComputationGoal( } } block() - }.also { result = it } + }.also { deferred = it } } /** * Reset the computation */ override fun reset() { - result?.cancel() - result = null + deferred?.cancel() + deferred = null } } @@ -90,7 +94,7 @@ public open class ComputationGoal( public fun Goal.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.(T) -> R, -): Goal = ComputationGoal(coroutineContext, listOf(this)) { +): Goal = LazyGoal(coroutineContext, listOf(this)) { block(await()) } @@ -100,7 +104,7 @@ public fun Goal.map( public fun Collection>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.(Collection) -> R, -): Goal = ComputationGoal(coroutineContext, this) { +): Goal = LazyGoal(coroutineContext, this) { block(map { run { it.await() } }) } @@ -113,7 +117,7 @@ public fun Collection>.reduce( public fun Map>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.(Map) -> R, -): Goal = ComputationGoal(coroutineContext, this.values) { +): Goal = LazyGoal(coroutineContext, this.values) { block(mapValues { it.value.await() }) } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt index 0a5de778..0fbff04f 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt @@ -15,14 +15,16 @@ */ package hep.dataforge.data -import hep.dataforge.meta.Meta import hep.dataforge.meta.get import hep.dataforge.meta.string +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.collect +import kotlin.reflect.KClass public interface GroupRule { - public operator fun invoke(node: DataNode): Map> + public suspend fun gather(dataType: KClass, set: DataSet): Map> - public companion object{ + public companion object { /** * Create grouping rule that creates groups for different values of value * field with name [key] @@ -31,19 +33,20 @@ public interface GroupRule { * @param defaultTagValue * @return */ - public fun byValue(key: String, defaultTagValue: String): GroupRule = object : - GroupRule { - override fun invoke(node: DataNode): Map> { - val map = HashMap>() + public fun byValue(scope: CoroutineScope, key: String, defaultTagValue: String): GroupRule = + object : GroupRule { - node.dataSequence().forEach { (name, data) -> - val tagValue = data.meta[key]?.string ?: defaultTagValue - map.getOrPut(tagValue) { DataNode.builder(node.type) }[name] = data + override suspend fun gather(dataType: KClass, set: DataSet): Map> { + val map = HashMap>() + + set.flow().collect { data -> + val tagValue = data.meta[key]?.string ?: defaultTagValue + map.getOrPut(tagValue) { MutableDataTree(dataType, scope) }.set(data.name, data.data) + } + + return map } - - return map.mapValues { it.value.build() } } - } // @ValueDef(key = "byValue", required = true, info = "The name of annotation value by which grouping should be made") @@ -52,17 +55,20 @@ public interface GroupRule { // def = "default", // info = "Default value which should be used for content in which the grouping value is not presented" // ) - public fun byMeta(config: Meta): GroupRule { - //TODO expand grouping options - return config["byValue"]?.string?.let { - byValue( - it, - config["defaultValue"]?.string ?: "default" - ) - } - ?: object : GroupRule { - override fun invoke(node: DataNode): Map> = mapOf("" to node) - } - } +// public fun byMeta(scope: CoroutineScope, config: Meta): GroupRule { +// //TODO expand grouping options +// return config["byValue"]?.string?.let { +// byValue( +// scope, +// it, +// config["defaultValue"]?.string ?: "default" +// ) +// } ?: object : GroupRule { +// override suspend fun gather( +// dataType: KClass, +// source: DataSource, +// ): Map> = mapOf("" to source) +// } +// } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt index 05ba5056..f89d6eeb 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt @@ -2,6 +2,10 @@ package hep.dataforge.data import hep.dataforge.meta.* import hep.dataforge.names.Name +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch import kotlin.reflect.KClass /** @@ -10,7 +14,7 @@ import kotlin.reflect.KClass public data class ActionEnv( val name: Name, val meta: Meta, - val actionMeta: Meta + val actionMeta: Meta, ) /** @@ -30,20 +34,22 @@ public class MapActionBuilder(public var name: Name, public var meta: Meta public class MapAction( - private val outputType: KClass, - private val block: MapActionBuilder.() -> Unit + public val outputType: KClass, + private val block: MapActionBuilder.() -> Unit, ) : Action { - override fun invoke(node: DataNode, meta: Meta): DataNode = DataTree(outputType) { - node.dataSequence().forEach { (name, data) -> - /* - * Creating a new environment for action using **old** name, old meta and task meta - */ - val env = ActionEnv(name, data.meta, meta) + override suspend fun run( + set: DataSet, + meta: Meta, + scope: CoroutineScope, + ): DataSet = DataTree.dynamic(outputType, scope) { + suspend fun mapOne(data: NamedData): NamedData { + // Creating a new environment for action using **old** name, old meta and task meta + val env = ActionEnv(data.name, data.meta, meta) //applying transformation from builder val builder = MapActionBuilder( - name, + data.name, data.meta.builder(), // using data meta meta ).apply(block) @@ -56,15 +62,26 @@ public class MapAction( val newData = data.map(outputType, meta = newMeta) { builder.result(env, it) } //setting the data node - this[newName] = newData + return newData.named(newName) + } + + collectFrom(set.flow().map(::mapOne)) + scope.launch { + set.updates.collect { name -> + //clear old nodes + remove(name) + //collect new items + collectFrom(set.flowChildren(name).map(::mapOne)) + } } } } -public inline fun DataNode.map( +public suspend inline fun DataSet.map( meta: Meta, - noinline action: MapActionBuilder.() -> Unit -): DataNode = MapAction(R::class, action).invoke(this, meta) + updatesScope: CoroutineScope, + noinline action: MapActionBuilder.() -> Unit, +): DataSet = MapAction(R::class, action).run(this, meta, updatesScope) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt new file mode 100644 index 00000000..62d7a80b --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt @@ -0,0 +1,159 @@ +package hep.dataforge.data + +import hep.dataforge.meta.* +import hep.dataforge.names.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlin.reflect.KClass + +/** + * A mutable [DataTree.Companion.dynamic]. It + */ +public class MutableDataTree( + override val dataType: KClass, + public val scope: CoroutineScope, +) : DataTree, DataSetBuilder { + private val mutex = Mutex() + private val treeItems = HashMap>() + + override suspend fun items(): Map> = mutex.withLock { treeItems } + + private val _updates = MutableSharedFlow() + + override val updates: Flow + get() = _updates + + private suspend fun remove(token: NameToken) { + mutex.withLock { + if (treeItems.remove(token) != null) { + _updates.emit(token.asName()) + } + } + } + + override fun remove(name: Name) { + scope.launch { + if (name.isEmpty()) error("Can't remove the root node") + (getItem(name.cutLast()).tree as? MutableDataTree)?.remove(name.lastOrNull()!!) + } + } + + private suspend fun set(token: NameToken, node: DataSet) { + //if (_map.containsKey(token)) error("Tree entry with name $token is not empty") + mutex.withLock { + treeItems[token] = DataTreeItem.Node(node.toMutableTree(scope)) + coroutineScope { + node.updates.onEach { + _updates.emit(token + it) + }.launchIn(this) + } + _updates.emit(token.asName()) + } + } + + private suspend fun set(token: NameToken, data: Data) { + mutex.withLock { + treeItems[token] = DataTreeItem.Leaf(data) + _updates.emit(token.asName()) + } + } + + private suspend fun getOrCreateNode(token: NameToken): MutableDataTree = + (treeItems[token] as? DataTreeItem.Node)?.tree as? MutableDataTree + ?: MutableDataTree(dataType, scope).also { set(token, it) } + + private suspend fun getOrCreateNode(name: Name): MutableDataTree { + return when (name.length) { + 0 -> this + 1 -> getOrCreateNode(name.firstOrNull()!!) + else -> getOrCreateNode(name.firstOrNull()!!).getOrCreateNode(name.cutFirst()) + } + } + + override fun set(name: Name, data: Data?) { + if (data == null) { + remove(name) + } else { + scope.launch { + when (name.length) { + 0 -> error("Can't add data with empty name") + 1 -> set(name.firstOrNull()!!, data) + 2 -> getOrCreateNode(name.cutLast()).set(name.lastOrNull()!!, data) + } + } + } + } + + private suspend fun setTree(name: Name, node: MutableDataTree) { + when (name.length) { + 0 -> error("Can't add data with empty name") + 1 -> set(name.firstOrNull()!!, node) + 2 -> getOrCreateNode(name.cutLast()).set(name.lastOrNull()!!, node) + } + } + + override suspend fun set(name: Name, dataSet: DataSet): Unit { + if (dataSet is MutableDataTree) { + setTree(name, dataSet) + } else { + setTree(name, dataSet.toMutableTree(scope)) + } + } + + override fun set(name: Name, block: DataSetBuilder.() -> Unit) { + scope.launch { + setTree(name, MutableDataTree(dataType, scope).apply(block)) + } + } + + public fun collectFrom(flow: Flow>) { + flow.onEach { + set(it.name, it.data) + }.launchIn(scope) + } +} + +public suspend fun DataTree.Companion.dynamic( + type: KClass, + updatesScope: CoroutineScope, + block: suspend MutableDataTree.() -> Unit, +): DataTree { + val tree = MutableDataTree(type, updatesScope) + tree.block() + return tree +} + +public suspend inline fun DataTree.Companion.dynamic( + updatesScope: CoroutineScope, + crossinline block: suspend MutableDataTree.() -> Unit, +): DataTree = MutableDataTree(T::class, updatesScope).apply { block() } + + +public suspend inline fun MutableDataTree.set( + name: Name, + noinline block: suspend MutableDataTree.() -> Unit, +): Unit = set(name, DataTree.dynamic(T::class, scope, block)) + +public suspend inline fun MutableDataTree.set( + name: String, + noinline block: suspend MutableDataTree.() -> Unit, +): Unit = set(name.toName(), DataTree.dynamic(T::class, scope, block)) + +/** + * Generate a mutable builder from this node. Node content is not changed + */ +public suspend fun DataSet.toMutableTree( + scope: CoroutineScope, +): MutableDataTree = MutableDataTree(dataType, scope).apply { + flow().collect { set(it.name, it.data) } + this@toMutableTree.updates.onEach { + set(it, getData(it)) + }.launchIn(scope) +} + +public fun MutableDataTree.branch(branchName: Name): MutableDataTree = + (this as DataTree).branch(branchName) as MutableDataTree diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt index d92a87cf..1cc4ec10 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt @@ -1,13 +1,19 @@ package hep.dataforge.data +import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder import hep.dataforge.names.Name import hep.dataforge.names.toName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.fold import kotlin.reflect.KClass -public class JoinGroup(public var name: String, internal val node: DataNode) { +@DFExperimental +public class JoinGroup(public var name: String, internal val set: DataSet) { public var meta: MetaBuilder = MetaBuilder() @@ -19,35 +25,44 @@ public class JoinGroup(public var name: String, internal val n } -public class ReduceGroupBuilder(public val actionMeta: Meta) { - private val groupRules: MutableList<(DataNode) -> List>> = ArrayList(); +@DFExperimental +public class ReduceGroupBuilder( + private val inputType: KClass, + private val scope: CoroutineScope, + public val actionMeta: Meta, +) { + private val groupRules: MutableList) -> List>> = ArrayList(); /** - * introduce grouping by value name + * introduce grouping by meta value */ public fun byValue(tag: String, defaultTag: String = "@default", action: JoinGroup.() -> Unit) { groupRules += { node -> - GroupRule.byValue(tag, defaultTag).invoke(node).map { + GroupRule.byValue(scope, tag, defaultTag).gather(inputType, node).map { JoinGroup(it.key, it.value).apply(action) } } } - /** - * Add a single fixed group to grouping rules - */ - public fun group(groupName: String, filter: DataFilter, action: JoinGroup.() -> Unit) { - groupRules += { node -> - listOf( - JoinGroup(groupName, node.filter(filter)).apply(action) - ) - } - } +// /** +// * Add a single fixed group to grouping rules +// */ +// public fun group(groupName: String, filter: DataMapper, action: JoinGroup.() -> Unit) { +// groupRules += { node -> +// listOf( +// JoinGroup(groupName, node.filter(filter)).apply(action) +// ) +// } +// } - public fun group(groupName: String, filter: (Name, Data) -> Boolean, action: JoinGroup.() -> Unit) { - groupRules += { node -> + public fun group( + groupName: String, + filter: suspend (Name, Data) -> Boolean, + action: JoinGroup.() -> Unit, + ) { + groupRules += { source -> listOf( - JoinGroup(groupName, node.filter(filter)).apply(action) + JoinGroup(groupName, source.filter(filter)).apply(action) ) } } @@ -61,27 +76,27 @@ public class ReduceGroupBuilder(public val actionMeta: Meta) { } } - internal fun buildGroups(input: DataNode): List> { + internal suspend fun buildGroups(input: DataSet): List> { return groupRules.flatMap { it.invoke(input) } } } - -/** - * The same rules as for KPipe - */ +@DFExperimental public class ReduceAction( - private val outputType: KClass, - private val action: ReduceGroupBuilder.() -> Unit -) : Action { + private val inputType: KClass, + outputType: KClass, + private val action: ReduceGroupBuilder.() -> Unit, +) : CachingAction(outputType) { + //TODO optimize reduction. Currently the whole action recalculates on push - override fun invoke(node: DataNode, meta: Meta): DataNode = DataTree(outputType) { - ReduceGroupBuilder(meta).apply(action).buildGroups(node).forEach { group -> - - //val laminate = Laminate(group.meta, meta) - - val dataMap = group.node.dataSequence().associate { it } + override fun CoroutineScope.transform(set: DataSet, meta: Meta, key: Name): Flow> = flow { + ReduceGroupBuilder(inputType,this@transform, meta).apply(action).buildGroups(set).forEach { group -> + val dataFlow: Map> = group.set.flow().fold(HashMap()) { acc, value -> + acc.apply { + acc[value.name] = value.data + } + } val groupName: String = group.name @@ -89,14 +104,13 @@ public class ReduceAction( val env = ActionEnv(groupName.toName(), groupMeta, meta) - val res: ComputationData = dataMap.reduce( + val res: LazyData = dataFlow.reduce( outputType, meta = groupMeta ) { group.result.invoke(env, it) } - set(env.name, res) + emit(res.named(env.name)) } - } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt index a4390849..f6ee51ad 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt @@ -6,20 +6,24 @@ import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.builder import hep.dataforge.names.Name import hep.dataforge.names.toName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import kotlin.collections.set import kotlin.reflect.KClass -public class FragmentRule(public val name: Name, public var meta: MetaBuilder) { - public lateinit var result: suspend (T) -> R - - public fun result(f: suspend (T) -> R) { - result = f; - } -} - public class SplitBuilder(public val name: Name, public val meta: Meta) { + + public class FragmentRule(public val name: Name, public var meta: MetaBuilder) { + public lateinit var result: suspend (T) -> R + + public fun result(f: suspend (T) -> R) { + result = f; + } + } + internal val fragments: MutableMap.() -> Unit> = HashMap() /** @@ -32,27 +36,40 @@ public class SplitBuilder(public val name: Name, public val me } } +/** + * Action that splits each incoming element into a number of fragments defined in builder + */ public class SplitAction( private val outputType: KClass, - private val action: SplitBuilder.() -> Unit + private val action: SplitBuilder.() -> Unit, ) : Action { - override fun invoke(node: DataNode, meta: Meta): DataNode = DataTree(outputType) { - node.dataSequence().forEach { (name, data) -> + override suspend fun run( + set: DataSet, + meta: Meta, + scope: CoroutineScope, + ): DataSet = DataTree.dynamic(outputType, scope) { + suspend fun splitOne(data: NamedData): Flow> { val laminate = Laminate(data.meta, meta) - val split = SplitBuilder(name, data.meta).apply(action) + val split = SplitBuilder(data.name, data.meta).apply(action) // apply individual fragment rules to result - split.fragments.forEach { (fragmentName, rule) -> - val env = FragmentRule(fragmentName, laminate.builder()) + return split.fragments.entries.asFlow().map { (fragmentName, rule) -> + val env = SplitBuilder.FragmentRule(fragmentName, laminate.builder()).apply(rule) + data.map(outputType, meta = env.meta) { env.result(it) }.named(fragmentName) + } + } - rule(env) - - val res = data.map(outputType, meta = env.meta) { env.result(it) } - set(env.name, res) + collectFrom(set.flow().flatMapConcat(transform = ::splitOne)) + scope.launch { + set.updates.collect { name -> + //clear old nodes + remove(name) + //collect new items + collectFrom(set.flowChildren(name).flatMapConcat(transform = ::splitOne)) } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt new file mode 100644 index 00000000..08e989d9 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt @@ -0,0 +1,79 @@ +package hep.dataforge.data + +import hep.dataforge.names.* +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.* +import kotlin.reflect.KClass + +private class StaticDataTree( + override val dataType: KClass, +) : DataSetBuilder, DataTree { + + private val items: MutableMap> = HashMap() + + override val updates: Flow = emptyFlow() + + override suspend fun items(): Map> = items + + override fun remove(name: Name) { + when (name.length) { + 0 -> error("Can't remove root tree node") + 1 -> items.remove(name.firstOrNull()!!) + else -> (items[name.firstOrNull()!!].tree as? StaticDataTree)?.remove(name.cutFirst()) + } + } + + fun getOrCreateNode(name: Name): StaticDataTree = when (name.length) { + 0 -> this + 1 -> { + val itemName = name.firstOrNull()!! + (items[itemName].tree as? StaticDataTree) ?: StaticDataTree(dataType).also { + items[itemName] = DataTreeItem.Node(it) + } + } + else -> getOrCreateNode(name.cutLast()).getOrCreateNode(name.lastOrNull()!!.asName()) + } + + private operator fun set(name: Name, item: DataTreeItem?) { + if (name.isEmpty()) error("Can't set top level tree node") + if (item == null) { + remove(name) + } else { + getOrCreateNode(name.cutLast()).items[name.lastOrNull()!!] = item + } + } + + override fun set(name: Name, data: Data?) { + set(name, data?.let { DataTreeItem.Leaf(it) }) + } + + override suspend fun set(name: Name, dataSet: DataSet) { + if (dataSet is StaticDataTree) { + set(name, DataTreeItem.Node(dataSet)) + } else { + coroutineScope { + dataSet.flow().collect { + set(name + it.name, it.data) + } + } + } + } + + override fun set(name: Name, block: DataSetBuilder.() -> Unit) { + val tree = StaticDataTree(dataType).apply(block) + set(name, DataTreeItem.Node(tree)) + } +} + +public fun DataTree.Companion.static( + dataType: KClass, + block: DataSetBuilder.() -> Unit, +): DataTree = StaticDataTree(dataType).apply(block) + +public inline fun DataTree.Companion.static( + noinline block: DataSetBuilder.() -> Unit, +): DataTree = static(T::class, block) + +public suspend fun DataSet.toStaticTree(): DataTree = StaticDataTree(dataType).apply { + update(this@toStaticTree) +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt new file mode 100644 index 00000000..82af17cd --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt @@ -0,0 +1,20 @@ +package hep.dataforge.data + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder + + +/** + * Get a metadata node for this set if it is present + */ +public suspend fun DataSet<*>.getMeta(): Meta? = getData(DataSet.META_KEY)?.meta + +/** + * Add meta-data node to a [DataSet] + */ +public fun DataSetBuilder<*>.meta(meta: Meta): Unit = set(DataSet.META_KEY, Data.empty(meta)) + +/** + * Add meta-data node to a [DataSet] + */ +public fun DataSetBuilder<*>.meta(metaBuilder: MetaBuilder.() -> Unit): Unit = meta(Meta(metaBuilder)) \ No newline at end of file diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/TypeFilteredDataNode.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/TypeFilteredDataNode.kt deleted file mode 100644 index b67becff..00000000 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/TypeFilteredDataNode.kt +++ /dev/null @@ -1,27 +0,0 @@ -package hep.dataforge.data - -import hep.dataforge.meta.Meta -import hep.dataforge.names.NameToken -import kotlin.reflect.KClass - - -/** - * A zero-copy data node wrapper that returns only children with appropriate type. - */ -public class TypeFilteredDataNode(public val origin: DataNode<*>, override val type: KClass) : DataNode { - override val meta: Meta get() = origin.meta - override val items: Map> by lazy { - origin.items.mapNotNull { (key, item) -> - when (item) { - is DataItem.Leaf -> { - (item.data.filterIsInstance(type))?.let { - key to DataItem.Leaf(it) - } - } - is DataItem.Node -> { - key to DataItem.Node(item.node.filterIsInstance(type)) - } - } - }.associate { it } - } -} \ No newline at end of file diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt index 877b6dbc..b1c75c7f 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt @@ -1,9 +1,7 @@ package hep.dataforge.data -import hep.dataforge.meta.Meta -import hep.dataforge.names.NameToken -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred +import hep.dataforge.names.Name +import hep.dataforge.names.toName import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf @@ -11,13 +9,8 @@ import kotlin.reflect.full.isSubclassOf /** * Block the thread and get data content */ -public fun Data.get(): T = runBlocking { await() } +public fun Data.value(): T = runBlocking { await() } -/** - * Check that node is compatible with given type meaning that each element could be cast to the type - */ -internal fun DataNode<*>.canCast(type: KClass): Boolean = - type.isSubclassOf(this.type) /** * Check if data could be safely cast to given class */ @@ -25,88 +18,53 @@ internal fun Data<*>.canCast(type: KClass): Boolean = this.type.isSubclassOf(type) -public fun Data.upcast(type: KClass): Data { - return object : Data by this { - override val type: KClass = type - } -} - -/** - * Safe upcast a [Data] to a supertype - */ -public inline fun Data.upcast(): Data = upcast(R::class) - -public fun DataItem<*>.canCast(type: KClass): Boolean = when (this) { - is DataItem.Node -> node.canCast(type) - is DataItem.Leaf -> data.canCast(type) -} - -/** - * Unsafe cast of data node - */ -@Suppress("UNCHECKED_CAST") -public fun Data<*>.cast(type: KClass): Data { - if(!canCast(type)) error("Can't cast ${this.type} to $type") - return object : Data { - override val meta: Meta get() = this@cast.meta - override val dependencies: Collection> get() = this@cast.dependencies - override val result: Deferred? get() = this@cast.result as Deferred? - override fun startAsync(coroutineScope: CoroutineScope): Deferred = this@cast.run { - startAsync(coroutineScope) as Deferred - } - - override fun reset() = this@cast.reset() - override val type: KClass = type - } -} - -public inline fun Data<*>.cast(): Data = cast(R::class) - -@Suppress("UNCHECKED_CAST") -public fun DataNode<*>.cast(type: KClass): DataNode { - return object : DataNode { - override val meta: Meta get() = this@cast.meta - override val type: KClass = type - override val items: Map> get() = this@cast.items as Map> - } -} - -public inline fun DataNode<*>.cast(): DataNode = cast(R::class) - -/** - * Check that node is compatible with given type meaning that each element could be cast to the type - */ -public fun DataNode<*>.ensureType(type: KClass) { - if (!canCast(type)) { - error("$type expected, but $type received") - } -} +//public fun Data.upcast(type: KClass): Data { +// return object : Data by this { +// override val type: KClass = type +// } +//} +// +///** +// * Safe upcast a [Data] to a supertype +// */ +//public inline fun Data.upcast(): Data = upcast(R::class) /** * Cast the node to given type if the cast is possible or return null */ -public fun Data<*>.filterIsInstance(type: KClass): Data? = - if (canCast(type)) cast(type) else null - -/** - * Filter a node by data and node type. Resulting node and its subnodes is guaranteed to have border type [type], - * but could contain empty nodes - */ -public fun DataNode<*>.filterIsInstance(type: KClass): DataNode { - return when { - canCast(type) -> cast(type) - this is TypeFilteredDataNode -> origin.filterIsInstance(type) - else -> TypeFilteredDataNode(this, type) +@Suppress("UNCHECKED_CAST") +public fun Data<*>.castOrNull(type: KClass): Data? = + if (!canCast(type)) null else object : Data by (this as Data) { + override val type: KClass = type } -} /** - * Filter all elements of given data item that could be cast to given type. If no elements are available, return null. + * Unsafe cast of data node */ -public fun DataItem<*>?.filterIsInstance(type: KClass): DataItem? = when (this) { - null -> null - is DataItem.Node -> DataItem.Node(this.node.filterIsInstance(type)) - is DataItem.Leaf -> this.data.filterIsInstance(type)?.let { DataItem.Leaf(it) } +public fun Data<*>.cast(type: KClass): Data = + castOrNull(type) ?: error("Can't cast ${this.type} to $type") + +public inline fun Data<*>.cast(): Data = cast(R::class) + +@Suppress("UNCHECKED_CAST") +public fun DataSet<*>.castOrNull(type: KClass): DataSet? = + if (!canCast(type)) null else object : DataSet by (this as DataSet) { + override val dataType: KClass = type + } + + +public fun DataSet<*>.cast(type: KClass): DataSet = + castOrNull(type) ?: error("Can't cast ${this.dataType} to $type") + +/** + * Check that node is compatible with given type meaning that each element could be cast to the type + */ +internal fun DataSet<*>.canCast(type: KClass): Boolean = + type.isSubclassOf(this.dataType) + + +public operator fun DataTree.get(name: Name): DataTreeItem? = runBlocking { + getItem(name) } -public inline fun DataItem<*>?.filterIsInstance(): DataItem? = this@filterIsInstance.filterIsInstance(R::class) \ No newline at end of file +public operator fun DataTree.get(name: String): DataTreeItem? = get(name.toName()) \ No newline at end of file diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/filterIsInstance.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/filterIsInstance.kt new file mode 100644 index 00000000..4997573a --- /dev/null +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/filterIsInstance.kt @@ -0,0 +1,29 @@ +package hep.dataforge.data + +import hep.dataforge.names.Name +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlin.reflect.KClass + + +public fun DataSet<*>.filterIsInstance(type: KClass): DataSet = object : DataSet { + override val dataType: KClass = type + + @Suppress("UNCHECKED_CAST") + override fun flow(): Flow> = this@filterIsInstance.flow().filter { + it.canCast(type) + }.map { + it as NamedData + } + + override suspend fun getData(name: Name): Data? = this@filterIsInstance.getData(name)?.castOrNull(type) + + override val updates: Flow = this@filterIsInstance.updates.filter { + val datum = this@filterIsInstance.getData(it) + datum?.canCast(type) ?: false + } + +} + +public inline fun DataSet<*>.filterIsInstance(): DataSet = filterIsInstance(R::class) \ No newline at end of file diff --git a/dataforge-data/src/commonTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt similarity index 55% rename from dataforge-data/src/commonTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt rename to dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt index ca7a1d8f..5fbeebe5 100644 --- a/dataforge-data/src/commonTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt @@ -1,5 +1,6 @@ package hep.dataforge.data +import kotlinx.coroutines.runBlocking import kotlin.test.Test import kotlin.test.assertTrue @@ -7,23 +8,24 @@ import kotlin.test.assertTrue internal class DataTreeBuilderTest{ @Test fun testDataUpdate(){ - val updateData = DataTree{ + val updateData: DataTree = DataTree.static{ "update" put { "a" put Data.static("a") "b" put Data.static("b") } } - val node = DataTree{ - node("primary"){ - static("a","a") - static("b","b") + val node = DataTree.static{ + set("primary"){ + data("a","a") + data("b","b") + } + data("root","root") + runBlocking { + update(updateData) } - static("root","root") - update(updateData) } - println(node.toMeta()) assertTrue { node["update.a"] != null } assertTrue { node["primary.a"] != null } diff --git a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt index 84cb2d3e..e63a3b3e 100644 --- a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt @@ -22,10 +22,10 @@ import net.mamoe.yamlkt.* public fun Meta.toYaml(): YamlMap { val map: Map = items.entries.associate { (key, item) -> key.toString() to when (item) { - is ValueItem -> { + is MetaItemValue -> { item.value.value } - is NodeItem -> { + is MetaItemNode -> { item.node.toYaml() } } @@ -53,7 +53,7 @@ private class YamlMeta(private val yamlMap: YamlMap, private val descriptor: Nod (it as YamlLiteral).content.parseValue() } ) - map[token] = ValueItem(listValue) + map[token] = MetaItemValue(listValue) } else value.forEachIndexed { index, yamlElement -> val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: ItemDescriptor.DEFAULT_INDEX_KEY val indexValue: String = (yamlElement as? YamlMap)?.getStringOrNull(indexKey) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt index c0186d66..43ee98b8 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/BinaryMetaFormat.kt @@ -19,7 +19,7 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { override fun invoke(meta: Meta, context: Context): MetaFormat = this override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta { - return (input.readMetaItem() as NodeItem).node + return (input.readMetaItem() as MetaItemNode).node } private fun Output.writeChar(char: Char) = writeByte(char.toByte()) @@ -85,10 +85,10 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { meta.items.forEach { (key, item) -> output.writeString(key.toString()) when (item) { - is ValueItem -> { + is MetaItemValue -> { output.writeValue(item.value) } - is NodeItem -> { + is MetaItemNode -> { writeObject(output, item.node) } } @@ -103,19 +103,19 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { @Suppress("UNCHECKED_CAST") public fun Input.readMetaItem(): TypedMetaItem { return when (val keyChar = readByte().toChar()) { - 'S' -> ValueItem(StringValue(readString())) - 'N' -> ValueItem(Null) - '+' -> ValueItem(True) - '-' -> ValueItem(True) - 's' -> ValueItem(NumberValue(readShort())) - 'i' -> ValueItem(NumberValue(readInt())) - 'l' -> ValueItem(NumberValue(readInt())) - 'f' -> ValueItem(NumberValue(readFloat())) - 'd' -> ValueItem(NumberValue(readDouble())) + 'S' -> MetaItemValue(StringValue(readString())) + 'N' -> MetaItemValue(Null) + '+' -> MetaItemValue(True) + '-' -> MetaItemValue(True) + 's' -> MetaItemValue(NumberValue(readShort())) + 'i' -> MetaItemValue(NumberValue(readInt())) + 'l' -> MetaItemValue(NumberValue(readInt())) + 'f' -> MetaItemValue(NumberValue(readFloat())) + 'd' -> MetaItemValue(NumberValue(readDouble())) 'L' -> { val length = readInt() - val list = (1..length).map { (readMetaItem() as ValueItem).value } - ValueItem(Value.of(list)) + val list = (1..length).map { (readMetaItem() as MetaItemValue).value } + MetaItemValue(Value.of(list)) } 'M' -> { val length = readInt() @@ -126,7 +126,7 @@ public object BinaryMetaFormat : MetaFormat, MetaFormatFactory { set(name, item) } } - NodeItem(meta) + MetaItemNode(meta) } else -> error("Unknown serialization key character: $keyChar") } diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt index 06cc9374..085c1cde 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/EnvelopeFormat.kt @@ -3,12 +3,13 @@ package hep.dataforge.io import hep.dataforge.context.Context import hep.dataforge.io.EnvelopeFormatFactory.Companion.ENVELOPE_FORMAT_TYPE import hep.dataforge.meta.Meta +import hep.dataforge.misc.Type import hep.dataforge.names.Name import hep.dataforge.names.asName -import hep.dataforge.type.Type import kotlinx.io.Input import kotlinx.io.Output -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf /** * A partially read envelope with meta, but without data @@ -16,6 +17,8 @@ import kotlin.reflect.KClass public data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?) public interface EnvelopeFormat : IOFormat { + override val type: KType get() = typeOf() + public val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat public fun readPartial(input: Input): PartialEnvelope @@ -37,7 +40,7 @@ public fun EnvelopeFormat.read(input: Input): Envelope = readObject(input) @Type(ENVELOPE_FORMAT_TYPE) public interface EnvelopeFormatFactory : IOFormatFactory, EnvelopeFormat { override val name: Name get() = "envelope".asName() - override val type: KClass get() = Envelope::class + override val type: KType get() = typeOf() override fun invoke(meta: Meta, context: Context): EnvelopeFormat diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt index 1fd845ea..6ba7d6e3 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOFormat.kt @@ -2,25 +2,28 @@ package hep.dataforge.io import hep.dataforge.context.Context import hep.dataforge.context.Factory -import hep.dataforge.context.Named import hep.dataforge.io.IOFormat.Companion.NAME_KEY import hep.dataforge.io.IOFormatFactory.Companion.IO_FORMAT_TYPE import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaItemValue import hep.dataforge.meta.MetaRepr -import hep.dataforge.meta.ValueItem +import hep.dataforge.misc.Named +import hep.dataforge.misc.Type import hep.dataforge.names.Name import hep.dataforge.names.asName -import hep.dataforge.type.Type import hep.dataforge.values.Value import kotlinx.io.* import kotlinx.io.buffer.Buffer import kotlinx.io.pool.ObjectPool -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf /** * And interface for reading and writing objects into with IO streams */ public interface IOFormat : MetaRepr { + public val type: KType + public fun writeObject(output: Output, obj: T) public fun readObject(input: Input): T @@ -42,10 +45,14 @@ public fun Binary.readWith(format: IOFormat): T = read { public fun Output.writeWith(format: IOFormat, obj: T): Unit = format.run { writeObject(this@writeWith, obj) } -public class ListIOFormat(public val format: IOFormat) : IOFormat> { +public inline fun IOFormat.Companion.listOf( + format: IOFormat, +): IOFormat> = object : IOFormat> { + override val type: KType = typeOf>() + override fun writeObject(output: Output, obj: List) { output.writeInt(obj.size) - this.format.run { + format.run { obj.forEach { writeObject(output, it) } @@ -63,9 +70,8 @@ public class ListIOFormat(public val format: IOFormat) : IOFormat

  • IOFormat.list: ListIOFormat get() = ListIOFormat(this) +} public fun ObjectPool.fill(block: Buffer.() -> Unit): Buffer { val buffer = borrow() @@ -82,7 +88,7 @@ public interface IOFormatFactory : Factory>, Named, MetaRep /** * Explicit type for dynamic type checks */ - public val type: KClass + public val type: KType override fun toMeta(): Meta = Meta { NAME_KEY put name.toString() @@ -100,7 +106,7 @@ public object DoubleIOFormat : IOFormat, IOFormatFactory { override val name: Name = "double".asName() - override val type: KClass get() = Double::class + override val type: KType get() = typeOf() override fun writeObject(output: Output, obj: kotlin.Double) { output.writeDouble(obj) @@ -114,14 +120,14 @@ public object ValueIOFormat : IOFormat, IOFormatFactory { override val name: Name = "value".asName() - override val type: KClass get() = Value::class + override val type: KType get() = typeOf() override fun writeObject(output: Output, obj: Value) { BinaryMetaFormat.run { output.writeValue(obj) } } override fun readObject(input: Input): Value { - return (BinaryMetaFormat.run { input.readMetaItem() } as? ValueItem)?.value + return (BinaryMetaFormat.run { input.readMetaItem() } as? MetaItemValue)?.value ?: error("The item is not a value") } } \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/JsonMetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/JsonMetaFormat.kt index 2be2fe51..255a08e5 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/JsonMetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/JsonMetaFormat.kt @@ -16,12 +16,16 @@ import kotlinx.io.text.readUtf8String import kotlinx.io.text.writeUtf8String import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject +import kotlin.reflect.KType +import kotlin.reflect.typeOf /** * A Json format for Meta representation */ public class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat { + override val type: KType get() = typeOf() + override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) { val jsonObject = meta.toJson(descriptor) output.writeUtf8String(json.encodeToString(JsonObject.serializer(), jsonObject)) diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt index 0fa51555..bb59e1a0 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/MetaFormat.kt @@ -4,20 +4,22 @@ import hep.dataforge.context.Context import hep.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.misc.Type import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.plus -import hep.dataforge.type.Type import kotlinx.io.ByteArrayInput import kotlinx.io.Input import kotlinx.io.Output import kotlinx.io.use -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf /** * A format for meta serialization */ public interface MetaFormat : IOFormat { + override val type: KType get() = typeOf() override fun writeObject(output: Output, obj: Meta) { writeMeta(output, obj, null) @@ -40,7 +42,7 @@ public interface MetaFormatFactory : IOFormatFactory, MetaFormat { override val name: Name get() = "meta".asName() + shortName - override val type: KClass get() = Meta::class + override val type: KType get() = typeOf() public val key: Short get() = name.hashCode().toShort() diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt index 0e53ed89..46126fac 100644 --- a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt @@ -8,7 +8,8 @@ import kotlinx.io.* import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption -import kotlin.reflect.full.isSuperclassOf +import kotlin.reflect.full.isSupertypeOf +import kotlin.reflect.typeOf import kotlin.streams.asSequence public fun Path.read(block: Input.() -> R): R = asBinary().read(block = block) @@ -59,7 +60,7 @@ public fun Path.readEnvelope(format: EnvelopeFormat): Envelope { @Suppress("UNCHECKED_CAST") @DFExperimental public inline fun IOPlugin.resolveIOFormat(): IOFormat? { - return ioFormatFactories.find { it.type.isSuperclassOf(T::class) } as IOFormat? + return ioFormatFactories.find { it.type.isSupertypeOf(typeOf())} as IOFormat? } /** @@ -115,18 +116,18 @@ public fun IOPlugin.writeMetaFile( * Return inferred [EnvelopeFormat] if only one format could read given file. If no format accepts file, return null. If * multiple formats accepts file, throw an error. */ -public fun IOPlugin.peekBinaryFormat(path: Path): EnvelopeFormat? { +public fun IOPlugin.peekFileEnvelopeFormat(path: Path): EnvelopeFormat? { val binary = path.asBinary() val formats = envelopeFormatFactories.mapNotNull { factory -> binary.read { - factory.peekFormat(this@peekBinaryFormat, this@read) + factory.peekFormat(this@peekFileEnvelopeFormat, this@read) } } return when (formats.size) { 0 -> null 1 -> formats.first() - else -> error("Envelope format binary recognition clash") + else -> error("Envelope format binary recognition clash: $formats") } } @@ -137,10 +138,10 @@ public val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data" * Read and envelope from file if the file exists, return null if file does not exist. * * If file is directory, then expect two files inside: - * * **meta.** for meta + * * **meta.** for meta * * **data** for data * - * If the file is envelope read it using [EnvelopeFormatFactory.peekFormat] functionality to infer format. + * If the file is envelope read it using [EnvelopeFormatFactory.peekFormat] functionality to infer format (if not overridden with [formatPicker]). * * If the file is not an envelope and [readNonEnvelopes] is true, return an Envelope without meta, using file as binary. * @@ -150,7 +151,7 @@ public val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data" public fun IOPlugin.readEnvelopeFile( path: Path, readNonEnvelopes: Boolean = false, - formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat, + formatPicker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekFileEnvelopeFormat, ): Envelope? { if (!Files.exists(path)) return null @@ -177,7 +178,7 @@ public fun IOPlugin.readEnvelopeFile( return SimpleEnvelope(meta, data) } - return formatPeeker(path)?.let { format -> + return formatPicker(path)?.let { format -> path.readEnvelope(format) } ?: if (readNonEnvelopes) { // if no format accepts file, read it as binary SimpleEnvelope(Meta.EMPTY, path.asBinary()) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt index 781acb46..530fe9aa 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Config.kt @@ -56,12 +56,12 @@ public class Config() : AbstractMutableMeta(), ObservableItemProvider { override fun replaceItem(key: NameToken, oldItem: TypedMetaItem?, newItem: TypedMetaItem?) { if (newItem == null) { children.remove(key) - if (oldItem != null && oldItem is NodeItem) { + if (oldItem != null && oldItem is MetaItemNode) { oldItem.node.removeListener(this) } } else { children[key] = newItem - if (newItem is NodeItem) { + if (newItem is MetaItemNode) { newItem.node.onChange(this) { name, oldChild, newChild -> itemChanged(key + name, oldChild, newChild) } @@ -102,8 +102,8 @@ public fun Meta.toConfig(): Config = Config().also { builder -> this.items.mapValues { entry -> val item = entry.value builder[entry.key.asName()] = when (item) { - is ValueItem -> item.value - is NodeItem -> NodeItem(item.node.asConfig()) + is MetaItemValue -> item.value + is MetaItemNode -> MetaItemNode(item.node.asConfig()) } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt index 7811e308..a0f8a538 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/ItemProvider.kt @@ -17,7 +17,7 @@ public fun interface ItemProvider { /** * Perform recursive item search using given [name]. Each [NameToken] is treated as a name in [Meta.items] of a parent node. * - * If [name] is empty return current [Meta] as a [NodeItem] + * If [name] is empty return current [Meta] as a [MetaItemNode] */ public operator fun ItemProvider?.get(name: Name): MetaItem? = this?.getItem(name) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt index 2c1e290b..c0628520 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/JsonMeta.kt @@ -38,10 +38,10 @@ private fun Meta.toJsonWithIndex(descriptor: NodeDescriptor?, indexValue: String val elementMap = HashMap() fun MetaItem.toJsonElement(itemDescriptor: ItemDescriptor?, index: String?): JsonElement = when (this) { - is ValueItem -> { + is MetaItemValue -> { value.toJson(itemDescriptor as? ValueDescriptor) } - is NodeItem -> { + is MetaItemNode -> { node.toJsonWithIndex(itemDescriptor as? NodeDescriptor, index) } } @@ -99,11 +99,11 @@ public fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value { public fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): TypedMetaItem = when (this) { is JsonPrimitive -> { val value = this.toValue(descriptor as? ValueDescriptor) - ValueItem(value) + MetaItemValue(value) } is JsonObject -> { val meta = JsonMeta(this, descriptor as? NodeDescriptor) - NodeItem(meta) + MetaItemNode(meta) } is JsonArray -> { if (this.all { it is JsonPrimitive }) { @@ -115,7 +115,7 @@ public fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): TypedMeta (it as JsonPrimitive).toValue(descriptor as? ValueDescriptor) }.asValue() } - ValueItem(value) + MetaItemValue(value) } else { //We can't return multiple items therefore we create top level node buildJsonObject { put(JSON_ARRAY_KEY, this@toMetaItem) }.toMetaItem(descriptor) @@ -136,10 +136,10 @@ public class JsonMeta(private val json: JsonObject, private val descriptor: Node val itemDescriptor = descriptor?.items?.get(jsonKey) when (value) { is JsonPrimitive -> { - map[key] = ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) + map[key] = MetaItemValue(value.toValue(itemDescriptor as? ValueDescriptor)) } is JsonObject -> { - map[key] = NodeItem( + map[key] = MetaItemNode( JsonMeta( value, itemDescriptor as? NodeDescriptor @@ -153,7 +153,7 @@ public class JsonMeta(private val json: JsonObject, private val descriptor: Node (it as JsonPrimitive).toValue(itemDescriptor as? ValueDescriptor) } ) - map[key] = ValueItem(listValue) + map[key] = MetaItemValue(listValue) } else value.forEachIndexed { index, jsonElement -> val indexKey = (itemDescriptor as? NodeDescriptor)?.indexKey ?: DEFAULT_INDEX_KEY val indexValue: String = (jsonElement as? JsonObject) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt index f1c77006..a51e9cf5 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt @@ -44,11 +44,11 @@ public class Laminate(layers: List) : MetaBase() { private fun Sequence.merge(): TypedMetaItem { return when { - all { it is ValueItem } -> //If all items are values, take first + all { it is MetaItemValue } -> //If all items are values, take first first().seal() - all { it is NodeItem } -> { + all { it is MetaItemNode } -> { //list nodes in item - val nodes = map { (it as NodeItem).node } + val nodes = map { (it as MetaItemNode).node } //represent as key->value entries val entries = nodes.flatMap { it.items.entries.asSequence() } //group by keys @@ -57,13 +57,13 @@ public class Laminate(layers: List) : MetaBase() { val items = groups.mapValues { entry -> entry.value.asSequence().map { it.value }.merge() } - NodeItem(SealedMeta(items)) + MetaItemNode(SealedMeta(items)) } else -> map { when (it) { - is ValueItem -> NodeItem(Meta { Meta.VALUE_KEY put it.value }) - is NodeItem -> it + is MetaItemValue -> MetaItemNode(Meta { Meta.VALUE_KEY put it.value }) + is MetaItemNode -> it } }.merge() } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt index 95d144b0..4d5225d2 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Meta.kt @@ -16,8 +16,8 @@ public interface MetaRepr { /** * Generic meta tree representation. Elements are [TypedMetaItem] objects that could be represented by three different entities: - * * [ValueItem] (leaf) - * * [NodeItem] single node + * * [MetaItemValue] (leaf) + * * [MetaItemNode] single node * * * Same name siblings are supported via elements with the same [Name] but different queries */ @@ -28,7 +28,7 @@ public interface Meta : MetaRepr, ItemProvider { public val items: Map override fun getItem(name: Name): MetaItem? { - if (name.isEmpty()) return NodeItem(this) + if (name.isEmpty()) return MetaItemNode(this) return name.firstOrNull()?.let { token -> val tail = name.cutFirst() when (tail.length) { @@ -68,8 +68,8 @@ public operator fun Meta.get(token: NameToken): MetaItem? = items.get(token) public fun Meta.valueSequence(): Sequence> { return items.asSequence().flatMap { (key, item) -> when (item) { - is ValueItem -> sequenceOf(key.asName() to item.value) - is NodeItem -> item.node.valueSequence().map { pair -> (key.asName() + pair.first) to pair.second } + is MetaItemValue -> sequenceOf(key.asName() to item.value) + is MetaItemNode -> item.node.valueSequence().map { pair -> (key.asName() + pair.first) to pair.second } } } } @@ -80,7 +80,7 @@ public fun Meta.valueSequence(): Sequence> { public fun Meta.itemSequence(): Sequence> = sequence { items.forEach { (key, item) -> yield(key.asName() to item) - if (item is NodeItem) { + if (item is MetaItemNode) { yieldAll(item.node.itemSequence().map { (innerKey, innerItem) -> (key + innerKey) to innerItem }) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index 859948c2..a977199f 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -71,7 +71,7 @@ public class MetaBuilder : AbstractMutableMeta() { set(this,value.toList()) } - public infix fun String.put(metaBuilder: MetaBuilder.() -> Unit) { + public inline infix fun String.put(metaBuilder: MetaBuilder.() -> Unit) { this@MetaBuilder[this] = MetaBuilder().apply(metaBuilder) } @@ -126,8 +126,8 @@ public fun Meta.builder(): MetaBuilder { items.mapValues { entry -> val item = entry.value builder[entry.key.asName()] = when (item) { - is ValueItem -> item.value - is NodeItem -> NodeItem(item.node.builder()) + is MetaItemValue -> item.value + is MetaItemNode -> MetaItemNode(item.node.builder()) } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt index bfc3a83c..d63238f9 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaItem.kt @@ -5,8 +5,8 @@ import kotlinx.serialization.Serializable /** * A member of the meta tree. Could be represented as one of following: - * * a [ValueItem] (leaf) - * * a [NodeItem] (node) + * * a [MetaItemValue] (leaf) + * * a [MetaItemNode] (node) */ @Serializable(MetaItemSerializer::class) public sealed class TypedMetaItem() { @@ -31,11 +31,11 @@ public sealed class TypedMetaItem() { public typealias MetaItem = TypedMetaItem<*> @Serializable(MetaItemSerializer::class) -public class ValueItem(public val value: Value) : TypedMetaItem() { +public class MetaItemValue(public val value: Value) : TypedMetaItem() { override fun toString(): String = value.toString() override fun equals(other: Any?): Boolean { - return this.value == (other as? ValueItem)?.value + return this.value == (other as? MetaItemValue)?.value } override fun hashCode(): Int { @@ -44,25 +44,25 @@ public class ValueItem(public val value: Value) : TypedMetaItem() { } @Serializable(MetaItemSerializer::class) -public class NodeItem(public val node: M) : TypedMetaItem() { +public class MetaItemNode(public val node: M) : TypedMetaItem() { //Fixing serializer for node could cause class cast problems, but it should not since Meta descendants are not serializable override fun toString(): String = node.toString() - override fun equals(other: Any?): Boolean = node == (other as? NodeItem<*>)?.node + override fun equals(other: Any?): Boolean = node == (other as? MetaItemNode<*>)?.node override fun hashCode(): Int = node.hashCode() } -public fun Value.asMetaItem(): ValueItem = ValueItem(this) -public fun M.asMetaItem(): NodeItem = NodeItem(this) +public fun Value.asMetaItem(): MetaItemValue = MetaItemValue(this) +public fun M.asMetaItem(): MetaItemNode = MetaItemNode(this) /** * Unsafe methods to access values and nodes directly from [TypedMetaItem] */ public val MetaItem?.value: Value? - get() = (this as? ValueItem)?.value - ?: (this?.node?.get(Meta.VALUE_KEY) as? ValueItem)?.value + get() = (this as? MetaItemValue)?.value + ?: (this?.node?.get(Meta.VALUE_KEY) as? MetaItemValue)?.value public val MetaItem?.string: String? get() = value?.string public val MetaItem?.boolean: Boolean? get() = value?.boolean @@ -73,17 +73,18 @@ public val MetaItem?.int: Int? get() = number?.toInt() public val MetaItem?.long: Long? get() = number?.toLong() public val MetaItem?.short: Short? get() = number?.toShort() -public inline fun > MetaItem?.enum(): E? = if (this is ValueItem && this.value is EnumValue<*>) { - this.value.value as E -} else { - string?.let { enumValueOf(it) } -} +public inline fun > MetaItem?.enum(): E? = + if (this is MetaItemValue && this.value is EnumValue<*>) { + this.value.value as E + } else { + string?.let { enumValueOf(it) } + } public val MetaItem.stringList: List? get() = value?.list?.map { it.string } public val TypedMetaItem?.node: M? get() = when (this) { null -> null - is ValueItem -> null//error("Trying to interpret value meta item as node item") - is NodeItem -> node + is MetaItemValue -> null//error("Trying to interpret value meta item as node item") + is MetaItemNode -> node } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt index 2dd86633..ed24b234 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaSerializer.kt @@ -38,10 +38,10 @@ public object MetaItemSerializer : KSerializer { override fun serialize(encoder: Encoder, value: MetaItem) { encoder.encodeStructure(descriptor) { - encodeBooleanElement(descriptor, 0, value is NodeItem) + encodeBooleanElement(descriptor, 0, value is MetaItemNode) when (value) { - is ValueItem -> encodeSerializableElement(descriptor, 1, ValueSerializer, value.value) - is NodeItem -> encodeSerializableElement(descriptor, 1, MetaSerializer, value.node) + is MetaItemValue -> encodeSerializableElement(descriptor, 1, ValueSerializer, value.value) + is MetaItemNode -> encodeSerializableElement(descriptor, 1, MetaSerializer, value.node) } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt index 63a1cb26..e9190b20 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemDelegate.kt @@ -119,7 +119,7 @@ public fun MutableItemProvider.node(key: Name? = null): ReadWriteProperty> M.node(key: Name? = null): ReadWriteProperty = - item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { NodeItem(it) } }) + item(key).convert(reader = { it?.let { it.node as M } }, writer = { it?.let { MetaItemNode(it) } }) /* Number delegates */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt index 4216f7a1..c06c57d8 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableItemProvider.kt @@ -64,7 +64,7 @@ public fun MutableItemProvider.setIndexed( metas: Iterable, indexFactory: (Meta, index: Int) -> String = { _, index -> index.toString() }, ) { - setIndexedItems(name, metas.map { NodeItem(it) }) { item, index -> indexFactory(item.node!!, index) } + setIndexedItems(name, metas.map { MetaItemNode(it) }) { item, index -> indexFactory(item.node!!, index) } } public operator fun MutableItemProvider.set(name: Name, metas: Iterable): Unit = setIndexed(name, metas) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index 7f8487f8..f5353cbd 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -30,8 +30,8 @@ public abstract class AbstractMutableMeta> : AbstractTypedMet private fun wrapItem(item: MetaItem?): TypedMetaItem? = when (item) { null -> null - is ValueItem -> item - is NodeItem -> NodeItem(wrapNode(item.node)) + is MetaItemValue -> item + is MetaItemNode -> MetaItemNode(wrapNode(item.node)) } /** @@ -56,7 +56,7 @@ public abstract class AbstractMutableMeta> : AbstractTypedMet val token = name.firstOrNull()!! //get existing or create new node. Query is ignored for new node if (items[token] == null) { - replaceItem(token, null, NodeItem(empty())) + replaceItem(token, null, MetaItemNode(empty())) } items[token]?.node!!.set(name.cutFirst(), item) } @@ -87,7 +87,7 @@ public fun MutableItemProvider.append(name: String, value: Any?): Unit = append( public fun > M.edit(name: Name, builder: M.() -> Unit) { val item = when (val existingItem = get(name)) { null -> empty().also { set(name, it) } - is NodeItem -> existingItem.node + is MetaItemNode -> existingItem.node else -> error("Can't edit value meta item") } item.apply(builder) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt index 75baa12c..2e559796 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/SealedMeta.kt @@ -18,6 +18,6 @@ public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(items.map @Suppress("UNCHECKED_CAST") public fun MetaItem.seal(): TypedMetaItem = when (this) { - is ValueItem -> this - is NodeItem -> NodeItem(node.seal()) + is MetaItemValue -> this + is MetaItemNode -> MetaItemNode(node.seal()) } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/DescriptorMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/DescriptorMeta.kt index d6cb15cb..6181bfbb 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/DescriptorMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/DescriptorMeta.kt @@ -24,16 +24,16 @@ private class DescriptorMeta(val descriptor: NodeDescriptor) : Meta, MetaBase() public fun NodeDescriptor.defaultMeta(): Laminate = Laminate(default, DescriptorMeta(this)) /** - * Build a default [NodeItem] from this node descriptor + * Build a default [MetaItemNode] from this node descriptor */ -internal fun NodeDescriptor.defaultItem(): NodeItem<*> = - NodeItem(defaultMeta()) +internal fun NodeDescriptor.defaultItem(): MetaItemNode<*> = + MetaItemNode(defaultMeta()) /** - * Build a default [ValueItem] from this descriptor + * Build a default [MetaItemValue] from this descriptor */ -internal fun ValueDescriptor.defaultItem(): ValueItem? { - return ValueItem(default ?: return null) +internal fun ValueDescriptor.defaultItem(): MetaItemValue? { + return MetaItemValue(default ?: return null) } /** diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt index 03762b29..7dd5f905 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt @@ -279,7 +279,7 @@ public class ValueDescriptor(config: Config = Config()) : ItemDescriptor(config) } }, writer = { - ValueItem(it.asValue()) + MetaItemValue(it.asValue()) } ) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt index ee30b613..d223ce99 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt @@ -11,8 +11,8 @@ import hep.dataforge.values.Value public fun Meta.toMap(descriptor: NodeDescriptor? = null): Map { return items.entries.associate { (token, item) -> token.toString() to when (item) { - is NodeItem -> item.node.toMap() - is ValueItem -> item.value.value + is MetaItemNode -> item.node.toMap() + is MetaItemValue -> item.value.value } } } @@ -26,15 +26,15 @@ public fun Map.toMeta(descriptor: NodeDescriptor? = null): Meta = @Suppress("UNCHECKED_CAST") fun toItem(value: Any?): MetaItem = when (value) { is MetaItem -> value - is Meta -> NodeItem(value) - is Map<*, *> -> NodeItem((value as Map).toMeta()) - else -> ValueItem(Value.of(value)) + is Meta -> MetaItemNode(value) + is Map<*, *> -> MetaItemNode((value as Map).toMeta()) + else -> MetaItemValue(Value.of(value)) } entries.forEach { (key, value) -> if (value is List<*>) { val items = value.map { toItem(it) } - if (items.all { it is ValueItem }) { + if (items.all { it is MetaItemValue }) { set(key, ListValue(items.map { it.value!! })) } else { setIndexedItems(key.toName(), value.map { toItem(it) }) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt index 9f15a078..9d1c08d8 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaConverter.kt @@ -19,90 +19,90 @@ public interface MetaConverter { public val meta: MetaConverter = object : MetaConverter { override fun itemToObject(item: MetaItem): Meta = when (item) { - is NodeItem -> item.node - is ValueItem -> item.value.toMeta() + is MetaItemNode -> item.node + is MetaItemValue -> item.value.toMeta() } - override fun objectToMetaItem(obj: Meta): MetaItem = NodeItem(obj) + override fun objectToMetaItem(obj: Meta): MetaItem = MetaItemNode(obj) } public val value: MetaConverter = object : MetaConverter { override fun itemToObject(item: MetaItem): Value = when (item) { - is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is ValueItem -> item.value + is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is MetaItemValue -> item.value } - override fun objectToMetaItem(obj: Value): MetaItem = ValueItem(obj) + override fun objectToMetaItem(obj: Value): MetaItem = MetaItemValue(obj) } public val string: MetaConverter = object : MetaConverter { override fun itemToObject(item: MetaItem): String = when (item) { - is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is ValueItem -> item.value + is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is MetaItemValue -> item.value }.string - override fun objectToMetaItem(obj: String): MetaItem = ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: String): MetaItem = MetaItemValue(obj.asValue()) } public val boolean: MetaConverter = object : MetaConverter { override fun itemToObject(item: MetaItem): Boolean = when (item) { - is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is ValueItem -> item.value + is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is MetaItemValue -> item.value }.boolean - override fun objectToMetaItem(obj: Boolean): MetaItem = ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Boolean): MetaItem = MetaItemValue(obj.asValue()) } public val number: MetaConverter = object : MetaConverter { override fun itemToObject(item: MetaItem): Number = when (item) { - is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is ValueItem -> item.value + is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is MetaItemValue -> item.value }.number - override fun objectToMetaItem(obj: Number): MetaItem = ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Number): MetaItem = MetaItemValue(obj.asValue()) } public val double: MetaConverter = object : MetaConverter { override fun itemToObject(item: MetaItem): Double = when (item) { - is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is ValueItem -> item.value + is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is MetaItemValue -> item.value }.double - override fun objectToMetaItem(obj: Double): MetaItem = ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Double): MetaItem = MetaItemValue(obj.asValue()) } public val float: MetaConverter = object : MetaConverter { override fun itemToObject(item: MetaItem): Float = when (item) { - is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is ValueItem -> item.value + is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is MetaItemValue -> item.value }.float - override fun objectToMetaItem(obj: Float): MetaItem = ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Float): MetaItem = MetaItemValue(obj.asValue()) } public val int: MetaConverter = object : MetaConverter { override fun itemToObject(item: MetaItem): Int = when (item) { - is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is ValueItem -> item.value + is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is MetaItemValue -> item.value }.int - override fun objectToMetaItem(obj: Int): MetaItem = ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Int): MetaItem = MetaItemValue(obj.asValue()) } public val long: MetaConverter = object : MetaConverter { override fun itemToObject(item: MetaItem): Long = when (item) { - is NodeItem -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") - is ValueItem -> item.value + is MetaItemNode -> item.node[Meta.VALUE_KEY].value ?: error("Can't convert node to a value") + is MetaItemValue -> item.value }.long - override fun objectToMetaItem(obj: Long): MetaItem = ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: Long): MetaItem = MetaItemValue(obj.asValue()) } public inline fun > enum(): MetaConverter = object : MetaConverter { @Suppress("USELESS_CAST") override fun itemToObject(item: MetaItem): E = item.enum() as? E ?: error("The Item is not a Enum") - override fun objectToMetaItem(obj: E): MetaItem = ValueItem(obj.asValue()) + override fun objectToMetaItem(obj: E): MetaItem = MetaItemValue(obj.asValue()) } public fun valueList(writer: (T) -> Value = {Value.of(it)}, reader: (Value) -> T): MetaConverter> = @@ -111,7 +111,7 @@ public interface MetaConverter { item.value?.list?.map(reader) ?: error("The item is not a value list") override fun objectToMetaItem(obj: List): MetaItem = - ValueItem(obj.map(writer).asValue()) + MetaItemValue(obj.map(writer).asValue()) } } @@ -120,5 +120,5 @@ public interface MetaConverter { public fun MetaConverter.nullableItemToObject(item: MetaItem?): T? = item?.let { itemToObject(it) } public fun MetaConverter.nullableObjectToMetaItem(obj: T?): MetaItem? = obj?.let { objectToMetaItem(it) } -public fun MetaConverter.metaToObject(meta: Meta): T = itemToObject(NodeItem(meta)) -public fun MetaConverter.valueToObject(value: Value): T = itemToObject(ValueItem(value)) +public fun MetaConverter.metaToObject(meta: Meta): T = itemToObject(MetaItemNode(meta)) +public fun MetaConverter.valueToObject(value: Value): T = itemToObject(MetaItemValue(value)) diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/Named.kt similarity index 97% rename from dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/Named.kt index e32946e0..466f5ee0 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Named.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/Named.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package hep.dataforge.context +package hep.dataforge.misc import hep.dataforge.names.Name import hep.dataforge.names.asName diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/type/Type.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/Type.kt similarity index 91% rename from dataforge-meta/src/commonMain/kotlin/hep/dataforge/type/Type.kt rename to dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/Type.kt index 34c2a113..6f6410b5 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/type/Type.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/Type.kt @@ -1,4 +1,4 @@ -package hep.dataforge.type +package hep.dataforge.misc /** * A text label for internal DataForge type classification. Alternative for mime container type. diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index 9cce4314..297cc29f 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -114,7 +114,7 @@ public fun String.toName(): Name { } else -> when (it) { '.' -> { - val query = if(queryBuilder.isEmpty()) null else queryBuilder.toString() + val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString() yield(NameToken(bodyBuilder.toString(), query)) bodyBuilder = StringBuilder() queryBuilder = StringBuilder() @@ -128,7 +128,7 @@ public fun String.toName(): Name { } } } - val query = if(queryBuilder.isEmpty()) null else queryBuilder.toString() + val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString() yield(NameToken(bodyBuilder.toString(), query)) } return Name(tokens.toList()) @@ -184,7 +184,16 @@ public fun Name.startsWith(token: NameToken): Boolean = firstOrNull() == token public fun Name.endsWith(token: NameToken): Boolean = lastOrNull() == token public fun Name.startsWith(name: Name): Boolean = - this.length >= name.length && tokens.subList(0, name.length) == name.tokens + this.length >= name.length && (this == name || tokens.subList(0, name.length) == name.tokens) public fun Name.endsWith(name: Name): Boolean = - this.length >= name.length && tokens.subList(length - name.length, length) == name.tokens \ No newline at end of file + this.length >= name.length && (this == name || tokens.subList(length - name.length, length) == name.tokens) + +/** + * if [this] starts with given [head] name, returns the reminder of the name (could be empty). Otherwise returns null + */ +public fun Name.removeHeadOrNull(head: Name): Name? = if (startsWith(head)) { + Name(tokens.subList(head.length, head.length)) +} else { + null +} \ No newline at end of file diff --git a/dataforge-meta/src/jsMain/kotlin/hep/dataforge/meta/DynamicMeta.kt b/dataforge-meta/src/jsMain/kotlin/hep/dataforge/meta/DynamicMeta.kt index dadecbfb..b75d487d 100644 --- a/dataforge-meta/src/jsMain/kotlin/hep/dataforge/meta/DynamicMeta.kt +++ b/dataforge-meta/src/jsMain/kotlin/hep/dataforge/meta/DynamicMeta.kt @@ -23,8 +23,8 @@ public fun Meta.toDynamic(): dynamic { if (this is DynamicMeta) return this.obj fun MetaItem.toDynamic(): dynamic = when (this) { - is ValueItem -> this.value.toDynamic() - is NodeItem -> this.node.toDynamic() + is MetaItemValue -> this.value.toDynamic() + is MetaItemNode -> this.node.toDynamic() } val res = js("{}") @@ -50,13 +50,13 @@ public class DynamicMeta(internal val obj: dynamic) : MetaBase() { @Suppress("UNCHECKED_CAST", "USELESS_CAST") private fun asItem(obj: dynamic): TypedMetaItem? { return when { - obj == null -> ValueItem(Null) - isArray(obj) && (obj as Array).all { isPrimitive(it) } -> ValueItem(Value.of(obj as Array)) + obj == null -> MetaItemValue(Null) + isArray(obj) && (obj as Array).all { isPrimitive(it) } -> MetaItemValue(Value.of(obj as Array)) else -> when (jsTypeOf(obj)) { - "boolean" -> ValueItem(Value.of(obj as Boolean)) - "number" -> ValueItem(Value.of(obj as Number)) - "string" -> ValueItem(Value.of(obj as String)) - "object" -> NodeItem(DynamicMeta(obj)) + "boolean" -> MetaItemValue(Value.of(obj as Boolean)) + "number" -> MetaItemValue(Value.of(obj as Number)) + "string" -> MetaItemValue(Value.of(obj as String)) + "object" -> MetaItemNode(DynamicMeta(obj)) else -> null } } @@ -68,7 +68,7 @@ public class DynamicMeta(internal val obj: dynamic) : MetaBase() { if (isArray(value)) { val array = value as Array return@flatMap if (array.all { isPrimitive(it) }) { - listOf(NameToken(key) to ValueItem(Value.of(array))) + listOf(NameToken(key) to MetaItemValue(Value.of(array))) } else { array.mapIndexedNotNull { index, it -> val item = asItem(it) ?: return@mapIndexedNotNull null diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt new file mode 100644 index 00000000..7bd607d2 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt @@ -0,0 +1,96 @@ +package hep.dataforge.workspace + +import hep.dataforge.data.* +import hep.dataforge.meta.* +import hep.dataforge.names.Name +import hep.dataforge.names.plus +import hep.dataforge.names.removeHeadOrNull +import hep.dataforge.names.toName +import kotlinx.coroutines.flow.* +import kotlin.reflect.KClass + +public interface DataPlacement: MetaRepr { + /** + * Select a placement for a data with given [name] and [meta]. The result is null if data should be ignored. + */ + public fun place(name: Name, meta: Meta, dataType: KClass<*>): Name? + + public companion object { + public val ALL: DataPlacement = object : DataPlacement { + override fun place(name: Name, meta: Meta, dataType: KClass<*>): Name = name + + override fun toMeta(): Meta = Meta{"from" put "*"} + } + + } +} + +public fun DataPlacement.place(datum: NamedData<*>): Name? = place(datum.name, datum.meta, datum.type) + +private class ArrangedDataSet( + private val source: DataSet, + private val placement: DataPlacement, +) : DataSet { + override val dataType: KClass get() = source.dataType + + override fun flow(): Flow> = source.flow().mapNotNull { + val newName = placement.place(it) ?: return@mapNotNull null + it.data.named(newName) + } + + override suspend fun getData(name: Name): Data? = flow().filter { it.name == name }.firstOrNull() + + override val updates: Flow = source.updates.flatMapConcat { + flowChildren(it).mapNotNull(placement::place) + } +} + +public class DataPlacementScheme : Scheme(), DataPlacement { + /** + * A source node for the filter + */ + public var from: String? by string() + + /** + * A target placement for the filtered node + */ + public var to: String? by string() + + /** + * A regular expression pattern for the filter + */ + public var pattern: String? by string() +// val prefix by string() +// val suffix by string() + + override fun place(name: Name, meta: Meta, dataType: KClass<*>): Name? { + val fromName = from?.toName() ?: Name.EMPTY + val nameReminder = name.removeHeadOrNull(fromName) ?: return null + val regex = pattern?.toRegex() + return if (regex == null || nameReminder.toString().matches(regex)) { + (to?.toName() ?: Name.EMPTY) + nameReminder + } else { + null + } + } + + public companion object : SchemeSpec(::DataPlacementScheme) +} + + +/** + * Apply data node rearrangement + */ +public fun DataSet.rearrange(placement: DataPlacement): DataSet = ArrangedDataSet(this, placement) + +///** +// * Mask data using [DataPlacementScheme] specification +// */ +//public fun DataSet.rearrange(placement: Meta): DataSet = +// rearrange(DataPlacementScheme.read(placement)) + +/** + * Mask data using [DataPlacementScheme] builder + */ +public fun DataSet.rearrange(placementBuilder: DataPlacementScheme.() -> Unit): DataSet = + rearrange(DataPlacementScheme(placementBuilder)) \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt index f6961866..53ebe5e0 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt @@ -1,56 +1,29 @@ package hep.dataforge.workspace -import hep.dataforge.data.DataFilter -import hep.dataforge.data.DataNode -import hep.dataforge.data.DataTree -import hep.dataforge.data.filter +import hep.dataforge.data.DataSet import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaRepr +import hep.dataforge.meta.builder import hep.dataforge.names.Name import hep.dataforge.names.asName -import hep.dataforge.names.isEmpty import hep.dataforge.names.plus /** * A dependency of the task which allows to lazily create a data tree for single dependency */ public sealed class Dependency : MetaRepr { - public abstract fun apply(workspace: Workspace): DataNode + public abstract suspend fun apply(workspace: Workspace): DataSet } -public class DataDependency(private val filter: DataFilter, private val placement: Name = Name.EMPTY) : Dependency() { - override fun apply(workspace: Workspace): DataNode { - val result = workspace.data.filter(filter) - return if (placement.isEmpty()) { - result - } else { - DataTree(Any::class) { this[placement] = result } - } - } +public class DataDependency(private val placement: DataPlacement = DataPlacement.ALL) : Dependency() { + override suspend fun apply(workspace: Workspace): DataSet = workspace.data.rearrange(placement) - override fun toMeta(): Meta = Meta { - "data" put filter.toMeta() - "to" put placement.toString() - } -} - -public class AllDataDependency(private val placement: Name = Name.EMPTY) : Dependency() { - override fun apply(workspace: Workspace): DataNode = if (placement.isEmpty()) { - workspace.data - } else { - DataTree(Any::class) { this[placement] = workspace.data } - } - - override fun toMeta(): MetaBuilder = Meta { - "data" put "@all" - "to" put placement.toString() - } + override fun toMeta(): Meta = placement.toMeta() } public abstract class TaskDependency( public val meta: Meta, - public val placement: Name = Name.EMPTY + protected val placement: DataPlacement, ) : Dependency() { public abstract fun resolveTask(workspace: Workspace): Task @@ -59,43 +32,42 @@ public abstract class TaskDependency( */ public abstract val name: Name - override fun apply(workspace: Workspace): DataNode { + override suspend fun apply(workspace: Workspace): DataSet { val task = resolveTask(workspace) - if (task.isTerminal) TODO("Support terminal task") val result = workspace.run(task, meta) - return if (placement.isEmpty()) { - result - } else { - DataTree(task.type) { this[placement] = result } - } - } - - override fun toMeta(): Meta = Meta { - "task" put name.toString() - "meta" put meta - "to" put placement.toString() + return result.rearrange(placement) } } -public class DirectTaskDependency( +public class ExternalTaskDependency( public val task: Task, meta: Meta, - placement: Name + placement: DataPlacement, ) : TaskDependency(meta, placement) { override fun resolveTask(workspace: Workspace): Task = task - override val name: Name get() = DIRECT_TASK_NAME + task.name + override val name: Name get() = EXTERNAL_TASK_NAME + task.name + + override fun toMeta(): Meta = placement.toMeta().builder().apply { + "name" put name.toString() + "task" put task.toString() + "meta" put meta + } public companion object { - public val DIRECT_TASK_NAME: Name = "@direct".asName() + public val EXTERNAL_TASK_NAME: Name = "@external".asName() } } public class WorkspaceTaskDependency( override val name: Name, meta: Meta, - placement: Name + placement: DataPlacement, ) : TaskDependency(meta, placement) { - override fun resolveTask(workspace: Workspace): Task<*> = - workspace.tasks[name] ?: error("Task with name $name is not found in the workspace") + override fun resolveTask(workspace: Workspace): Task<*> = workspace.tasks[name] + ?: error("Task with name $name is not found in the workspace") + + override fun toMeta(): Meta { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt index dc540a21..7c5b039f 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt @@ -1,7 +1,7 @@ package hep.dataforge.workspace import hep.dataforge.context.logger -import hep.dataforge.data.DataNode +import hep.dataforge.data.DataSet import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.get @@ -17,10 +17,10 @@ public class GenericTask( override val type: KClass, override val descriptor: NodeDescriptor, private val modelTransform: TaskModelBuilder.(Meta) -> Unit, - private val dataTransform: Workspace.() -> TaskModel.(DataNode) -> DataNode + private val dataTransform: Workspace.() -> suspend TaskModel.(DataSet) -> DataSet ) : Task { - override fun run(workspace: Workspace, model: TaskModel): DataNode { + override suspend fun run(workspace: Workspace, model: TaskModel): DataSet { //validate model validate(model) @@ -42,11 +42,11 @@ public class GenericTask( * task. By default model uses the meta node with the same node as the name of the task. * * @param workspace - * @param taskConfig + * @param taskMeta * @return */ - override fun build(workspace: Workspace, taskConfig: Meta): TaskModel { - val taskMeta = taskConfig[name]?.node ?: taskConfig + override fun build(workspace: Workspace, taskMeta: Meta): TaskModel { + val taskMeta = taskMeta[name]?.node ?: taskMeta val builder = TaskModelBuilder(name, taskMeta) builder.modelTransform(taskMeta) return builder.build() diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt index adec8bb9..4da2f694 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt @@ -3,7 +3,7 @@ package hep.dataforge.workspace import hep.dataforge.context.Context import hep.dataforge.context.gather import hep.dataforge.context.toMap -import hep.dataforge.data.DataNode +import hep.dataforge.data.DataTree import hep.dataforge.meta.Meta import hep.dataforge.names.Name @@ -13,7 +13,7 @@ import hep.dataforge.names.Name */ public class SimpleWorkspace( override val context: Context, - override val data: DataNode, + override val data: DataTree, override val targets: Map, tasks: Collection> ) : Workspace { diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt index 9534c7ec..b17eacbd 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt @@ -1,19 +1,15 @@ package hep.dataforge.workspace -import hep.dataforge.context.Named -import hep.dataforge.data.DataNode +import hep.dataforge.data.DataSet import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.Described -import hep.dataforge.type.Type +import hep.dataforge.misc.Named +import hep.dataforge.misc.Type import hep.dataforge.workspace.Task.Companion.TYPE import kotlin.reflect.KClass @Type(TYPE) public interface Task : Named, Described { - /** - * Terminal task is the one that could not build model lazily - */ - public val isTerminal: Boolean get() = false /** * The explicit type of the node returned by the task @@ -21,13 +17,13 @@ public interface Task : Named, Described { public val type: KClass /** - * Build a model for this task + * Build a model for this task. Does not run any computations unless task [isEager] * * @param workspace - * @param taskConfig + * @param taskMeta * @return */ - public fun build(workspace: Workspace, taskConfig: Meta): TaskModel + public fun build(workspace: Workspace, taskMeta: Meta): TaskModel /** * Check if the model is valid and is acceptable by the task. Throw exception if not. @@ -46,7 +42,7 @@ public interface Task : Named, Described { * @param model - a model to be executed * @return */ - public fun run(workspace: Workspace, model: TaskModel): DataNode + public suspend fun run(workspace: Workspace, model: TaskModel): DataSet public companion object { public const val TYPE: String = "task" diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt index 5053292e..2ce74197 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt @@ -5,15 +5,16 @@ */ package hep.dataforge.workspace -import hep.dataforge.data.DataFilter import hep.dataforge.data.DataTree -import hep.dataforge.data.DataTreeBuilder +import hep.dataforge.data.dynamic +import hep.dataforge.data.update import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.toName import hep.dataforge.workspace.TaskModel.Companion.MODEL_TARGET_KEY +//FIXME TaskModel should store individual propagation of all data elements, not just nodes /** * A model for task execution @@ -24,7 +25,7 @@ import hep.dataforge.workspace.TaskModel.Companion.MODEL_TARGET_KEY public data class TaskModel( val name: Name, val meta: Meta, - val dependencies: Collection + val dependencies: Collection, ) : MetaRepr { //TODO provide a way to get task descriptor //TODO add pre-run check of task result type? @@ -35,12 +36,10 @@ public data class TaskModel( "dependsOn" put { val dataDependencies = dependencies.filterIsInstance() val taskDependencies = dependencies.filterIsInstance>() - setIndexed("data".toName(), dataDependencies.map { it.toMeta() }) + setIndexed("data".toName(), dataDependencies.map { it.toMeta() }) //Should list all data here setIndexed( "task".toName(), - taskDependencies.map { it.toMeta() }) { _, index -> - taskDependencies[index].name.toString() - } + taskDependencies.map { it.toMeta() }) { _, index -> taskDependencies[index].name.toString() } //TODO ensure all dependencies are listed } } @@ -53,12 +52,10 @@ public data class TaskModel( /** * Build input for the task */ -public fun TaskModel.buildInput(workspace: Workspace): DataTree { - return DataTreeBuilder(Any::class).apply { - dependencies.forEach { dep -> - update(dep.apply(workspace)) - } - }.build() +public suspend fun TaskModel.buildInput(workspace: Workspace): DataTree = DataTree.dynamic(workspace.context) { + dependencies.forEach { dep -> + update(dep.apply(workspace)) + } } public interface TaskDependencyContainer { @@ -71,59 +68,53 @@ public interface TaskDependencyContainer { */ public fun TaskDependencyContainer.dependsOn( name: Name, - placement: Name = Name.EMPTY, - meta: Meta = defaultMeta -): WorkspaceTaskDependency = - WorkspaceTaskDependency(name, meta, placement).also { add(it) } + placement: DataPlacement = DataPlacement.ALL, + meta: Meta = defaultMeta, +): WorkspaceTaskDependency = WorkspaceTaskDependency(name, meta, placement).also { add(it) } public fun TaskDependencyContainer.dependsOn( name: String, - placement: Name = Name.EMPTY, - meta: Meta = defaultMeta -): WorkspaceTaskDependency = - dependsOn(name.toName(), placement, meta) + placement: DataPlacement = DataPlacement.ALL, + meta: Meta = defaultMeta, +): WorkspaceTaskDependency = dependsOn(name.toName(), placement, meta) public fun TaskDependencyContainer.dependsOn( task: Task, - placement: Name = Name.EMPTY, - meta: Meta = defaultMeta -): DirectTaskDependency = - DirectTaskDependency(task, meta, placement).also { add(it) } + placement: DataPlacement = DataPlacement.ALL, + meta: Meta = defaultMeta, +): ExternalTaskDependency = ExternalTaskDependency(task, meta, placement).also { add(it) } + public fun TaskDependencyContainer.dependsOn( task: Task, - placement: String, - meta: Meta = defaultMeta -): DirectTaskDependency = - DirectTaskDependency(task, meta, placement.toName()).also { add(it) } - -public fun TaskDependencyContainer.dependsOn( - task: Task, - placement: Name = Name.EMPTY, - metaBuilder: MetaBuilder.() -> Unit -): DirectTaskDependency = - dependsOn(task, placement, Meta(metaBuilder)) + placement: DataPlacement = DataPlacement.ALL, + metaBuilder: MetaBuilder.() -> Unit, +): ExternalTaskDependency = dependsOn(task, placement, Meta(metaBuilder)) /** * Add custom data dependency */ -public fun TaskDependencyContainer.data(action: DataFilter.() -> Unit): DataDependency = - DataDependency(DataFilter(action)).also { add(it) } +public fun TaskDependencyContainer.data(action: DataPlacementScheme.() -> Unit): DataDependency = + DataDependency(DataPlacementScheme(action)).also { add(it) } /** * User-friendly way to add data dependency */ -public fun TaskDependencyContainer.data(pattern: String? = null, from: String? = null, to: String? = null): DataDependency = +public fun TaskDependencyContainer.data( + pattern: String? = null, + from: String? = null, + to: String? = null, +): DataDependency = data { pattern?.let { this.pattern = it } from?.let { this.from = it } to?.let { this.to = it } } -/** - * Add all data as root node - */ -public fun TaskDependencyContainer.allData(to: Name = Name.EMPTY): AllDataDependency = AllDataDependency(to).also { add(it) } +///** +// * Add all data as root node +// */ +//public fun TaskDependencyContainer.allData(to: Name = Name.EMPTY): AllDataDependency = AllDataDependency(to).also { add(it) } /** * A builder for [TaskModel] diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt index 589dce0a..a1c7d27c 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt @@ -1,15 +1,13 @@ package hep.dataforge.workspace import hep.dataforge.context.ContextAware -import hep.dataforge.data.Data -import hep.dataforge.data.DataNode -import hep.dataforge.data.dataSequence +import hep.dataforge.data.DataSet import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder +import hep.dataforge.misc.Type import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.provider.Provider -import hep.dataforge.type.Type @Type(Workspace.TYPE) @@ -17,7 +15,7 @@ public interface Workspace : ContextAware, Provider { /** * The whole data node for current workspace */ - public val data: DataNode + public val data: DataSet /** * All targets associated with the workspace @@ -33,8 +31,7 @@ public interface Workspace : ContextAware, Provider { return when (target) { "target", Meta.TYPE -> targets.mapKeys { it.key.toName() } Task.TYPE -> tasks - Data.TYPE -> data.dataSequence().toMap() - //DataNode.TYPE -> data.nodes.toMap() + //Data.TYPE -> data.flow().toMap() else -> emptyMap() } } @@ -42,7 +39,7 @@ public interface Workspace : ContextAware, Provider { /** * Invoke a task in the workspace utilizing caching if possible */ - public fun run(task: Task, config: Meta): DataNode { + public suspend fun run(task: Task, config: Meta): DataSet { val model = task.build(this, config) task.validate(model) return task.run(this, model) @@ -53,20 +50,20 @@ public interface Workspace : ContextAware, Provider { } } -public fun Workspace.run(task: Task<*>, target: String): DataNode { +public suspend fun Workspace.run(task: Task<*>, target: String): DataSet { val meta = targets[target] ?: error("A target with name $target not found in $this") return run(task, meta) } -public fun Workspace.run(task: String, target: String): DataNode = +public suspend fun Workspace.run(task: String, target: String): DataSet = tasks[task.toName()]?.let { run(it, target) } ?: error("Task with name $task not found") -public fun Workspace.run(task: String, meta: Meta): DataNode = +public suspend fun Workspace.run(task: String, meta: Meta): DataSet = tasks[task.toName()]?.let { run(it, meta) } ?: error("Task with name $task not found") -public fun Workspace.run(task: String, block: MetaBuilder.() -> Unit = {}): DataNode = +public suspend fun Workspace.run(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = run(task, Meta(block)) -public fun Workspace.run(task: Task, metaBuilder: MetaBuilder.() -> Unit = {}): DataNode = +public suspend fun Workspace.run(task: Task, metaBuilder: MetaBuilder.() -> Unit = {}): DataSet = run(task, Meta(metaBuilder)) \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt index 461409e2..c659d2c5 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt @@ -8,8 +8,12 @@ import kotlin.reflect.KClass /** * Convert an [Envelope] to a data via given format. The actual parsing is done lazily. */ -public fun Envelope.toData(type: KClass, format: IOFormat): Data = Data(type, meta) { - data?.readWith(format) ?: error("Can't convert envelope without data to Data") +public fun Envelope.toData(format: IOFormat): Data { + @Suppress("UNCHECKED_CAST") + val kclass: KClass = format.type.classifier as? KClass ?: error("IOFormat type is not a class") + return Data(kclass, meta) { + data?.readWith(format) ?: error("Can't convert envelope without data to Data") + } } public suspend fun Data.toEnvelope(format: IOFormat): Envelope { diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index 3117473f..f41cfa49 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -3,41 +3,23 @@ package hep.dataforge.workspace import hep.dataforge.context.Context import hep.dataforge.context.logger import hep.dataforge.data.* -import hep.dataforge.meta.DFBuilder -import hep.dataforge.meta.Meta +import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.get -import hep.dataforge.meta.string import hep.dataforge.names.Name -import hep.dataforge.names.isEmpty -import hep.dataforge.names.toName import kotlin.reflect.KClass +private typealias DataTransformation = suspend (context: Context, model: TaskModel, data: DataSet) -> DataSet + @DFBuilder -public class TaskBuilder(public val name: Name, public val type: KClass) { - private var modelTransform: TaskModelBuilder.(Meta) -> Unit = { allData() } +@DFExperimental +public class TaskBuilder(private val name: Name, public val type: KClass) { + private var modelTransform: TaskModelBuilder.(Meta) -> Unit = { + data() + } // private val additionalDependencies = HashSet() private var descriptor: NodeDescriptor? = null - private val dataTransforms: MutableList = ArrayList() - - /** - * TODO will look better as extension class - */ - private inner class DataTransformation( - val from: String = "", - val to: String = "", - val transform: (Context, TaskModel, DataNode) -> DataNode, - ) { - operator fun invoke(workspace: Workspace, model: TaskModel, node: DataNode): DataNode? { - val localData = if (from.isEmpty()) { - node - } else { - node[from].node ?: return null - } - return transform(workspace.context, model, localData) - } - } + private val dataTransforms: MutableList> = ArrayList() // override fun add(dependency: Dependency) { // additionalDependencies.add(dependency) @@ -47,75 +29,74 @@ public class TaskBuilder(public val name: Name, public val type: KClass this.modelTransform = modelTransform } - /** - * Add a transformation on untyped data - */ - @JvmName("rawTransform") - public fun transform( - from: String = "", - to: String = "", - block: TaskEnv.(DataNode<*>) -> DataNode, - ) { - dataTransforms += DataTransformation(from, to) { context, model, data -> - val env = TaskEnv(Name.EMPTY, model.meta, context, data) - env.block(data) - } - } - - public fun transform( - inputType: KClass, - from: String = "", - to: String = "", - block: TaskEnv.(DataNode) -> DataNode, - ) { - dataTransforms += DataTransformation(from, to) { context, model, data -> - data.ensureType(inputType) - val env = TaskEnv(Name.EMPTY, model.meta, context, data) - env.block(data.cast(inputType)) - } - } - - public inline fun transform( - from: String = "", - to: String = "", - noinline block: TaskEnv.(DataNode) -> DataNode, - ) { - transform(T::class, from, to, block) - } - - /** - * Perform given action on data elements in `from` node in input and put the result to `to` node - */ - public inline fun action( - from: String = "", - to: String = "", - crossinline block: TaskEnv.() -> Action, - ) { - transform(from, to) { data: DataNode -> - block().invoke(data, meta) - } - } public class TaskEnv( public val name: Name, public val meta: Meta, public val context: Context, - public val data: DataNode, + public val data: DataSet, ) { - public operator fun DirectTaskDependency.invoke(): DataNode = if (placement.isEmpty()) { - data.cast(task.type) - } else { - data[placement].node?.cast(task.type) - ?: error("Could not find results of direct task dependency $this at \"$placement\"") +// public operator fun DirectTaskDependency.invoke(): DataSet = if (placement.isEmpty()) { +// data.cast(task.type) +// } else { +// data[placement].tree?.cast(task.type) +// ?: error("Could not find results of direct task dependency $this at \"$placement\"") +// } + } + + /** + * Add a transformation on untyped data + * @param from the prefix for root node in data + * @param to the prefix for the target node. + */ + @JvmName("rawTransform") + public fun transform( + from: Name = Name.EMPTY, + to: Name = Name.EMPTY, + block: TaskEnv.(DataSet<*>) -> DataSet, + ) { + dataTransforms += { context, model, data -> + val env = TaskEnv(Name.EMPTY, model.meta, context, data) + val startData = data.branch(from) + env.block(startData).withNamePrefix(to) } } + public fun transform( + inputType: KClass, + block: suspend TaskEnv.(DataSet) -> DataSet, + ) { + dataTransforms += { context, model, data -> + val env = TaskEnv(Name.EMPTY, model.meta, context, data) + env.block(data.filterIsInstance(inputType)) + } + } + + public inline fun transform( + noinline block: suspend TaskEnv.(DataSet) -> DataSet, + ): Unit = transform(T::class, block) + + + /** + * Perform given action on data elements in `from` node in input and put the result to `to` node + */ + public inline fun action( + from: Name = Name.EMPTY, + to: Name = Name.EMPTY, + crossinline block: TaskEnv.() -> Action, + ) { + transform { data: DataSet -> + block().run(data, meta, context) + } + } + + /** * A customized map action with ability to change meta and name */ public inline fun mapAction( - from: String = "", - to: String = "", + from: Name = Name.EMPTY, + to: Name = Name.EMPTY, crossinline block: MapActionBuilder.(TaskEnv) -> Unit, ) { action(from, to) { @@ -130,12 +111,12 @@ public class TaskBuilder(public val name: Name, public val type: KClass * A simple map action without changing meta or name */ public inline fun map( - from: String = "", - to: String = "", + from: Name = Name.EMPTY, + to: Name = Name.EMPTY, crossinline block: suspend TaskEnv.(T) -> R, ) { action(from, to) { - MapAction(type) { + MapAction(type) { //TODO automatically append task meta result = { data -> block(data) @@ -148,13 +129,13 @@ public class TaskBuilder(public val name: Name, public val type: KClass * Join elements in gathered data by multiple groups */ public inline fun reduceByGroup( - from: String = "", - to: String = "", + from: Name = Name.EMPTY, + to: Name = Name.EMPTY, crossinline block: ReduceGroupBuilder.(TaskEnv) -> Unit, //TODO needs KEEP-176 ) { action(from, to) { val env = this - ReduceAction(type) { block(env) } + ReduceAction(inputType = T::class, outputType = type) { block(env) } } } @@ -162,12 +143,12 @@ public class TaskBuilder(public val name: Name, public val type: KClass * Join all elemlents in gathered data matching input type */ public inline fun reduce( - from: String = "", - to: String = "", + from: Name = Name.EMPTY, + to: Name = Name.EMPTY, crossinline block: suspend TaskEnv.(Map) -> R, ) { action(from, to) { - ReduceAction(type) { + ReduceAction(inputType = T::class, outputType = type) { result( actionMeta[TaskModel.MODEL_TARGET_KEY]?.string ?: "@anonymous" ) { data -> @@ -181,8 +162,8 @@ public class TaskBuilder(public val name: Name, public val type: KClass * Split each element in gathered data into fixed number of fragments */ public inline fun split( - from: String = "", - to: String = "", + from: Name = Name.EMPTY, + to: Name = Name.EMPTY, crossinline block: SplitBuilder.(TaskEnv) -> Unit, //TODO needs KEEP-176 ) { action(from, to) { @@ -206,26 +187,19 @@ public class TaskBuilder(public val name: Name, public val type: KClass modelTransform ) { val workspace = this - return@GenericTask { data -> + { dataSet -> val model = this if (dataTransforms.isEmpty()) { //return data node as is logger.warn { "No transformation present, returning input data" } - data.ensureType(type) - data.cast(type) + dataSet.castOrNull(type) ?: error("$type expected, but $type received") } else { - val builder = DataTreeBuilder(type) + val builder = MutableDataTree(type, workspace.context) dataTransforms.forEach { transformation -> - val res = transformation(workspace, model, data) - if (res != null) { - if (transformation.to.isEmpty()) { - builder.update(res) - } else { - builder[transformation.to.toName()] = res - } - } + val res = transformation(workspace.context, model, dataSet) + builder.update(res) } - builder.build() + builder } } } diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt index 1289516f..1575a4b5 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -3,11 +3,10 @@ package hep.dataforge.workspace import hep.dataforge.context.Context import hep.dataforge.context.ContextBuilder import hep.dataforge.context.Global -import hep.dataforge.data.DataNode -import hep.dataforge.data.DataTreeBuilder +import hep.dataforge.data.DataTree +import hep.dataforge.data.MutableDataTree import hep.dataforge.meta.* import hep.dataforge.names.Name -import hep.dataforge.names.isEmpty import hep.dataforge.names.toName import kotlin.reflect.KClass @@ -15,7 +14,7 @@ import kotlin.reflect.KClass public interface WorkspaceBuilder { public val parentContext: Context public var context: Context - public var data: DataTreeBuilder + public var data: MutableDataTree public var tasks: MutableSet> public var targets: MutableMap @@ -31,23 +30,17 @@ public fun WorkspaceBuilder.context(name: String = "WORKSPACE", block: ContextBu public inline fun WorkspaceBuilder.data( name: Name = Name.EMPTY, - noinline block: DataTreeBuilder.() -> Unit -): DataNode { - val node = DataTreeBuilder(T::class).apply(block) - if (name.isEmpty()) { - @Suppress("UNCHECKED_CAST") - data = node as DataTreeBuilder - } else { - data[name] = node - } - return node.build() + noinline block: MutableDataTree.() -> Unit, +): DataTree { + TODO() + //data.branch(name).apply(block) } @JvmName("rawData") public fun WorkspaceBuilder.data( name: Name = Name.EMPTY, - block: DataTreeBuilder.() -> Unit -): DataNode = data(name, block) + block: MutableDataTree.() -> Unit, +): DataTree = data(name, block) public fun WorkspaceBuilder.target(name: String, block: MetaBuilder.() -> Unit) { @@ -68,18 +61,18 @@ public fun WorkspaceBuilder.target(name: String, base: String, block: MetaBuilde public fun WorkspaceBuilder.task( name: String, type: KClass, - builder: TaskBuilder.() -> Unit + builder: TaskBuilder.() -> Unit, ): Task = TaskBuilder(name.toName(), type).apply(builder).build().also { tasks.add(it) } public inline fun WorkspaceBuilder.task( name: String, - noinline builder: TaskBuilder.() -> Unit + noinline builder: TaskBuilder.() -> Unit, ): Task = task(name, T::class, builder) @JvmName("rawTask") public fun WorkspaceBuilder.task( name: String, - builder: TaskBuilder.() -> Unit + builder: TaskBuilder.() -> Unit, ): Task = task(name, Any::class, builder) /** @@ -87,12 +80,12 @@ public fun WorkspaceBuilder.task( */ public class SimpleWorkspaceBuilder(override val parentContext: Context) : WorkspaceBuilder { override var context: Context = parentContext - override var data: DataTreeBuilder = DataTreeBuilder(Any::class) + override var data: MutableDataTree = MutableDataTree(Any::class,context) override var tasks: MutableSet> = HashSet() override var targets: MutableMap = HashMap() override fun build(): SimpleWorkspace { - return SimpleWorkspace(context, data.build(), targets, tasks) + return SimpleWorkspace(context, data, targets, tasks) } } diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt index 2cf203d5..28a98f2a 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt @@ -5,6 +5,7 @@ import hep.dataforge.data.* import hep.dataforge.io.* import hep.dataforge.meta.* import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import kotlinx.io.asOutput import java.nio.file.FileSystem @@ -14,9 +15,15 @@ import java.nio.file.StandardOpenOption import java.nio.file.spi.FileSystemProvider import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.streams.toList -public typealias FileFormatResolver = (Path, Meta) -> IOFormat +//public typealias FileFormatResolver = (Path, Meta) -> IOFormat + +public interface FileFormatResolver{ + public val type: KType + public operator fun invoke (path: Path, meta: Meta): IOFormat +} private fun newZFS(path: Path): FileSystem { val fsProvider = FileSystemProvider.installedProviders().find { it.scheme == "jar" } @@ -36,25 +43,23 @@ private fun newZFS(path: Path): FileSystem { @DFExperimental public fun IOPlugin.readDataFile( path: Path, - type: KClass, formatResolver: FileFormatResolver, ): Data { val envelope = readEnvelopeFile(path, true) ?: error("Can't read data from $path") val format = formatResolver(path, envelope.meta) - return envelope.toData(type, format) + return envelope.toData(format) } @DFExperimental -public inline fun IOPlugin.readDataFile(path: Path): Data = - readDataFile(path, T::class) { _, _ -> - resolveIOFormat() ?: error("Can't resolve IO format for ${T::class}") - } +public inline fun IOPlugin.readDataFile(path: Path): Data = readDataFile(path) { _, _ -> + resolveIOFormat() ?: error("Can't resolve IO format for ${T::class}") +} /** * Add file/directory-based data tree item */ @DFExperimental -public fun DataTreeBuilder.file( +public suspend fun DataSetBuilder.file( plugin: IOPlugin, path: Path, formatResolver: FileFormatResolver, @@ -62,18 +67,18 @@ public fun DataTreeBuilder.file( //If path is a single file or a special directory, read it as single datum if (!Files.isDirectory(path) || Files.list(path).allMatch { it.fileName.toString().startsWith("@") }) { plugin.run { - val data = readDataFile(path, type, formatResolver) + val data = readDataFile(path, formatResolver) val name = data.meta[Envelope.ENVELOPE_NAME_KEY].string ?: path.fileName.toString().replace(".df", "") - datum(name, data) + set(name, data) } } else { //otherwise, read as directory plugin.run { - val data = readDataDirectory(path, type, formatResolver) - val name = data.meta[Envelope.ENVELOPE_NAME_KEY].string + val data = readDataDirectory(path, formatResolver) + val name = data.getMeta()[Envelope.ENVELOPE_NAME_KEY].string ?: path.fileName.toString().replace(".df", "") - node(name, data) + set(name, data) } } } @@ -84,31 +89,32 @@ public fun DataTreeBuilder.file( @DFExperimental public fun IOPlugin.readDataDirectory( path: Path, - type: KClass, formatResolver: FileFormatResolver, -): DataNode { +): DataTree { //read zipped data node if (path.fileName != null && path.fileName.toString().endsWith(".zip")) { //Using explicit Zip file system to avoid bizarre compatibility bugs val fs = newZFS(path) - return readDataDirectory(fs.rootDirectories.first(), type, formatResolver) + return readDataDirectory(fs.rootDirectories.first(), formatResolver) } if (!Files.isDirectory(path)) error("Provided path $path is not a directory") - return DataTree(type) { - Files.list(path).forEach { path -> + return DataTree.static(formatResolver.type) { + Files.list(path).toList().forEach { path -> val fileName = path.fileName.toString() if (fileName.startsWith(IOPlugin.META_FILE_NAME)) { meta(readMetaFile(path)) } else if (!fileName.startsWith("@")) { - file(this@readDataDirectory, path, formatResolver) + runBlocking { + file(this@readDataDirectory, path, formatResolver) + } } } } } @DFExperimental -public inline fun IOPlugin.readDataDirectory(path: Path): DataNode = - readDataDirectory(path, T::class) { _, _ -> +public inline fun IOPlugin.readDataDirectory(path: Path): DataTree = + readDataDirectory(path) { _, _ -> resolveIOFormat() ?: error("Can't resolve IO format for ${T::class}") } @@ -118,7 +124,7 @@ public inline fun IOPlugin.readDataDirectory(path: Path): Data @DFExperimental public suspend fun IOPlugin.writeDataDirectory( path: Path, - node: DataNode, + tree: DataTree, format: IOFormat, envelopeFormat: EnvelopeFormat? = null, metaFormat: MetaFormatFactory? = null, @@ -129,13 +135,13 @@ public suspend fun IOPlugin.writeDataDirectory( } else if (!Files.isDirectory(path)) { error("Can't write a node into file") } - node.items.forEach { (token, item) -> + tree.items().forEach { (token, item) -> val childPath = path.resolve(token.toString()) when (item) { is DataItem.Node -> { - writeDataDirectory(childPath, item.node, format, envelopeFormat) + writeDataDirectory(childPath, item.tree, format, envelopeFormat) } - is DataItem.Leaf -> { + is DataTreeItem.Leaf -> { val envelope = item.data.toEnvelope(format) if (envelopeFormat != null) { writeEnvelopeFile(childPath, envelope, envelopeFormat, metaFormat) @@ -145,8 +151,9 @@ public suspend fun IOPlugin.writeDataDirectory( } } } - if (!node.meta.isEmpty()) { - writeMetaFile(path, node.meta, metaFormat ?: JsonMetaFormat) + val treeMeta = tree.getMeta() + if (treeMeta != null) { + writeMetaFile(path, treeMeta, metaFormat ?: JsonMetaFormat) } } } @@ -154,26 +161,26 @@ public suspend fun IOPlugin.writeDataDirectory( private suspend fun ZipOutputStream.writeNode( name: String, - item: DataItem, + treeItem: DataTreeItem, dataFormat: IOFormat, envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat, ) { withContext(Dispatchers.IO) { - when (item) { - is DataItem.Leaf -> { + when (treeItem) { + is DataTreeItem.Leaf -> { //TODO add directory-based envelope writer - val envelope = item.data.toEnvelope(dataFormat) + val envelope = treeItem.data.toEnvelope(dataFormat) val entry = ZipEntry(name) putNextEntry(entry) envelopeFormat.run { writeObject(asOutput(), envelope) } } - is DataItem.Node -> { + is DataTreeItem.Node -> { val entry = ZipEntry("$name/") putNextEntry(entry) closeEntry() - item.node.items.forEach { (token, item) -> + treeItem.tree.items().forEach { (token, item) -> val childName = "$name/$token" writeNode(childName, item, dataFormat, envelopeFormat) } @@ -185,7 +192,7 @@ private suspend fun ZipOutputStream.writeNode( @DFExperimental suspend fun IOPlugin.writeZip( path: Path, - node: DataNode, + tree: DataTree, format: IOFormat, envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat, ) { @@ -201,7 +208,7 @@ suspend fun IOPlugin.writeZip( StandardOpenOption.TRUNCATE_EXISTING) val zos = ZipOutputStream(fos) zos.use { - it.writeNode("", DataItem.Node(node), format, envelopeFormat) + it.writeNode("", DataTreeItem.Node(tree), format, envelopeFormat) } // if (Files.exists(actualFile) && Files.size(path) == 0.toLong()) { diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index 6e0002ed..8853e7f2 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -5,11 +5,15 @@ import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginTag import hep.dataforge.data.* import hep.dataforge.meta.Meta -import hep.dataforge.names.asName +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.reduce +import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass import kotlin.test.Test import kotlin.test.assertEquals +public fun DataSet.first(): NamedData? = runBlocking { flow().firstOrNull() } class DataPropagationTestPlugin : WorkspacePlugin() { override val tag: PluginTag = Companion.tag @@ -19,9 +23,9 @@ class DataPropagationTestPlugin : WorkspacePlugin() { allData() } transform { data -> - DataTree { - val result = data.dataSequence().map { it.second.get() }.reduce { acc, pair -> acc + pair } - set("result".asName(), Data { result }) + DataTree.dynamic { + val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } + data("result", result) } } } @@ -32,9 +36,9 @@ class DataPropagationTestPlugin : WorkspacePlugin() { data("myData\\[12\\]") } transform { data -> - DataTree { - val result = data.dataSequence().map { it.second.get() }.reduce { acc, pair -> acc + pair } - set("result".asName(), Data { result }) + DataTree.dynamic { + val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } + data("result", result) } } } @@ -44,9 +48,9 @@ class DataPropagationTestPlugin : WorkspacePlugin() { data(pattern = "myData.*") } transform { data -> - DataTree{ - val result = data.dataSequence().map { it.second.get() }.reduce { acc, pair -> acc + pair } - set("result".asName(), Data { result }) + DataTree.dynamic { + val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } + data("result", result) } } } @@ -69,7 +73,7 @@ class DataPropagationTest { } data { repeat(100) { - static("myData[$it]", it) + data("myData[$it]", it) } } } @@ -77,18 +81,18 @@ class DataPropagationTest { @Test fun testAllData() { val node = testWorkspace.run("Test.allData") - assertEquals(4950, node.first()!!.get()) + assertEquals(4950, node.first()!!.value()) } @Test fun testAllRegexData() { val node = testWorkspace.run("Test.allRegexData") - assertEquals(4950, node.first()!!.get()) + assertEquals(4950, node.first()!!.value()) } @Test fun testSingleData() { val node = testWorkspace.run("Test.singleData") - assertEquals(12, node.first()!!.get()) + assertEquals(12, node.first()!!.value()) } } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt index 66646359..5401adf2 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt @@ -12,24 +12,29 @@ import kotlinx.io.Output import kotlinx.io.text.readUtf8String import kotlinx.io.text.writeUtf8String import java.nio.file.Files +import kotlin.reflect.KType +import kotlin.reflect.typeOf import kotlin.test.Test import kotlin.test.assertEquals class FileDataTest { - val dataNode = DataTree { - node("dir") { - static("a", "Some string") { + val dataNode = DataTree.static { + set("dir") { + data("a", "Some string") { "content" put "Some string" } } - static("b", "root data") + data("b", "root data") meta { "content" put "This is root meta node" } } object StringIOFormat : IOFormat { + + override val type: KType = typeOf() + override fun writeObject(output: Output, obj: String) { output.writeUtf8String(obj) } @@ -55,7 +60,7 @@ class FileDataTest { println(dir.toUri().toString()) val reconstructed = readDataDirectory(dir, String::class) { _, _ -> StringIOFormat } assertEquals(dataNode["dir.a"]?.meta, reconstructed["dir.a"]?.meta) - assertEquals(dataNode["b"]?.data?.get(), reconstructed["b"]?.data?.get()) + assertEquals(dataNode["b"]?.data?.get(), reconstructed["b"]?.data?.value()) } } @@ -71,7 +76,7 @@ class FileDataTest { println(zip.toUri().toString()) val reconstructed = readDataDirectory(zip, String::class) { _, _ -> StringIOFormat } assertEquals(dataNode["dir.a"]?.meta, reconstructed["dir.a"]?.meta) - assertEquals(dataNode["b"]?.data?.get(), reconstructed["b"]?.data?.get()) + assertEquals(dataNode["b"]?.data?.get(), reconstructed["b"]?.data?.value()) } } } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index c894f6a0..b58c32c2 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -4,6 +4,8 @@ import hep.dataforge.context.* import hep.dataforge.data.* import hep.dataforge.meta.* import hep.dataforge.names.plus +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass import kotlin.test.Test import kotlin.test.assertEquals @@ -12,13 +14,25 @@ import kotlin.test.assertTrue /** * Make a fake-factory for a one single plugin. Useful for unique or test plugins */ -public inline fun P.toFactory(): PluginFactory

    = object : PluginFactory

    { +public inline fun P.toFactory(): PluginFactory

    = object : PluginFactory

    { override fun invoke(meta: Meta, context: Context): P = this@toFactory override val tag: PluginTag = this@toFactory.tag override val type: KClass = P::class } +public fun DataTree<*>.toMeta(): Meta = Meta { + "type" put (dataType.simpleName ?: "undefined") + "items" put { + runBlocking { + flow().collect { + it.name.toString() put it.data.meta + } + } + } +} + + class SimpleWorkspaceTest { val testPlugin = object : WorkspacePlugin() { override val tag: PluginTag = PluginTag("test") @@ -39,7 +53,7 @@ class SimpleWorkspaceTest { data { repeat(100) { - static("myData[$it]", it) + data("myData[$it]", it) } } @@ -47,7 +61,7 @@ class SimpleWorkspaceTest { model { data("myData\\[12\\]") } - map{ + map { it } } @@ -75,13 +89,13 @@ class SimpleWorkspaceTest { val linearDep = dependsOn(linear, placement = "linear") } transform { data -> - val squareNode = data["square"].node!!.cast()//squareDep() - val linearNode = data["linear"].node!!.cast()//linearDep() - DataTree { - squareNode.dataSequence().forEach { (name, _) -> - val newData = Data { - val squareValue = squareNode[name].data!!.get() - val linearValue = linearNode[name].data!!.get() + val squareNode = data["square"].tree!!.filterIsInstance() //squareDep() + val linearNode = data["linear"].tree!!.filterIsInstance() //linearDep() + DataTree.dynamic { + squareNode.flow().collect { + val newData: Data = Data { + val squareValue = squareNode.getData(it.name)!!.value() + val linearValue = linearNode.getData(it.name)!!.value() squareValue + linearValue } set(name, newData) @@ -145,13 +159,13 @@ class SimpleWorkspaceTest { fun testWorkspace() { val node = workspace.run("sum") val res = node.first() - assertEquals(328350, res?.get()) + assertEquals(328350, res?.value()) } @Test fun testMetaPropagation() { val node = workspace.run("sum") { "testFlag" put true } - val res = node.first()?.get() + val res = node.first()?.value() } @Test @@ -170,6 +184,8 @@ class SimpleWorkspaceTest { @Test fun testGather() { val node = workspace.run("filterOne") - assertEquals(12, node.first()?.get()) + runBlocking { + assertEquals(12, node.first()?.value()) + } } } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4d9ca164..da9702f9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index ac734625..f3ac6394 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,8 +10,8 @@ pluginManagement { maven("https://dl.bintray.com/mipt-npm/dev") } - val toolsVersion = "0.7.0" - val kotlinVersion = "1.4.20" + val toolsVersion = "0.7.1" + val kotlinVersion = "1.4.21" plugins { id("ru.mipt.npm.project") version toolsVersion From 23fae9794f7f8241371416ad0a88b8ce24671841 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 10 Jan 2021 17:46:53 +0300 Subject: [PATCH 34/59] WIP full data refactor --- .../hep/dataforge/data/CachingAction.kt | 6 +- .../kotlin/hep/dataforge/data/DataSet.kt | 74 ++++--------------- .../kotlin/hep/dataforge/data/DataTree.kt | 6 +- .../kotlin/hep/dataforge/data/MapAction.kt | 2 +- .../hep/dataforge/data/MutableDataTree.kt | 4 +- .../kotlin/hep/dataforge/data/SplitAction.kt | 4 +- .../hep/dataforge/data/dataSetOperations.kt | 72 ++++++++++++++++++ .../kotlin/hep/dataforge/data/dataJVM.kt | 11 +-- .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 6 +- .../transformations/MetaTransformation.kt | 2 +- .../hep/dataforge/workspace/DataPlacement.kt | 8 ++ .../hep/dataforge/workspace/Dependency.kt | 4 +- .../hep/dataforge/workspace/TaskModel.kt | 4 +- .../hep/dataforge/workspace/TaskBuilder.kt | 2 +- .../dataforge/workspace/WorkspaceBuilder.kt | 2 +- .../hep/dataforge/workspace/fileData.kt | 30 +++++--- .../hep/dataforge/workspace/tasksJVM.kt | 9 +++ .../workspace/DataPropagationTest.kt | 26 ++++--- .../hep/dataforge/workspace/FileDataTest.kt | 20 +++-- .../workspace/SimpleWorkspaceTest.kt | 24 +++--- 20 files changed, 190 insertions(+), 126 deletions(-) create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt create mode 100644 dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/tasksJVM.kt diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt index 52afc8bf..88d001ad 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt @@ -6,7 +6,7 @@ import hep.dataforge.names.startsWith import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect -import kotlin.reflect.KType +import kotlin.reflect.KClass /** * Remove all values with keys starting with [name] @@ -20,7 +20,7 @@ internal fun MutableMap.removeWhatStartsWith(name: Name) { * An action that caches results on-demand and recalculates them on source push */ public abstract class CachingAction( - public val outputType: KType, + public val outputType: KClass, ) : Action { protected abstract fun CoroutineScope.transform( @@ -33,7 +33,7 @@ public abstract class CachingAction( set: DataSet, meta: Meta, scope: CoroutineScope, - ): DataSet = DataTree.dynamic(outputType, scope) { + ): DataSet = DataTree.dynamic(outputType,scope) { collectFrom(scope.transform(set, meta)) scope.let { set.updates.collect { diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt index d14dec4d..c8373949 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt @@ -1,6 +1,7 @@ package hep.dataforge.data -import hep.dataforge.meta.DFExperimental +import hep.dataforge.meta.Meta +import hep.dataforge.meta.set import hep.dataforge.names.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* @@ -46,28 +47,6 @@ public interface DataSet { } } -/** - * A stateless filtered [DataSet] - */ -@DFExperimental -public fun DataSet.filter( - predicate: suspend (Name, Data) -> Boolean, -): DataSet = object : DataSet { - override val dataType: KClass get() = this@filter.dataType - - override fun flow(): Flow> = - this@filter.flow().filter { predicate(it.name, it.data) } - - override suspend fun getData(name: Name): Data? = this@filter.getData(name)?.takeIf { - predicate(name, it) - } - - override val updates: Flow = this@filter.updates.filter flowFilter@{ name -> - val theData = this@filter.getData(name) ?: return@flowFilter false - predicate(name, theData) - } -} - /** * Flow all data nodes with names starting with [branchName] */ @@ -75,40 +54,6 @@ public fun DataSet.flowChildren(branchName: Name): Flow DataSet.branch(branchName: Name): DataSet = if (branchName.isEmpty()) this -else object : DataSet { - override val dataType: KClass get() = this@branch.dataType - - override fun flow(): Flow> = this@branch.flow().mapNotNull { - it.name.removeHeadOrNull(branchName)?.let { name -> - it.data.named(name) - } - } - - override suspend fun getData(name: Name): Data? = this@branch.getData(branchName + name) - - override val updates: Flow get() = this@branch.updates.mapNotNull { it.removeHeadOrNull(branchName) } -} - -/** - * Generate a wrapper data set with a given name prefix appended to all names - */ -public fun DataSet.withNamePrefix(prefix: Name): DataSet = if (prefix.isEmpty()) this -else object : DataSet { - override val dataType: KClass get() = this@withNamePrefix.dataType - - override fun flow(): Flow> = this@withNamePrefix.flow().map { it.data.named(prefix + it.name) } - - override suspend fun getData(name: Name): Data? = - name.removeHeadOrNull(name)?.let { this@withNamePrefix.getData(it) } - - override val updates: Flow get() = this@withNamePrefix.updates.map { prefix + it } - -} - /** * Start computation for all goals in data node and return a job for the whole node */ @@ -118,4 +63,17 @@ public fun DataSet.startAll(coroutineScope: CoroutineScope): Job = }.toList().joinAll() } -public suspend fun DataSet.join(): Unit = coroutineScope { startAll(this).join() } \ No newline at end of file +public suspend fun DataSet.join(): Unit = coroutineScope { startAll(this).join() } + +public suspend fun DataSet<*>.toMeta(): Meta = Meta { + flow().collect { + if (it.name.endsWith(DataSet.META_KEY)) { + set(it.name, it.meta) + } else { + it.name put { + "type" put it.type.simpleName + "meta" put it.meta + } + } + } +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt index 0cb383b0..5f6e198d 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt @@ -97,10 +97,10 @@ public fun DataTree.itemFlow(): Flow>> = * Get a branch of this [DataTree] with a given [branchName]. * The difference from similar method for [DataSet] is that internal logic is more simple and the return value is a [DataTree] */ -public fun DataTree.branch(branchName: Name): DataTree = object : DataTree { - override val dataType: KClass get() = this@branch.dataType +public operator fun DataTree.get(branchName: Name): DataTree = object : DataTree { + override val dataType: KClass get() = this@get.dataType - override val updates: Flow = this@branch.updates.mapNotNull { it.removeHeadOrNull(branchName) } + override val updates: Flow = this@get.updates.mapNotNull { it.removeHeadOrNull(branchName) } override suspend fun items(): Map> = getItem(branchName).tree?.items() ?: emptyMap() } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt index f89d6eeb..e51f5fb0 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt @@ -50,7 +50,7 @@ public class MapAction( //applying transformation from builder val builder = MapActionBuilder( data.name, - data.meta.builder(), // using data meta + data.meta.toMutableMeta(), // using data meta meta ).apply(block) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt index 62d7a80b..d46419c7 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt @@ -155,5 +155,5 @@ public suspend fun DataSet.toMutableTree( }.launchIn(scope) } -public fun MutableDataTree.branch(branchName: Name): MutableDataTree = - (this as DataTree).branch(branchName) as MutableDataTree +public fun MutableDataTree.get(branchName: Name): MutableDataTree = + (this as DataTree).get(branchName) as MutableDataTree diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt index f6ee51ad..851cc82f 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt @@ -3,7 +3,7 @@ package hep.dataforge.data import hep.dataforge.meta.Laminate import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder -import hep.dataforge.meta.builder +import hep.dataforge.meta.toMutableMeta import hep.dataforge.names.Name import hep.dataforge.names.toName import kotlinx.coroutines.CoroutineScope @@ -58,7 +58,7 @@ public class SplitAction( // apply individual fragment rules to result return split.fragments.entries.asFlow().map { (fragmentName, rule) -> - val env = SplitBuilder.FragmentRule(fragmentName, laminate.builder()).apply(rule) + val env = SplitBuilder.FragmentRule(fragmentName, laminate.toMutableMeta()).apply(rule) data.map(outputType, meta = env.meta) { env.result(it) }.named(fragmentName) } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt new file mode 100644 index 00000000..24846c30 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt @@ -0,0 +1,72 @@ +package hep.dataforge.data + +import hep.dataforge.meta.DFExperimental +import hep.dataforge.names.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull +import kotlin.reflect.KClass + + +/** + * A stateless filtered [DataSet] + */ +@DFExperimental +public fun DataSet.filter( + predicate: suspend (Name, Data) -> Boolean, +): DataSet = object : DataSet { + override val dataType: KClass get() = this@filter.dataType + + override fun flow(): Flow> = + this@filter.flow().filter { predicate(it.name, it.data) } + + override suspend fun getData(name: Name): Data? = this@filter.getData(name)?.takeIf { + predicate(name, it) + } + + override val updates: Flow = this@filter.updates.filter flowFilter@{ name -> + val theData = this@filter.getData(name) ?: return@flowFilter false + predicate(name, theData) + } +} + + +/** + * Generate a wrapper data set with a given name prefix appended to all names + */ +public fun DataSet.withNamePrefix(prefix: Name): DataSet = if (prefix.isEmpty()) this +else object : DataSet { + override val dataType: KClass get() = this@withNamePrefix.dataType + + override fun flow(): Flow> = this@withNamePrefix.flow().map { it.data.named(prefix + it.name) } + + override suspend fun getData(name: Name): Data? = + name.removeHeadOrNull(name)?.let { this@withNamePrefix.getData(it) } + + override val updates: Flow get() = this@withNamePrefix.updates.map { prefix + it } +} + + +/** + * Get a subset of data starting with a given [branchName] + */ +public operator fun DataSet.get(branchName: Name): DataSet = if (branchName.isEmpty()) this +else object : DataSet { + override val dataType: KClass get() = this@get.dataType + + override fun flow(): Flow> = this@get.flow().mapNotNull { + it.name.removeHeadOrNull(branchName)?.let { name -> + it.data.named(name) + } + } + + override suspend fun getData(name: Name): Data? = this@get.getData(branchName + name) + + override val updates: Flow get() = this@get.updates.mapNotNull { it.removeHeadOrNull(branchName) } +} + +public operator fun DataSet.get(branchName: String): DataSet = this@get.get(branchName.toName()) + +@DFExperimental +public suspend fun DataSet.rootData(): Data? = getData(Name.EMPTY) diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt index b1c75c7f..775adfc1 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt @@ -1,7 +1,5 @@ package hep.dataforge.data -import hep.dataforge.names.Name -import hep.dataforge.names.toName import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf @@ -60,11 +58,4 @@ public fun DataSet<*>.cast(type: KClass): DataSet = * Check that node is compatible with given type meaning that each element could be cast to the type */ internal fun DataSet<*>.canCast(type: KClass): Boolean = - type.isSubclassOf(this.dataType) - - -public operator fun DataTree.get(name: Name): DataTreeItem? = runBlocking { - getItem(name) -} - -public operator fun DataTree.get(name: String): DataTreeItem? = get(name.toName()) \ No newline at end of file + type.isSubclassOf(this.dataType) \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index a977199f..f2016b80 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -12,7 +12,7 @@ import kotlin.jvm.JvmName */ @DFBuilder public class MetaBuilder : AbstractMutableMeta() { - override fun wrapNode(meta: Meta): MetaBuilder = if (meta is MetaBuilder) meta else meta.builder() + override fun wrapNode(meta: Meta): MetaBuilder = if (meta is MetaBuilder) meta else meta.toMutableMeta() override fun empty(): MetaBuilder = MetaBuilder() public infix fun String.put(item: MetaItem?) { @@ -121,13 +121,13 @@ public class MetaBuilder : AbstractMutableMeta() { /** * For safety, builder always copies the initial meta even if it is builder itself */ -public fun Meta.builder(): MetaBuilder { +public fun Meta.toMutableMeta(): MetaBuilder { return MetaBuilder().also { builder -> items.mapValues { entry -> val item = entry.value builder[entry.key.asName()] = when (item) { is MetaItemValue -> item.value - is MetaItemNode -> MetaItemNode(item.node.builder()) + is MetaItemNode -> MetaItemNode(item.node.toMutableMeta()) } } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt index 865dd776..710998ef 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt @@ -116,7 +116,7 @@ public inline class MetaTransformation(public val transformations: Collection rule.selectItems(source).forEach { name -> remove(name) diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt index 7bd607d2..04e3ecb4 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt @@ -22,6 +22,14 @@ public interface DataPlacement: MetaRepr { override fun toMeta(): Meta = Meta{"from" put "*"} } + public fun into(target: Name): DataPlacement = DataPlacementScheme{ + to = target.toString() + } + + public fun into(target: String): DataPlacement = DataPlacementScheme{ + to = target + } + } } diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt index 53ebe5e0..83b2027c 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt @@ -3,7 +3,7 @@ package hep.dataforge.workspace import hep.dataforge.data.DataSet import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr -import hep.dataforge.meta.builder +import hep.dataforge.meta.toMutableMeta import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.plus @@ -48,7 +48,7 @@ public class ExternalTaskDependency( override val name: Name get() = EXTERNAL_TASK_NAME + task.name - override fun toMeta(): Meta = placement.toMeta().builder().apply { + override fun toMeta(): Meta = placement.toMeta().toMutableMeta().apply { "name" put name.toString() "task" put task.toString() "meta" put meta diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt index 2ce74197..590d2e2a 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt @@ -123,8 +123,8 @@ public class TaskModelBuilder(public val name: Name, meta: Meta = Meta.EMPTY) : /** * Meta for current task. By default uses the whole input meta */ - public var meta: MetaBuilder = meta.builder() - private val dependencies: HashSet = HashSet() + public var meta: MetaBuilder = meta.toMutableMeta() + private val dependencies: HashSet = HashSet() override val defaultMeta: Meta get() = meta diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index f41cfa49..a34ca581 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -57,7 +57,7 @@ public class TaskBuilder(private val name: Name, public val type: KClas ) { dataTransforms += { context, model, data -> val env = TaskEnv(Name.EMPTY, model.meta, context, data) - val startData = data.branch(from) + val startData = data.get(from) env.block(startData).withNamePrefix(to) } } diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt index 1575a4b5..b78c76ab 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -52,7 +52,7 @@ public fun WorkspaceBuilder.target(name: String, block: MetaBuilder.() -> Unit) */ public fun WorkspaceBuilder.target(name: String, base: String, block: MetaBuilder.() -> Unit) { val parentTarget = targets[base] ?: error("Base target with name $base not found") - targets[name] = parentTarget.builder() + targets[name] = parentTarget.toMutableMeta() .apply { "@baseTarget" put base } .apply(block) .seal() diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt index 28a98f2a..d1601c91 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt @@ -15,16 +15,30 @@ import java.nio.file.StandardOpenOption import java.nio.file.spi.FileSystemProvider import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream +import kotlin.reflect.KClass import kotlin.reflect.KType +import kotlin.reflect.typeOf import kotlin.streams.toList //public typealias FileFormatResolver = (Path, Meta) -> IOFormat -public interface FileFormatResolver{ +public interface FileFormatResolver { public val type: KType - public operator fun invoke (path: Path, meta: Meta): IOFormat + public operator fun invoke(path: Path, meta: Meta): IOFormat } +@PublishedApi +internal inline fun IOPlugin.formatResolver(): FileFormatResolver = + object : FileFormatResolver { + override val type: KType = typeOf() + + override fun invoke(path: Path, meta: Meta): IOFormat = + resolveIOFormat() ?: error("Can't resolve IO format for ${T::class}") + } + +private val FileFormatResolver.kClass: KClass + get() = type.classifier as? KClass ?: error("Format resolver actual type does not correspond to type parameter") + private fun newZFS(path: Path): FileSystem { val fsProvider = FileSystemProvider.installedProviders().find { it.scheme == "jar" } ?: error("Zip file system provider not found") @@ -51,9 +65,7 @@ public fun IOPlugin.readDataFile( } @DFExperimental -public inline fun IOPlugin.readDataFile(path: Path): Data = readDataFile(path) { _, _ -> - resolveIOFormat() ?: error("Can't resolve IO format for ${T::class}") -} +public inline fun IOPlugin.readDataFile(path: Path): Data = readDataFile(path, formatResolver()) /** * Add file/directory-based data tree item @@ -98,7 +110,7 @@ public fun IOPlugin.readDataDirectory( return readDataDirectory(fs.rootDirectories.first(), formatResolver) } if (!Files.isDirectory(path)) error("Provided path $path is not a directory") - return DataTree.static(formatResolver.type) { + return DataTree.static(formatResolver.kClass) { Files.list(path).toList().forEach { path -> val fileName = path.fileName.toString() if (fileName.startsWith(IOPlugin.META_FILE_NAME)) { @@ -114,9 +126,7 @@ public fun IOPlugin.readDataDirectory( @DFExperimental public inline fun IOPlugin.readDataDirectory(path: Path): DataTree = - readDataDirectory(path) { _, _ -> - resolveIOFormat() ?: error("Can't resolve IO format for ${T::class}") - } + readDataDirectory(path, formatResolver()) /** * Write data tree to existing directory or create a new one using default [java.nio.file.FileSystem] provider @@ -138,7 +148,7 @@ public suspend fun IOPlugin.writeDataDirectory( tree.items().forEach { (token, item) -> val childPath = path.resolve(token.toString()) when (item) { - is DataItem.Node -> { + is DataTreeItem.Node -> { writeDataDirectory(childPath, item.tree, format, envelopeFormat) } is DataTreeItem.Leaf -> { diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/tasksJVM.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/tasksJVM.kt new file mode 100644 index 00000000..6d4b29bd --- /dev/null +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/tasksJVM.kt @@ -0,0 +1,9 @@ +package hep.dataforge.workspace + +import hep.dataforge.data.DataSet +import hep.dataforge.meta.MetaBuilder +import kotlinx.coroutines.runBlocking + +public fun Workspace.runBlocking(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = runBlocking{ + run(task, block) +} \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index 8853e7f2..9a531731 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -20,10 +20,10 @@ class DataPropagationTestPlugin : WorkspacePlugin() { val testAllData = task("allData", Int::class) { model { - allData() + data() } transform { data -> - DataTree.dynamic { + DataTree.dynamic(context) { val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } data("result", result) } @@ -36,7 +36,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { data("myData\\[12\\]") } transform { data -> - DataTree.dynamic { + DataTree.dynamic(context) { val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } data("result", result) } @@ -48,7 +48,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { data(pattern = "myData.*") } transform { data -> - DataTree.dynamic { + DataTree.dynamic(context) { val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } data("result", result) } @@ -80,19 +80,25 @@ class DataPropagationTest { @Test fun testAllData() { - val node = testWorkspace.run("Test.allData") - assertEquals(4950, node.first()!!.value()) + runBlocking { + val node = testWorkspace.run("Test.allData") + assertEquals(4950, node.first()!!.value()) + } } @Test fun testAllRegexData() { - val node = testWorkspace.run("Test.allRegexData") - assertEquals(4950, node.first()!!.value()) + runBlocking { + val node = testWorkspace.run("Test.allRegexData") + assertEquals(4950, node.first()!!.value()) + } } @Test fun testSingleData() { - val node = testWorkspace.run("Test.singleData") - assertEquals(12, node.first()!!.value()) + runBlocking { + val node = testWorkspace.run("Test.singleData") + assertEquals(12, node.first()!!.value()) + } } } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt index 5401adf2..a2a029d5 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt @@ -12,6 +12,7 @@ import kotlinx.io.Output import kotlinx.io.text.readUtf8String import kotlinx.io.text.writeUtf8String import java.nio.file.Files +import java.nio.file.Path import kotlin.reflect.KType import kotlin.reflect.typeOf import kotlin.test.Test @@ -49,6 +50,13 @@ class FileDataTest { } + object StringFormatResolver: FileFormatResolver{ + override val type: KType = typeOf() + + override fun invoke(path: Path, meta: Meta): IOFormat =StringIOFormat + + } + @Test @DFExperimental fun testDataWriteRead() { @@ -58,9 +66,9 @@ class FileDataTest { writeDataDirectory(dir, dataNode, StringIOFormat) } println(dir.toUri().toString()) - val reconstructed = readDataDirectory(dir, String::class) { _, _ -> StringIOFormat } - assertEquals(dataNode["dir.a"]?.meta, reconstructed["dir.a"]?.meta) - assertEquals(dataNode["b"]?.data?.get(), reconstructed["b"]?.data?.value()) + val reconstructed = readDataDirectory(dir,StringFormatResolver) + assertEquals(dataNode["dir.a"].data?.meta, reconstructed["dir.a"].data?.meta) + assertEquals(dataNode["b"]?.data?.value(), reconstructed["b"]?.data?.value()) } } @@ -74,9 +82,9 @@ class FileDataTest { writeZip(zip, dataNode, StringIOFormat) } println(zip.toUri().toString()) - val reconstructed = readDataDirectory(zip, String::class) { _, _ -> StringIOFormat } - assertEquals(dataNode["dir.a"]?.meta, reconstructed["dir.a"]?.meta) - assertEquals(dataNode["b"]?.data?.get(), reconstructed["b"]?.data?.value()) + val reconstructed = readDataDirectory(zip, StringFormatResolver) + assertEquals(dataNode["dir.a"].data?.meta, reconstructed["dir.a"].data?.meta) + assertEquals(dataNode["b"]?.data?.value(), reconstructed["b"]?.data?.value()) } } } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index b58c32c2..680b0016 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -85,13 +85,13 @@ class SimpleWorkspaceTest { val fullSquare = task("fullsquare") { model { - val squareDep = dependsOn(square, placement = "square") - val linearDep = dependsOn(linear, placement = "linear") + val squareDep = dependsOn(square, placement = DataPlacement.into("square")) + val linearDep = dependsOn(linear, placement = DataPlacement.into("linear")) } transform { data -> - val squareNode = data["square"].tree!!.filterIsInstance() //squareDep() - val linearNode = data["linear"].tree!!.filterIsInstance() //linearDep() - DataTree.dynamic { + val squareNode = data["square"].filterIsInstance() //squareDep() + val linearNode = data["linear"].filterIsInstance() //linearDep() + DataTree.dynamic(context) { squareNode.flow().collect { val newData: Data = Data { val squareValue = squareNode.getData(it.name)!!.value() @@ -142,7 +142,7 @@ class SimpleWorkspaceTest { val customPipeTask = task("custom") { mapAction { - meta = meta.builder().apply { + meta = meta.toMutableMeta().apply { "newValue" put 22 } name += "new" @@ -157,14 +157,14 @@ class SimpleWorkspaceTest { @Test fun testWorkspace() { - val node = workspace.run("sum") + val node = workspace.runBlocking("sum") val res = node.first() assertEquals(328350, res?.value()) } @Test fun testMetaPropagation() { - val node = workspace.run("sum") { "testFlag" put true } + val node = workspace.runBlocking("sum") { "testFlag" put true } val res = node.first()?.value() } @@ -177,13 +177,15 @@ class SimpleWorkspaceTest { @Test fun testFullSquare() { - val node = workspace.run("fullsquare") - println(node.toMeta()) + runBlocking { + val node = workspace.run("fullsquare") + println(node.toMeta()) + } } @Test fun testGather() { - val node = workspace.run("filterOne") + val node = workspace.runBlocking("filterOne") runBlocking { assertEquals(12, node.first()?.value()) } From b916a038f70a9a4c20753513924d2677ee1c9896 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 10 Jan 2021 18:18:34 +0300 Subject: [PATCH 35/59] WIP full data refactor --- .../kotlin/hep/dataforge/data/Data.kt | 2 +- .../kotlin/hep/dataforge/data/DataTree.kt | 8 +++++--- .../hep/dataforge/data/MutableDataTree.kt | 3 --- .../hep/dataforge/data/dataSetOperations.kt | 12 +++++------ .../hep/dataforge/data/DataTreeBuilderTest.kt | 4 ++-- .../hep/dataforge/workspace/TaskBuilder.kt | 2 +- .../dataforge/workspace/WorkspaceBuilder.kt | 20 +++++-------------- .../hep/dataforge/workspace/tasksJVM.kt | 9 --------- .../workspace/DataPropagationTest.kt | 2 +- .../hep/dataforge/workspace/FileDataTest.kt | 12 +++++++---- .../workspace/SimpleWorkspaceTest.kt | 17 ++++++---------- 11 files changed, 35 insertions(+), 56 deletions(-) delete mode 100644 dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/tasksJVM.kt diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 259c69e3..954df54d 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -44,7 +44,7 @@ public interface Data : Goal, MetaRepr { /** * An empty data containing only meta */ - public fun empty(meta: Meta): Data = object : Data { + public fun empty(meta: Meta): Data = object : Data { override val type: KClass = Nothing::class override val meta: Meta = meta override val dependencies: Collection> = emptyList() diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt index 5f6e198d..939066a9 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt @@ -67,6 +67,8 @@ public interface DataTree : DataSet { } } +public suspend fun DataSet.getData(name: String): Data? = getData(name.toName()) + /** * Get a [DataTreeItem] with given [name] or null if the item does not exist */ @@ -97,10 +99,10 @@ public fun DataTree.itemFlow(): Flow>> = * Get a branch of this [DataTree] with a given [branchName]. * The difference from similar method for [DataSet] is that internal logic is more simple and the return value is a [DataTree] */ -public operator fun DataTree.get(branchName: Name): DataTree = object : DataTree { - override val dataType: KClass get() = this@get.dataType +public fun DataTree.branch(branchName: Name): DataTree = object : DataTree { + override val dataType: KClass get() = this@branch.dataType - override val updates: Flow = this@get.updates.mapNotNull { it.removeHeadOrNull(branchName) } + override val updates: Flow = this@branch.updates.mapNotNull { it.removeHeadOrNull(branchName) } override suspend fun items(): Map> = getItem(branchName).tree?.items() ?: emptyMap() } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt index d46419c7..863dda43 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt @@ -154,6 +154,3 @@ public suspend fun DataSet.toMutableTree( set(it, getData(it)) }.launchIn(scope) } - -public fun MutableDataTree.get(branchName: Name): MutableDataTree = - (this as DataTree).get(branchName) as MutableDataTree diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt index 24846c30..9db5f963 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt @@ -51,22 +51,22 @@ else object : DataSet { /** * Get a subset of data starting with a given [branchName] */ -public operator fun DataSet.get(branchName: Name): DataSet = if (branchName.isEmpty()) this +public fun DataSet.branch(branchName: Name): DataSet = if (branchName.isEmpty()) this else object : DataSet { - override val dataType: KClass get() = this@get.dataType + override val dataType: KClass get() = this@branch.dataType - override fun flow(): Flow> = this@get.flow().mapNotNull { + override fun flow(): Flow> = this@branch.flow().mapNotNull { it.name.removeHeadOrNull(branchName)?.let { name -> it.data.named(name) } } - override suspend fun getData(name: Name): Data? = this@get.getData(branchName + name) + override suspend fun getData(name: Name): Data? = this@branch.getData(branchName + name) - override val updates: Flow get() = this@get.updates.mapNotNull { it.removeHeadOrNull(branchName) } + override val updates: Flow get() = this@branch.updates.mapNotNull { it.removeHeadOrNull(branchName) } } -public operator fun DataSet.get(branchName: String): DataSet = this@get.get(branchName.toName()) +public fun DataSet.branch(branchName: String): DataSet = this@branch.branch(branchName.toName()) @DFExperimental public suspend fun DataSet.rootData(): Data? = getData(Name.EMPTY) diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt index 5fbeebe5..f93d6ee7 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt @@ -27,8 +27,8 @@ internal class DataTreeBuilderTest{ } - assertTrue { node["update.a"] != null } - assertTrue { node["primary.a"] != null } + assertTrue { node.branch("update.a") != null } + assertTrue { node.branch("primary.a") != null } } } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index a34ca581..f41cfa49 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -57,7 +57,7 @@ public class TaskBuilder(private val name: Name, public val type: KClas ) { dataTransforms += { context, model, data -> val env = TaskEnv(Name.EMPTY, model.meta, context, data) - val startData = data.get(from) + val startData = data.branch(from) env.block(startData).withNamePrefix(to) } } diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt index b78c76ab..83be2f63 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -3,10 +3,8 @@ package hep.dataforge.workspace import hep.dataforge.context.Context import hep.dataforge.context.ContextBuilder import hep.dataforge.context.Global -import hep.dataforge.data.DataTree import hep.dataforge.data.MutableDataTree import hep.dataforge.meta.* -import hep.dataforge.names.Name import hep.dataforge.names.toName import kotlin.reflect.KClass @@ -28,19 +26,11 @@ public fun WorkspaceBuilder.context(name: String = "WORKSPACE", block: ContextBu context = ContextBuilder(parentContext, name).apply(block).build() } -public inline fun WorkspaceBuilder.data( - name: Name = Name.EMPTY, - noinline block: MutableDataTree.() -> Unit, -): DataTree { - TODO() - //data.branch(name).apply(block) -} - -@JvmName("rawData") -public fun WorkspaceBuilder.data( - name: Name = Name.EMPTY, +public inline fun WorkspaceBuilder.data( block: MutableDataTree.() -> Unit, -): DataTree = data(name, block) +): Unit{ + data.apply(block) +} public fun WorkspaceBuilder.target(name: String, block: MetaBuilder.() -> Unit) { @@ -80,7 +70,7 @@ public fun WorkspaceBuilder.task( */ public class SimpleWorkspaceBuilder(override val parentContext: Context) : WorkspaceBuilder { override var context: Context = parentContext - override var data: MutableDataTree = MutableDataTree(Any::class,context) + override var data: MutableDataTree = MutableDataTree(Any::class, context) override var tasks: MutableSet> = HashSet() override var targets: MutableMap = HashMap() diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/tasksJVM.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/tasksJVM.kt deleted file mode 100644 index 6d4b29bd..00000000 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/tasksJVM.kt +++ /dev/null @@ -1,9 +0,0 @@ -package hep.dataforge.workspace - -import hep.dataforge.data.DataSet -import hep.dataforge.meta.MetaBuilder -import kotlinx.coroutines.runBlocking - -public fun Workspace.runBlocking(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = runBlocking{ - run(task, block) -} \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index 9a531731..323b44de 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -13,7 +13,7 @@ import kotlin.reflect.KClass import kotlin.test.Test import kotlin.test.assertEquals -public fun DataSet.first(): NamedData? = runBlocking { flow().firstOrNull() } +fun DataSet.first(): NamedData? = runBlocking { flow().firstOrNull() } class DataPropagationTestPlugin : WorkspacePlugin() { override val tag: PluginTag = Companion.tag diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt index a2a029d5..b5a7c445 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt @@ -67,8 +67,10 @@ class FileDataTest { } println(dir.toUri().toString()) val reconstructed = readDataDirectory(dir,StringFormatResolver) - assertEquals(dataNode["dir.a"].data?.meta, reconstructed["dir.a"].data?.meta) - assertEquals(dataNode["b"]?.data?.value(), reconstructed["b"]?.data?.value()) + runBlocking { + assertEquals(dataNode.getData("dir.a")?.meta, reconstructed.getData("dir.a")?.meta) + assertEquals(dataNode.getData("b")?.value(), reconstructed.getData("b")?.value()) + } } } @@ -83,8 +85,10 @@ class FileDataTest { } println(zip.toUri().toString()) val reconstructed = readDataDirectory(zip, StringFormatResolver) - assertEquals(dataNode["dir.a"].data?.meta, reconstructed["dir.a"].data?.meta) - assertEquals(dataNode["b"]?.data?.value(), reconstructed["b"]?.data?.value()) + runBlocking { + assertEquals(dataNode.getData("dir.a")?.meta, reconstructed.getData("dir.a")?.meta) + assertEquals(dataNode.getData("b")?.value(), reconstructed.getData("b")?.value()) + } } } } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index 680b0016..c0379594 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -6,6 +6,7 @@ import hep.dataforge.meta.* import hep.dataforge.names.plus import kotlinx.coroutines.flow.collect import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Timeout import kotlin.reflect.KClass import kotlin.test.Test import kotlin.test.assertEquals @@ -21,15 +22,8 @@ public inline fun P.toFactory(): PluginFactory

    = object override val type: KClass = P::class } -public fun DataTree<*>.toMeta(): Meta = Meta { - "type" put (dataType.simpleName ?: "undefined") - "items" put { - runBlocking { - flow().collect { - it.name.toString() put it.data.meta - } - } - } +public fun Workspace.runBlocking(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = runBlocking{ + run(task, block) } @@ -89,8 +83,8 @@ class SimpleWorkspaceTest { val linearDep = dependsOn(linear, placement = DataPlacement.into("linear")) } transform { data -> - val squareNode = data["square"].filterIsInstance() //squareDep() - val linearNode = data["linear"].filterIsInstance() //linearDep() + val squareNode = data.branch("square").filterIsInstance() //squareDep() + val linearNode = data.branch("linear").filterIsInstance() //linearDep() DataTree.dynamic(context) { squareNode.flow().collect { val newData: Data = Data { @@ -163,6 +157,7 @@ class SimpleWorkspaceTest { } @Test + @Timeout(400) fun testMetaPropagation() { val node = workspace.runBlocking("sum") { "testFlag" put true } val res = node.first()?.value() From 33343806934ace780efc73e0a0abf5ffc9eb29bf Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 12 Jan 2021 18:34:25 +0300 Subject: [PATCH 36/59] Add toString to Path --- .../kotlin/hep/dataforge/provider/Path.kt | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt index 77f05771..149a9ece 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt @@ -19,28 +19,15 @@ import hep.dataforge.names.Name import hep.dataforge.names.toName /** - * - * * Path interface. * - * @author Alexander Nozik - * @version $Id: $Id */ public inline class Path(public val tokens: List) : Iterable { - public val head: PathToken? get() = tokens.firstOrNull() - - public val length: Int get() = tokens.size - - /** - * Returns non-empty optional containing the chain without first segment in case of chain path. - * - * @return - */ - public val tail: Path? get() = if (tokens.isEmpty()) null else Path(tokens.drop(1)) - override fun iterator(): Iterator = tokens.iterator() + override fun toString(): String = tokens.joinToString(separator = PATH_SEGMENT_SEPARATOR) + public companion object { public const val PATH_SEGMENT_SEPARATOR: String = "/" @@ -52,6 +39,19 @@ public inline class Path(public val tokens: List) : Iterable Date: Sat, 16 Jan 2021 12:44:39 +0300 Subject: [PATCH 37/59] [WIP] redo DataSet fill and updates --- .../kotlin/hep/dataforge/data/Action.kt | 8 +- .../hep/dataforge/data/CachingAction.kt | 21 ++-- .../kotlin/hep/dataforge/data/Data.kt | 23 +++- .../kotlin/hep/dataforge/data/DataSet.kt | 4 +- .../hep/dataforge/data/DataSetBuilder.kt | 67 +++++++--- .../kotlin/hep/dataforge/data/DataTree.kt | 8 +- .../kotlin/hep/dataforge/data/GroupRule.kt | 2 +- .../kotlin/hep/dataforge/data/MapAction.kt | 39 +++--- .../hep/dataforge/data/MutableDataTree.kt | 116 +++++++----------- .../kotlin/hep/dataforge/data/SplitAction.kt | 25 ++-- .../hep/dataforge/data/StaticDataTree.kt | 26 ++-- .../kotlin/hep/dataforge/data/dataSetMeta.kt | 4 +- .../kotlin/hep/dataforge/data/ActionsTest.kt | 39 ++++++ .../hep/dataforge/data/DataTreeBuilderTest.kt | 65 +++++++--- .../kotlin/hep/dataforge/names/Name.kt | 2 +- .../kotlin/hep/dataforge/workspace/Task.kt | 3 +- .../hep/dataforge/workspace/TaskModel.kt | 11 +- .../hep/dataforge/workspace/TaskBuilder.kt | 25 ++-- .../dataforge/workspace/WorkspaceBuilder.kt | 4 +- .../workspace/DataPropagationTest.kt | 2 +- .../workspace/SimpleWorkspaceTest.kt | 7 +- 21 files changed, 301 insertions(+), 200 deletions(-) create mode 100644 dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt index 514efe08..3b72c718 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* /** - * A simple data transformation on a data node. Actions should avoid doing actual dependency evaluation in [run]. + * A simple data transformation on a data node. Actions should avoid doing actual dependency evaluation in [execute]. */ public interface Action { /** @@ -14,7 +14,7 @@ public interface Action { * * [scope] context used to compute the initial result, also it is used for updates propagation */ - public suspend fun run(set: DataSet, meta: Meta, scope: CoroutineScope): DataSet + public suspend fun execute(dataSet: DataSet, meta: Meta = Meta.EMPTY, scope: CoroutineScope? = null): DataSet public companion object } @@ -25,8 +25,8 @@ public interface Action { public infix fun Action.then(action: Action): Action { // TODO introduce composite action and add optimize by adding action to the list return object : Action { - override suspend fun run(set: DataSet, meta: Meta, scope: CoroutineScope): DataSet { - return action.run(this@then.run(set, meta, scope), meta, scope) + override suspend fun execute(dataSet: DataSet, meta: Meta, scope: CoroutineScope?): DataSet { + return action.execute(this@then.execute(dataSet, meta, scope), meta, scope) } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt index 88d001ad..45d3e888 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt @@ -4,6 +4,7 @@ import hep.dataforge.meta.Meta import hep.dataforge.names.Name import hep.dataforge.names.startsWith import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlin.reflect.KClass @@ -29,20 +30,22 @@ public abstract class CachingAction( key: Name = Name.EMPTY, ): Flow> - override suspend fun run( - set: DataSet, + override suspend fun execute( + dataSet: DataSet, meta: Meta, - scope: CoroutineScope, - ): DataSet = DataTree.dynamic(outputType,scope) { - collectFrom(scope.transform(set, meta)) - scope.let { - set.updates.collect { + scope: CoroutineScope?, + ): DataSet = DataTree.dynamic(outputType) { + coroutineScope { + collectFrom(transform(dataSet, meta)) + } + scope?.let { + dataSet.updates.collect { //clear old nodes remove(it) //collect new items - collectFrom(scope.transform(set, meta, it)) + collectFrom(scope.transform(dataSet, meta, it)) //FIXME if the target is data, updates are fired twice } } } -} \ No newline at end of file +} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 954df54d..39983534 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -44,13 +44,15 @@ public interface Data : Goal, MetaRepr { /** * An empty data containing only meta */ - public fun empty(meta: Meta): Data = object : Data { + public fun empty(meta: Meta): Data = object : Data { override val type: KClass = Nothing::class override val meta: Meta = meta override val dependencies: Collection> = emptyList() - override val deferred: Deferred get() = GlobalScope.async(start = CoroutineStart.LAZY) { - error("The Data is empty and could not be computed") - } + override val deferred: Deferred + get() = GlobalScope.async(start = CoroutineStart.LAZY) { + error("The Data is empty and could not be computed") + } + override fun async(coroutineScope: CoroutineScope): Deferred = deferred override fun reset() {} } @@ -92,7 +94,18 @@ public inline fun Data( public class NamedData internal constructor( override val name: Name, public val data: Data, -) : Data by data, Named +) : Data by data, Named { + override fun toString(): String = buildString { + append("NamedData(name=\"$name\"") + if(data is StaticData){ + append(", value=${data.value}") + } + if(!data.meta.isEmpty()){ + append(", meta=${data.meta}") + } + append(")") + } +} public fun Data.named(name: Name): NamedData = if (this is NamedData) { NamedData(name, this.data) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt index c8373949..84c053f4 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt @@ -76,4 +76,6 @@ public suspend fun DataSet<*>.toMeta(): Meta = Meta { } } } -} \ No newline at end of file +} + +public val DataSet.updatesWithData: Flow> get() = updates.mapNotNull { getData(it)?.named(it) } \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt index 2ddc3106..17120270 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt @@ -4,50 +4,81 @@ import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder import hep.dataforge.names.Name +import hep.dataforge.names.plus import hep.dataforge.names.toName import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect public interface DataSetBuilder { - public fun remove(name: Name) + /** + * Remove all data items starting with [name] + */ + public suspend fun remove(name: Name) - public operator fun set(name: Name, data: Data?) + public suspend fun set(name: Name, data: Data?) - public suspend fun set(name: Name, dataSet: DataSet) - - public operator fun set(name: Name, block: DataSetBuilder.() -> Unit) + /** + * Set a current state of given [dataSet] into a branch [name]. Does not propagate updates + */ + public suspend fun set(name: Name, dataSet: DataSet){ + //remove previous items + remove(name) + //Set new items + dataSet.flow().collect { + set(name + it.name, it.data) + } + } /** * Append data to node */ - public infix fun String.put(data: Data): Unit = set(toName(), data) + public suspend infix fun String.put(data: Data): Unit = set(toName(), data) /** * Append node */ - public suspend infix fun String.put(tree: DataSet): Unit = set(toName(), tree) + public suspend infix fun String.put(dataSet: DataSet): Unit = set(toName(), dataSet) /** * Build and append node */ - public infix fun String.put(block: DataSetBuilder.() -> Unit): Unit = set(toName(), block) + public suspend infix fun String.put(block: suspend DataSetBuilder.() -> Unit): Unit = set(toName(), block) +} +private class SubSetBuilder(private val parent: DataSetBuilder, private val branch: Name) : + DataSetBuilder { + override suspend fun remove(name: Name) { + parent.remove(branch + name) + } + + override suspend fun set(name: Name, data: Data?) { + parent.set(branch + name, data) + } + + override suspend fun set(name: Name, dataSet: DataSet) { + parent.set(branch + name, dataSet) + } +} + +public suspend fun DataSetBuilder.set(name: Name, block: suspend DataSetBuilder.() -> Unit){ + SubSetBuilder(this,name).apply { block() } } -public operator fun DataSetBuilder.set(name: String, data: Data) { - this@set[name.toName()] = data +public suspend fun DataSetBuilder.set(name: String, data: Data) { + set(name.toName(), data) } -public fun DataSetBuilder.data(name: Name, data: T, meta: Meta = Meta.EMPTY) { +public suspend fun DataSetBuilder.data(name: Name, data: T, meta: Meta = Meta.EMPTY) { set(name, Data.static(data, meta)) } -public fun DataSetBuilder.data(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { +public suspend fun DataSetBuilder.data(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { set(name, Data.static(data, Meta(block))) } -public fun DataSetBuilder.data(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { +public suspend fun DataSetBuilder.data(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { set(name.toName(), Data.static(data, Meta(block))) } @@ -55,7 +86,7 @@ public suspend fun DataSetBuilder.set(name: String, set: DataSet this.set(name.toName(), set) } -public operator fun DataSetBuilder.set(name: String, block: DataSetBuilder.() -> Unit): Unit = +public suspend fun DataSetBuilder.set(name: String, block: suspend DataSetBuilder.() -> Unit): Unit = this@set.set(name.toName(), block) @@ -63,9 +94,15 @@ public operator fun DataSetBuilder.set(name: String, block: DataSet * Update data with given node data and meta with node meta. */ @DFExperimental -public suspend fun DataSetBuilder.update(tree: DataSet): Unit = coroutineScope{ +public suspend fun DataSetBuilder.update(tree: DataSet): Unit = coroutineScope { tree.flow().collect { //TODO check if the place is occupied set(it.name, it.data) } } + +public suspend fun DataSetBuilder.collectFrom(flow: Flow>) { + flow.collect { + set(it.name, it.data) + } +} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt index 939066a9..ac4d3166 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt @@ -46,9 +46,11 @@ public interface DataTree : DataSet { override fun flow(): Flow> = flow { items().forEach { (token, childItem: DataTreeItem) -> - when (childItem) { - is DataTreeItem.Leaf -> emit(childItem.data.named(token.asName())) - is DataTreeItem.Node -> emitAll(childItem.tree.flow().map { it.named(token + it.name) }) + if(!token.body.startsWith("@")) { + when (childItem) { + is DataTreeItem.Leaf -> emit(childItem.data.named(token.asName())) + is DataTreeItem.Node -> emitAll(childItem.tree.flow().map { it.named(token + it.name) }) + } } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt index 0fbff04f..fdf83372 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt @@ -41,7 +41,7 @@ public interface GroupRule { set.flow().collect { data -> val tagValue = data.meta[key]?.string ?: defaultTagValue - map.getOrPut(tagValue) { MutableDataTree(dataType, scope) }.set(data.name, data.data) + map.getOrPut(tagValue) { MutableDataTree(dataType) }.set(data.name, data.data) } return map diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt index e51f5fb0..47e1c411 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt @@ -33,16 +33,16 @@ public class MapActionBuilder(public var name: Name, public var meta: Meta } -public class MapAction( +public class MapAction( public val outputType: KClass, private val block: MapActionBuilder.() -> Unit, ) : Action { - override suspend fun run( - set: DataSet, + override suspend fun execute( + dataSet: DataSet, meta: Meta, - scope: CoroutineScope, - ): DataSet = DataTree.dynamic(outputType, scope) { + scope: CoroutineScope?, + ): DataSet { suspend fun mapOne(data: NamedData): NamedData { // Creating a new environment for action using **old** name, old meta and task meta val env = ActionEnv(data.name, data.meta, meta) @@ -65,23 +65,26 @@ public class MapAction( return newData.named(newName) } - collectFrom(set.flow().map(::mapOne)) - scope.launch { - set.updates.collect { name -> - //clear old nodes - remove(name) - //collect new items - collectFrom(set.flowChildren(name).map(::mapOne)) + val flow = dataSet.flow().map(::mapOne) + + return DataTree.dynamic(outputType) { + collectFrom(flow) + scope?.launch { + dataSet.updates.collect { name -> + //clear old nodes + remove(name) + //collect new items + collectFrom(dataSet.flowChildren(name).map(::mapOne)) + } } } } } -public suspend inline fun DataSet.map( - meta: Meta, - updatesScope: CoroutineScope, - noinline action: MapActionBuilder.() -> Unit, -): DataSet = MapAction(R::class, action).run(this, meta, updatesScope) - + +@Suppress("FunctionName") +public inline fun MapAction( + noinline builder: MapActionBuilder.() -> Unit, +): MapAction = MapAction(R::class, builder) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt index 863dda43..d6c49f59 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt @@ -3,7 +3,7 @@ package hep.dataforge.data import hep.dataforge.meta.* import hep.dataforge.names.* import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex @@ -15,12 +15,13 @@ import kotlin.reflect.KClass */ public class MutableDataTree( override val dataType: KClass, - public val scope: CoroutineScope, ) : DataTree, DataSetBuilder { private val mutex = Mutex() private val treeItems = HashMap>() - override suspend fun items(): Map> = mutex.withLock { treeItems } + override suspend fun items(): Map> = mutex.withLock { + treeItems.filter { !it.key.body.startsWith("@") } + } private val _updates = MutableSharedFlow() @@ -35,36 +36,37 @@ public class MutableDataTree( } } - override fun remove(name: Name) { - scope.launch { - if (name.isEmpty()) error("Can't remove the root node") - (getItem(name.cutLast()).tree as? MutableDataTree)?.remove(name.lastOrNull()!!) - } + override suspend fun remove(name: Name) { + if (name.isEmpty()) error("Can't remove the root node") + (getItem(name.cutLast()).tree as? MutableDataTree)?.remove(name.lastOrNull()!!) } - private suspend fun set(token: NameToken, node: DataSet) { - //if (_map.containsKey(token)) error("Tree entry with name $token is not empty") - mutex.withLock { - treeItems[token] = DataTreeItem.Node(node.toMutableTree(scope)) - coroutineScope { - node.updates.onEach { - _updates.emit(token + it) - }.launchIn(this) - } - _updates.emit(token.asName()) - } - } +// private suspend fun set(token: NameToken, node: DataSet) { +// //if (_map.containsKey(token)) error("Tree entry with name $token is not empty") +// mutex.withLock { +// treeItems[token] = DataTreeItem.Node(node.toMutableTree()) +// coroutineScope { +// node.updates.onEach { +// _updates.emit(token + it) +// }.launchIn(this) +// } +// _updates.emit(token.asName()) +// } +// } private suspend fun set(token: NameToken, data: Data) { mutex.withLock { treeItems[token] = DataTreeItem.Leaf(data) - _updates.emit(token.asName()) } } private suspend fun getOrCreateNode(token: NameToken): MutableDataTree = (treeItems[token] as? DataTreeItem.Node)?.tree as? MutableDataTree - ?: MutableDataTree(dataType, scope).also { set(token, it) } + ?: MutableDataTree(dataType).also { + mutex.withLock { + treeItems[token] = DataTreeItem.Node(it) + } + } private suspend fun getOrCreateNode(name: Name): MutableDataTree { return when (name.length) { @@ -74,83 +76,53 @@ public class MutableDataTree( } } - override fun set(name: Name, data: Data?) { + override suspend fun set(name: Name, data: Data?) { if (data == null) { remove(name) } else { - scope.launch { - when (name.length) { - 0 -> error("Can't add data with empty name") - 1 -> set(name.firstOrNull()!!, data) - 2 -> getOrCreateNode(name.cutLast()).set(name.lastOrNull()!!, data) - } + when (name.length) { + 0 -> error("Can't add data with empty name") + 1 -> set(name.firstOrNull()!!, data) + 2 -> getOrCreateNode(name.cutLast()).set(name.lastOrNull()!!, data) } } + _updates.emit(name) } - private suspend fun setTree(name: Name, node: MutableDataTree) { - when (name.length) { - 0 -> error("Can't add data with empty name") - 1 -> set(name.firstOrNull()!!, node) - 2 -> getOrCreateNode(name.cutLast()).set(name.lastOrNull()!!, node) + /** + * Copy given data set and mirror its changes to this [MutableDataTree] in [this@setAndObserve]. Returns an update [Job] + */ + public fun CoroutineScope.setAndObserve(name: Name, dataSet: DataSet): Job = launch { + set(name, dataSet) + dataSet.updates.collect { nameInBranch -> + set(name + nameInBranch, dataSet.getData(nameInBranch)) } } - - override suspend fun set(name: Name, dataSet: DataSet): Unit { - if (dataSet is MutableDataTree) { - setTree(name, dataSet) - } else { - setTree(name, dataSet.toMutableTree(scope)) - } - } - - override fun set(name: Name, block: DataSetBuilder.() -> Unit) { - scope.launch { - setTree(name, MutableDataTree(dataType, scope).apply(block)) - } - } - - public fun collectFrom(flow: Flow>) { - flow.onEach { - set(it.name, it.data) - }.launchIn(scope) - } } +/** + * Create a dynamic tree. Initial data is placed synchronously. Updates are propagated via [updatesScope] + */ public suspend fun DataTree.Companion.dynamic( type: KClass, - updatesScope: CoroutineScope, block: suspend MutableDataTree.() -> Unit, ): DataTree { - val tree = MutableDataTree(type, updatesScope) + val tree = MutableDataTree(type) tree.block() return tree } public suspend inline fun DataTree.Companion.dynamic( - updatesScope: CoroutineScope, crossinline block: suspend MutableDataTree.() -> Unit, -): DataTree = MutableDataTree(T::class, updatesScope).apply { block() } +): DataTree = MutableDataTree(T::class).apply { block() } public suspend inline fun MutableDataTree.set( name: Name, noinline block: suspend MutableDataTree.() -> Unit, -): Unit = set(name, DataTree.dynamic(T::class, scope, block)) +): Unit = set(name, DataTree.dynamic(T::class, block)) public suspend inline fun MutableDataTree.set( name: String, noinline block: suspend MutableDataTree.() -> Unit, -): Unit = set(name.toName(), DataTree.dynamic(T::class, scope, block)) - -/** - * Generate a mutable builder from this node. Node content is not changed - */ -public suspend fun DataSet.toMutableTree( - scope: CoroutineScope, -): MutableDataTree = MutableDataTree(dataType, scope).apply { - flow().collect { set(it.name, it.data) } - this@toMutableTree.updates.onEach { - set(it, getData(it)) - }.launchIn(scope) -} +): Unit = set(name.toName(), DataTree.dynamic(T::class, block)) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt index 851cc82f..f2e35767 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt @@ -13,7 +13,6 @@ import kotlin.collections.set import kotlin.reflect.KClass - public class SplitBuilder(public val name: Name, public val meta: Meta) { public class FragmentRule(public val name: Name, public var meta: MetaBuilder) { @@ -44,11 +43,11 @@ public class SplitAction( private val action: SplitBuilder.() -> Unit, ) : Action { - override suspend fun run( - set: DataSet, + override suspend fun execute( + dataSet: DataSet, meta: Meta, - scope: CoroutineScope, - ): DataSet = DataTree.dynamic(outputType, scope) { + scope: CoroutineScope?, + ): DataSet { suspend fun splitOne(data: NamedData): Flow> { val laminate = Laminate(data.meta, meta) @@ -63,13 +62,15 @@ public class SplitAction( } } - collectFrom(set.flow().flatMapConcat(transform = ::splitOne)) - scope.launch { - set.updates.collect { name -> - //clear old nodes - remove(name) - //collect new items - collectFrom(set.flowChildren(name).flatMapConcat(transform = ::splitOne)) + return DataTree.dynamic(outputType) { + collectFrom(dataSet.flow().flatMapConcat(transform = ::splitOne)) + scope?.launch { + dataSet.updates.collect { name -> + //clear old nodes + remove(name) + //collect new items + collectFrom(dataSet.flowChildren(name).flatMapConcat(transform = ::splitOne)) + } } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt index 08e989d9..0d26005a 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt @@ -5,7 +5,8 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.* import kotlin.reflect.KClass -private class StaticDataTree( +@PublishedApi +internal class StaticDataTree( override val dataType: KClass, ) : DataSetBuilder, DataTree { @@ -13,9 +14,9 @@ private class StaticDataTree( override val updates: Flow = emptyFlow() - override suspend fun items(): Map> = items + override suspend fun items(): Map> = items.filter { !it.key.body.startsWith("@") } - override fun remove(name: Name) { + override suspend fun remove(name: Name) { when (name.length) { 0 -> error("Can't remove root tree node") 1 -> items.remove(name.firstOrNull()!!) @@ -34,7 +35,7 @@ private class StaticDataTree( else -> getOrCreateNode(name.cutLast()).getOrCreateNode(name.lastOrNull()!!.asName()) } - private operator fun set(name: Name, item: DataTreeItem?) { + private suspend fun set(name: Name, item: DataTreeItem?) { if (name.isEmpty()) error("Can't set top level tree node") if (item == null) { remove(name) @@ -43,7 +44,7 @@ private class StaticDataTree( } } - override fun set(name: Name, data: Data?) { + override suspend fun set(name: Name, data: Data?) { set(name, data?.let { DataTreeItem.Leaf(it) }) } @@ -58,20 +59,15 @@ private class StaticDataTree( } } } - - override fun set(name: Name, block: DataSetBuilder.() -> Unit) { - val tree = StaticDataTree(dataType).apply(block) - set(name, DataTreeItem.Node(tree)) - } } -public fun DataTree.Companion.static( +public suspend fun DataTree.Companion.static( dataType: KClass, - block: DataSetBuilder.() -> Unit, -): DataTree = StaticDataTree(dataType).apply(block) + block: suspend DataSetBuilder.() -> Unit, +): DataTree = StaticDataTree(dataType).apply { block() } -public inline fun DataTree.Companion.static( - noinline block: DataSetBuilder.() -> Unit, +public suspend inline fun DataTree.Companion.static( + noinline block: suspend DataSetBuilder.() -> Unit, ): DataTree = static(T::class, block) public suspend fun DataSet.toStaticTree(): DataTree = StaticDataTree(dataType).apply { diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt index 82af17cd..4b833e10 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt @@ -12,9 +12,9 @@ public suspend fun DataSet<*>.getMeta(): Meta? = getData(DataSet.META_KEY)?.meta /** * Add meta-data node to a [DataSet] */ -public fun DataSetBuilder<*>.meta(meta: Meta): Unit = set(DataSet.META_KEY, Data.empty(meta)) +public suspend fun DataSetBuilder<*>.meta(meta: Meta): Unit = set(DataSet.META_KEY, Data.empty(meta)) /** * Add meta-data node to a [DataSet] */ -public fun DataSetBuilder<*>.meta(metaBuilder: MetaBuilder.() -> Unit): Unit = meta(Meta(metaBuilder)) \ No newline at end of file +public suspend fun DataSetBuilder<*>.meta(metaBuilder: MetaBuilder.() -> Unit): Unit = meta(Meta(metaBuilder)) \ No newline at end of file diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt new file mode 100644 index 00000000..d1944785 --- /dev/null +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt @@ -0,0 +1,39 @@ +package hep.dataforge.data + +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class ActionsTest { + val data: DataTree = runBlocking { + DataTree.static { + repeat(10) { + data(it.toString(), it) + } + } + } + + @Test + fun testStaticMapAction() { + val plusOne = MapAction { + result { it + 1 } + } + runBlocking { + val result = plusOne.execute(data) + assertEquals(2, result.getData("1")?.value()) + } + } + + @Test + fun testDynamicMapAction() { + val plusOne = MapAction { + result { it + 1 } + } + val datum = runBlocking { + val result = plusOne.execute(data, scope = this) + result.getData("1")?.value() + } + assertEquals(2, datum) + } + +} \ No newline at end of file diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt index f93d6ee7..3770a670 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt @@ -1,34 +1,69 @@ package hep.dataforge.data -import kotlinx.coroutines.runBlocking +import hep.dataforge.names.toName +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.collect import kotlin.test.Test -import kotlin.test.assertTrue +import kotlin.test.assertEquals -internal class DataTreeBuilderTest{ +internal class DataTreeBuilderTest { @Test - fun testDataUpdate(){ - val updateData: DataTree = DataTree.static{ + fun testDataUpdate() = runBlocking { + val updateData: DataTree = DataTree.static { "update" put { "a" put Data.static("a") "b" put Data.static("b") } } - val node = DataTree.static{ - set("primary"){ - data("a","a") - data("b","b") - } - data("root","root") - runBlocking { - update(updateData) + val node = DataTree.static { + set("primary") { + data("a", "a") + data("b", "b") } + data("root", "root") + update(updateData) } - assertTrue { node.branch("update.a") != null } - assertTrue { node.branch("primary.a") != null } + assertEquals("a", node.getData("update.a")?.value()) + assertEquals("a", node.getData("primary.a")?.value()) + } + + @Test + fun testDynamicUpdates() = runBlocking { + try { + supervisorScope { + val subNode = DataTree.dynamic { + launch { + repeat(10) { + delay(10) + data("value", it) + } + } + } + launch { + subNode.updatesWithData.collect { + println(it) + } + } + val rootNode = DataTree.dynamic { + setAndObserve("sub".toName(), subNode) + } + + launch { + rootNode.updatesWithData.collect { + println(it) + } + } + delay(200) + assertEquals(9, rootNode.getData("sub.value")?.value()) + cancel() + } + } catch (t: Throwable) { + if (t !is CancellationException) throw t + } } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index 297cc29f..5c1a6bd3 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -193,7 +193,7 @@ public fun Name.endsWith(name: Name): Boolean = * if [this] starts with given [head] name, returns the reminder of the name (could be empty). Otherwise returns null */ public fun Name.removeHeadOrNull(head: Name): Name? = if (startsWith(head)) { - Name(tokens.subList(head.length, head.length)) + Name(tokens.subList(head.length, length)) } else { null } \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt index b17eacbd..4f6c9c17 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt @@ -3,13 +3,12 @@ package hep.dataforge.workspace import hep.dataforge.data.DataSet import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.Described -import hep.dataforge.misc.Named import hep.dataforge.misc.Type import hep.dataforge.workspace.Task.Companion.TYPE import kotlin.reflect.KClass @Type(TYPE) -public interface Task : Named, Described { +public interface Task : Described { /** * The explicit type of the node returned by the task diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt index 590d2e2a..51072ed9 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt @@ -104,12 +104,11 @@ public fun TaskDependencyContainer.data( pattern: String? = null, from: String? = null, to: String? = null, -): DataDependency = - data { - pattern?.let { this.pattern = it } - from?.let { this.from = it } - to?.let { this.to = it } - } +): DataDependency = data { + pattern?.let { this.pattern = it } + from?.let { this.from = it } + to?.let { this.to = it } +} ///** // * Add all data as root node diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index f41cfa49..6c95bb64 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -35,14 +35,7 @@ public class TaskBuilder(private val name: Name, public val type: KClas public val meta: Meta, public val context: Context, public val data: DataSet, - ) { -// public operator fun DirectTaskDependency.invoke(): DataSet = if (placement.isEmpty()) { -// data.cast(task.type) -// } else { -// data[placement].tree?.cast(task.type) -// ?: error("Could not find results of direct task dependency $this at \"$placement\"") -// } - } + ) /** * Add a transformation on untyped data @@ -86,7 +79,7 @@ public class TaskBuilder(private val name: Name, public val type: KClas crossinline block: TaskEnv.() -> Action, ) { transform { data: DataSet -> - block().run(data, meta, context) + block().execute(data, meta, context) } } @@ -194,15 +187,19 @@ public class TaskBuilder(private val name: Name, public val type: KClas logger.warn { "No transformation present, returning input data" } dataSet.castOrNull(type) ?: error("$type expected, but $type received") } else { - val builder = MutableDataTree(type, workspace.context) - dataTransforms.forEach { transformation -> - val res = transformation(workspace.context, model, dataSet) - builder.update(res) + DataTree.dynamic(type, workspace.context){ + dataTransforms.forEach { transformation -> + val res = transformation(workspace.context, model, dataSet) + update(res) + } } - builder } } } } } +@DFExperimental +public suspend inline fun TaskBuilder.TaskEnv.dataTree( + crossinline block: suspend MutableDataTree.() -> Unit, +): DataTree = DataTree.dynamic(context, block) \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt index 83be2f63..48942ad5 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -37,6 +37,8 @@ public fun WorkspaceBuilder.target(name: String, block: MetaBuilder.() -> Unit) targets[name] = Meta(block).seal() } +class WorkspaceTask(val workspace: Workspace, val name: String) + /** * Use existing target as a base updating it with the block */ @@ -52,7 +54,7 @@ public fun WorkspaceBuilder.task( name: String, type: KClass, builder: TaskBuilder.() -> Unit, -): Task = TaskBuilder(name.toName(), type).apply(builder).build().also { tasks.add(it) } +): WorkspaceTask = TaskBuilder(name.toName(), type).apply(builder).build().also { tasks.add(it) } public inline fun WorkspaceBuilder.task( name: String, diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index 323b44de..9e98a276 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -33,7 +33,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { val testSingleData = task("singleData", Int::class) { model { - data("myData\\[12\\]") + data(pattern = "myData\\[12\\]") } transform { data -> DataTree.dynamic(context) { diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index c0379594..817f37b6 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -85,7 +85,7 @@ class SimpleWorkspaceTest { transform { data -> val squareNode = data.branch("square").filterIsInstance() //squareDep() val linearNode = data.branch("linear").filterIsInstance() //linearDep() - DataTree.dynamic(context) { + dataTree { squareNode.flow().collect { val newData: Data = Data { val squareValue = squareNode.getData(it.name)!!.value() @@ -150,6 +150,7 @@ class SimpleWorkspaceTest { } @Test + @Timeout(1) fun testWorkspace() { val node = workspace.runBlocking("sum") val res = node.first() @@ -157,7 +158,7 @@ class SimpleWorkspaceTest { } @Test - @Timeout(400) + @Timeout(1) fun testMetaPropagation() { val node = workspace.runBlocking("sum") { "testFlag" put true } val res = node.first()?.value() @@ -179,7 +180,7 @@ class SimpleWorkspaceTest { } @Test - fun testGather() { + fun testFilter() { val node = workspace.runBlocking("filterOne") runBlocking { assertEquals(12, node.first()?.value()) From ac8631e3a09e27a531846c4bc4ca44e5c2a12cee Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 17 Jan 2021 19:11:49 +0300 Subject: [PATCH 38/59] [WIP] redo Workspace --- .../{MutableDataTree.kt => ActiveDataTree.kt} | 55 +++---- .../hep/dataforge/data/CachingAction.kt | 2 +- .../kotlin/hep/dataforge/data/Data.kt | 22 --- .../kotlin/hep/dataforge/data/DataSet.kt | 12 +- .../kotlin/hep/dataforge/data/DataTree.kt | 15 -- .../kotlin/hep/dataforge/data/GroupRule.kt | 4 +- .../kotlin/hep/dataforge/data/MapAction.kt | 2 +- .../kotlin/hep/dataforge/data/NamedData.kt | 32 ++++ .../kotlin/hep/dataforge/data/SplitAction.kt | 2 +- .../hep/dataforge/data/StaticDataTree.kt | 12 +- .../hep/dataforge/data/DataTreeBuilderTest.kt | 4 +- .../hep/dataforge/scripting/BuildersKtTest.kt | 1 - .../hep/dataforge/workspace/Dependency.kt | 8 +- .../hep/dataforge/workspace/GenericTask.kt | 55 ------- .../dataforge/workspace/SimpleWorkspace.kt | 11 +- .../hep/dataforge/workspace/StageData.kt | 47 ++++++ .../hep/dataforge/workspace/StageDataSet.kt | 46 ++++++ .../kotlin/hep/dataforge/workspace/Task.kt | 49 ------ .../hep/dataforge/workspace/TaskModel.kt | 141 ------------------ .../hep/dataforge/workspace/WorkStage.kt | 23 +++ .../hep/dataforge/workspace/Workspace.kt | 55 ++++--- .../hep/dataforge/workspace/TaskBuilder.kt | 11 +- .../dataforge/workspace/WorkspaceBuilder.kt | 16 +- .../dataforge/workspace/WorkspacePlugin.kt | 10 +- .../workspace/DataPropagationTest.kt | 13 +- .../workspace/SimpleWorkspaceTest.kt | 8 +- 26 files changed, 256 insertions(+), 400 deletions(-) rename dataforge-data/src/commonMain/kotlin/hep/dataforge/data/{MutableDataTree.kt => ActiveDataTree.kt} (57%) create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt delete mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageData.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageDataSet.kt delete mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt delete mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkStage.kt diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt similarity index 57% rename from dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt rename to dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt index d6c49f59..82563eac 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MutableDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt @@ -11,11 +11,11 @@ import kotlinx.coroutines.sync.withLock import kotlin.reflect.KClass /** - * A mutable [DataTree.Companion.dynamic]. It + * A mutable [DataTree.Companion.active]. It */ -public class MutableDataTree( +public class ActiveDataTree( override val dataType: KClass, -) : DataTree, DataSetBuilder { +) : DataTree, DataSetBuilder, ActiveDataSet { private val mutex = Mutex() private val treeItems = HashMap>() @@ -38,37 +38,24 @@ public class MutableDataTree( override suspend fun remove(name: Name) { if (name.isEmpty()) error("Can't remove the root node") - (getItem(name.cutLast()).tree as? MutableDataTree)?.remove(name.lastOrNull()!!) + (getItem(name.cutLast()).tree as? ActiveDataTree)?.remove(name.lastOrNull()!!) } -// private suspend fun set(token: NameToken, node: DataSet) { -// //if (_map.containsKey(token)) error("Tree entry with name $token is not empty") -// mutex.withLock { -// treeItems[token] = DataTreeItem.Node(node.toMutableTree()) -// coroutineScope { -// node.updates.onEach { -// _updates.emit(token + it) -// }.launchIn(this) -// } -// _updates.emit(token.asName()) -// } -// } - private suspend fun set(token: NameToken, data: Data) { mutex.withLock { treeItems[token] = DataTreeItem.Leaf(data) } } - private suspend fun getOrCreateNode(token: NameToken): MutableDataTree = - (treeItems[token] as? DataTreeItem.Node)?.tree as? MutableDataTree - ?: MutableDataTree(dataType).also { + private suspend fun getOrCreateNode(token: NameToken): ActiveDataTree = + (treeItems[token] as? DataTreeItem.Node)?.tree as? ActiveDataTree + ?: ActiveDataTree(dataType).also { mutex.withLock { treeItems[token] = DataTreeItem.Node(it) } } - private suspend fun getOrCreateNode(name: Name): MutableDataTree { + private suspend fun getOrCreateNode(name: Name): ActiveDataTree { return when (name.length) { 0 -> this 1 -> getOrCreateNode(name.firstOrNull()!!) @@ -90,7 +77,7 @@ public class MutableDataTree( } /** - * Copy given data set and mirror its changes to this [MutableDataTree] in [this@setAndObserve]. Returns an update [Job] + * Copy given data set and mirror its changes to this [ActiveDataTree] in [this@setAndObserve]. Returns an update [Job] */ public fun CoroutineScope.setAndObserve(name: Name, dataSet: DataSet): Job = launch { set(name, dataSet) @@ -103,26 +90,26 @@ public class MutableDataTree( /** * Create a dynamic tree. Initial data is placed synchronously. Updates are propagated via [updatesScope] */ -public suspend fun DataTree.Companion.dynamic( +public suspend fun DataTree.Companion.active( type: KClass, - block: suspend MutableDataTree.() -> Unit, + block: suspend ActiveDataTree.() -> Unit, ): DataTree { - val tree = MutableDataTree(type) + val tree = ActiveDataTree(type) tree.block() return tree } -public suspend inline fun DataTree.Companion.dynamic( - crossinline block: suspend MutableDataTree.() -> Unit, -): DataTree = MutableDataTree(T::class).apply { block() } +public suspend inline fun DataTree.Companion.active( + crossinline block: suspend ActiveDataTree.() -> Unit, +): DataTree = ActiveDataTree(T::class).apply { block() } -public suspend inline fun MutableDataTree.set( +public suspend inline fun ActiveDataTree.set( name: Name, - noinline block: suspend MutableDataTree.() -> Unit, -): Unit = set(name, DataTree.dynamic(T::class, block)) + noinline block: suspend ActiveDataTree.() -> Unit, +): Unit = set(name, DataTree.active(T::class, block)) -public suspend inline fun MutableDataTree.set( +public suspend inline fun ActiveDataTree.set( name: String, - noinline block: suspend MutableDataTree.() -> Unit, -): Unit = set(name.toName(), DataTree.dynamic(T::class, block)) + noinline block: suspend ActiveDataTree.() -> Unit, +): Unit = set(name.toName(), DataTree.active(T::class, block)) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt index 45d3e888..e440a765 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt @@ -34,7 +34,7 @@ public abstract class CachingAction( dataSet: DataSet, meta: Meta, scope: CoroutineScope?, - ): DataSet = DataTree.dynamic(outputType) { + ): DataSet = DataTree.active(outputType) { coroutineScope { collectFrom(transform(dataSet, meta)) } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 39983534..57647c03 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -3,9 +3,7 @@ package hep.dataforge.data import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.isEmpty -import hep.dataforge.misc.Named import hep.dataforge.misc.Type -import hep.dataforge.names.Name import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -91,27 +89,7 @@ public inline fun Data( noinline block: suspend CoroutineScope.() -> T, ): Data = Data(T::class, meta, context, dependencies, block) -public class NamedData internal constructor( - override val name: Name, - public val data: Data, -) : Data by data, Named { - override fun toString(): String = buildString { - append("NamedData(name=\"$name\"") - if(data is StaticData){ - append(", value=${data.value}") - } - if(!data.meta.isEmpty()){ - append(", meta=${data.meta}") - } - append(")") - } -} -public fun Data.named(name: Name): NamedData = if (this is NamedData) { - NamedData(name, this.data) -} else { - NamedData(name, this) -} public fun Data.map( outputType: KClass, diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt index 84c053f4..1c68c02a 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt @@ -34,6 +34,12 @@ public interface DataSet { public suspend fun listChildren(prefix: Name = Name.EMPTY): List = flow().map { it.name }.filter { it.startsWith(prefix) && (it.length == prefix.length + 1) }.toList() + public companion object { + public val META_KEY: Name = "@meta".asName() + } +} + +public interface ActiveDataSet: DataSet{ /** * A flow of updated item names. Updates are propagated in a form of [Flow] of names of updated nodes. * Those can include new data items and replacement of existing ones. The replaced items could update existing data content @@ -41,12 +47,10 @@ public interface DataSet { * */ public val updates: Flow - - public companion object { - public val META_KEY: Name = "@meta".asName() - } } +public val DataSet.updates: Flow get() = if(this is ActiveDataSet) updates else emptyFlow() + /** * Flow all data nodes with names starting with [branchName] */ diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt index ac4d3166..659ff029 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt @@ -31,19 +31,6 @@ public interface DataTree : DataSet { */ public suspend fun items(): Map> -// override fun flow(): Flow> = flow flowBuilder@{ -// val item = getItem(root) ?: return@flowBuilder -// when (item) { -// is DataTreeItem.Leaf -> emit(item.data.named(root)) -// is DataTreeItem.Node -> item.tree.items().forEach { (token, childItem: DataTreeItem) -> -// when (childItem) { -// is DataTreeItem.Leaf -> emit(childItem.data.named(root + token)) -// is DataTreeItem.Node -> emitAll(childItem.tree.flow().map { it.named(root + token + it.name) }) -// } -// } -// } -// } - override fun flow(): Flow> = flow { items().forEach { (token, childItem: DataTreeItem) -> if(!token.body.startsWith("@")) { @@ -104,7 +91,5 @@ public fun DataTree.itemFlow(): Flow>> = public fun DataTree.branch(branchName: Name): DataTree = object : DataTree { override val dataType: KClass get() = this@branch.dataType - override val updates: Flow = this@branch.updates.mapNotNull { it.removeHeadOrNull(branchName) } - override suspend fun items(): Map> = getItem(branchName).tree?.items() ?: emptyMap() } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt index fdf83372..fcd22eae 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt @@ -37,11 +37,11 @@ public interface GroupRule { object : GroupRule { override suspend fun gather(dataType: KClass, set: DataSet): Map> { - val map = HashMap>() + val map = HashMap>() set.flow().collect { data -> val tagValue = data.meta[key]?.string ?: defaultTagValue - map.getOrPut(tagValue) { MutableDataTree(dataType) }.set(data.name, data.data) + map.getOrPut(tagValue) { ActiveDataTree(dataType) }.set(data.name, data.data) } return map diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt index 47e1c411..ec6442d9 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt @@ -67,7 +67,7 @@ public class MapAction( val flow = dataSet.flow().map(::mapOne) - return DataTree.dynamic(outputType) { + return DataTree.active(outputType) { collectFrom(flow) scope?.launch { dataSet.updates.collect { name -> diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt new file mode 100644 index 00000000..aa5afcdc --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt @@ -0,0 +1,32 @@ +package hep.dataforge.data + +import hep.dataforge.meta.isEmpty +import hep.dataforge.misc.Named +import hep.dataforge.names.Name + +public interface NamedData : Named, Data { + override val name: Name + public val data: Data +} + +private class NamedDataImpl( + override val name: Name, + override val data: Data, +) : Data by data, NamedData { + override fun toString(): String = buildString { + append("NamedData(name=\"$name\"") + if (data is StaticData) { + append(", value=${data.value}") + } + if (!data.meta.isEmpty()) { + append(", meta=${data.meta}") + } + append(")") + } +} + +public fun Data.named(name: Name): NamedData = if (this is NamedData) { + NamedDataImpl(name, this.data) +} else { + NamedDataImpl(name, this) +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt index f2e35767..48f4fc93 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt @@ -62,7 +62,7 @@ public class SplitAction( } } - return DataTree.dynamic(outputType) { + return DataTree.active(outputType) { collectFrom(dataSet.flow().flatMapConcat(transform = ::splitOne)) scope?.launch { dataSet.updates.collect { name -> diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt index 0d26005a..c3191ef9 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt @@ -12,8 +12,6 @@ internal class StaticDataTree( private val items: MutableMap> = HashMap() - override val updates: Flow = emptyFlow() - override suspend fun items(): Map> = items.filter { !it.key.body.startsWith("@") } override suspend fun remove(name: Name) { @@ -61,15 +59,15 @@ internal class StaticDataTree( } } -public suspend fun DataTree.Companion.static( +public suspend fun DataTree( dataType: KClass, block: suspend DataSetBuilder.() -> Unit, ): DataTree = StaticDataTree(dataType).apply { block() } -public suspend inline fun DataTree.Companion.static( +public suspend inline fun DataTree( noinline block: suspend DataSetBuilder.() -> Unit, -): DataTree = static(T::class, block) +): DataTree = DataTree(T::class, block) -public suspend fun DataSet.toStaticTree(): DataTree = StaticDataTree(dataType).apply { - update(this@toStaticTree) +public suspend fun DataSet.seal(): DataTree = DataTree(dataType){ + update(this@seal) } \ No newline at end of file diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt index 3770a670..98feae57 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt @@ -35,7 +35,7 @@ internal class DataTreeBuilderTest { fun testDynamicUpdates() = runBlocking { try { supervisorScope { - val subNode = DataTree.dynamic { + val subNode = DataTree.active { launch { repeat(10) { delay(10) @@ -48,7 +48,7 @@ internal class DataTreeBuilderTest { println(it) } } - val rootNode = DataTree.dynamic { + val rootNode = DataTree.active { setAndObserve("sub".toName(), subNode) } diff --git a/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt b/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt index 6dd61105..7d96c168 100644 --- a/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt +++ b/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt @@ -5,7 +5,6 @@ import hep.dataforge.meta.get import hep.dataforge.meta.int import hep.dataforge.workspace.SimpleWorkspaceBuilder import hep.dataforge.workspace.context -import hep.dataforge.workspace.target import kotlin.test.Test import kotlin.test.assertEquals diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt index 83b2027c..3c342e90 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt @@ -25,7 +25,7 @@ public abstract class TaskDependency( public val meta: Meta, protected val placement: DataPlacement, ) : Dependency() { - public abstract fun resolveTask(workspace: Workspace): Task + public abstract fun resolveTask(workspace: Workspace): WorkStage /** * A name of the dependency for logging and serialization @@ -40,11 +40,11 @@ public abstract class TaskDependency( } public class ExternalTaskDependency( - public val task: Task, + public val task: WorkStage, meta: Meta, placement: DataPlacement, ) : TaskDependency(meta, placement) { - override fun resolveTask(workspace: Workspace): Task = task + override fun resolveTask(workspace: Workspace): WorkStage = task override val name: Name get() = EXTERNAL_TASK_NAME + task.name @@ -64,7 +64,7 @@ public class WorkspaceTaskDependency( meta: Meta, placement: DataPlacement, ) : TaskDependency(meta, placement) { - override fun resolveTask(workspace: Workspace): Task<*> = workspace.tasks[name] + override fun resolveTask(workspace: Workspace): WorkStage<*> = workspace.stages[name] ?: error("Task with name $name is not found in the workspace") override fun toMeta(): Meta { diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt deleted file mode 100644 index 7c5b039f..00000000 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/GenericTask.kt +++ /dev/null @@ -1,55 +0,0 @@ -package hep.dataforge.workspace - -import hep.dataforge.context.logger -import hep.dataforge.data.DataSet -import hep.dataforge.meta.Meta -import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.get -import hep.dataforge.meta.node -import hep.dataforge.names.Name -import kotlin.reflect.KClass - -//data class TaskEnv(val workspace: Workspace, val model: TaskModel) - - -public class GenericTask( - override val name: Name, - override val type: KClass, - override val descriptor: NodeDescriptor, - private val modelTransform: TaskModelBuilder.(Meta) -> Unit, - private val dataTransform: Workspace.() -> suspend TaskModel.(DataSet) -> DataSet -) : Task { - - override suspend fun run(workspace: Workspace, model: TaskModel): DataSet { - //validate model - validate(model) - - // gather data - val input = model.buildInput(workspace)// gather(workspace, model) - - //execute - workspace.logger.info{"Starting task '$name' on ${model.target} with meta: \n${model.meta}"} - val output = dataTransform(workspace).invoke(model, input) - - //handle result - //output.handle(model.context.dispatcher) { this.handle(it) } - - return output - } - - /** - * Build new TaskModel and apply specific model transformation for this - * task. By default model uses the meta node with the same node as the name of the task. - * - * @param workspace - * @param taskMeta - * @return - */ - override fun build(workspace: Workspace, taskMeta: Meta): TaskModel { - val taskMeta = taskMeta[name]?.node ?: taskMeta - val builder = TaskModelBuilder(name, taskMeta) - builder.modelTransform(taskMeta) - return builder.build() - } - //TODO add validation -} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt index 4da2f694..442e52bf 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt @@ -2,8 +2,7 @@ package hep.dataforge.workspace import hep.dataforge.context.Context import hep.dataforge.context.gather -import hep.dataforge.context.toMap -import hep.dataforge.data.DataTree +import hep.dataforge.data.DataSet import hep.dataforge.meta.Meta import hep.dataforge.names.Name @@ -13,13 +12,13 @@ import hep.dataforge.names.Name */ public class SimpleWorkspace( override val context: Context, - override val data: DataTree, + override val data: DataSet, override val targets: Map, - tasks: Collection> + stages: Map> ) : Workspace { - override val tasks: Map> by lazy { - context.gather>(Task.TYPE) + tasks.toMap() + override val stages: Map> by lazy { + context.gather>(WorkStage.TYPE) + stages } public companion object { diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageData.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageData.kt new file mode 100644 index 00000000..f17cb09a --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageData.kt @@ -0,0 +1,47 @@ +package hep.dataforge.workspace + +import hep.dataforge.data.Data +import hep.dataforge.data.NamedData +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name + +/** + * A [Workspace]-locked [NamedData], that serves as a computation model. + */ +public interface StageData : NamedData { + /** + * The [Workspace] this data belongs to + */ + public val workspace: Workspace + + /** + * The name of the stage that produced this data. [Name.EMPTY] if the workspace intrinsic data is used. + */ + public val stage: Name + + /** + * Stage configuration used to produce this data. + */ + public val stageMeta: Meta + + /** + * Dependencies that allow to compute transitive dependencies as well. + */ + override val dependencies: Collection> +} + +private class StageDataImpl( + override val workspace: Workspace, + override val data: Data, + override val name: Name, + override val stage: Name, + override val stageMeta: Meta, +) : StageData, Data by data { + override val dependencies: Collection> = data.dependencies.map { + it as? StageData<*> ?: error("StageData can't depend on external data") + } +} + +internal fun Workspace.internalize(data: Data, name: Name, stage: Name, stageMeta: Meta): StageData = + StageDataImpl(this, data, name, stage, stageMeta) + diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageDataSet.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageDataSet.kt new file mode 100644 index 00000000..c0bf3784 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageDataSet.kt @@ -0,0 +1,46 @@ +package hep.dataforge.workspace + +import hep.dataforge.data.DataSet +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** + * A result of a [WorkStage] + */ +public interface StageDataSet : DataSet { + /** + * The [Workspace] this [DataSet] belongs to + */ + public val workspace: Workspace + + /** + * The [Name] of the stage that produced this [DataSet] + */ + public val stageName: Name + + /** + * The configuration of the stage that produced this [DataSet] + */ + public val stageMeta: Meta + + override fun flow(): Flow> + override suspend fun getData(name: Name): StageData? +} + +private class StageDataSetImpl( + override val workspace: Workspace, + val dataSet: DataSet, + override val stageName: Name, + override val stageMeta: Meta, +) : StageDataSet, DataSet by dataSet { + + override fun flow(): Flow> = dataSet.flow().map { + workspace.internalize(it, it.name, stageName, stageMeta) + } + + override suspend fun getData(name: Name): StageData? = dataSet.getData(name)?.let { + workspace.internalize(it, name, stageName, stageMeta) + } +} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt deleted file mode 100644 index 4f6c9c17..00000000 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt +++ /dev/null @@ -1,49 +0,0 @@ -package hep.dataforge.workspace - -import hep.dataforge.data.DataSet -import hep.dataforge.meta.Meta -import hep.dataforge.meta.descriptors.Described -import hep.dataforge.misc.Type -import hep.dataforge.workspace.Task.Companion.TYPE -import kotlin.reflect.KClass - -@Type(TYPE) -public interface Task : Described { - - /** - * The explicit type of the node returned by the task - */ - public val type: KClass - - /** - * Build a model for this task. Does not run any computations unless task [isEager] - * - * @param workspace - * @param taskMeta - * @return - */ - public fun build(workspace: Workspace, taskMeta: Meta): TaskModel - - /** - * Check if the model is valid and is acceptable by the task. Throw exception if not. - * - * @param model - */ - public fun validate(model: TaskModel) { - if(this.name != model.name) error("The task $name could not be run with model from task ${model.name}") - } - - /** - * Run given task model. Type check expected to be performed before actual - * calculation. - * - * @param workspace - a workspace to run task model in - * @param model - a model to be executed - * @return - */ - public suspend fun run(workspace: Workspace, model: TaskModel): DataSet - - public companion object { - public const val TYPE: String = "task" - } -} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt deleted file mode 100644 index 51072ed9..00000000 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskModel.kt +++ /dev/null @@ -1,141 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package hep.dataforge.workspace - -import hep.dataforge.data.DataTree -import hep.dataforge.data.dynamic -import hep.dataforge.data.update -import hep.dataforge.meta.* -import hep.dataforge.names.Name -import hep.dataforge.names.asName -import hep.dataforge.names.toName -import hep.dataforge.workspace.TaskModel.Companion.MODEL_TARGET_KEY - -//FIXME TaskModel should store individual propagation of all data elements, not just nodes - -/** - * A model for task execution - * @param name the name of the task - * @param meta the meta for the task (not for the whole configuration) - * @param dependencies a list of direct dependencies for this task - */ -public data class TaskModel( - val name: Name, - val meta: Meta, - val dependencies: Collection, -) : MetaRepr { - //TODO provide a way to get task descriptor - //TODO add pre-run check of task result type? - - override fun toMeta(): Meta = Meta { - "name" put name.toString() - "meta" put meta - "dependsOn" put { - val dataDependencies = dependencies.filterIsInstance() - val taskDependencies = dependencies.filterIsInstance>() - setIndexed("data".toName(), dataDependencies.map { it.toMeta() }) //Should list all data here - setIndexed( - "task".toName(), - taskDependencies.map { it.toMeta() }) { _, index -> taskDependencies[index].name.toString() } - //TODO ensure all dependencies are listed - } - } - - public companion object { - public val MODEL_TARGET_KEY: Name = "@target".asName() - } -} - -/** - * Build input for the task - */ -public suspend fun TaskModel.buildInput(workspace: Workspace): DataTree = DataTree.dynamic(workspace.context) { - dependencies.forEach { dep -> - update(dep.apply(workspace)) - } -} - -public interface TaskDependencyContainer { - public val defaultMeta: Meta - public fun add(dependency: Dependency) -} - -/** - * Add dependency for a task defined in a workspace and resolved by - */ -public fun TaskDependencyContainer.dependsOn( - name: Name, - placement: DataPlacement = DataPlacement.ALL, - meta: Meta = defaultMeta, -): WorkspaceTaskDependency = WorkspaceTaskDependency(name, meta, placement).also { add(it) } - -public fun TaskDependencyContainer.dependsOn( - name: String, - placement: DataPlacement = DataPlacement.ALL, - meta: Meta = defaultMeta, -): WorkspaceTaskDependency = dependsOn(name.toName(), placement, meta) - -public fun TaskDependencyContainer.dependsOn( - task: Task, - placement: DataPlacement = DataPlacement.ALL, - meta: Meta = defaultMeta, -): ExternalTaskDependency = ExternalTaskDependency(task, meta, placement).also { add(it) } - - -public fun TaskDependencyContainer.dependsOn( - task: Task, - placement: DataPlacement = DataPlacement.ALL, - metaBuilder: MetaBuilder.() -> Unit, -): ExternalTaskDependency = dependsOn(task, placement, Meta(metaBuilder)) - -/** - * Add custom data dependency - */ -public fun TaskDependencyContainer.data(action: DataPlacementScheme.() -> Unit): DataDependency = - DataDependency(DataPlacementScheme(action)).also { add(it) } - -/** - * User-friendly way to add data dependency - */ -public fun TaskDependencyContainer.data( - pattern: String? = null, - from: String? = null, - to: String? = null, -): DataDependency = data { - pattern?.let { this.pattern = it } - from?.let { this.from = it } - to?.let { this.to = it } -} - -///** -// * Add all data as root node -// */ -//public fun TaskDependencyContainer.allData(to: Name = Name.EMPTY): AllDataDependency = AllDataDependency(to).also { add(it) } - -/** - * A builder for [TaskModel] - */ -public class TaskModelBuilder(public val name: Name, meta: Meta = Meta.EMPTY) : TaskDependencyContainer { - /** - * Meta for current task. By default uses the whole input meta - */ - public var meta: MetaBuilder = meta.toMutableMeta() - private val dependencies: HashSet = HashSet() - - override val defaultMeta: Meta get() = meta - - override fun add(dependency: Dependency) { - dependencies.add(dependency) - } - - public var target: String by this.meta.string(key = MODEL_TARGET_KEY, default = "") - - - public fun build(): TaskModel = TaskModel(name, meta.seal(), dependencies) -} - - -public val TaskModel.target: String get() = meta[MODEL_TARGET_KEY]?.string ?: "" \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkStage.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkStage.kt new file mode 100644 index 00000000..663e7d4b --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkStage.kt @@ -0,0 +1,23 @@ +package hep.dataforge.workspace + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.descriptors.Described +import hep.dataforge.misc.Type +import hep.dataforge.workspace.WorkStage.Companion.TYPE + +@Type(TYPE) +public interface WorkStage : Described { + + /** + * Compute a [StageDataSet] using given meta. In general, the result is lazy and represents both computation model + * and a handler for actual result + * + * @param workspace a workspace to run task model in + * @param meta configuration for current stage computation + */ + public suspend fun execute(workspace: Workspace, meta: Meta): StageDataSet + + public companion object { + public const val TYPE: String = "workspace.stage" + } +} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt index a1c7d27c..738e574b 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt @@ -1,9 +1,7 @@ package hep.dataforge.workspace import hep.dataforge.context.ContextAware -import hep.dataforge.data.DataSet import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaBuilder import hep.dataforge.misc.Type import hep.dataforge.names.Name import hep.dataforge.names.toName @@ -15,7 +13,7 @@ public interface Workspace : ContextAware, Provider { /** * The whole data node for current workspace */ - public val data: DataSet + public val data: StageDataSet<*> /** * All targets associated with the workspace @@ -23,47 +21,46 @@ public interface Workspace : ContextAware, Provider { public val targets: Map /** - * All tasks associated with the workspace + * All stages associated with the workspace */ - public val tasks: Map> + public val stages: Map> override fun content(target: String): Map { return when (target) { "target", Meta.TYPE -> targets.mapKeys { it.key.toName() } - Task.TYPE -> tasks + WorkStage.TYPE -> stages //Data.TYPE -> data.flow().toMap() else -> emptyMap() } } - /** - * Invoke a task in the workspace utilizing caching if possible - */ - public suspend fun run(task: Task, config: Meta): DataSet { - val model = task.build(this, config) - task.validate(model) - return task.run(this, model) - } - public companion object { public const val TYPE: String = "workspace" } } -public suspend fun Workspace.run(task: Task<*>, target: String): DataSet { - val meta = targets[target] ?: error("A target with name $target not found in $this") - return run(task, meta) +public suspend fun Workspace.stage(taskName: Name, taskMeta: Meta): StageDataSet<*> { + val task = stages[taskName] ?: error("Task with name $taskName not found in the workspace") + return task.execute(this, taskMeta) } +public suspend fun Workspace.getData(taskName: Name, taskMeta: Meta, name: Name): StageData<*>? = + stage(taskName, taskMeta).getData(name) -public suspend fun Workspace.run(task: String, target: String): DataSet = - tasks[task.toName()]?.let { run(it, target) } ?: error("Task with name $task not found") - -public suspend fun Workspace.run(task: String, meta: Meta): DataSet = - tasks[task.toName()]?.let { run(it, meta) } ?: error("Task with name $task not found") - -public suspend fun Workspace.run(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = - run(task, Meta(block)) - -public suspend fun Workspace.run(task: Task, metaBuilder: MetaBuilder.() -> Unit = {}): DataSet = - run(task, Meta(metaBuilder)) \ No newline at end of file +//public suspend fun Workspace.execute(task: WorkStage<*>, target: String): DataSet { +// val meta = targets[target] ?: error("A target with name $target not found in $this") +// return run(task, meta) +//} +// +// +//public suspend fun Workspace.execute(task: String, target: String): DataSet = +// stages[task.toName()]?.let { execute(it, target) } ?: error("Task with name $task not found") +// +//public suspend fun Workspace.execute(task: String, meta: Meta): DataSet = +// stages[task.toName()]?.let { run(it, meta) } ?: error("Task with name $task not found") +// +//public suspend fun Workspace.execute(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = +// execute(task, Meta(block)) +// +//public suspend fun Workspace.execute(task: WorkStage, metaBuilder: MetaBuilder.() -> Unit = {}): DataSet = +// run(task, Meta(metaBuilder)) \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt index 6c95bb64..4a8e0acd 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt @@ -1,11 +1,14 @@ package hep.dataforge.workspace import hep.dataforge.context.Context -import hep.dataforge.context.logger import hep.dataforge.data.* import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.names.Name +import hep.dataforge.workspace.old.GenericTask +import hep.dataforge.workspace.old.TaskModel +import hep.dataforge.workspace.old.TaskModelBuilder +import hep.dataforge.workspace.old.data import kotlin.reflect.KClass private typealias DataTransformation = suspend (context: Context, model: TaskModel, data: DataSet) -> DataSet @@ -187,7 +190,7 @@ public class TaskBuilder(private val name: Name, public val type: KClas logger.warn { "No transformation present, returning input data" } dataSet.castOrNull(type) ?: error("$type expected, but $type received") } else { - DataTree.dynamic(type, workspace.context){ + DataTree.active(type, workspace.context){ dataTransforms.forEach { transformation -> val res = transformation(workspace.context, model, dataSet) update(res) @@ -201,5 +204,5 @@ public class TaskBuilder(private val name: Name, public val type: KClas @DFExperimental public suspend inline fun TaskBuilder.TaskEnv.dataTree( - crossinline block: suspend MutableDataTree.() -> Unit, -): DataTree = DataTree.dynamic(context, block) \ No newline at end of file + crossinline block: suspend ActiveDataTree.() -> Unit, +): DataTree = DataTree.active(context, block) \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt index 48942ad5..e92950ff 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -3,7 +3,7 @@ package hep.dataforge.workspace import hep.dataforge.context.Context import hep.dataforge.context.ContextBuilder import hep.dataforge.context.Global -import hep.dataforge.data.MutableDataTree +import hep.dataforge.data.ActiveDataTree import hep.dataforge.meta.* import hep.dataforge.names.toName import kotlin.reflect.KClass @@ -12,8 +12,8 @@ import kotlin.reflect.KClass public interface WorkspaceBuilder { public val parentContext: Context public var context: Context - public var data: MutableDataTree - public var tasks: MutableSet> + public var data: ActiveDataTree + public var tasks: MutableSet> public var targets: MutableMap public fun build(): Workspace @@ -27,7 +27,7 @@ public fun WorkspaceBuilder.context(name: String = "WORKSPACE", block: ContextBu } public inline fun WorkspaceBuilder.data( - block: MutableDataTree.() -> Unit, + block: ActiveDataTree.() -> Unit, ): Unit{ data.apply(block) } @@ -59,21 +59,21 @@ public fun WorkspaceBuilder.task( public inline fun WorkspaceBuilder.task( name: String, noinline builder: TaskBuilder.() -> Unit, -): Task = task(name, T::class, builder) +): WorkStage = task(name, T::class, builder) @JvmName("rawTask") public fun WorkspaceBuilder.task( name: String, builder: TaskBuilder.() -> Unit, -): Task = task(name, Any::class, builder) +): WorkStage = task(name, Any::class, builder) /** * A builder for a simple workspace */ public class SimpleWorkspaceBuilder(override val parentContext: Context) : WorkspaceBuilder { override var context: Context = parentContext - override var data: MutableDataTree = MutableDataTree(Any::class, context) - override var tasks: MutableSet> = HashSet() + override var data: ActiveDataTree = ActiveDataTree(Any::class, context) + override var tasks: MutableSet> = HashSet() override var targets: MutableMap = HashMap() override fun build(): SimpleWorkspace { diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt index 9079139d..0ccaccf3 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt @@ -1,26 +1,26 @@ package hep.dataforge.workspace import hep.dataforge.context.AbstractPlugin -import hep.dataforge.context.toMap import hep.dataforge.names.Name import hep.dataforge.names.toName +import hep.dataforge.workspace.old.GenericTask import kotlin.reflect.KClass /** * An abstract plugin with some additional boilerplate to effectively work with workspace context */ public abstract class WorkspacePlugin : AbstractPlugin() { - private val _tasks = HashSet>() - public val tasks: Collection> get() = _tasks + private val _tasks = HashSet>() + public val tasks: Collection> get() = _tasks override fun content(target: String): Map { return when (target) { - Task.TYPE -> tasks.toMap() + WorkStage.TYPE -> tasks.toMap() else -> emptyMap() } } - public fun task(task: Task<*>){ + public fun task(task: WorkStage<*>){ _tasks.add(task) } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index 9e98a276..ba8db797 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -5,6 +5,7 @@ import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginTag import hep.dataforge.data.* import hep.dataforge.meta.Meta +import hep.dataforge.workspace.old.data import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.reduce @@ -23,7 +24,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { data() } transform { data -> - DataTree.dynamic(context) { + DataTree.active(context) { val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } data("result", result) } @@ -36,7 +37,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { data(pattern = "myData\\[12\\]") } transform { data -> - DataTree.dynamic(context) { + DataTree.active(context) { val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } data("result", result) } @@ -48,7 +49,7 @@ class DataPropagationTestPlugin : WorkspacePlugin() { data(pattern = "myData.*") } transform { data -> - DataTree.dynamic(context) { + DataTree.active(context) { val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } data("result", result) } @@ -81,7 +82,7 @@ class DataPropagationTest { @Test fun testAllData() { runBlocking { - val node = testWorkspace.run("Test.allData") + val node = testWorkspace.execute("Test.allData") assertEquals(4950, node.first()!!.value()) } } @@ -89,7 +90,7 @@ class DataPropagationTest { @Test fun testAllRegexData() { runBlocking { - val node = testWorkspace.run("Test.allRegexData") + val node = testWorkspace.execute("Test.allRegexData") assertEquals(4950, node.first()!!.value()) } } @@ -97,7 +98,7 @@ class DataPropagationTest { @Test fun testSingleData() { runBlocking { - val node = testWorkspace.run("Test.singleData") + val node = testWorkspace.execute("Test.singleData") assertEquals(12, node.first()!!.value()) } } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index 817f37b6..11562a31 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -4,6 +4,8 @@ import hep.dataforge.context.* import hep.dataforge.data.* import hep.dataforge.meta.* import hep.dataforge.names.plus +import hep.dataforge.workspace.old.data +import hep.dataforge.workspace.old.dependsOn import kotlinx.coroutines.flow.collect import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Timeout @@ -23,7 +25,7 @@ public inline fun P.toFactory(): PluginFactory

    = object } public fun Workspace.runBlocking(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = runBlocking{ - run(task, block) + execute(task, block) } @@ -166,7 +168,7 @@ class SimpleWorkspaceTest { @Test fun testPluginTask() { - val tasks = workspace.tasks + val tasks = workspace.stages assertTrue { tasks["test.test"] != null } //val node = workspace.run("test.test", "empty") } @@ -174,7 +176,7 @@ class SimpleWorkspaceTest { @Test fun testFullSquare() { runBlocking { - val node = workspace.run("fullsquare") + val node = workspace.execute("fullsquare") println(node.toMeta()) } } From 366d32a04aeb00968a56d62b98c77d826897935c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 24 Jan 2021 22:11:25 +0300 Subject: [PATCH 39/59] Name matching --- .../kotlin/hep/dataforge/names/Name.kt | 13 +++++ .../kotlin/hep/dataforge/names/nameMatcher.kt | 47 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/nameMatcher.kt diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index 5c1a6bd3..5d9cc7bb 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -1,5 +1,6 @@ package hep.dataforge.names +import hep.dataforge.meta.DFExperimental import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.PrimitiveKind @@ -39,6 +40,18 @@ public class Name(public val tokens: List) { public companion object : KSerializer { public const val NAME_SEPARATOR: String = "." + /** + * Match any single token (both body and index) + */ + @DFExperimental + public val MATCH_ANY_TOKEN: NameToken = NameToken("*") + + /** + * Token that allows to match the whole tail or the whole head of the name. Must match at least one token. + */ + @DFExperimental + public val MATCH_ALL_TOKEN: NameToken = NameToken("**") + public val EMPTY: Name = Name(emptyList()) override val descriptor: SerialDescriptor = diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/nameMatcher.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/nameMatcher.kt new file mode 100644 index 00000000..363109f1 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/nameMatcher.kt @@ -0,0 +1,47 @@ +package hep.dataforge.names + +import hep.dataforge.meta.DFExperimental + + +/** + * Checks if this token matches a given [NameToken]. The match successful if: + * * Token body matches pattern body as a regex + * * Index body matches pattern body as a regex of both are null + */ +@DFExperimental +public fun NameToken.matches(pattern: NameToken): Boolean { + if (pattern == Name.MATCH_ANY_TOKEN) return true + val bodyMatches = body.matches(pattern.body.toRegex()) + val indexMatches = (index == null && pattern.index == null) || pattern.index?.let { patternIndex -> + (index ?: "").matches(patternIndex.toRegex()) + } ?: false + return bodyMatches && indexMatches +} + + +/** + * Matches all names in pattern according to [NameToken.matches] rules. + */ +@DFExperimental +public fun Name.matches(pattern: Name): Boolean = when { + pattern.endsWith(Name.MATCH_ALL_TOKEN) -> { + length >= pattern.length + && Name(tokens.subList(0, pattern.length - 1)).matches(pattern.cutLast()) + } + pattern.startsWith(Name.MATCH_ALL_TOKEN) -> { + length >= pattern.length + && Name(tokens.subList(tokens.size - pattern.length + 1, tokens.size)).matches(pattern.cutFirst()) + } + else -> { + tokens.indices.forEach { + val thisToken = tokens.getOrNull(it) ?: return false + if (thisToken == Name.MATCH_ALL_TOKEN) error("Match-all token in the middle of the name is not supported yet") + val patternToken = pattern.tokens.getOrNull(it) ?: return false + if (!thisToken.matches(patternToken)) return false + } + true + } +} + +@OptIn(DFExperimental::class) +public fun Name.matches(pattern: String): Boolean = matches(pattern.toName()) \ No newline at end of file From 9dacd98f64daecebb9df27238180ccd92e08f468 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 26 Jan 2021 15:11:12 +0300 Subject: [PATCH 40/59] Full Workspace and data refactor --- .../hep/dataforge/data/ActiveDataTree.kt | 20 +- .../hep/dataforge/data/CachingAction.kt | 6 +- .../kotlin/hep/dataforge/data/Data.kt | 81 +------ .../kotlin/hep/dataforge/data/DataSet.kt | 15 +- .../hep/dataforge/data/DataSetBuilder.kt | 58 ++--- .../kotlin/hep/dataforge/data/Goal.kt | 63 +++--- .../data/GoalExecutionRestriction.kt | 17 ++ .../kotlin/hep/dataforge/data/GoalLogger.kt | 13 ++ .../kotlin/hep/dataforge/data/GroupRule.kt | 26 ++- .../kotlin/hep/dataforge/data/MapAction.kt | 6 +- .../kotlin/hep/dataforge/data/ReduceAction.kt | 2 +- .../kotlin/hep/dataforge/data/SplitAction.kt | 6 +- .../hep/dataforge/data/StaticDataTree.kt | 10 +- .../{dataSetOperations.kt => dataFilter.kt} | 11 +- .../kotlin/hep/dataforge/data/dataSetMeta.kt | 2 +- .../hep/dataforge/data/dataTransform.kt | 181 +++++++++++++++ .../kotlin/hep/dataforge/data/dataJVM.kt | 21 +- .../hep/dataforge/data/filterIsInstance.kt | 29 --- .../kotlin/hep/dataforge/data/select.kt | 49 +++++ .../kotlin/hep/dataforge/data/ActionsTest.kt | 7 +- .../hep/dataforge/data/DataTreeBuilderTest.kt | 18 +- .../meta/descriptors/ItemDescriptor.kt | 6 +- .../hep/dataforge/meta/MetaDelegateTest.kt | 2 +- .../hep/dataforge/names/NameMatchTest.kt | 32 +++ .../hep/dataforge/scripting/Builders.kt | 4 +- .../hep/dataforge/scripting/BuildersKtTest.kt | 7 +- .../dataforge/workspace/ContextGoalLogger.kt | 17 ++ .../hep/dataforge/workspace/DataPlacement.kt | 104 --------- .../hep/dataforge/workspace/Dependency.kt | 73 ------ .../dataforge/workspace/SimpleWorkspace.kt | 12 +- .../hep/dataforge/workspace/StageDataSet.kt | 46 ---- .../kotlin/hep/dataforge/workspace/Task.kt | 74 +++++++ .../workspace/{StageData.kt => TaskData.kt} | 26 +-- .../hep/dataforge/workspace/TaskResult.kt | 49 +++++ .../hep/dataforge/workspace/WorkStage.kt | 23 -- .../hep/dataforge/workspace/Workspace.kt | 45 ++-- .../dataforge/workspace/WorkspaceBuilder.kt | 103 +++++++++ .../hep/dataforge/workspace/TaskBuilder.kt | 208 ------------------ .../dataforge/workspace/WorkspaceBuilder.kt | 87 -------- .../dataforge/workspace/WorkspacePlugin.kt | 30 +-- .../hep/dataforge/workspace/fileData.kt | 10 +- .../hep/dataforge/workspace/taskBuilders.kt | 24 ++ .../workspace/workspaceExtensions.kt | 8 + .../workspace/DataPropagationTest.kt | 70 ++---- .../hep/dataforge/workspace/FileDataTest.kt | 38 ++-- .../workspace/SimpleWorkspaceTest.kt | 162 +++++++------- 46 files changed, 880 insertions(+), 1021 deletions(-) create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GoalExecutionRestriction.kt create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GoalLogger.kt rename dataforge-data/src/commonMain/kotlin/hep/dataforge/data/{dataSetOperations.kt => dataFilter.kt} (93%) create mode 100644 dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt delete mode 100644 dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/filterIsInstance.kt create mode 100644 dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt create mode 100644 dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameMatchTest.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/ContextGoalLogger.kt delete mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt delete mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt delete mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageDataSet.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt rename dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/{StageData.kt => TaskData.kt} (58%) create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskResult.kt delete mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkStage.kt create mode 100644 dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt delete mode 100644 dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt delete mode 100644 dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt create mode 100644 dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/taskBuilders.kt create mode 100644 dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/workspaceExtensions.kt diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt index 82563eac..b8200fa5 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt @@ -63,7 +63,7 @@ public class ActiveDataTree( } } - override suspend fun set(name: Name, data: Data?) { + override suspend fun emit(name: Name, data: Data?) { if (data == null) { remove(name) } else { @@ -80,9 +80,9 @@ public class ActiveDataTree( * Copy given data set and mirror its changes to this [ActiveDataTree] in [this@setAndObserve]. Returns an update [Job] */ public fun CoroutineScope.setAndObserve(name: Name, dataSet: DataSet): Job = launch { - set(name, dataSet) + emit(name, dataSet) dataSet.updates.collect { nameInBranch -> - set(name + nameInBranch, dataSet.getData(nameInBranch)) + emit(name + nameInBranch, dataSet.getData(nameInBranch)) } } } @@ -90,7 +90,8 @@ public class ActiveDataTree( /** * Create a dynamic tree. Initial data is placed synchronously. Updates are propagated via [updatesScope] */ -public suspend fun DataTree.Companion.active( +@Suppress("FunctionName") +public suspend fun ActiveDataTree( type: KClass, block: suspend ActiveDataTree.() -> Unit, ): DataTree { @@ -99,17 +100,18 @@ public suspend fun DataTree.Companion.active( return tree } -public suspend inline fun DataTree.Companion.active( +@Suppress("FunctionName") +public suspend inline fun ActiveDataTree( crossinline block: suspend ActiveDataTree.() -> Unit, ): DataTree = ActiveDataTree(T::class).apply { block() } -public suspend inline fun ActiveDataTree.set( +public suspend inline fun ActiveDataTree.emit( name: Name, noinline block: suspend ActiveDataTree.() -> Unit, -): Unit = set(name, DataTree.active(T::class, block)) +): Unit = emit(name, ActiveDataTree(T::class, block)) -public suspend inline fun ActiveDataTree.set( +public suspend inline fun ActiveDataTree.emit( name: String, noinline block: suspend ActiveDataTree.() -> Unit, -): Unit = set(name.toName(), DataTree.active(T::class, block)) +): Unit = emit(name.toName(), ActiveDataTree(T::class, block)) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt index e440a765..5f8f8704 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt @@ -34,16 +34,16 @@ public abstract class CachingAction( dataSet: DataSet, meta: Meta, scope: CoroutineScope?, - ): DataSet = DataTree.active(outputType) { + ): DataSet = ActiveDataTree(outputType) { coroutineScope { - collectFrom(transform(dataSet, meta)) + populate(transform(dataSet, meta)) } scope?.let { dataSet.updates.collect { //clear old nodes remove(it) //collect new items - collectFrom(scope.transform(dataSet, meta, it)) + populate(scope.transform(dataSet, meta, it)) //FIXME if the target is data, updates are fired twice } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 57647c03..6d0c4c38 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -62,7 +62,7 @@ public class LazyData( override val meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), - block: suspend CoroutineScope.() -> T, + block: suspend () -> T, ) : Data, LazyGoal(context, dependencies, block) public class StaticData( @@ -78,7 +78,7 @@ public fun Data( meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), - block: suspend CoroutineScope.() -> T, + block: suspend () -> T, ): Data = LazyData(type, meta, context, dependencies, block) @Suppress("FunctionName") @@ -86,80 +86,5 @@ public inline fun Data( meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), - noinline block: suspend CoroutineScope.() -> T, + noinline block: suspend () -> T, ): Data = Data(T::class, meta, context, dependencies, block) - - - -public fun Data.map( - outputType: KClass, - coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = this.meta, - block: suspend CoroutineScope.(T) -> R, -): Data = LazyData(outputType, meta, coroutineContext, listOf(this)) { - block(await()) -} - - -/** - * Create a data pipe - */ -public inline fun Data.map( - coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = this.meta, - noinline block: suspend CoroutineScope.(T) -> R, -): Data = LazyData(R::class, meta, coroutineContext, listOf(this)) { - block(await()) -} - -/** - * Create a joined data. - */ -public inline fun Collection>.reduce( - coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta, - noinline block: suspend CoroutineScope.(Collection) -> R, -): Data = LazyData( - R::class, - meta, - coroutineContext, - this -) { - block(map { run { it.await() } }) -} - -public fun Map>.reduce( - outputType: KClass, - coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta, - block: suspend CoroutineScope.(Map) -> R, -): LazyData = LazyData( - outputType, - meta, - coroutineContext, - this.values -) { - block(mapValues { it.value.await() }) -} - - -/** - * A joining of multiple data into a single one - * @param K type of the map key - * @param T type of the input goal - * @param R type of the result goal - */ -public inline fun Map>.reduce( - coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta, - noinline block: suspend CoroutineScope.(Map) -> R, -): LazyData = LazyData( - R::class, - meta, - coroutineContext, - this.values -) { - block(mapValues { it.value.await() }) -} - - diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt index 1c68c02a..db20fb16 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt @@ -36,10 +36,21 @@ public interface DataSet { public companion object { public val META_KEY: Name = "@meta".asName() + + /** + * An empty [DataSet] that suits all types + */ + public val EMPTY: DataSet = object : DataSet { + override val dataType: KClass = Nothing::class + + override fun flow(): Flow> = emptyFlow() + + override suspend fun getData(name: Name): Data? = null + } } } -public interface ActiveDataSet: DataSet{ +public interface ActiveDataSet : DataSet { /** * A flow of updated item names. Updates are propagated in a form of [Flow] of names of updated nodes. * Those can include new data items and replacement of existing ones. The replaced items could update existing data content @@ -49,7 +60,7 @@ public interface ActiveDataSet: DataSet{ public val updates: Flow } -public val DataSet.updates: Flow get() = if(this is ActiveDataSet) updates else emptyFlow() +public val DataSet.updates: Flow get() = if (this is ActiveDataSet) updates else emptyFlow() /** * Flow all data nodes with names starting with [branchName] diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt index 17120270..5a0a3b55 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt @@ -16,34 +16,37 @@ public interface DataSetBuilder { */ public suspend fun remove(name: Name) - public suspend fun set(name: Name, data: Data?) + public suspend fun emit(name: Name, data: Data?) /** * Set a current state of given [dataSet] into a branch [name]. Does not propagate updates */ - public suspend fun set(name: Name, dataSet: DataSet){ + public suspend fun emit(name: Name, dataSet: DataSet) { //remove previous items - remove(name) + if (name != Name.EMPTY) { + remove(name) + } + //Set new items dataSet.flow().collect { - set(name + it.name, it.data) + emit(name + it.name, it.data) } } /** * Append data to node */ - public suspend infix fun String.put(data: Data): Unit = set(toName(), data) + public suspend infix fun String.put(data: Data): Unit = emit(toName(), data) /** * Append node */ - public suspend infix fun String.put(dataSet: DataSet): Unit = set(toName(), dataSet) + public suspend infix fun String.put(dataSet: DataSet): Unit = emit(toName(), dataSet) /** * Build and append node */ - public suspend infix fun String.put(block: suspend DataSetBuilder.() -> Unit): Unit = set(toName(), block) + public suspend infix fun String.put(block: suspend DataSetBuilder.() -> Unit): Unit = emit(toName(), block) } private class SubSetBuilder(private val parent: DataSetBuilder, private val branch: Name) : @@ -52,57 +55,60 @@ private class SubSetBuilder(private val parent: DataSetBuilder, p parent.remove(branch + name) } - override suspend fun set(name: Name, data: Data?) { - parent.set(branch + name, data) + override suspend fun emit(name: Name, data: Data?) { + parent.emit(branch + name, data) } - override suspend fun set(name: Name, dataSet: DataSet) { - parent.set(branch + name, dataSet) + override suspend fun emit(name: Name, dataSet: DataSet) { + parent.emit(branch + name, dataSet) } } -public suspend fun DataSetBuilder.set(name: Name, block: suspend DataSetBuilder.() -> Unit){ - SubSetBuilder(this,name).apply { block() } +public suspend fun DataSetBuilder.emit(name: Name, block: suspend DataSetBuilder.() -> Unit) { + SubSetBuilder(this, name).apply { block() } } -public suspend fun DataSetBuilder.set(name: String, data: Data) { - set(name.toName(), data) +public suspend fun DataSetBuilder.emit(name: String, data: Data) { + emit(name.toName(), data) } public suspend fun DataSetBuilder.data(name: Name, data: T, meta: Meta = Meta.EMPTY) { - set(name, Data.static(data, meta)) + emit(name, Data.static(data, meta)) } public suspend fun DataSetBuilder.data(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { - set(name, Data.static(data, Meta(block))) + emit(name, Data.static(data, Meta(block))) } public suspend fun DataSetBuilder.data(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { - set(name.toName(), Data.static(data, Meta(block))) + emit(name.toName(), Data.static(data, Meta(block))) } -public suspend fun DataSetBuilder.set(name: String, set: DataSet) { - this.set(name.toName(), set) +public suspend fun DataSetBuilder.emit(name: String, set: DataSet) { + this.emit(name.toName(), set) } -public suspend fun DataSetBuilder.set(name: String, block: suspend DataSetBuilder.() -> Unit): Unit = - this@set.set(name.toName(), block) +public suspend fun DataSetBuilder.emit(name: String, block: suspend DataSetBuilder.() -> Unit): Unit = + this@emit.emit(name.toName(), block) +public suspend fun DataSetBuilder.emit(data: NamedData) { + emit(data.name, data.data) +} /** * Update data with given node data and meta with node meta. */ @DFExperimental -public suspend fun DataSetBuilder.update(tree: DataSet): Unit = coroutineScope { +public suspend fun DataSetBuilder.populate(tree: DataSet): Unit = coroutineScope { tree.flow().collect { //TODO check if the place is occupied - set(it.name, it.data) + emit(it.name, it.data) } } -public suspend fun DataSetBuilder.collectFrom(flow: Flow>) { +public suspend fun DataSetBuilder.populate(flow: Flow>) { flow.collect { - set(it.name, it.data) + emit(it.name, it.data) } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt index 3bde5971..eef9e7cb 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt @@ -49,10 +49,13 @@ public open class StaticGoal(public val value: T) : Goal { } } +/** + * @param coroutineContext additional context information + */ public open class LazyGoal( private val coroutineContext: CoroutineContext = EmptyCoroutineContext, override val dependencies: Collection> = emptyList(), - public val block: suspend CoroutineScope.() -> T, + public val block: suspend () -> T, ) : Goal { final override var deferred: Deferred? = null @@ -61,20 +64,40 @@ public open class LazyGoal( /** * Get ongoing computation or start a new one. * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. + * If [GoalExecutionRestriction] is present in the [coroutineScope] context, the call could produce a error a warning + * depending on the settings. */ @DFExperimental override fun async(coroutineScope: CoroutineScope): Deferred { + val log = coroutineScope.coroutineContext[GoalLogger] + // Check if context restricts goal computation + coroutineScope.coroutineContext[GoalExecutionRestriction]?.let { restriction -> + when (restriction.policy) { + GoalExecutionRestrictionPolicy.WARNING -> log?.emit(GoalLogger.WARNING_TAG) { "Goal eager execution is prohibited by the coroutine scope policy" } + GoalExecutionRestrictionPolicy.ERROR -> error("Goal eager execution is prohibited by the coroutine scope policy") + else -> { + /*do nothing*/ + } + } + } + + log?.emit { "Starting dependencies computation for ${this@LazyGoal}" } val startedDependencies = this.dependencies.map { goal -> goal.run { async(coroutineScope) } } return deferred ?: coroutineScope.async( - this.coroutineContext + CoroutineMonitor() + Dependencies(startedDependencies) + coroutineContext + + CoroutineMonitor() + + Dependencies(startedDependencies) + + GoalExecutionRestriction(GoalExecutionRestrictionPolicy.NONE) // Remove restrictions on goal execution ) { + //cancel execution if error encountered in one of dependencies startedDependencies.forEach { deferred -> deferred.invokeOnCompletion { error -> if (error != null) this.cancel(CancellationException("Dependency $deferred failed with error: ${error.message}")) } } + coroutineContext[GoalLogger]?.emit { "Starting computation of ${this@LazyGoal}" } block() }.also { deferred = it } } @@ -86,38 +109,4 @@ public open class LazyGoal( deferred?.cancel() deferred = null } -} - -/** - * Create a one-to-one goal based on existing goal - */ -public fun Goal.map( - coroutineContext: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.(T) -> R, -): Goal = LazyGoal(coroutineContext, listOf(this)) { - block(await()) -} - -/** - * Create a joining goal. - */ -public fun Collection>.reduce( - coroutineContext: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.(Collection) -> R, -): Goal = LazyGoal(coroutineContext, this) { - block(map { run { it.await() } }) -} - -/** - * A joining goal for a map - * @param K type of the map key - * @param T type of the input goal - * @param R type of the result goal - */ -public fun Map>.reduce( - coroutineContext: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.(Map) -> R, -): Goal = LazyGoal(coroutineContext, this.values) { - block(mapValues { it.value.await() }) -} - +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GoalExecutionRestriction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GoalExecutionRestriction.kt new file mode 100644 index 00000000..38e439d5 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GoalExecutionRestriction.kt @@ -0,0 +1,17 @@ +package hep.dataforge.data + +import kotlin.coroutines.CoroutineContext + +public enum class GoalExecutionRestrictionPolicy { + NONE, + WARNING, + ERROR +} + +public class GoalExecutionRestriction( + public val policy: GoalExecutionRestrictionPolicy = GoalExecutionRestrictionPolicy.ERROR, +) : CoroutineContext.Element { + override val key: CoroutineContext.Key<*> get() = Companion + + public companion object : CoroutineContext.Key +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GoalLogger.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GoalLogger.kt new file mode 100644 index 00000000..f0520578 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GoalLogger.kt @@ -0,0 +1,13 @@ +package hep.dataforge.data + +import kotlin.coroutines.CoroutineContext + +public interface GoalLogger : CoroutineContext.Element { + override val key: CoroutineContext.Key<*> get() = GoalLogger + + public fun emit(vararg tags: String, message: suspend () -> String) + + public companion object : CoroutineContext.Key{ + public const val WARNING_TAG: String = "WARNING" + } +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt index fcd22eae..41e1de53 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt @@ -33,20 +33,26 @@ public interface GroupRule { * @param defaultTagValue * @return */ - public fun byValue(scope: CoroutineScope, key: String, defaultTagValue: String): GroupRule = - object : GroupRule { + public fun byValue( + scope: CoroutineScope, + key: String, + defaultTagValue: String, + ): GroupRule = object : GroupRule { - override suspend fun gather(dataType: KClass, set: DataSet): Map> { - val map = HashMap>() + override suspend fun gather( + dataType: KClass, + set: DataSet, + ): Map> { + val map = HashMap>() - set.flow().collect { data -> - val tagValue = data.meta[key]?.string ?: defaultTagValue - map.getOrPut(tagValue) { ActiveDataTree(dataType) }.set(data.name, data.data) - } - - return map + set.flow().collect { data -> + val tagValue = data.meta[key]?.string ?: defaultTagValue + map.getOrPut(tagValue) { ActiveDataTree(dataType) }.emit(data.name, data.data) } + + return map } + } // @ValueDef(key = "byValue", required = true, info = "The name of annotation value by which grouping should be made") diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt index ec6442d9..cfd37f0a 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt @@ -67,14 +67,14 @@ public class MapAction( val flow = dataSet.flow().map(::mapOne) - return DataTree.active(outputType) { - collectFrom(flow) + return ActiveDataTree(outputType) { + populate(flow) scope?.launch { dataSet.updates.collect { name -> //clear old nodes remove(name) //collect new items - collectFrom(dataSet.flowChildren(name).map(::mapOne)) + populate(dataSet.flowChildren(name).map(::mapOne)) } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt index 1cc4ec10..b326b344 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt @@ -104,7 +104,7 @@ public class ReduceAction( val env = ActionEnv(groupName.toName(), groupMeta, meta) - val res: LazyData = dataFlow.reduce( + val res: LazyData = dataFlow.reduceToData( outputType, meta = groupMeta ) { group.result.invoke(env, it) } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt index 48f4fc93..3d8fca5a 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt @@ -62,14 +62,14 @@ public class SplitAction( } } - return DataTree.active(outputType) { - collectFrom(dataSet.flow().flatMapConcat(transform = ::splitOne)) + return ActiveDataTree(outputType) { + populate(dataSet.flow().flatMapConcat(transform = ::splitOne)) scope?.launch { dataSet.updates.collect { name -> //clear old nodes remove(name) //collect new items - collectFrom(dataSet.flowChildren(name).flatMapConcat(transform = ::splitOne)) + populate(dataSet.flowChildren(name).flatMapConcat(transform = ::splitOne)) } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt index c3191ef9..05cd85de 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt @@ -42,32 +42,34 @@ internal class StaticDataTree( } } - override suspend fun set(name: Name, data: Data?) { + override suspend fun emit(name: Name, data: Data?) { set(name, data?.let { DataTreeItem.Leaf(it) }) } - override suspend fun set(name: Name, dataSet: DataSet) { + override suspend fun emit(name: Name, dataSet: DataSet) { if (dataSet is StaticDataTree) { set(name, DataTreeItem.Node(dataSet)) } else { coroutineScope { dataSet.flow().collect { - set(name + it.name, it.data) + emit(name + it.name, it.data) } } } } } +@Suppress("FunctionName") public suspend fun DataTree( dataType: KClass, block: suspend DataSetBuilder.() -> Unit, ): DataTree = StaticDataTree(dataType).apply { block() } +@Suppress("FunctionName") public suspend inline fun DataTree( noinline block: suspend DataSetBuilder.() -> Unit, ): DataTree = DataTree(T::class, block) public suspend fun DataSet.seal(): DataTree = DataTree(dataType){ - update(this@seal) + populate(this@seal) } \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt similarity index 93% rename from dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt rename to dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt index 9db5f963..5f2b2a8a 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetOperations.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt @@ -12,10 +12,9 @@ import kotlin.reflect.KClass /** * A stateless filtered [DataSet] */ -@DFExperimental public fun DataSet.filter( predicate: suspend (Name, Data) -> Boolean, -): DataSet = object : DataSet { +): ActiveDataSet = object : ActiveDataSet { override val dataType: KClass get() = this@filter.dataType override fun flow(): Flow> = @@ -36,7 +35,7 @@ public fun DataSet.filter( * Generate a wrapper data set with a given name prefix appended to all names */ public fun DataSet.withNamePrefix(prefix: Name): DataSet = if (prefix.isEmpty()) this -else object : DataSet { +else object : ActiveDataSet { override val dataType: KClass get() = this@withNamePrefix.dataType override fun flow(): Flow> = this@withNamePrefix.flow().map { it.data.named(prefix + it.name) } @@ -51,8 +50,9 @@ else object : DataSet { /** * Get a subset of data starting with a given [branchName] */ -public fun DataSet.branch(branchName: Name): DataSet = if (branchName.isEmpty()) this -else object : DataSet { +public fun DataSet.branch(branchName: Name): DataSet = if (branchName.isEmpty()) { + this +} else object : ActiveDataSet { override val dataType: KClass get() = this@branch.dataType override fun flow(): Flow> = this@branch.flow().mapNotNull { @@ -70,3 +70,4 @@ public fun DataSet.branch(branchName: String): DataSet = this@br @DFExperimental public suspend fun DataSet.rootData(): Data? = getData(Name.EMPTY) + diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt index 4b833e10..5ca07aa5 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataSetMeta.kt @@ -12,7 +12,7 @@ public suspend fun DataSet<*>.getMeta(): Meta? = getData(DataSet.META_KEY)?.meta /** * Add meta-data node to a [DataSet] */ -public suspend fun DataSetBuilder<*>.meta(meta: Meta): Unit = set(DataSet.META_KEY, Data.empty(meta)) +public suspend fun DataSetBuilder<*>.meta(meta: Meta): Unit = emit(DataSet.META_KEY, Data.empty(meta)) /** * Add meta-data node to a [DataSet] diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt new file mode 100644 index 00000000..39f5f306 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt @@ -0,0 +1,181 @@ +package hep.dataforge.data + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.seal +import hep.dataforge.meta.toMutableMeta +import kotlinx.coroutines.flow.* +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext +import kotlin.reflect.KClass + + +public fun Data.map( + outputType: KClass, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = this.meta, + block: suspend (T) -> R, +): Data = LazyData(outputType, meta, coroutineContext, listOf(this)) { + block(await()) +} + +/** + * Create a data mapping + */ +public inline fun Data.map( + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = this.meta, + crossinline block: suspend (T) -> R, +): Data = LazyData(R::class, meta, coroutineContext, listOf(this)) { + block(await()) +} + +/** + * Combine this data with the other data using [block] + */ +public inline fun Data.combine( + other: Data, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = this.meta, + crossinline block: suspend (left: T1, right: T2) -> R, +): Data = LazyData(R::class, meta, coroutineContext, listOf(this,other)) { + block(await(), other.await()) +} + + +//data collection operations + +/** + * Create a joined data. + */ +public inline fun Collection>.reduceToData( + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = Meta.EMPTY, + noinline block: suspend (Collection) -> R, +): Data = LazyData( + R::class, + meta, + coroutineContext, + this +) { + block(map { run { it.await() } }) +} + +public fun Map>.reduceToData( + outputType: KClass, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = Meta.EMPTY, + block: suspend (Map) -> R, +): LazyData = LazyData( + outputType, + meta, + coroutineContext, + this.values +) { + block(mapValues { it.value.await() }) +} + + +/** + * A joining of multiple data into a single one + * @param K type of the map key + * @param T type of the input goal + * @param R type of the result goal + */ +public inline fun Map>.reduceToData( + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = Meta.EMPTY, + noinline block: suspend (Map) -> R, +): LazyData = LazyData( + R::class, + meta, + coroutineContext, + this.values +) { + block(mapValues { it.value.await() }) +} + +/** + * Transform a [Flow] of [NamedData] to a single [Data]. Execution restrictions are removed for inner [Flow] + */ +public suspend fun Flow>.reduceToData( + outputType: KClass, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = Meta.EMPTY, + transformation: suspend (Flow>) -> R, +): LazyData = LazyData( + outputType, + meta, + coroutineContext, + toList() +) { + transformation(this) +} + +//flow operations + +public suspend inline fun Flow>.reduceToData( + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = Meta.EMPTY, + noinline transformation: suspend (Flow>) -> R, +): LazyData = reduceToData(R::class, coroutineContext, meta) { + transformation(it) +} + +/** + * Fold a flow of named data into a single [Data] + */ +public suspend inline fun Flow>.foldToData( + initial: R, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = Meta.EMPTY, + noinline block: suspend (result: R, data: NamedData) -> R, +): LazyData = reduceToData( + coroutineContext, meta +) { + it.fold(initial, block) +} + +//DataSet operations + +public suspend fun DataSet.map( + outputType: KClass, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + metaTransform: MetaBuilder.() -> Unit = {}, + block: suspend (T) -> R, +): DataSet = DataTree(outputType) { + populate( + flow().map { + val newMeta = it.meta.toMutableMeta().apply(metaTransform).seal() + it.map(outputType, coroutineContext, newMeta, block).named(it.name) + } + ) +} + +public suspend inline fun DataSet.map( + coroutineContext: CoroutineContext = EmptyCoroutineContext, + noinline metaTransform: MetaBuilder.() -> Unit = {}, + noinline block: suspend (T) -> R, +): DataSet = map(R::class, coroutineContext, metaTransform, block) + +public suspend fun DataSet.forEach(block: suspend (NamedData) -> Unit) { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + flow().collect { + block(it) + } +} + +public suspend inline fun DataSet.reduceToData( + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = Meta.EMPTY, + noinline transformation: suspend (Flow>) -> R, +): LazyData = flow().reduceToData(coroutineContext, meta, transformation) + +public suspend inline fun DataSet.foldToData( + initial: R, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + meta: Meta = Meta.EMPTY, + noinline block: suspend (result: R, data: NamedData) -> R, +): LazyData = flow().foldToData(initial, coroutineContext, meta, block) \ No newline at end of file diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt index 775adfc1..4178766a 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt @@ -1,31 +1,12 @@ package hep.dataforge.data -import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf -/** - * Block the thread and get data content - */ -public fun Data.value(): T = runBlocking { await() } - /** * Check if data could be safely cast to given class */ -internal fun Data<*>.canCast(type: KClass): Boolean = - this.type.isSubclassOf(type) - - -//public fun Data.upcast(type: KClass): Data { -// return object : Data by this { -// override val type: KClass = type -// } -//} -// -///** -// * Safe upcast a [Data] to a supertype -// */ -//public inline fun Data.upcast(): Data = upcast(R::class) +internal fun Data<*>.canCast(type: KClass): Boolean = this.type.isSubclassOf(type) /** * Cast the node to given type if the cast is possible or return null diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/filterIsInstance.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/filterIsInstance.kt deleted file mode 100644 index 4997573a..00000000 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/filterIsInstance.kt +++ /dev/null @@ -1,29 +0,0 @@ -package hep.dataforge.data - -import hep.dataforge.names.Name -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlin.reflect.KClass - - -public fun DataSet<*>.filterIsInstance(type: KClass): DataSet = object : DataSet { - override val dataType: KClass = type - - @Suppress("UNCHECKED_CAST") - override fun flow(): Flow> = this@filterIsInstance.flow().filter { - it.canCast(type) - }.map { - it as NamedData - } - - override suspend fun getData(name: Name): Data? = this@filterIsInstance.getData(name)?.castOrNull(type) - - override val updates: Flow = this@filterIsInstance.updates.filter { - val datum = this@filterIsInstance.getData(it) - datum?.canCast(type) ?: false - } - -} - -public inline fun DataSet<*>.filterIsInstance(): DataSet = filterIsInstance(R::class) \ No newline at end of file diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt new file mode 100644 index 00000000..5b30a0d3 --- /dev/null +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt @@ -0,0 +1,49 @@ +package hep.dataforge.data + +import hep.dataforge.meta.DFExperimental +import hep.dataforge.names.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlin.reflect.KClass + + +/** + * Select all data matching given type and filters. Does not modify paths + */ +@OptIn(DFExperimental::class) +public fun DataSet<*>.select( + type: KClass, + namePattern: Name? = null, +): ActiveDataSet = object : ActiveDataSet { + override val dataType: KClass = type + + @Suppress("UNCHECKED_CAST") + override fun flow(): Flow> = this@select.flow().filter { + it.canCast(type) && (namePattern == null || it.name.matches(namePattern)) + }.map { + it as NamedData + } + + override suspend fun getData(name: Name): Data? = this@select.getData(name)?.castOrNull(type) + + override val updates: Flow = this@select.updates.filter { + val datum = this@select.getData(it) + datum?.canCast(type) ?: false + } + +} + +/** + * Select a single datum of the appropriate type + */ +public inline fun DataSet<*>.select(namePattern: Name? = null): DataSet = + select(R::class, namePattern) + +public suspend fun DataSet<*>.selectOne(type: KClass, name: Name): NamedData? = + getData(name)?.castOrNull(type)?.named(name) + +public suspend inline fun DataSet<*>.selectOne(name: Name): NamedData? = selectOne(R::class, name) + +public suspend inline fun DataSet<*>.selectOne(name: String): NamedData? = + selectOne(R::class, name.toName()) \ No newline at end of file diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt index d1944785..50240b28 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt @@ -4,9 +4,14 @@ import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Test import kotlin.test.assertEquals +/** + * Block the thread and get data content + */ +public fun Data.value(): T = runBlocking { await() } + class ActionsTest { val data: DataTree = runBlocking { - DataTree.static { + DataTree { repeat(10) { data(it.toString(), it) } diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt index 98feae57..4050e731 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt @@ -10,20 +10,20 @@ import kotlin.test.assertEquals internal class DataTreeBuilderTest { @Test fun testDataUpdate() = runBlocking { - val updateData: DataTree = DataTree.static { + val updateData: DataTree = DataTree { "update" put { "a" put Data.static("a") "b" put Data.static("b") } } - val node = DataTree.static { - set("primary") { + val node = DataTree { + emit("primary") { data("a", "a") data("b", "b") } data("root", "root") - update(updateData) + populate(updateData) } @@ -34,13 +34,15 @@ internal class DataTreeBuilderTest { @Test fun testDynamicUpdates() = runBlocking { try { + lateinit var updateJob: Job supervisorScope { - val subNode = DataTree.active { - launch { + val subNode = ActiveDataTree { + updateJob = launch { repeat(10) { delay(10) data("value", it) } + delay(10) } } launch { @@ -48,7 +50,7 @@ internal class DataTreeBuilderTest { println(it) } } - val rootNode = DataTree.active { + val rootNode = ActiveDataTree { setAndObserve("sub".toName(), subNode) } @@ -57,7 +59,7 @@ internal class DataTreeBuilderTest { println(it) } } - delay(200) + updateJob.join() assertEquals(9, rootNode.getData("sub.value")?.value()) cancel() } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt index 7dd5f905..e03c832a 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt @@ -192,13 +192,13 @@ public class NodeDescriptor(config: Config = Config()) : ItemDescriptor(config) internal val ITEM_KEY: Name = "item".asName() internal val IS_NODE_KEY: Name = "@isNode".asName() - public inline operator fun invoke(block: NodeDescriptor.() -> Unit): NodeDescriptor = - NodeDescriptor().apply(block) - //TODO infer descriptor from spec } } +public inline fun NodeDescriptor(block: NodeDescriptor.() -> Unit): NodeDescriptor = + NodeDescriptor().apply(block) + /** * Get a descriptor item associated with given name or null if item for given name not provided */ diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt index a6b514d4..c460c602 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaDelegateTest.kt @@ -38,7 +38,7 @@ class MetaDelegateTest { assertEquals("theString", testObject.myValue) assertEquals(TestEnum.NO, testObject.enumValue) assertEquals(2.2, testObject.safeValue) - assertEquals("ddd", testObject.inner?.innerValue) + assertEquals("ddd", testObject.inner.innerValue) } diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameMatchTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameMatchTest.kt new file mode 100644 index 00000000..b91d94a6 --- /dev/null +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameMatchTest.kt @@ -0,0 +1,32 @@ +package hep.dataforge.names + +import hep.dataforge.meta.DFExperimental +import kotlin.test.Test +import kotlin.test.assertFails +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +@OptIn(DFExperimental::class) +class NameMatchTest { + @Test + fun matchWildCards() { + val theName = "a.b.c.d".toName() + assertTrue { theName.matches("a.b.**") } + assertTrue { theName.matches("a.*.c.**") } + assertTrue { theName.matches("**.d") } + assertTrue { theName.matches("**.b.**") } + assertTrue { theName.matches("a.*.*.d") } + assertFails { theName.matches("a.**.d") } + assertFalse { theName.matches("a.b.c.d.**") } + } + + @Test + fun matchPattern() { + val theName = "a[dd+2].b[13].c.d[\"d\"]".toName() + assertTrue { theName.matches("a[.*].b[.*].c[.*].d[.*]") } + assertTrue { theName.matches("a[.*].b[.*].c.d[.*]") } + assertFalse { theName.matches("a[.*].b[.*].*.d") } + assertTrue { theName.matches("""\\w[dd\\+2].b[.*].c[.*].d[.*]""") } + assertFalse { theName.matches("""\\s[dd\\+2].b[.*].c[.*].d[.*]""") } + } +} \ No newline at end of file diff --git a/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt index 1e325595..1842ba45 100644 --- a/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt +++ b/dataforge-scripting/src/jvmMain/kotlin/hep/dataforge/scripting/Builders.kt @@ -3,7 +3,6 @@ package hep.dataforge.scripting import hep.dataforge.context.Context import hep.dataforge.context.Global import hep.dataforge.context.logger -import hep.dataforge.workspace.SimpleWorkspaceBuilder import hep.dataforge.workspace.Workspace import hep.dataforge.workspace.WorkspaceBuilder import java.io.File @@ -17,7 +16,7 @@ import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost public object Builders { private fun buildWorkspace(source: SourceCode, context: Context = Global): Workspace { - val builder = SimpleWorkspaceBuilder(context) + val builder = WorkspaceBuilder(context) val workspaceScriptConfiguration = ScriptCompilationConfiguration { // baseClass(Any::class) @@ -30,6 +29,7 @@ public object Builders { dependenciesFromCurrentContext(wholeClasspath = true) } hostConfiguration(defaultJvmScriptingHostConfiguration) + compilerOptions("-jvm-target", Runtime.version().feature().toString()) } val evaluationConfiguration = ScriptEvaluationConfiguration { diff --git a/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt b/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt index 7d96c168..cd0985a7 100644 --- a/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt +++ b/dataforge-scripting/src/jvmTest/kotlin/hep/dataforge/scripting/BuildersKtTest.kt @@ -3,8 +3,9 @@ package hep.dataforge.scripting import hep.dataforge.context.Global import hep.dataforge.meta.get import hep.dataforge.meta.int -import hep.dataforge.workspace.SimpleWorkspaceBuilder -import hep.dataforge.workspace.context +import hep.dataforge.workspace.WorkspaceBuilder + +import hep.dataforge.workspace.target import kotlin.test.Test import kotlin.test.assertEquals @@ -12,7 +13,7 @@ import kotlin.test.assertEquals class BuildersKtTest { @Test fun checkBuilder(){ - val workspace = SimpleWorkspaceBuilder(Global).apply { + val workspace = WorkspaceBuilder(Global).apply { println("I am working") context("test") diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/ContextGoalLogger.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/ContextGoalLogger.kt new file mode 100644 index 00000000..aae36ac6 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/ContextGoalLogger.kt @@ -0,0 +1,17 @@ +package hep.dataforge.workspace + +import hep.dataforge.context.Context +import hep.dataforge.context.logger +import hep.dataforge.data.GoalLogger +import kotlinx.coroutines.launch + +public class ContextGoalLogger(public val context: Context) : GoalLogger { + override fun emit(vararg tags: String, message: suspend () -> String) { + context.launch { + val text = message() + context.logger.info { text } + } + } +} + +public val Workspace.goalLogger: GoalLogger get() = ContextGoalLogger(context) \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt deleted file mode 100644 index 04e3ecb4..00000000 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/DataPlacement.kt +++ /dev/null @@ -1,104 +0,0 @@ -package hep.dataforge.workspace - -import hep.dataforge.data.* -import hep.dataforge.meta.* -import hep.dataforge.names.Name -import hep.dataforge.names.plus -import hep.dataforge.names.removeHeadOrNull -import hep.dataforge.names.toName -import kotlinx.coroutines.flow.* -import kotlin.reflect.KClass - -public interface DataPlacement: MetaRepr { - /** - * Select a placement for a data with given [name] and [meta]. The result is null if data should be ignored. - */ - public fun place(name: Name, meta: Meta, dataType: KClass<*>): Name? - - public companion object { - public val ALL: DataPlacement = object : DataPlacement { - override fun place(name: Name, meta: Meta, dataType: KClass<*>): Name = name - - override fun toMeta(): Meta = Meta{"from" put "*"} - } - - public fun into(target: Name): DataPlacement = DataPlacementScheme{ - to = target.toString() - } - - public fun into(target: String): DataPlacement = DataPlacementScheme{ - to = target - } - - } -} - -public fun DataPlacement.place(datum: NamedData<*>): Name? = place(datum.name, datum.meta, datum.type) - -private class ArrangedDataSet( - private val source: DataSet, - private val placement: DataPlacement, -) : DataSet { - override val dataType: KClass get() = source.dataType - - override fun flow(): Flow> = source.flow().mapNotNull { - val newName = placement.place(it) ?: return@mapNotNull null - it.data.named(newName) - } - - override suspend fun getData(name: Name): Data? = flow().filter { it.name == name }.firstOrNull() - - override val updates: Flow = source.updates.flatMapConcat { - flowChildren(it).mapNotNull(placement::place) - } -} - -public class DataPlacementScheme : Scheme(), DataPlacement { - /** - * A source node for the filter - */ - public var from: String? by string() - - /** - * A target placement for the filtered node - */ - public var to: String? by string() - - /** - * A regular expression pattern for the filter - */ - public var pattern: String? by string() -// val prefix by string() -// val suffix by string() - - override fun place(name: Name, meta: Meta, dataType: KClass<*>): Name? { - val fromName = from?.toName() ?: Name.EMPTY - val nameReminder = name.removeHeadOrNull(fromName) ?: return null - val regex = pattern?.toRegex() - return if (regex == null || nameReminder.toString().matches(regex)) { - (to?.toName() ?: Name.EMPTY) + nameReminder - } else { - null - } - } - - public companion object : SchemeSpec(::DataPlacementScheme) -} - - -/** - * Apply data node rearrangement - */ -public fun DataSet.rearrange(placement: DataPlacement): DataSet = ArrangedDataSet(this, placement) - -///** -// * Mask data using [DataPlacementScheme] specification -// */ -//public fun DataSet.rearrange(placement: Meta): DataSet = -// rearrange(DataPlacementScheme.read(placement)) - -/** - * Mask data using [DataPlacementScheme] builder - */ -public fun DataSet.rearrange(placementBuilder: DataPlacementScheme.() -> Unit): DataSet = - rearrange(DataPlacementScheme(placementBuilder)) \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt deleted file mode 100644 index 3c342e90..00000000 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Dependency.kt +++ /dev/null @@ -1,73 +0,0 @@ -package hep.dataforge.workspace - -import hep.dataforge.data.DataSet -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaRepr -import hep.dataforge.meta.toMutableMeta -import hep.dataforge.names.Name -import hep.dataforge.names.asName -import hep.dataforge.names.plus - -/** - * A dependency of the task which allows to lazily create a data tree for single dependency - */ -public sealed class Dependency : MetaRepr { - public abstract suspend fun apply(workspace: Workspace): DataSet -} - -public class DataDependency(private val placement: DataPlacement = DataPlacement.ALL) : Dependency() { - override suspend fun apply(workspace: Workspace): DataSet = workspace.data.rearrange(placement) - - override fun toMeta(): Meta = placement.toMeta() -} - -public abstract class TaskDependency( - public val meta: Meta, - protected val placement: DataPlacement, -) : Dependency() { - public abstract fun resolveTask(workspace: Workspace): WorkStage - - /** - * A name of the dependency for logging and serialization - */ - public abstract val name: Name - - override suspend fun apply(workspace: Workspace): DataSet { - val task = resolveTask(workspace) - val result = workspace.run(task, meta) - return result.rearrange(placement) - } -} - -public class ExternalTaskDependency( - public val task: WorkStage, - meta: Meta, - placement: DataPlacement, -) : TaskDependency(meta, placement) { - override fun resolveTask(workspace: Workspace): WorkStage = task - - override val name: Name get() = EXTERNAL_TASK_NAME + task.name - - override fun toMeta(): Meta = placement.toMeta().toMutableMeta().apply { - "name" put name.toString() - "task" put task.toString() - "meta" put meta - } - - public companion object { - public val EXTERNAL_TASK_NAME: Name = "@external".asName() - } -} - -public class WorkspaceTaskDependency( - override val name: Name, - meta: Meta, - placement: DataPlacement, -) : TaskDependency(meta, placement) { - override fun resolveTask(workspace: Workspace): WorkStage<*> = workspace.stages[name] - ?: error("Task with name $name is not found in the workspace") - - override fun toMeta(): Meta { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt index 442e52bf..ab8d0465 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/SimpleWorkspace.kt @@ -12,16 +12,14 @@ import hep.dataforge.names.Name */ public class SimpleWorkspace( override val context: Context, - override val data: DataSet, + data: DataSet<*>, override val targets: Map, - stages: Map> + private val externalTasks: Map>, ) : Workspace { - override val stages: Map> by lazy { - context.gather>(WorkStage.TYPE) + stages - } + override val data: TaskResult<*> = internalize(data, Name.EMPTY, Meta.EMPTY) - public companion object { + override val tasks: Map> + get() = context.gather>(Task.TYPE) + externalTasks - } } \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageDataSet.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageDataSet.kt deleted file mode 100644 index c0bf3784..00000000 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageDataSet.kt +++ /dev/null @@ -1,46 +0,0 @@ -package hep.dataforge.workspace - -import hep.dataforge.data.DataSet -import hep.dataforge.meta.Meta -import hep.dataforge.names.Name -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map - -/** - * A result of a [WorkStage] - */ -public interface StageDataSet : DataSet { - /** - * The [Workspace] this [DataSet] belongs to - */ - public val workspace: Workspace - - /** - * The [Name] of the stage that produced this [DataSet] - */ - public val stageName: Name - - /** - * The configuration of the stage that produced this [DataSet] - */ - public val stageMeta: Meta - - override fun flow(): Flow> - override suspend fun getData(name: Name): StageData? -} - -private class StageDataSetImpl( - override val workspace: Workspace, - val dataSet: DataSet, - override val stageName: Name, - override val stageMeta: Meta, -) : StageDataSet, DataSet by dataSet { - - override fun flow(): Flow> = dataSet.flow().map { - workspace.internalize(it, it.name, stageName, stageMeta) - } - - override suspend fun getData(name: Name): StageData? = dataSet.getData(name)?.let { - workspace.internalize(it, name, stageName, stageMeta) - } -} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt new file mode 100644 index 00000000..f2808cd5 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt @@ -0,0 +1,74 @@ +package hep.dataforge.workspace + +import hep.dataforge.data.DataSetBuilder +import hep.dataforge.data.DataTree +import hep.dataforge.data.GoalExecutionRestriction +import hep.dataforge.meta.Meta +import hep.dataforge.meta.descriptors.Described +import hep.dataforge.meta.descriptors.ItemDescriptor +import hep.dataforge.misc.Type +import hep.dataforge.names.Name +import hep.dataforge.workspace.Task.Companion.TYPE +import kotlinx.coroutines.withContext +import kotlin.reflect.KClass + +@Type(TYPE) +public interface Task : Described { + + public val type: KClass + + /** + * Compute a [TaskResult] using given meta. In general, the result is lazy and represents both computation model + * and a handler for actual result + * + * @param workspace a workspace to run task in + * @param taskName the name of the task in this workspace + * @param taskMeta configuration for current stage computation + */ + public suspend fun execute(workspace: Workspace, taskName: Name, taskMeta: Meta): TaskResult + + public companion object { + public const val TYPE: String = "workspace.stage" + } +} + +public class TaskResultBuilder( + public val workspace: Workspace, + public val taskName: Name, + public val taskMeta: Meta, + private val dataSync: DataSetBuilder, +) : DataSetBuilder by dataSync + +/** + * Create a [Task] that composes a result using [builder]. Only data from the workspace could be used. + * Data dependency cycles are not allowed. + */ +@Suppress("FunctionName") +public fun Task( + resultType: KClass, + descriptor: ItemDescriptor? = null, + builder: suspend TaskResultBuilder.() -> Unit, +): Task = object : Task { + + override val type: KClass = resultType + + override val descriptor: ItemDescriptor? = descriptor + + override suspend fun execute( + workspace: Workspace, + taskName: Name, + taskMeta: Meta, + ): TaskResult = withContext(GoalExecutionRestriction() + workspace.goalLogger) { + //TODO use safe builder and check for external data on add and detects cycles + val dataset = DataTree(type) { + TaskResultBuilder(workspace,taskName, taskMeta, this).apply { builder() } + } + workspace.internalize(dataset, taskName, taskMeta) + } +} + +@Suppress("FunctionName") +public inline fun Task( + descriptor: ItemDescriptor? = null, + noinline builder: suspend TaskResultBuilder.() -> Unit, +): Task = Task(T::class, descriptor, builder) \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageData.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt similarity index 58% rename from dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageData.kt rename to dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt index f17cb09a..f317b00a 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/StageData.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt @@ -8,7 +8,7 @@ import hep.dataforge.names.Name /** * A [Workspace]-locked [NamedData], that serves as a computation model. */ -public interface StageData : NamedData { +public interface TaskData : NamedData { /** * The [Workspace] this data belongs to */ @@ -17,31 +17,31 @@ public interface StageData : NamedData { /** * The name of the stage that produced this data. [Name.EMPTY] if the workspace intrinsic data is used. */ - public val stage: Name + public val task: Name /** * Stage configuration used to produce this data. */ - public val stageMeta: Meta + public val taskMeta: Meta /** * Dependencies that allow to compute transitive dependencies as well. */ - override val dependencies: Collection> +// override val dependencies: Collection> } -private class StageDataImpl( +private class TaskDataImpl( override val workspace: Workspace, override val data: Data, override val name: Name, - override val stage: Name, - override val stageMeta: Meta, -) : StageData, Data by data { - override val dependencies: Collection> = data.dependencies.map { - it as? StageData<*> ?: error("StageData can't depend on external data") - } + override val task: Name, + override val taskMeta: Meta, +) : TaskData, Data by data { +// override val dependencies: Collection> = data.dependencies.map { +// it as? TaskData<*> ?: error("TaskData can't depend on external data") +// } } -internal fun Workspace.internalize(data: Data, name: Name, stage: Name, stageMeta: Meta): StageData = - StageDataImpl(this, data, name, stage, stageMeta) +internal fun Workspace.internalize(data: Data, name: Name, stage: Name, stageMeta: Meta): TaskData = + TaskDataImpl(this, data, name, stage, stageMeta) diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskResult.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskResult.kt new file mode 100644 index 00000000..c8adcd9a --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskResult.kt @@ -0,0 +1,49 @@ +package hep.dataforge.workspace + +import hep.dataforge.data.DataSet +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** + * A result of a [Task] + */ +public interface TaskResult : DataSet { + /** + * The [Workspace] this [DataSet] belongs to + */ + public val workspace: Workspace + + /** + * The [Name] of the stage that produced this [DataSet] + */ + public val taskName: Name + + /** + * The configuration of the stage that produced this [DataSet] + */ + public val taskMeta: Meta + + override fun flow(): Flow> + override suspend fun getData(name: Name): TaskData? +} + +private class TaskResultImpl( + override val workspace: Workspace, + val dataSet: DataSet, + override val taskName: Name, + override val taskMeta: Meta, +) : TaskResult, DataSet by dataSet { + + override fun flow(): Flow> = dataSet.flow().map { + workspace.internalize(it, it.name, taskName, taskMeta) + } + + override suspend fun getData(name: Name): TaskData? = dataSet.getData(name)?.let { + workspace.internalize(it, name, taskName, taskMeta) + } +} + +internal fun Workspace.internalize(dataSet: DataSet, stage: Name, stageMeta: Meta): TaskResult = + TaskResultImpl(this, dataSet, stage, stageMeta) \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkStage.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkStage.kt deleted file mode 100644 index 663e7d4b..00000000 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkStage.kt +++ /dev/null @@ -1,23 +0,0 @@ -package hep.dataforge.workspace - -import hep.dataforge.meta.Meta -import hep.dataforge.meta.descriptors.Described -import hep.dataforge.misc.Type -import hep.dataforge.workspace.WorkStage.Companion.TYPE - -@Type(TYPE) -public interface WorkStage : Described { - - /** - * Compute a [StageDataSet] using given meta. In general, the result is lazy and represents both computation model - * and a handler for actual result - * - * @param workspace a workspace to run task model in - * @param meta configuration for current stage computation - */ - public suspend fun execute(workspace: Workspace, meta: Meta): StageDataSet - - public companion object { - public const val TYPE: String = "workspace.stage" - } -} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt index 738e574b..af5ee317 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt @@ -2,6 +2,7 @@ package hep.dataforge.workspace import hep.dataforge.context.ContextAware import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder import hep.dataforge.misc.Type import hep.dataforge.names.Name import hep.dataforge.names.toName @@ -13,7 +14,7 @@ public interface Workspace : ContextAware, Provider { /** * The whole data node for current workspace */ - public val data: StageDataSet<*> + public val data: TaskResult<*> /** * All targets associated with the workspace @@ -23,44 +24,36 @@ public interface Workspace : ContextAware, Provider { /** * All stages associated with the workspace */ - public val stages: Map> + public val tasks: Map> override fun content(target: String): Map { return when (target) { "target", Meta.TYPE -> targets.mapKeys { it.key.toName() } - WorkStage.TYPE -> stages + Task.TYPE -> tasks //Data.TYPE -> data.flow().toMap() else -> emptyMap() } } + public suspend fun produce(taskName: Name, taskMeta: Meta): TaskResult<*> { + if (taskName == Name.EMPTY) return data + val task = tasks[taskName] ?: error("Task with name $taskName not found in the workspace") + return task.execute(this, taskName, taskMeta) + } + + public suspend fun produceData(taskName: Name, taskMeta: Meta, name: Name): TaskData<*>? = + produce(taskName, taskMeta).getData(name) + public companion object { public const val TYPE: String = "workspace" } } -public suspend fun Workspace.stage(taskName: Name, taskMeta: Meta): StageDataSet<*> { - val task = stages[taskName] ?: error("Task with name $taskName not found in the workspace") - return task.execute(this, taskMeta) -} +public suspend fun Workspace.produce(task: String, target: String): TaskResult<*> = + produce(task.toName(), targets[target] ?: error("Target with key $target not found in $this")) -public suspend fun Workspace.getData(taskName: Name, taskMeta: Meta, name: Name): StageData<*>? = - stage(taskName, taskMeta).getData(name) +public suspend fun Workspace.produce(task: String, meta: Meta): TaskResult<*> = + produce(task.toName(), meta) -//public suspend fun Workspace.execute(task: WorkStage<*>, target: String): DataSet { -// val meta = targets[target] ?: error("A target with name $target not found in $this") -// return run(task, meta) -//} -// -// -//public suspend fun Workspace.execute(task: String, target: String): DataSet = -// stages[task.toName()]?.let { execute(it, target) } ?: error("Task with name $task not found") -// -//public suspend fun Workspace.execute(task: String, meta: Meta): DataSet = -// stages[task.toName()]?.let { run(it, meta) } ?: error("Task with name $task not found") -// -//public suspend fun Workspace.execute(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = -// execute(task, Meta(block)) -// -//public suspend fun Workspace.execute(task: WorkStage, metaBuilder: MetaBuilder.() -> Unit = {}): DataSet = -// run(task, Meta(metaBuilder)) \ No newline at end of file +public suspend fun Workspace.produce(task: String, block: MetaBuilder.() -> Unit = {}): TaskResult<*> = + produce(task, Meta(block)) diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt new file mode 100644 index 00000000..aa3519a3 --- /dev/null +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -0,0 +1,103 @@ +package hep.dataforge.workspace + +import hep.dataforge.context.Context +import hep.dataforge.context.ContextBuilder +import hep.dataforge.context.Global +import hep.dataforge.data.ActiveDataTree +import hep.dataforge.data.DataSet +import hep.dataforge.data.DataSetBuilder +import hep.dataforge.data.DataTree +import hep.dataforge.meta.DFBuilder +import hep.dataforge.meta.DFExperimental +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.names.Name +import hep.dataforge.names.toName +import kotlin.properties.PropertyDelegateProvider +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KClass + +public data class TaskReference(public val taskName: Name, public val task: Task) + +public interface TaskContainer { + public fun registerTask(taskName: Name, task: Task<*>) +} + +public fun TaskContainer.registerTask( + resultType: KClass, + name: String, + descriptorBuilder: NodeDescriptor.() -> Unit = {}, + builder: suspend TaskResultBuilder.() -> Unit, +): Unit = registerTask(name.toName(), Task(resultType, NodeDescriptor(descriptorBuilder), builder)) + +public inline fun TaskContainer.registerTask( + name: String, + noinline descriptorBuilder: NodeDescriptor.() -> Unit = {}, + noinline builder: suspend TaskResultBuilder.() -> Unit, +): Unit = registerTask(T::class, name, descriptorBuilder, builder) + +public inline fun TaskContainer.task( + noinline descriptorBuilder: NodeDescriptor.() -> Unit = {}, + noinline builder: suspend TaskResultBuilder.() -> Unit, +): PropertyDelegateProvider>> = PropertyDelegateProvider { _, property -> + val taskName = property.name.toName() + val task = Task(T::class, NodeDescriptor(descriptorBuilder), builder) + registerTask(taskName, task) + ReadOnlyProperty { _, _ -> TaskReference(taskName, task) } +} + + +public class WorkspaceBuilder(private val parentContext: Context = Global) : TaskContainer { + private var context: Context? = null + private var data: DataSet<*>? = null + private val targets: HashMap = HashMap() + private val tasks = HashMap>() + + /** + * Define a context for the workspace + */ + public fun context(name: String = "workspace", block: ContextBuilder.() -> Unit = {}) { + this.context = ContextBuilder(parentContext, name).apply(block).build() + } + + /** + * Define intrinsic data for the workspace + */ + public suspend fun buildData(builder: suspend DataSetBuilder.() -> Unit) { + data = DataTree(builder) + } + + @DFExperimental + public suspend fun buildActiveData(builder: suspend ActiveDataTree.() -> Unit) { + data = ActiveDataTree(builder) + } + + /** + * Define a new target + */ + public fun target(name: String, meta: Meta?) { + if (meta == null) { + targets.remove(name) + } else { + targets[name] = meta + } + } + + override fun registerTask(taskName: Name, task: Task<*>) { + tasks[taskName] = task + } + + public fun build(): Workspace = SimpleWorkspace(context ?: parentContext, data ?: DataSet.EMPTY, targets, tasks) +} + +/** + * Define a new target with a builder + */ +public inline fun WorkspaceBuilder.target(name: String, metaBuilder: MetaBuilder.() -> Unit): Unit = + target(name, Meta(metaBuilder)) + +@DFBuilder +public fun Workspace(parentContext: Context = Global, builder: WorkspaceBuilder.() -> Unit): Workspace { + return WorkspaceBuilder(parentContext).apply(builder).build() +} \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt deleted file mode 100644 index 4a8e0acd..00000000 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/TaskBuilder.kt +++ /dev/null @@ -1,208 +0,0 @@ -package hep.dataforge.workspace - -import hep.dataforge.context.Context -import hep.dataforge.data.* -import hep.dataforge.meta.* -import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.names.Name -import hep.dataforge.workspace.old.GenericTask -import hep.dataforge.workspace.old.TaskModel -import hep.dataforge.workspace.old.TaskModelBuilder -import hep.dataforge.workspace.old.data -import kotlin.reflect.KClass - -private typealias DataTransformation = suspend (context: Context, model: TaskModel, data: DataSet) -> DataSet - -@DFBuilder -@DFExperimental -public class TaskBuilder(private val name: Name, public val type: KClass) { - private var modelTransform: TaskModelBuilder.(Meta) -> Unit = { - data() - } - - // private val additionalDependencies = HashSet() - private var descriptor: NodeDescriptor? = null - private val dataTransforms: MutableList> = ArrayList() - -// override fun add(dependency: Dependency) { -// additionalDependencies.add(dependency) -// } - - public fun model(modelTransform: TaskModelBuilder.(Meta) -> Unit) { - this.modelTransform = modelTransform - } - - - public class TaskEnv( - public val name: Name, - public val meta: Meta, - public val context: Context, - public val data: DataSet, - ) - - /** - * Add a transformation on untyped data - * @param from the prefix for root node in data - * @param to the prefix for the target node. - */ - @JvmName("rawTransform") - public fun transform( - from: Name = Name.EMPTY, - to: Name = Name.EMPTY, - block: TaskEnv.(DataSet<*>) -> DataSet, - ) { - dataTransforms += { context, model, data -> - val env = TaskEnv(Name.EMPTY, model.meta, context, data) - val startData = data.branch(from) - env.block(startData).withNamePrefix(to) - } - } - - public fun transform( - inputType: KClass, - block: suspend TaskEnv.(DataSet) -> DataSet, - ) { - dataTransforms += { context, model, data -> - val env = TaskEnv(Name.EMPTY, model.meta, context, data) - env.block(data.filterIsInstance(inputType)) - } - } - - public inline fun transform( - noinline block: suspend TaskEnv.(DataSet) -> DataSet, - ): Unit = transform(T::class, block) - - - /** - * Perform given action on data elements in `from` node in input and put the result to `to` node - */ - public inline fun action( - from: Name = Name.EMPTY, - to: Name = Name.EMPTY, - crossinline block: TaskEnv.() -> Action, - ) { - transform { data: DataSet -> - block().execute(data, meta, context) - } - } - - - /** - * A customized map action with ability to change meta and name - */ - public inline fun mapAction( - from: Name = Name.EMPTY, - to: Name = Name.EMPTY, - crossinline block: MapActionBuilder.(TaskEnv) -> Unit, - ) { - action(from, to) { - val env = this - MapAction(type) { - block(env) - } - } - } - - /** - * A simple map action without changing meta or name - */ - public inline fun map( - from: Name = Name.EMPTY, - to: Name = Name.EMPTY, - crossinline block: suspend TaskEnv.(T) -> R, - ) { - action(from, to) { - MapAction(type) { - //TODO automatically append task meta - result = { data -> - block(data) - } - } - } - } - - /** - * Join elements in gathered data by multiple groups - */ - public inline fun reduceByGroup( - from: Name = Name.EMPTY, - to: Name = Name.EMPTY, - crossinline block: ReduceGroupBuilder.(TaskEnv) -> Unit, //TODO needs KEEP-176 - ) { - action(from, to) { - val env = this - ReduceAction(inputType = T::class, outputType = type) { block(env) } - } - } - - /** - * Join all elemlents in gathered data matching input type - */ - public inline fun reduce( - from: Name = Name.EMPTY, - to: Name = Name.EMPTY, - crossinline block: suspend TaskEnv.(Map) -> R, - ) { - action(from, to) { - ReduceAction(inputType = T::class, outputType = type) { - result( - actionMeta[TaskModel.MODEL_TARGET_KEY]?.string ?: "@anonymous" - ) { data -> - block(data) - } - } - } - } - - /** - * Split each element in gathered data into fixed number of fragments - */ - public inline fun split( - from: Name = Name.EMPTY, - to: Name = Name.EMPTY, - crossinline block: SplitBuilder.(TaskEnv) -> Unit, //TODO needs KEEP-176 - ) { - action(from, to) { - val env = this - SplitAction(type) { block(this, env) } - } - } - - /** - * Use DSL to create a descriptor for this task - */ - public fun description(transform: NodeDescriptor.() -> Unit) { - this.descriptor = NodeDescriptor().apply(transform) - } - - internal fun build(): GenericTask { - return GenericTask( - name, - type, - descriptor ?: NodeDescriptor(), - modelTransform - ) { - val workspace = this - { dataSet -> - val model = this - if (dataTransforms.isEmpty()) { - //return data node as is - logger.warn { "No transformation present, returning input data" } - dataSet.castOrNull(type) ?: error("$type expected, but $type received") - } else { - DataTree.active(type, workspace.context){ - dataTransforms.forEach { transformation -> - val res = transformation(workspace.context, model, dataSet) - update(res) - } - } - } - } - } - } -} - -@DFExperimental -public suspend inline fun TaskBuilder.TaskEnv.dataTree( - crossinline block: suspend ActiveDataTree.() -> Unit, -): DataTree = DataTree.active(context, block) \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt deleted file mode 100644 index e92950ff..00000000 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ /dev/null @@ -1,87 +0,0 @@ -package hep.dataforge.workspace - -import hep.dataforge.context.Context -import hep.dataforge.context.ContextBuilder -import hep.dataforge.context.Global -import hep.dataforge.data.ActiveDataTree -import hep.dataforge.meta.* -import hep.dataforge.names.toName -import kotlin.reflect.KClass - -@DFBuilder -public interface WorkspaceBuilder { - public val parentContext: Context - public var context: Context - public var data: ActiveDataTree - public var tasks: MutableSet> - public var targets: MutableMap - - public fun build(): Workspace -} - -/** - * Set the context for future workspcace - */ -public fun WorkspaceBuilder.context(name: String = "WORKSPACE", block: ContextBuilder.() -> Unit = {}) { - context = ContextBuilder(parentContext, name).apply(block).build() -} - -public inline fun WorkspaceBuilder.data( - block: ActiveDataTree.() -> Unit, -): Unit{ - data.apply(block) -} - - -public fun WorkspaceBuilder.target(name: String, block: MetaBuilder.() -> Unit) { - targets[name] = Meta(block).seal() -} - -class WorkspaceTask(val workspace: Workspace, val name: String) - -/** - * Use existing target as a base updating it with the block - */ -public fun WorkspaceBuilder.target(name: String, base: String, block: MetaBuilder.() -> Unit) { - val parentTarget = targets[base] ?: error("Base target with name $base not found") - targets[name] = parentTarget.toMutableMeta() - .apply { "@baseTarget" put base } - .apply(block) - .seal() -} - -public fun WorkspaceBuilder.task( - name: String, - type: KClass, - builder: TaskBuilder.() -> Unit, -): WorkspaceTask = TaskBuilder(name.toName(), type).apply(builder).build().also { tasks.add(it) } - -public inline fun WorkspaceBuilder.task( - name: String, - noinline builder: TaskBuilder.() -> Unit, -): WorkStage = task(name, T::class, builder) - -@JvmName("rawTask") -public fun WorkspaceBuilder.task( - name: String, - builder: TaskBuilder.() -> Unit, -): WorkStage = task(name, Any::class, builder) - -/** - * A builder for a simple workspace - */ -public class SimpleWorkspaceBuilder(override val parentContext: Context) : WorkspaceBuilder { - override var context: Context = parentContext - override var data: ActiveDataTree = ActiveDataTree(Any::class, context) - override var tasks: MutableSet> = HashSet() - override var targets: MutableMap = HashMap() - - override fun build(): SimpleWorkspace { - return SimpleWorkspace(context, data, targets, tasks) - } -} - -public fun Workspace( - parent: Context = Global, - block: SimpleWorkspaceBuilder.() -> Unit, -): Workspace = SimpleWorkspaceBuilder(parent).apply(block).build() \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt index 0ccaccf3..b30d141b 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt @@ -2,41 +2,21 @@ package hep.dataforge.workspace import hep.dataforge.context.AbstractPlugin import hep.dataforge.names.Name -import hep.dataforge.names.toName -import hep.dataforge.workspace.old.GenericTask -import kotlin.reflect.KClass /** * An abstract plugin with some additional boilerplate to effectively work with workspace context */ -public abstract class WorkspacePlugin : AbstractPlugin() { - private val _tasks = HashSet>() - public val tasks: Collection> get() = _tasks +public abstract class WorkspacePlugin : AbstractPlugin(), TaskContainer { + private val tasks = HashMap>() override fun content(target: String): Map { return when (target) { - WorkStage.TYPE -> tasks.toMap() + Task.TYPE -> tasks else -> emptyMap() } } - public fun task(task: WorkStage<*>){ - _tasks.add(task) + override fun registerTask(taskName: Name, task: Task<*>) { + tasks[taskName] = task } - - public fun task( - name: String, - type: KClass, - builder: TaskBuilder.() -> Unit - ): GenericTask = TaskBuilder(name.toName(), type).apply(builder).build().also { - _tasks.add(it) - } - - public inline fun task( - name: String, - noinline builder: TaskBuilder.() -> Unit - ): GenericTask = task(name, T::class, builder) - -// -////TODO add delegates to build gradle-like tasks } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt index d1601c91..29ff0c62 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt @@ -82,7 +82,7 @@ public suspend fun DataSetBuilder.file( val data = readDataFile(path, formatResolver) val name = data.meta[Envelope.ENVELOPE_NAME_KEY].string ?: path.fileName.toString().replace(".df", "") - set(name, data) + emit(name, data) } } else { //otherwise, read as directory @@ -90,7 +90,7 @@ public suspend fun DataSetBuilder.file( val data = readDataDirectory(path, formatResolver) val name = data.getMeta()[Envelope.ENVELOPE_NAME_KEY].string ?: path.fileName.toString().replace(".df", "") - set(name, data) + emit(name, data) } } } @@ -99,7 +99,7 @@ public suspend fun DataSetBuilder.file( * Read the directory as a data node. If [path] is a zip archive, read it as directory */ @DFExperimental -public fun IOPlugin.readDataDirectory( +public suspend fun IOPlugin.readDataDirectory( path: Path, formatResolver: FileFormatResolver, ): DataTree { @@ -110,7 +110,7 @@ public fun IOPlugin.readDataDirectory( return readDataDirectory(fs.rootDirectories.first(), formatResolver) } if (!Files.isDirectory(path)) error("Provided path $path is not a directory") - return DataTree.static(formatResolver.kClass) { + return DataTree(formatResolver.kClass) { Files.list(path).toList().forEach { path -> val fileName = path.fileName.toString() if (fileName.startsWith(IOPlugin.META_FILE_NAME)) { @@ -125,7 +125,7 @@ public fun IOPlugin.readDataDirectory( } @DFExperimental -public inline fun IOPlugin.readDataDirectory(path: Path): DataTree = +public suspend inline fun IOPlugin.readDataDirectory(path: Path): DataTree = readDataDirectory(path, formatResolver()) /** diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/taskBuilders.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/taskBuilders.kt new file mode 100644 index 00000000..fce1a372 --- /dev/null +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/taskBuilders.kt @@ -0,0 +1,24 @@ +package hep.dataforge.workspace + +import hep.dataforge.data.DataSet +import hep.dataforge.data.select +import hep.dataforge.meta.Meta +import hep.dataforge.names.Name + +public suspend inline fun TaskResultBuilder.from( + task: Name, + taskMeta: Meta = Meta.EMPTY, +): DataSet = workspace.produce(task, taskMeta).select() + + +@Suppress("UNCHECKED_CAST") +public suspend fun TaskResultBuilder<*>.from( + reference: TaskReference, + taskMeta: Meta = Meta.EMPTY, +): DataSet { + if (workspace.tasks[reference.taskName] == reference.task) { + return workspace.produce(reference.taskName, taskMeta) as TaskResult + } else { + throw error("Task ${reference.taskName} does not belong to the workspace") + } +} diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/workspaceExtensions.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/workspaceExtensions.kt new file mode 100644 index 00000000..00f213f5 --- /dev/null +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/workspaceExtensions.kt @@ -0,0 +1,8 @@ +package hep.dataforge.workspace + +import hep.dataforge.data.DataSetBuilder +import kotlinx.coroutines.runBlocking + +public fun WorkspaceBuilder.data(builder: suspend DataSetBuilder.() -> Unit): Unit = runBlocking { + buildData(builder) +} \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index ba8db797..41b08d2a 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -5,54 +5,28 @@ import hep.dataforge.context.PluginFactory import hep.dataforge.context.PluginTag import hep.dataforge.data.* import hep.dataforge.meta.Meta -import hep.dataforge.workspace.old.data -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.reduce +import hep.dataforge.names.toName +import kotlinx.coroutines.flow.single import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass import kotlin.test.Test import kotlin.test.assertEquals -fun DataSet.first(): NamedData? = runBlocking { flow().firstOrNull() } - class DataPropagationTestPlugin : WorkspacePlugin() { override val tag: PluginTag = Companion.tag - val testAllData = task("allData", Int::class) { - model { - data() - } - transform { data -> - DataTree.active(context) { - val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } - data("result", result) - } + val allData by task { + val selectedData = workspace.data.select() + val result: Data = selectedData.flow().foldToData(0) { result, data -> + result + data.await() } + emit("result", result) } - val testSingleData = task("singleData", Int::class) { - model { - data(pattern = "myData\\[12\\]") - } - transform { data -> - DataTree.active(context) { - val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } - data("result", result) - } - } - } - - val testAllRegexData = task("allRegexData", Int::class) { - model { - data(pattern = "myData.*") - } - transform { data -> - DataTree.active(context) { - val result = data.flow().map { it.value() }.reduce { acc, pair -> acc + pair } - data("result", result) - } + val singleData by task { + workspace.data.select().getData("myData[12]".toName())?.let { + emit("result", it) } } @@ -72,9 +46,11 @@ class DataPropagationTest { context { plugin(DataPropagationTestPlugin) } - data { - repeat(100) { - data("myData[$it]", it) + runBlocking { + data { + repeat(100) { + data("myData[$it]", it) + } } } } @@ -82,24 +58,16 @@ class DataPropagationTest { @Test fun testAllData() { runBlocking { - val node = testWorkspace.execute("Test.allData") - assertEquals(4950, node.first()!!.value()) - } - } - - @Test - fun testAllRegexData() { - runBlocking { - val node = testWorkspace.execute("Test.allRegexData") - assertEquals(4950, node.first()!!.value()) + val node = testWorkspace.produce("Test.allData") + assertEquals(4950, node.flow().single().await()) } } @Test fun testSingleData() { runBlocking { - val node = testWorkspace.execute("Test.singleData") - assertEquals(12, node.first()!!.value()) + val node = testWorkspace.produce("Test.singleData") + assertEquals(12, node.flow().single().await()) } } } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt index b5a7c445..ff22054d 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt @@ -20,15 +20,17 @@ import kotlin.test.assertEquals class FileDataTest { - val dataNode = DataTree.static { - set("dir") { - data("a", "Some string") { - "content" put "Some string" + val dataNode = runBlocking { + DataTree { + emit("dir") { + data("a", "Some string") { + "content" put "Some string" + } + } + data("b", "root data") + meta { + "content" put "This is root meta node" } - } - data("b", "root data") - meta { - "content" put "This is root meta node" } } @@ -50,10 +52,10 @@ class FileDataTest { } - object StringFormatResolver: FileFormatResolver{ + object StringFormatResolver : FileFormatResolver { override val type: KType = typeOf() - override fun invoke(path: Path, meta: Meta): IOFormat =StringIOFormat + override fun invoke(path: Path, meta: Meta): IOFormat = StringIOFormat } @@ -64,12 +66,10 @@ class FileDataTest { val dir = Files.createTempDirectory("df_data_node") runBlocking { writeDataDirectory(dir, dataNode, StringIOFormat) - } - println(dir.toUri().toString()) - val reconstructed = readDataDirectory(dir,StringFormatResolver) - runBlocking { + println(dir.toUri().toString()) + val reconstructed = readDataDirectory(dir, StringFormatResolver) assertEquals(dataNode.getData("dir.a")?.meta, reconstructed.getData("dir.a")?.meta) - assertEquals(dataNode.getData("b")?.value(), reconstructed.getData("b")?.value()) + assertEquals(dataNode.getData("b")?.await(), reconstructed.getData("b")?.await()) } } } @@ -82,12 +82,10 @@ class FileDataTest { val zip = Files.createTempFile("df_data_node", ".zip") runBlocking { writeZip(zip, dataNode, StringIOFormat) - } - println(zip.toUri().toString()) - val reconstructed = readDataDirectory(zip, StringFormatResolver) - runBlocking { + println(zip.toUri().toString()) + val reconstructed = readDataDirectory(zip, StringFormatResolver) assertEquals(dataNode.getData("dir.a")?.meta, reconstructed.getData("dir.a")?.meta) - assertEquals(dataNode.getData("b")?.value(), reconstructed.getData("b")?.value()) + assertEquals(dataNode.getData("b")?.await(), reconstructed.getData("b")?.await()) } } } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index 11562a31..2147294b 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -4,9 +4,8 @@ import hep.dataforge.context.* import hep.dataforge.data.* import hep.dataforge.meta.* import hep.dataforge.names.plus -import hep.dataforge.workspace.old.data -import hep.dataforge.workspace.old.dependsOn -import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.single import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Timeout import kotlin.reflect.KClass @@ -14,6 +13,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue + /** * Make a fake-factory for a one single plugin. Useful for unique or test plugins */ @@ -24,8 +24,8 @@ public inline fun P.toFactory(): PluginFactory

    = object override val type: KClass = P::class } -public fun Workspace.runBlocking(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = runBlocking{ - execute(task, block) +public fun Workspace.runBlocking(task: String, block: MetaBuilder.() -> Unit = {}): DataSet = runBlocking { + produce(task, block) } @@ -33,12 +33,18 @@ class SimpleWorkspaceTest { val testPlugin = object : WorkspacePlugin() { override val tag: PluginTag = PluginTag("test") - val contextTask = task("test", Any::class) { - map { - context.logger.info { "Test: $it" } - } + val test by task { + populate( + workspace.data.map { + it.also { + logger.info { "Test: $it" } + } + } + ) } } + + val testPluginFactory = testPlugin.toFactory() val workspace = Workspace { @@ -53,98 +59,82 @@ class SimpleWorkspaceTest { } } - val filterTask = task("filterOne") { - model { - data("myData\\[12\\]") - } - map { - it + val filterOne by task { + workspace.data.selectOne("myData[12]")?.let { source -> + emit(source.name, source.map { it }) } } - val square = task("square") { - map { data -> - if (meta["testFlag"].boolean == true) { + val square by task { + workspace.data.select().forEach { data -> + if (data.meta["testFlag"].boolean == true) { println("flag") } - context.logger.info { "Starting square on $data" } - data * data + val value = data.await() + workspace.logger.info { "Starting square on $value" } + emit(data.name, data.map { it * it }) } } - val linear = task("linear") { - map { data -> - context.logger.info { "Starting linear on $data" } - data * 2 + 1 + val linear by task { + workspace.data.select().forEach { data -> + workspace.logger.info { "Starting linear on $data" } + emit(data.name, data.data.map { it * 2 + 1 }) } } - val fullSquare = task("fullsquare") { - model { - val squareDep = dependsOn(square, placement = DataPlacement.into("square")) - val linearDep = dependsOn(linear, placement = DataPlacement.into("linear")) - } - transform { data -> - val squareNode = data.branch("square").filterIsInstance() //squareDep() - val linearNode = data.branch("linear").filterIsInstance() //linearDep() - dataTree { - squareNode.flow().collect { - val newData: Data = Data { - val squareValue = squareNode.getData(it.name)!!.value() - val linearValue = linearNode.getData(it.name)!!.value() - squareValue + linearValue - } - set(name, newData) - } + val fullSquare by task { + val squareData = from(square) + val linearData = from(linear) + squareData.forEach { data -> + val newData: Data = data.combine(linearData.getData(data.name)!!) { l, r -> + l + r } + emit(data.name, newData) } } - task("sum") { - model { - dependsOn(square) - } - reduce { data -> - context.logger.info { "Starting sum" } - data.values.sum() + val sum by task { + workspace.logger.info { "Starting sum" } + val res = from(square).foldToData(0) { l, r -> + l + r.await() } + emit("sum", res) } - val average = task("average") { - reduceByGroup { env -> - group("even", filter = { name, _ -> name.toString().toInt() % 2 == 0 }) { - result { data -> - env.context.logger.info { "Starting even" } - data.values.average() - } - } - group("odd", filter = { name, _ -> name.toString().toInt() % 2 == 1 }) { - result { data -> - env.context.logger.info { "Starting odd" } - data.values.average() - } - } + val averageByGroup by task { + val evenSum = workspace.data.filter { name, _ -> + name.toString().toInt() % 2 == 0 + }.select().foldToData(0) { l, r -> + l + r.await() } + + emit("even", evenSum) + val oddSum = workspace.data.filter { name, _ -> + name.toString().toInt() % 2 == 1 + }.select().foldToData(0) { l, r -> + l + r.await() + } + emit("odd", oddSum) } - task("delta") { - model { - dependsOn(average) - } - reduce { data -> - data["even"]!! - data["odd"]!! + val delta by task { + val averaged = from(averageByGroup) + val even = averaged.getData("event")!! + val odd = averaged.getData("odd")!! + val res = even.combine(odd) { l, r -> + l - r } + emit("res", res) } - val customPipeTask = task("custom") { - mapAction { - meta = meta.toMutableMeta().apply { + val customPipe by task { + workspace.data.select().forEach { data -> + val meta = data.meta.toMutableMeta().apply { "newValue" put 22 } - name += "new" - result { - meta["value"].int ?: 0 + it - } + emit(data.name + "new", data.map { (data.meta["value"].int ?: 0) + it }) + } } @@ -154,21 +144,25 @@ class SimpleWorkspaceTest { @Test @Timeout(1) fun testWorkspace() { - val node = workspace.runBlocking("sum") - val res = node.first() - assertEquals(328350, res?.value()) + runBlocking { + val node = workspace.runBlocking("sum") + val res = node.flow().single() + assertEquals(328350, res.await()) + } } @Test @Timeout(1) fun testMetaPropagation() { - val node = workspace.runBlocking("sum") { "testFlag" put true } - val res = node.first()?.value() + runBlocking { + val node = workspace.produce("sum") { "testFlag" put true } + val res = node.flow().single().await() + } } @Test fun testPluginTask() { - val tasks = workspace.stages + val tasks = workspace.tasks assertTrue { tasks["test.test"] != null } //val node = workspace.run("test.test", "empty") } @@ -176,16 +170,16 @@ class SimpleWorkspaceTest { @Test fun testFullSquare() { runBlocking { - val node = workspace.execute("fullsquare") + val node = workspace.produce("fullSquare") println(node.toMeta()) } } @Test fun testFilter() { - val node = workspace.runBlocking("filterOne") runBlocking { - assertEquals(12, node.first()?.value()) + val node = workspace.produce("filterOne") + assertEquals(12, node.flow().first().await()) } } } \ No newline at end of file From 80d3a64cdf66dcfb66da0eae40d0bdee36a886ac Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 26 Jan 2021 15:16:18 +0300 Subject: [PATCH 41/59] Gradle to 6.8.1 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index da9702f9..28ff446a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 2291072e2638aadbbaf1f27138a96b2606d65f16 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 26 Jan 2021 16:13:31 +0300 Subject: [PATCH 42/59] Cleanup after task refactoring --- .../hep/dataforge/{data => actions}/Action.kt | 11 ++++- .../dataforge/{data => actions}/MapAction.kt | 3 +- .../dataforge/{data => actions}/NamedData.kt | 4 +- .../{data => actions}/ReduceAction.kt | 14 +------ .../{data => actions}/SplitAction.kt | 3 +- .../hep/dataforge/data/CachingAction.kt | 2 + .../kotlin/hep/dataforge/data/DataSet.kt | 2 + .../hep/dataforge/data/DataSetBuilder.kt | 1 + .../kotlin/hep/dataforge/data/DataTree.kt | 2 + .../kotlin/hep/dataforge/data/dataFilter.kt | 2 + .../hep/dataforge/data/dataTransform.kt | 41 +++++++++++-------- .../kotlin/hep/dataforge/data/select.kt | 2 + .../kotlin/hep/dataforge/data/ActionsTest.kt | 1 + .../kotlin/hep/dataforge/workspace/Task.kt | 10 ++--- .../hep/dataforge/workspace/TaskData.kt | 2 +- .../workspace/SimpleWorkspaceTest.kt | 1 + 16 files changed, 60 insertions(+), 41 deletions(-) rename dataforge-data/src/commonMain/kotlin/hep/dataforge/{data => actions}/Action.kt (79%) rename dataforge-data/src/commonMain/kotlin/hep/dataforge/{data => actions}/MapAction.kt (97%) rename dataforge-data/src/commonMain/kotlin/hep/dataforge/{data => actions}/NamedData.kt (89%) rename dataforge-data/src/commonMain/kotlin/hep/dataforge/{data => actions}/ReduceAction.kt (89%) rename dataforge-data/src/commonMain/kotlin/hep/dataforge/{data => actions}/SplitAction.kt (97%) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/Action.kt similarity index 79% rename from dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt rename to dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/Action.kt index 3b72c718..a7c51e8f 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Action.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/Action.kt @@ -1,5 +1,7 @@ -package hep.dataforge.data +package hep.dataforge.actions +import hep.dataforge.data.DataSet +import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* @@ -31,3 +33,10 @@ public infix fun Action.then(action: Action DataSet.transformWith( + action: Action, + meta: Meta = Meta.EMPTY, + scope: CoroutineScope? = null, +): DataSet = action.execute(this, meta, scope) + diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt similarity index 97% rename from dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt rename to dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt index cfd37f0a..45632b43 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt @@ -1,5 +1,6 @@ -package hep.dataforge.data +package hep.dataforge.actions +import hep.dataforge.data.* import hep.dataforge.meta.* import hep.dataforge.names.Name import kotlinx.coroutines.CoroutineScope diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/NamedData.kt similarity index 89% rename from dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt rename to dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/NamedData.kt index aa5afcdc..126c972b 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/NamedData.kt @@ -1,5 +1,7 @@ -package hep.dataforge.data +package hep.dataforge.actions +import hep.dataforge.data.Data +import hep.dataforge.data.StaticData import hep.dataforge.meta.isEmpty import hep.dataforge.misc.Named import hep.dataforge.names.Name diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt similarity index 89% rename from dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt rename to dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt index b326b344..7bc4ab07 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt @@ -1,5 +1,6 @@ -package hep.dataforge.data +package hep.dataforge.actions +import hep.dataforge.data.* import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder @@ -44,17 +45,6 @@ public class ReduceGroupBuilder( } } -// /** -// * Add a single fixed group to grouping rules -// */ -// public fun group(groupName: String, filter: DataMapper, action: JoinGroup.() -> Unit) { -// groupRules += { node -> -// listOf( -// JoinGroup(groupName, node.filter(filter)).apply(action) -// ) -// } -// } - public fun group( groupName: String, filter: suspend (Name, Data) -> Boolean, diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt similarity index 97% rename from dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt rename to dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt index 3d8fca5a..aaec3836 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt @@ -1,5 +1,6 @@ -package hep.dataforge.data +package hep.dataforge.actions +import hep.dataforge.data.* import hep.dataforge.meta.Laminate import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt index 5f8f8704..0e4ddd97 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt @@ -1,5 +1,7 @@ package hep.dataforge.data +import hep.dataforge.actions.Action +import hep.dataforge.actions.NamedData import hep.dataforge.meta.Meta import hep.dataforge.names.Name import hep.dataforge.names.startsWith diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt index db20fb16..c401b275 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt @@ -1,5 +1,7 @@ package hep.dataforge.data +import hep.dataforge.actions.NamedData +import hep.dataforge.actions.named import hep.dataforge.meta.Meta import hep.dataforge.meta.set import hep.dataforge.names.* diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt index 5a0a3b55..6e3669b7 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt @@ -1,5 +1,6 @@ package hep.dataforge.data +import hep.dataforge.actions.NamedData import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt index 659ff029..0354b161 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt @@ -1,5 +1,7 @@ package hep.dataforge.data +import hep.dataforge.actions.NamedData +import hep.dataforge.actions.named import hep.dataforge.meta.* import hep.dataforge.misc.Type import hep.dataforge.names.* diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt index 5f2b2a8a..4d500587 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt @@ -1,5 +1,7 @@ package hep.dataforge.data +import hep.dataforge.actions.NamedData +import hep.dataforge.actions.named import hep.dataforge.meta.DFExperimental import hep.dataforge.names.* import kotlinx.coroutines.flow.Flow diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt index 39f5f306..31e30fb1 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt @@ -1,5 +1,7 @@ package hep.dataforge.data +import hep.dataforge.actions.NamedData +import hep.dataforge.actions.named import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.seal @@ -11,36 +13,41 @@ import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.reflect.KClass - +/** + * Lazily transform this data to another data. By convention [block] should not use external data (be pure). + * @param coroutineContext additional [CoroutineContext] elements used for data computation. + * @param meta for the resulting data. By default equals input data. + * @param block the transformation itself + */ public fun Data.map( outputType: KClass, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, block: suspend (T) -> R, -): Data = LazyData(outputType, meta, coroutineContext, listOf(this)) { +): LazyData = LazyData(outputType, meta, coroutineContext, listOf(this)) { block(await()) } /** - * Create a data mapping + * See [map] */ public inline fun Data.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, crossinline block: suspend (T) -> R, -): Data = LazyData(R::class, meta, coroutineContext, listOf(this)) { +): LazyData = LazyData(R::class, meta, coroutineContext, listOf(this)) { block(await()) } /** - * Combine this data with the other data using [block] + * Combine this data with the other data using [block]. See [map] for other details */ -public inline fun Data.combine( +public inline fun Data.combine( other: Data, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, crossinline block: suspend (left: T1, right: T2) -> R, -): Data = LazyData(R::class, meta, coroutineContext, listOf(this,other)) { +): LazyData = LazyData(R::class, meta, coroutineContext, listOf(this, other)) { block(await(), other.await()) } @@ -48,19 +55,19 @@ public inline fun Data.combine( //data collection operations /** - * Create a joined data. + * Lazily reduce a collection of [Data] to a single data. */ public inline fun Collection>.reduceToData( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, - noinline block: suspend (Collection) -> R, -): Data = LazyData( + crossinline block: suspend (Collection) -> R, +): LazyData = LazyData( R::class, meta, coroutineContext, this ) { - block(map { run { it.await() } }) + block(map { it.await() }) } public fun Map>.reduceToData( @@ -79,7 +86,7 @@ public fun Map>.reduceToData( /** - * A joining of multiple data into a single one + * Lazily reduce a [Map] of [Data] with any static key. * @param K type of the map key * @param T type of the input goal * @param R type of the result goal @@ -97,8 +104,10 @@ public inline fun Map>.reduceToData( block(mapValues { it.value.await() }) } +//flow operations + /** - * Transform a [Flow] of [NamedData] to a single [Data]. Execution restrictions are removed for inner [Flow] + * Transform a [Flow] of [NamedData] to a single [Data]. */ public suspend fun Flow>.reduceToData( outputType: KClass, @@ -114,8 +123,6 @@ public suspend fun Flow>.reduceToData( transformation(this) } -//flow operations - public suspend inline fun Flow>.reduceToData( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, @@ -145,7 +152,7 @@ public suspend fun DataSet.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, metaTransform: MetaBuilder.() -> Unit = {}, block: suspend (T) -> R, -): DataSet = DataTree(outputType) { +): DataTree = DataTree(outputType) { populate( flow().map { val newMeta = it.meta.toMutableMeta().apply(metaTransform).seal() @@ -158,7 +165,7 @@ public suspend inline fun DataSet.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, noinline metaTransform: MetaBuilder.() -> Unit = {}, noinline block: suspend (T) -> R, -): DataSet = map(R::class, coroutineContext, metaTransform, block) +): DataTree = map(R::class, coroutineContext, metaTransform, block) public suspend fun DataSet.forEach(block: suspend (NamedData) -> Unit) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt index 5b30a0d3..5217d1f0 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt @@ -1,5 +1,7 @@ package hep.dataforge.data +import hep.dataforge.actions.NamedData +import hep.dataforge.actions.named import hep.dataforge.meta.DFExperimental import hep.dataforge.names.* import kotlinx.coroutines.flow.Flow diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt index 50240b28..686762d9 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt @@ -1,5 +1,6 @@ package hep.dataforge.data +import hep.dataforge.actions.MapAction import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Test import kotlin.test.assertEquals diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt index f2808cd5..df3c16af 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt @@ -15,8 +15,6 @@ import kotlin.reflect.KClass @Type(TYPE) public interface Task : Described { - public val type: KClass - /** * Compute a [TaskResult] using given meta. In general, the result is lazy and represents both computation model * and a handler for actual result @@ -36,8 +34,8 @@ public class TaskResultBuilder( public val workspace: Workspace, public val taskName: Name, public val taskMeta: Meta, - private val dataSync: DataSetBuilder, -) : DataSetBuilder by dataSync + private val dataDrop: DataSetBuilder, +) : DataSetBuilder by dataDrop /** * Create a [Task] that composes a result using [builder]. Only data from the workspace could be used. @@ -50,8 +48,6 @@ public fun Task( builder: suspend TaskResultBuilder.() -> Unit, ): Task = object : Task { - override val type: KClass = resultType - override val descriptor: ItemDescriptor? = descriptor override suspend fun execute( @@ -60,7 +56,7 @@ public fun Task( taskMeta: Meta, ): TaskResult = withContext(GoalExecutionRestriction() + workspace.goalLogger) { //TODO use safe builder and check for external data on add and detects cycles - val dataset = DataTree(type) { + val dataset = DataTree(resultType) { TaskResultBuilder(workspace,taskName, taskMeta, this).apply { builder() } } workspace.internalize(dataset, taskName, taskMeta) diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt index f317b00a..37862a48 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt @@ -1,7 +1,7 @@ package hep.dataforge.workspace +import hep.dataforge.actions.NamedData import hep.dataforge.data.Data -import hep.dataforge.data.NamedData import hep.dataforge.meta.Meta import hep.dataforge.names.Name diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index 2147294b..3b290d4e 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -1,5 +1,6 @@ package hep.dataforge.workspace +import hep.dataforge.actions.get import hep.dataforge.context.* import hep.dataforge.data.* import hep.dataforge.meta.* From 25cba0c6de7ba0808c924c77f737def77eb98821 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 26 Jan 2021 18:26:28 +0300 Subject: [PATCH 43/59] bump version to 0.3.0-dev-1 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index b13264c5..b4862160 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.3.0-dev") +val dataforgeVersion by extra("0.3.0-dev-1") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") From e88178ffe7589b508aade9130be5513b07057ec6 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 28 Jan 2021 10:25:54 +0300 Subject: [PATCH 44/59] readEnvelopeFile throws exception instead of returning null for missing encoders --- build.gradle.kts | 2 +- dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b4862160..ca5e63bc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.3.0-dev-1") +val dataforgeVersion by extra("0.3.0-dev-2") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt index 46126fac..2f644878 100644 --- a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt @@ -152,8 +152,8 @@ public fun IOPlugin.readEnvelopeFile( path: Path, readNonEnvelopes: Boolean = false, formatPicker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekFileEnvelopeFormat, -): Envelope? { - if (!Files.exists(path)) return null +): Envelope { + if (!Files.exists(path)) error("File with path $path does not exist") //read two-files directory if (Files.isDirectory(path)) { @@ -182,7 +182,7 @@ public fun IOPlugin.readEnvelopeFile( path.readEnvelope(format) } ?: if (readNonEnvelopes) { // if no format accepts file, read it as binary SimpleEnvelope(Meta.EMPTY, path.asBinary()) - } else null + } else error("Can't infer format for file $path") } /** From 66355793ee0a5143be296a353bb45f4d39aaf11e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 28 Jan 2021 10:34:28 +0300 Subject: [PATCH 45/59] Remove restriction on empty ListValue since it have a separate type now. --- .../kotlin/hep/dataforge/io/MetaFormatTest.kt | 24 +++++++++++++++++++ .../kotlin/hep/dataforge/values/Value.kt | 8 +++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt index af33cfde..9e71a907 100644 --- a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt +++ b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MetaFormatTest.kt @@ -2,6 +2,7 @@ package hep.dataforge.io import hep.dataforge.meta.* import hep.dataforge.meta.JsonMeta.Companion.JSON_ARRAY_KEY +import hep.dataforge.values.ListValue import hep.dataforge.values.number import kotlinx.io.asBinary import kotlinx.serialization.json.* @@ -79,4 +80,27 @@ class MetaFormatTest { assertEquals(listOf(1.0, 2.0, 3.0), meta["$JSON_ARRAY_KEY[2"].value?.list?.map { it.number.toDouble() }) } + @Test + fun testJsonStringToMeta(){ + val jsonString = """ + { + "comments": [ + ], + "end_time": "2018-04-13T22:01:46", + "format_description": "https://docs.google.com/document/d/12qmnZRO55y6zr08Wf-BQYAmklqgf5y3j_gD_VkNscXc/edit?usp=sharing", + "iteration_info": { + "iteration": 4, + "reverse": false + }, + "operator": "Vasiliy", + "programm_revision": "1.1.1-79-g7c0cad6", + "start_time": "2018-04-13T21:42:04", + "type": "info_file" + } + """.trimIndent() + val json = Json.parseToJsonElement(jsonString) + val meta = json.toMetaItem().node!! + assertEquals(ListValue.EMPTY, meta["comments"].value) + } + } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt index 676f7134..73b1318c 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt @@ -179,10 +179,6 @@ public class EnumValue>(override val value: E) : Value { } public class ListValue(override val list: List) : Value, Iterable { - init { - require(list.isNotEmpty()) { "Can't create list value from empty list" } - } - override val value: List get() = list override val type: ValueType get() = ValueType.LIST @@ -202,6 +198,10 @@ public class ListValue(override val list: List) : Value, Iterable override fun hashCode(): Int { return list.hashCode() } + + public companion object{ + public val EMPTY: ListValue = ListValue(emptyList()) + } } public fun Number.asValue(): Value = NumberValue(this) From 0e053ab78d445ea81a711735e70be7de3d24c002 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 30 Jan 2021 19:49:28 +0300 Subject: [PATCH 46/59] Move yaml to multiplatform --- .github/workflows/{gradle.yml => build.yml} | 4 +++- CHANGELOG.md | 1 + build.gradle.kts | 2 +- dataforge-io/dataforge-io-yaml/build.gradle.kts | 9 ++++++--- .../hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt | 4 ++-- .../kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt | 0 .../kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt | 0 settings.gradle.kts | 4 ++-- 8 files changed, 15 insertions(+), 9 deletions(-) rename .github/workflows/{gradle.yml => build.yml} (68%) rename dataforge-io/dataforge-io-yaml/src/{jvmMain => commonMain}/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt (96%) rename dataforge-io/dataforge-io-yaml/src/{jvmMain => commonMain}/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt (100%) rename dataforge-io/dataforge-io-yaml/src/{jvmTest => commonTest}/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt (100%) diff --git a/.github/workflows/gradle.yml b/.github/workflows/build.yml similarity index 68% rename from .github/workflows/gradle.yml rename to .github/workflows/build.yml index adc74adf..e780ac7e 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/build.yml @@ -8,10 +8,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew - name: Build with Gradle run: ./gradlew build diff --git a/CHANGELOG.md b/CHANGELOG.md index 92ac8df8..0f9f236d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - `Path` builders - Special ValueType for lists - `copy` method to descriptors +- Multiplatform yaml meta ### Changed - `ListValue` and `DoubleArrayValue` implement `Iterable`. diff --git a/build.gradle.kts b/build.gradle.kts index ca5e63bc..acdfeb81 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.3.0-dev-2") +val dataforgeVersion by extra("0.3.0-dev-3") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") diff --git a/dataforge-io/dataforge-io-yaml/build.gradle.kts b/dataforge-io/dataforge-io-yaml/build.gradle.kts index e5b23ce7..b8ce9caf 100644 --- a/dataforge-io/dataforge-io-yaml/build.gradle.kts +++ b/dataforge-io/dataforge-io-yaml/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("ru.mipt.npm.mpp") + id("ru.mipt.npm.native") } description = "YAML meta IO" @@ -9,15 +10,17 @@ repositories{ } kscience { - useSerialization() + useSerialization{ + yamlKt() + } } kotlin { sourceSets { - jvmMain{ + commonMain{ dependencies { api(project(":dataforge-io")) - api("net.mamoe.yamlkt:yamlkt:${ru.mipt.npm.gradle.KScienceVersions.Serialization.yamlKtVersion}") +// api("net.mamoe.yamlkt:yamlkt:${ru.mipt.npm.gradle.KScienceVersions.Serialization.yamlKtVersion}") } } } diff --git a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt b/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt similarity index 96% rename from dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt rename to dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt index 5abcec3f..254234dd 100644 --- a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt @@ -22,7 +22,7 @@ public class FrontMatterEnvelopeFormat( var offset = 0u do { line = input.readUtf8Line() //?: error("Input does not contain front matter separator") - offset += line.toByteArray().size.toUInt() + offset += line.encodeToByteArray().size.toUInt() } while (!line.startsWith(SEPARATOR)) val readMetaFormat = @@ -34,7 +34,7 @@ public class FrontMatterEnvelopeFormat( do { line = input.readUtf8Line() writeUtf8String(line + "\r\n") - offset += line.toByteArray().size.toUInt() + offset += line.encodeToByteArray().size.toUInt() } while (!line.startsWith(SEPARATOR)) }.read { readMetaFormat.readMeta(input) diff --git a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt b/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt similarity index 100% rename from dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt rename to dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt diff --git a/dataforge-io/dataforge-io-yaml/src/jvmTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt b/dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt similarity index 100% rename from dataforge-io/dataforge-io-yaml/src/jvmTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt rename to dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index f3ac6394..96a64daf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,8 +10,8 @@ pluginManagement { maven("https://dl.bintray.com/mipt-npm/dev") } - val toolsVersion = "0.7.1" - val kotlinVersion = "1.4.21" + val toolsVersion = "0.7.4" + val kotlinVersion = "1.4.30-RC" plugins { id("ru.mipt.npm.project") version toolsVersion From e66ae408cd53c45af50ca6788a6c672a1f877e44 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 31 Jan 2021 20:55:01 +0300 Subject: [PATCH 47/59] Fix return type for ActiveDataTree.kt --- .github/workflows/publish.yml | 40 +++++++++++++++++++ .../hep/dataforge/data/ActiveDataTree.kt | 4 +- 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..0d472b1b --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,40 @@ +name: Bintray Publish + +on: + release: + types: + - created + +jobs: + build-on-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Gradle clean + run: ./gradlew clean + - name: Gradle build + run: ./gradlew build + - name: Run release task + run: ./gradlew release -PbintrayUser=${{ secrets.BINTRAY_USER }} -PbintrayApiKey=${{ secrets.BINTRAY_KEY }} + build-on-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Gradle clean + run: ./gradlew clean + - name: Gradle build + run: ./gradlew build + - name: Run release task + run: ./gradlew release -PbintrayUser=${{ secrets.BINTRAY_USER }} -PbintrayApiKey=${{ secrets.BINTRAY_KEY }} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt index b8200fa5..eb6e5582 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt @@ -94,7 +94,7 @@ public class ActiveDataTree( public suspend fun ActiveDataTree( type: KClass, block: suspend ActiveDataTree.() -> Unit, -): DataTree { +): ActiveDataTree { val tree = ActiveDataTree(type) tree.block() return tree @@ -103,7 +103,7 @@ public suspend fun ActiveDataTree( @Suppress("FunctionName") public suspend inline fun ActiveDataTree( crossinline block: suspend ActiveDataTree.() -> Unit, -): DataTree = ActiveDataTree(T::class).apply { block() } +): ActiveDataTree = ActiveDataTree(T::class).apply { block() } public suspend inline fun ActiveDataTree.emit( From 4d19d97c53991777d61cf33116749fbde8922ba1 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 31 Jan 2021 22:34:48 +0300 Subject: [PATCH 48/59] Update DataSetBuilder API --- dataforge-data/build.gradle.kts | 4 ++ .../hep/dataforge/data/DataSetBuilder.kt | 38 +++++++++++++------ .../kotlin/hep/dataforge/data/ActionsTest.kt | 2 +- .../hep/dataforge/data/DataTreeBuilderTest.kt | 8 ++-- .../workspace/DataPropagationTest.kt | 2 +- .../hep/dataforge/workspace/FileDataTest.kt | 4 +- .../workspace/SimpleWorkspaceTest.kt | 2 +- 7 files changed, 39 insertions(+), 21 deletions(-) diff --git a/dataforge-data/build.gradle.kts b/dataforge-data/build.gradle.kts index f28b10e5..7ae0c747 100644 --- a/dataforge-data/build.gradle.kts +++ b/dataforge-data/build.gradle.kts @@ -21,3 +21,7 @@ kotlin { } } } + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL +} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt index 6e3669b7..2787e529 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt @@ -74,18 +74,6 @@ public suspend fun DataSetBuilder.emit(name: String, data: Data) emit(name.toName(), data) } -public suspend fun DataSetBuilder.data(name: Name, data: T, meta: Meta = Meta.EMPTY) { - emit(name, Data.static(data, meta)) -} - -public suspend fun DataSetBuilder.data(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { - emit(name, Data.static(data, Meta(block))) -} - -public suspend fun DataSetBuilder.data(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { - emit(name.toName(), Data.static(data, Meta(block))) -} - public suspend fun DataSetBuilder.emit(name: String, set: DataSet) { this.emit(name.toName(), set) } @@ -97,6 +85,32 @@ public suspend fun DataSetBuilder.emit(data: NamedData) { emit(data.name, data.data) } +/** + * Produce lazy [Data] and emit it into the [DataSetBuilder] + */ +public suspend inline fun DataSetBuilder.emitLazy( + name: String, + meta: Meta, + noinline producer: suspend () -> T, +) { + val data = Data(meta, block = producer) + emit(name, data) +} + +/** + * Emit a static data with the fixed value + */ +public suspend fun DataSetBuilder.emitStatic(name: String, data: T, meta: Meta = Meta.EMPTY): Unit = + emit(name, Data.static(data, meta)) + +public suspend fun DataSetBuilder.emitStatic( + name: String, + data: T, + metaBuilder: MetaBuilder.() -> Unit, +) { + emit(name.toName(), Data.static(data, Meta(metaBuilder))) +} + /** * Update data with given node data and meta with node meta. */ diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt index 686762d9..453dd2f1 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt @@ -14,7 +14,7 @@ class ActionsTest { val data: DataTree = runBlocking { DataTree { repeat(10) { - data(it.toString(), it) + emitStatic(it.toString(), it) } } } diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt index 4050e731..55ed1523 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt @@ -19,10 +19,10 @@ internal class DataTreeBuilderTest { val node = DataTree { emit("primary") { - data("a", "a") - data("b", "b") + emitStatic("a", "a") + emitStatic("b", "b") } - data("root", "root") + emitStatic("root", "root") populate(updateData) } @@ -40,7 +40,7 @@ internal class DataTreeBuilderTest { updateJob = launch { repeat(10) { delay(10) - data("value", it) + emitStatic("value", it) } delay(10) } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index 41b08d2a..16c1460b 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -49,7 +49,7 @@ class DataPropagationTest { runBlocking { data { repeat(100) { - data("myData[$it]", it) + emitStatic("myData[$it]", it) } } } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt index ff22054d..50a4bd0d 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt @@ -23,11 +23,11 @@ class FileDataTest { val dataNode = runBlocking { DataTree { emit("dir") { - data("a", "Some string") { + emitStatic("a", "Some string") { "content" put "Some string" } } - data("b", "root data") + emitStatic("b", "root data") meta { "content" put "This is root meta node" } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index 3b290d4e..c81621b9 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -56,7 +56,7 @@ class SimpleWorkspaceTest { data { repeat(100) { - data("myData[$it]", it) + emitStatic("myData[$it]", it) } } From 11ba116a890ddc2cae34de3b54036c2bbf4b233c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 31 Jan 2021 22:37:32 +0300 Subject: [PATCH 49/59] Update DataSetBuilder API --- .../kotlin/hep/dataforge/data/DataSetBuilder.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt index 2787e529..2451dc33 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt @@ -90,19 +90,31 @@ public suspend fun DataSetBuilder.emit(data: NamedData) { */ public suspend inline fun DataSetBuilder.emitLazy( name: String, - meta: Meta, + meta: Meta = Meta.EMPTY, noinline producer: suspend () -> T, ) { val data = Data(meta, block = producer) emit(name, data) } +public suspend inline fun DataSetBuilder.emitLazy( + name: Name, + meta: Meta = Meta.EMPTY, + noinline producer: suspend () -> T, +){ + val data = Data(meta, block = producer) + emit(name, data) +} + /** * Emit a static data with the fixed value */ public suspend fun DataSetBuilder.emitStatic(name: String, data: T, meta: Meta = Meta.EMPTY): Unit = emit(name, Data.static(data, meta)) +public suspend fun DataSetBuilder.emitStatic(name: Name, data: T, meta: Meta = Meta.EMPTY): Unit = + emit(name, Data.static(data, meta)) + public suspend fun DataSetBuilder.emitStatic( name: String, data: T, From 6a0bfae931e78f636413cbcfe9ecab60f4463e2c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 2 Feb 2021 12:09:44 +0300 Subject: [PATCH 50/59] minor data refactor --- .github/workflows/build.yml | 20 +++--- .github/workflows/publish.yml | 70 +++++++++---------- .../hep/dataforge/actions/ReduceAction.kt | 2 +- .../hep/dataforge/data/DataSetBuilder.kt | 10 +-- .../kotlin/hep/dataforge/data/GroupRule.kt | 34 +++------ .../hep/dataforge/data/StaticDataTree.kt | 4 +- .../kotlin/hep/dataforge/data/dataFilter.kt | 2 - .../kotlin/hep/dataforge/data/ActionsTest.kt | 2 +- .../hep/dataforge/data/DataTreeBuilderTest.kt | 26 +++++-- .../workspace/DataPropagationTest.kt | 2 +- .../hep/dataforge/workspace/FileDataTest.kt | 4 +- .../workspace/SimpleWorkspaceTest.kt | 2 +- 12 files changed, 89 insertions(+), 89 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e780ac7e..6a362884 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,6 @@ name: Gradle build -on: [push] +on: [ push ] jobs: build: @@ -8,12 +8,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Build with Gradle - run: ./gradlew build + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0d472b1b..0fbf9c1e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,40 +1,40 @@ name: Bintray Publish on: - release: - types: - - created + release: + types: + - created jobs: - build-on-windows: - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Gradle clean - run: ./gradlew clean - - name: Gradle build - run: ./gradlew build - - name: Run release task - run: ./gradlew release -PbintrayUser=${{ secrets.BINTRAY_USER }} -PbintrayApiKey=${{ secrets.BINTRAY_KEY }} - build-on-macos: - runs-on: macos-latest - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Gradle clean - run: ./gradlew clean - - name: Gradle build - run: ./gradlew build - - name: Run release task - run: ./gradlew release -PbintrayUser=${{ secrets.BINTRAY_USER }} -PbintrayApiKey=${{ secrets.BINTRAY_KEY }} + build-on-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Gradle clean + run: ./gradlew clean + - name: Gradle build + run: ./gradlew build + - name: Run release task + run: ./gradlew release -PbintrayUser=${{ secrets.BINTRAY_USER }} -PbintrayApiKey=${{ secrets.BINTRAY_KEY }} + build-on-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Gradle clean + run: ./gradlew clean + - name: Gradle build + run: ./gradlew build + - name: Run release task + run: ./gradlew release -PbintrayUser=${{ secrets.BINTRAY_USER }} -PbintrayApiKey=${{ secrets.BINTRAY_KEY }} diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt index 7bc4ab07..505b8040 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt @@ -39,7 +39,7 @@ public class ReduceGroupBuilder( */ public fun byValue(tag: String, defaultTag: String = "@default", action: JoinGroup.() -> Unit) { groupRules += { node -> - GroupRule.byValue(scope, tag, defaultTag).gather(inputType, node).map { + GroupRule.byMetaValue(scope, tag, defaultTag).gather(inputType, node).map { JoinGroup(it.key, it.value).apply(action) } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt index 2451dc33..f001c4ad 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt @@ -88,7 +88,7 @@ public suspend fun DataSetBuilder.emit(data: NamedData) { /** * Produce lazy [Data] and emit it into the [DataSetBuilder] */ -public suspend inline fun DataSetBuilder.emitLazy( +public suspend inline fun DataSetBuilder.produce( name: String, meta: Meta = Meta.EMPTY, noinline producer: suspend () -> T, @@ -97,7 +97,7 @@ public suspend inline fun DataSetBuilder.emitLazy( emit(name, data) } -public suspend inline fun DataSetBuilder.emitLazy( +public suspend inline fun DataSetBuilder.produce( name: Name, meta: Meta = Meta.EMPTY, noinline producer: suspend () -> T, @@ -109,13 +109,13 @@ public suspend inline fun DataSetBuilder.emitLazy( /** * Emit a static data with the fixed value */ -public suspend fun DataSetBuilder.emitStatic(name: String, data: T, meta: Meta = Meta.EMPTY): Unit = +public suspend fun DataSetBuilder.static(name: String, data: T, meta: Meta = Meta.EMPTY): Unit = emit(name, Data.static(data, meta)) -public suspend fun DataSetBuilder.emitStatic(name: Name, data: T, meta: Meta = Meta.EMPTY): Unit = +public suspend fun DataSetBuilder.static(name: Name, data: T, meta: Meta = Meta.EMPTY): Unit = emit(name, Data.static(data, meta)) -public suspend fun DataSetBuilder.emitStatic( +public suspend fun DataSetBuilder.static( name: String, data: T, metaBuilder: MetaBuilder.() -> Unit, diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt index 41e1de53..414fa17e 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt @@ -19,6 +19,7 @@ import hep.dataforge.meta.get import hep.dataforge.meta.string import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import kotlin.reflect.KClass public interface GroupRule { @@ -33,7 +34,7 @@ public interface GroupRule { * @param defaultTagValue * @return */ - public fun byValue( + public fun byMetaValue( scope: CoroutineScope, key: String, defaultTagValue: String, @@ -50,31 +51,16 @@ public interface GroupRule { map.getOrPut(tagValue) { ActiveDataTree(dataType) }.emit(data.name, data.data) } + scope.launch { + set.updates.collect { name -> + val data = set.getData(name) + val tagValue = data?.meta[key]?.string ?: defaultTagValue + map.getOrPut(tagValue) { ActiveDataTree(dataType) }.emit(name, data) + } + } + return map } } - - - // @ValueDef(key = "byValue", required = true, info = "The name of annotation value by which grouping should be made") -// @ValueDef( -// key = "defaultValue", -// def = "default", -// info = "Default value which should be used for content in which the grouping value is not presented" -// ) -// public fun byMeta(scope: CoroutineScope, config: Meta): GroupRule { -// //TODO expand grouping options -// return config["byValue"]?.string?.let { -// byValue( -// scope, -// it, -// config["defaultValue"]?.string ?: "default" -// ) -// } ?: object : GroupRule { -// override suspend fun gather( -// dataType: KClass, -// source: DataSource, -// ): Map> = mapOf("" to source) -// } -// } } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt index 05cd85de..4741a464 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt @@ -2,7 +2,7 @@ package hep.dataforge.data import hep.dataforge.names.* import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.collect import kotlin.reflect.KClass @PublishedApi @@ -22,7 +22,7 @@ internal class StaticDataTree( } } - fun getOrCreateNode(name: Name): StaticDataTree = when (name.length) { + private fun getOrCreateNode(name: Name): StaticDataTree = when (name.length) { 0 -> this 1 -> { val itemName = name.firstOrNull()!! diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt index 4d500587..aff59e61 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt @@ -32,7 +32,6 @@ public fun DataSet.filter( } } - /** * Generate a wrapper data set with a given name prefix appended to all names */ @@ -48,7 +47,6 @@ else object : ActiveDataSet { override val updates: Flow get() = this@withNamePrefix.updates.map { prefix + it } } - /** * Get a subset of data starting with a given [branchName] */ diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt index 453dd2f1..81049612 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt @@ -14,7 +14,7 @@ class ActionsTest { val data: DataTree = runBlocking { DataTree { repeat(10) { - emitStatic(it.toString(), it) + static(it.toString(), it) } } } diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt index 55ed1523..cf34cbb8 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt @@ -8,6 +8,22 @@ import kotlin.test.assertEquals internal class DataTreeBuilderTest { + @Test + fun testTreeBuild() = runBlocking { + val node = DataTree { + "primary" put { + static("a", "a") + static("b", "b") + } + static("c.d", "c.d") + static("c.f", "c.f") + } + assertEquals("a", node.getData("primary.a")?.value()) + assertEquals("b", node.getData("primary.b")?.value()) + assertEquals("c.d", node.getData("c.d")?.value()) + assertEquals("c.f", node.getData("c.f")?.value()) + } + @Test fun testDataUpdate() = runBlocking { val updateData: DataTree = DataTree { @@ -18,11 +34,11 @@ internal class DataTreeBuilderTest { } val node = DataTree { - emit("primary") { - emitStatic("a", "a") - emitStatic("b", "b") + "primary" put { + static("a", "a") + static("b", "b") } - emitStatic("root", "root") + static("root", "root") populate(updateData) } @@ -40,7 +56,7 @@ internal class DataTreeBuilderTest { updateJob = launch { repeat(10) { delay(10) - emitStatic("value", it) + static("value", it) } delay(10) } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt index 16c1460b..3b70ed5e 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/DataPropagationTest.kt @@ -49,7 +49,7 @@ class DataPropagationTest { runBlocking { data { repeat(100) { - emitStatic("myData[$it]", it) + static("myData[$it]", it) } } } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt index 50a4bd0d..e089e448 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt @@ -23,11 +23,11 @@ class FileDataTest { val dataNode = runBlocking { DataTree { emit("dir") { - emitStatic("a", "Some string") { + static("a", "Some string") { "content" put "Some string" } } - emitStatic("b", "root data") + static("b", "root data") meta { "content" put "This is root meta node" } diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index c81621b9..b6bc9a1c 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -56,7 +56,7 @@ class SimpleWorkspaceTest { data { repeat(100) { - emitStatic("myData[$it]", it) + static("myData[$it]", it) } } From fcd99b1ca895fd1dab650fb8104ad16b9d9c8ef3 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 2 Feb 2021 17:48:25 +0300 Subject: [PATCH 51/59] [WIP] change KClass to KType --- dataforge-data/build.gradle.kts | 4 -- .../kotlin/hep/dataforge/actions/MapAction.kt | 12 ++-- .../hep/dataforge/actions/ReduceAction.kt | 3 +- .../hep/dataforge/actions/SplitAction.kt | 10 +-- .../hep/dataforge/data/ActiveDataTree.kt | 17 ++--- .../hep/dataforge/data/CachingAction.kt | 6 +- .../kotlin/hep/dataforge/data/Data.kt | 25 +++---- .../kotlin/hep/dataforge/data/DataSet.kt | 10 +-- .../hep/dataforge/data/StaticDataTree.kt | 13 ++-- .../hep/dataforge/data/dataTransform.kt | 27 ++++---- .../kotlin/hep/dataforge/data/dataJVM.kt | 42 ------------ .../kotlin/hep/dataforge/data/select.kt | 67 +++++++++++++++---- .../kotlin/hep/dataforge/tables/CastColumn.kt | 4 +- 13 files changed, 123 insertions(+), 117 deletions(-) delete mode 100644 dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt diff --git a/dataforge-data/build.gradle.kts b/dataforge-data/build.gradle.kts index 7ae0c747..16d98ced 100644 --- a/dataforge-data/build.gradle.kts +++ b/dataforge-data/build.gradle.kts @@ -12,10 +12,6 @@ kotlin { commonMain{ dependencies { api(project(":dataforge-meta")) - } - } - jvmMain{ - dependencies{ api(kotlin("reflect")) } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt index 45632b43..77acc6a4 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt @@ -7,7 +7,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf /** * Action environment includes data name, data meta and action configuration meta @@ -34,8 +35,9 @@ public class MapActionBuilder(public var name: Name, public var meta: Meta } -public class MapAction( - public val outputType: KClass, +@PublishedApi +internal class MapAction( + private val outputType: KType, private val block: MapActionBuilder.() -> Unit, ) : Action { @@ -84,8 +86,8 @@ public class MapAction( @Suppress("FunctionName") -public inline fun MapAction( +public inline fun Action.Companion.map( noinline builder: MapActionBuilder.() -> Unit, -): MapAction = MapAction(R::class, builder) +): Action = MapAction(typeOf(), builder) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt index 505b8040..f3cf9fdc 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.fold import kotlin.reflect.KClass +import kotlin.reflect.KType @DFExperimental @@ -75,7 +76,7 @@ public class ReduceGroupBuilder( @DFExperimental public class ReduceAction( private val inputType: KClass, - outputType: KClass, + outputType: KType, private val action: ReduceGroupBuilder.() -> Unit, ) : CachingAction(outputType) { //TODO optimize reduction. Currently the whole action recalculates on push diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt index aaec3836..c659d5a1 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlin.collections.set -import kotlin.reflect.KClass +import kotlin.reflect.KType public class SplitBuilder(public val name: Name, public val meta: Meta) { @@ -39,8 +39,8 @@ public class SplitBuilder(public val name: Name, public val me /** * Action that splits each incoming element into a number of fragments defined in builder */ -public class SplitAction( - private val outputType: KClass, +internal class SplitAction( + private val outputType: KType, private val action: SplitBuilder.() -> Unit, ) : Action { @@ -59,11 +59,11 @@ public class SplitAction( // apply individual fragment rules to result return split.fragments.entries.asFlow().map { (fragmentName, rule) -> val env = SplitBuilder.FragmentRule(fragmentName, laminate.toMutableMeta()).apply(rule) - data.map(outputType, meta = env.meta) { env.result(it) }.named(fragmentName) + data.map(outputType, meta = env.meta) { env.result(it) }.named(fragmentName) } } - return ActiveDataTree(outputType) { + return ActiveDataTree(outputType) { populate(dataSet.flow().flatMapConcat(transform = ::splitOne)) scope?.launch { dataSet.updates.collect { name -> diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt index eb6e5582..5d197982 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ActiveDataTree.kt @@ -8,13 +8,14 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf /** * A mutable [DataTree.Companion.active]. It */ public class ActiveDataTree( - override val dataType: KClass, + override val dataType: KType, ) : DataTree, DataSetBuilder, ActiveDataSet { private val mutex = Mutex() private val treeItems = HashMap>() @@ -49,7 +50,7 @@ public class ActiveDataTree( private suspend fun getOrCreateNode(token: NameToken): ActiveDataTree = (treeItems[token] as? DataTreeItem.Node)?.tree as? ActiveDataTree - ?: ActiveDataTree(dataType).also { + ?: ActiveDataTree(dataType).also { mutex.withLock { treeItems[token] = DataTreeItem.Node(it) } @@ -92,10 +93,10 @@ public class ActiveDataTree( */ @Suppress("FunctionName") public suspend fun ActiveDataTree( - type: KClass, + type: KType, block: suspend ActiveDataTree.() -> Unit, ): ActiveDataTree { - val tree = ActiveDataTree(type) + val tree = ActiveDataTree(type) tree.block() return tree } @@ -103,15 +104,15 @@ public suspend fun ActiveDataTree( @Suppress("FunctionName") public suspend inline fun ActiveDataTree( crossinline block: suspend ActiveDataTree.() -> Unit, -): ActiveDataTree = ActiveDataTree(T::class).apply { block() } +): ActiveDataTree = ActiveDataTree(typeOf()).apply { block() } public suspend inline fun ActiveDataTree.emit( name: Name, noinline block: suspend ActiveDataTree.() -> Unit, -): Unit = emit(name, ActiveDataTree(T::class, block)) +): Unit = emit(name, ActiveDataTree(typeOf(), block)) public suspend inline fun ActiveDataTree.emit( name: String, noinline block: suspend ActiveDataTree.() -> Unit, -): Unit = emit(name.toName(), ActiveDataTree(T::class, block)) +): Unit = emit(name.toName(), ActiveDataTree(typeOf(), block)) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt index 0e4ddd97..c4fb4f90 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect -import kotlin.reflect.KClass +import kotlin.reflect.KType /** * Remove all values with keys starting with [name] @@ -23,7 +23,7 @@ internal fun MutableMap.removeWhatStartsWith(name: Name) { * An action that caches results on-demand and recalculates them on source push */ public abstract class CachingAction( - public val outputType: KClass, + public val outputType: KType, ) : Action { protected abstract fun CoroutineScope.transform( @@ -36,7 +36,7 @@ public abstract class CachingAction( dataSet: DataSet, meta: Meta, scope: CoroutineScope?, - ): DataSet = ActiveDataTree(outputType) { + ): DataSet = ActiveDataTree(outputType) { coroutineScope { populate(transform(dataSet, meta)) } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 6d0c4c38..0a768ee4 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -7,7 +7,8 @@ import hep.dataforge.misc.Type import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf /** * A data element characterized by its meta @@ -17,7 +18,7 @@ public interface Data : Goal, MetaRepr { /** * Type marker for the data. The type is known before the calculation takes place so it could be checked. */ - public val type: KClass + public val type: KType /** * Meta for the data @@ -25,7 +26,7 @@ public interface Data : Goal, MetaRepr { public val meta: Meta override fun toMeta(): Meta = Meta { - "type" put (type.simpleName ?: "undefined") + "type" put (type.toString()) if (!meta.isEmpty()) { "meta" put meta } @@ -34,16 +35,17 @@ public interface Data : Goal, MetaRepr { public companion object { public const val TYPE: String = "data" - public fun static( + public inline fun static( value: T, meta: Meta = Meta.EMPTY, - ): Data = StaticData(value, meta) + ): Data = StaticData(typeOf(),value, meta) /** * An empty data containing only meta */ public fun empty(meta: Meta): Data = object : Data { - override val type: KClass = Nothing::class + private val nothing: Nothing get() = error("this is nothing") + override val type: KType = this::nothing.returnType override val meta: Meta = meta override val dependencies: Collection> = emptyList() override val deferred: Deferred @@ -58,7 +60,7 @@ public interface Data : Goal, MetaRepr { } public class LazyData( - override val type: KClass, + override val type: KType, override val meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), @@ -66,15 +68,14 @@ public class LazyData( ) : Data, LazyGoal(context, dependencies, block) public class StaticData( + override val type: KType, value: T, override val meta: Meta = Meta.EMPTY, -) : Data, StaticGoal(value) { - override val type: KClass get() = value::class -} +) : Data, StaticGoal(value) @Suppress("FunctionName") public fun Data( - type: KClass, + type: KType, meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), @@ -87,4 +88,4 @@ public inline fun Data( context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), noinline block: suspend () -> T, -): Data = Data(T::class, meta, context, dependencies, block) +): Data = Data(typeOf(), meta, context, dependencies, block) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt index c401b275..129f657f 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt @@ -7,14 +7,14 @@ import hep.dataforge.meta.set import hep.dataforge.names.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import kotlin.reflect.KClass +import kotlin.reflect.KType public interface DataSet { /** * The minimal common ancestor to all data in the node */ - public val dataType: KClass + public val dataType: KType /** * Traverse this provider or its child. The order is not guaranteed. @@ -43,7 +43,9 @@ public interface DataSet { * An empty [DataSet] that suits all types */ public val EMPTY: DataSet = object : DataSet { - override val dataType: KClass = Nothing::class + override val dataType: KType = this::nothing.returnType + + private val nothing: Nothing get() = error("this is nothing") override fun flow(): Flow> = emptyFlow() @@ -88,7 +90,7 @@ public suspend fun DataSet<*>.toMeta(): Meta = Meta { set(it.name, it.meta) } else { it.name put { - "type" put it.type.simpleName + "type" put it.type.toString() "meta" put it.meta } } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt index 4741a464..950cb1f1 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/StaticDataTree.kt @@ -3,11 +3,12 @@ package hep.dataforge.data import hep.dataforge.names.* import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.collect -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf @PublishedApi internal class StaticDataTree( - override val dataType: KClass, + override val dataType: KType, ) : DataSetBuilder, DataTree { private val items: MutableMap> = HashMap() @@ -26,7 +27,7 @@ internal class StaticDataTree( 0 -> this 1 -> { val itemName = name.firstOrNull()!! - (items[itemName].tree as? StaticDataTree) ?: StaticDataTree(dataType).also { + (items[itemName].tree as? StaticDataTree) ?: StaticDataTree(dataType).also { items[itemName] = DataTreeItem.Node(it) } } @@ -61,14 +62,14 @@ internal class StaticDataTree( @Suppress("FunctionName") public suspend fun DataTree( - dataType: KClass, + dataType: KType, block: suspend DataSetBuilder.() -> Unit, -): DataTree = StaticDataTree(dataType).apply { block() } +): DataTree = StaticDataTree(dataType).apply { block() } @Suppress("FunctionName") public suspend inline fun DataTree( noinline block: suspend DataSetBuilder.() -> Unit, -): DataTree = DataTree(T::class, block) +): DataTree = DataTree(typeOf(), block) public suspend fun DataSet.seal(): DataTree = DataTree(dataType){ populate(this@seal) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt index 31e30fb1..fdc24c1c 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt @@ -11,7 +11,8 @@ import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf /** * Lazily transform this data to another data. By convention [block] should not use external data (be pure). @@ -19,8 +20,8 @@ import kotlin.reflect.KClass * @param meta for the resulting data. By default equals input data. * @param block the transformation itself */ -public fun Data.map( - outputType: KClass, +private fun Data.map( + outputType: KType, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, block: suspend (T) -> R, @@ -35,7 +36,7 @@ public inline fun Data.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, crossinline block: suspend (T) -> R, -): LazyData = LazyData(R::class, meta, coroutineContext, listOf(this)) { +): LazyData = LazyData(typeOf(), meta, coroutineContext, listOf(this)) { block(await()) } @@ -47,7 +48,7 @@ public inline fun Data.combine( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, crossinline block: suspend (left: T1, right: T2) -> R, -): LazyData = LazyData(R::class, meta, coroutineContext, listOf(this, other)) { +): LazyData = LazyData(typeOf(), meta, coroutineContext, listOf(this, other)) { block(await(), other.await()) } @@ -62,7 +63,7 @@ public inline fun Collection>.reduceToData( meta: Meta = Meta.EMPTY, crossinline block: suspend (Collection) -> R, ): LazyData = LazyData( - R::class, + typeOf(), meta, coroutineContext, this @@ -71,7 +72,7 @@ public inline fun Collection>.reduceToData( } public fun Map>.reduceToData( - outputType: KClass, + outputType: KType, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, block: suspend (Map) -> R, @@ -96,7 +97,7 @@ public inline fun Map>.reduceToData( meta: Meta = Meta.EMPTY, noinline block: suspend (Map) -> R, ): LazyData = LazyData( - R::class, + typeOf(), meta, coroutineContext, this.values @@ -110,7 +111,7 @@ public inline fun Map>.reduceToData( * Transform a [Flow] of [NamedData] to a single [Data]. */ public suspend fun Flow>.reduceToData( - outputType: KClass, + outputType: KType, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, transformation: suspend (Flow>) -> R, @@ -127,7 +128,7 @@ public suspend inline fun Flow>.reduceTo coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, noinline transformation: suspend (Flow>) -> R, -): LazyData = reduceToData(R::class, coroutineContext, meta) { +): LazyData = reduceToData(typeOf(), coroutineContext, meta) { transformation(it) } @@ -148,11 +149,11 @@ public suspend inline fun Flow>.foldToDa //DataSet operations public suspend fun DataSet.map( - outputType: KClass, + outputType: KType, coroutineContext: CoroutineContext = EmptyCoroutineContext, metaTransform: MetaBuilder.() -> Unit = {}, block: suspend (T) -> R, -): DataTree = DataTree(outputType) { +): DataTree = DataTree(outputType) { populate( flow().map { val newMeta = it.meta.toMutableMeta().apply(metaTransform).seal() @@ -165,7 +166,7 @@ public suspend inline fun DataSet.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, noinline metaTransform: MetaBuilder.() -> Unit = {}, noinline block: suspend (T) -> R, -): DataTree = map(R::class, coroutineContext, metaTransform, block) +): DataTree = map(typeOf(), coroutineContext, metaTransform, block) public suspend fun DataSet.forEach(block: suspend (NamedData) -> Unit) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt deleted file mode 100644 index 4178766a..00000000 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt +++ /dev/null @@ -1,42 +0,0 @@ -package hep.dataforge.data - -import kotlin.reflect.KClass -import kotlin.reflect.full.isSubclassOf - -/** - * Check if data could be safely cast to given class - */ -internal fun Data<*>.canCast(type: KClass): Boolean = this.type.isSubclassOf(type) - -/** - * Cast the node to given type if the cast is possible or return null - */ -@Suppress("UNCHECKED_CAST") -public fun Data<*>.castOrNull(type: KClass): Data? = - if (!canCast(type)) null else object : Data by (this as Data) { - override val type: KClass = type - } - -/** - * Unsafe cast of data node - */ -public fun Data<*>.cast(type: KClass): Data = - castOrNull(type) ?: error("Can't cast ${this.type} to $type") - -public inline fun Data<*>.cast(): Data = cast(R::class) - -@Suppress("UNCHECKED_CAST") -public fun DataSet<*>.castOrNull(type: KClass): DataSet? = - if (!canCast(type)) null else object : DataSet by (this as DataSet) { - override val dataType: KClass = type - } - - -public fun DataSet<*>.cast(type: KClass): DataSet = - castOrNull(type) ?: error("Can't cast ${this.dataType} to $type") - -/** - * Check that node is compatible with given type meaning that each element could be cast to the type - */ -internal fun DataSet<*>.canCast(type: KClass): Boolean = - type.isSubclassOf(this.dataType) \ No newline at end of file diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt index 5217d1f0..def3133a 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt @@ -3,26 +3,69 @@ package hep.dataforge.data import hep.dataforge.actions.NamedData import hep.dataforge.actions.named import hep.dataforge.meta.DFExperimental -import hep.dataforge.names.* +import hep.dataforge.names.Name +import hep.dataforge.names.matches +import hep.dataforge.names.toName import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.full.isSubtypeOf +import kotlin.reflect.typeOf +/** + * Check if data could be safely cast to given class + */ +private fun Data<*>.canCast(type: KType): Boolean = this.type.isSubtypeOf(type) + +/** + * Cast the node to given type if the cast is possible or return null + */ +@Suppress("UNCHECKED_CAST") +private fun Data<*>.castOrNull(type: KType): Data? = + if (!canCast(type)) null else object : Data by (this as Data) { + override val type: KType = type + } + +/** + * Unsafe cast of data node + */ +private fun Data<*>.cast(type: KType): Data = + castOrNull(type) ?: error("Can't cast ${this.type} to $type") + +private inline fun Data<*>.cast(): Data = cast(typeOf()) + +@Suppress("UNCHECKED_CAST") +private fun DataSet<*>.castOrNull(type: KType): DataSet? = + if (!canCast(type)) null else object : DataSet by (this as DataSet) { + override val dataType: KType = type + } + + +private fun DataSet<*>.cast(type: KType): DataSet = + castOrNull(type) ?: error("Can't cast ${this.dataType} to $type") + +/** + * Check that node is compatible with given type meaning that each element could be cast to the type + */ +private fun DataSet<*>.canCast(type: KType): Boolean = + type.isSubtypeOf(this.dataType) + /** * Select all data matching given type and filters. Does not modify paths */ @OptIn(DFExperimental::class) -public fun DataSet<*>.select( - type: KClass, +@PublishedApi +internal fun DataSet<*>.select( + type: KType, namePattern: Name? = null, ): ActiveDataSet = object : ActiveDataSet { - override val dataType: KClass = type + override val dataType = type @Suppress("UNCHECKED_CAST") override fun flow(): Flow> = this@select.flow().filter { - it.canCast(type) && (namePattern == null || it.name.matches(namePattern)) + it.type.isSubtypeOf(type) && (namePattern == null || it.name.matches(namePattern)) }.map { it as NamedData } @@ -31,7 +74,7 @@ public fun DataSet<*>.select( override val updates: Flow = this@select.updates.filter { val datum = this@select.getData(it) - datum?.canCast(type) ?: false + datum?.canCast(type) ?: false } } @@ -40,12 +83,12 @@ public fun DataSet<*>.select( * Select a single datum of the appropriate type */ public inline fun DataSet<*>.select(namePattern: Name? = null): DataSet = - select(R::class, namePattern) + select(typeOf(), namePattern) -public suspend fun DataSet<*>.selectOne(type: KClass, name: Name): NamedData? = - getData(name)?.castOrNull(type)?.named(name) +public suspend fun DataSet<*>.selectOne(type: KType, name: Name): NamedData? = + getData(name)?.castOrNull(type)?.named(name) -public suspend inline fun DataSet<*>.selectOne(name: Name): NamedData? = selectOne(R::class, name) +public suspend inline fun DataSet<*>.selectOne(name: Name): NamedData? = selectOne(typeOf(), name) public suspend inline fun DataSet<*>.selectOne(name: String): NamedData? = - selectOne(R::class, name.toName()) \ No newline at end of file + selectOne(typeOf(), name.toName()) \ No newline at end of file diff --git a/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt b/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt index 6990f1c6..0ab6c515 100644 --- a/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt +++ b/dataforge-tables/src/jvmMain/kotlin/hep/dataforge/tables/CastColumn.kt @@ -4,8 +4,8 @@ import hep.dataforge.meta.Meta import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KClass import kotlin.reflect.KProperty -import kotlin.reflect.full.cast import kotlin.reflect.full.isSubclassOf +import kotlin.reflect.safeCast @Suppress("UNCHECKED_CAST") public fun Column<*>.cast(type: KClass): Column { @@ -22,7 +22,7 @@ public class CastColumn(private val origin: Column<*>, override val typ override val size: Int get() = origin.size - override fun get(index: Int): T? = type.cast(origin[index]) + override fun get(index: Int): T? = type.safeCast(origin[index]) } public class ColumnProperty(public val table: Table, public val type: KClass) : ReadOnlyProperty> { From 730ac69544fb26b8b350c7da28e8414bda4dc649 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 5 Feb 2021 09:49:35 +0300 Subject: [PATCH 52/59] build tools 0.7.5 and JVM-IR --- .../kotlin/hep/dataforge/properties/bindings.kt | 4 ++-- dataforge-io/dataforge-io-yaml/build.gradle.kts | 15 +++++++++------ .../io/yaml/FrontMatterEnvelopeFormat.kt | 5 ++--- .../hep/dataforge/io/yaml/YamlMetaFormat.kt | 7 +++---- .../hep/dataforge/io/yaml/YamlMetaFormatTest.kt | 0 .../kotlin/hep/dataforge/io/FileBinaryTest.kt | 2 +- .../kotlin/hep/dataforge/io/FileEnvelopeTest.kt | 4 ++-- settings.gradle.kts | 4 ++-- 8 files changed, 21 insertions(+), 20 deletions(-) rename dataforge-io/dataforge-io-yaml/src/{commonMain => jvmMain}/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt (97%) rename dataforge-io/dataforge-io-yaml/src/{commonMain => jvmMain}/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt (94%) rename dataforge-io/dataforge-io-yaml/src/{commonTest => jvmTest}/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt (100%) diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/properties/bindings.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/properties/bindings.kt index 05818cae..7ef8d72d 100644 --- a/dataforge-context/src/jsMain/kotlin/hep/dataforge/properties/bindings.kt +++ b/dataforge-context/src/jsMain/kotlin/hep/dataforge/properties/bindings.kt @@ -4,7 +4,7 @@ import hep.dataforge.meta.DFExperimental import org.w3c.dom.HTMLInputElement @DFExperimental -fun HTMLInputElement.bindValue(property: Property) { +public fun HTMLInputElement.bindValue(property: Property) { if (this.onchange != null) error("Input element already bound") this.onchange = { property.value = this.value @@ -18,7 +18,7 @@ fun HTMLInputElement.bindValue(property: Property) { } @DFExperimental -fun HTMLInputElement.bindChecked(property: Property) { +public fun HTMLInputElement.bindChecked(property: Property) { if (this.onchange != null) error("Input element already bound") this.onchange = { property.value = this.checked diff --git a/dataforge-io/dataforge-io-yaml/build.gradle.kts b/dataforge-io/dataforge-io-yaml/build.gradle.kts index b8ce9caf..d325a8c8 100644 --- a/dataforge-io/dataforge-io-yaml/build.gradle.kts +++ b/dataforge-io/dataforge-io-yaml/build.gradle.kts @@ -1,14 +1,10 @@ plugins { id("ru.mipt.npm.mpp") - id("ru.mipt.npm.native") +// id("ru.mipt.npm.native") } description = "YAML meta IO" -repositories{ - jcenter() -} - kscience { useSerialization{ yamlKt() @@ -20,8 +16,15 @@ kotlin { commonMain{ dependencies { api(project(":dataforge-io")) -// api("net.mamoe.yamlkt:yamlkt:${ru.mipt.npm.gradle.KScienceVersions.Serialization.yamlKtVersion}") + //api("net.mamoe.yamlkt:yamlkt:${ru.mipt.npm.gradle.KScienceVersions.Serialization.yamlKtVersion}") } } } } + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE + description =""" + YAML meta converters and Front Matter envelope format + """.trimIndent() +} diff --git a/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt similarity index 97% rename from dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt rename to dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt index 254234dd..69100422 100644 --- a/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt @@ -17,8 +17,7 @@ public class FrontMatterEnvelopeFormat( ) : EnvelopeFormat { override fun readPartial(input: Input): PartialEnvelope { - @Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER") - var line = "" + var line: String var offset = 0u do { line = input.readUtf8Line() //?: error("Input does not contain front matter separator") @@ -44,7 +43,7 @@ public class FrontMatterEnvelopeFormat( } override fun readObject(input: Input): Envelope { - var line = "" + var line: String do { line = input.readUtf8Line() //?: error("Input does not contain front matter separator") } while (!line.startsWith(SEPARATOR)) diff --git a/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt similarity index 94% rename from dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt rename to dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt index e63a3b3e..160683a6 100644 --- a/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt @@ -86,16 +86,15 @@ public fun YamlMap.toMeta(): Meta = YamlMeta(this) */ @DFExperimental public class YamlMetaFormat(private val meta: Meta) : MetaFormat { - private val coder = Yaml.default override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) { val yaml = meta.toYaml() - val string = coder.encodeToString(yaml) + val string = Yaml.encodeToString(yaml) output.writeUtf8String(string) } override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta { - val yaml = coder.decodeYamlMapFromString(input.readUtf8String()) + val yaml = Yaml.decodeYamlMapFromString(input.readUtf8String()) return yaml.toMeta() } @@ -116,7 +115,7 @@ public class YamlMetaFormat(private val meta: Meta) : MetaFormat { override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit = default.writeMeta(output, meta, descriptor) - override fun readMeta(input: kotlinx.io.Input, descriptor: NodeDescriptor?): Meta = + override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta = default.readMeta(input, descriptor) } } \ No newline at end of file diff --git a/dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt b/dataforge-io/dataforge-io-yaml/src/jvmTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt similarity index 100% rename from dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt rename to dataforge-io/dataforge-io-yaml/src/jvmTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt diff --git a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileBinaryTest.kt b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileBinaryTest.kt index 7ccc65e7..4971bf6c 100644 --- a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileBinaryTest.kt +++ b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileBinaryTest.kt @@ -54,7 +54,7 @@ class FileBinaryTest { val tmpPath = Files.createTempFile("dataforge_test", ".df") Global.io.writeEnvelopeFile(tmpPath, envelope) - val binary = Global.io.readEnvelopeFile(tmpPath)?.data!! + val binary = Global.io.readEnvelopeFile(tmpPath).data!! assertEquals(binary.size, binary.toByteArray().size) } } \ No newline at end of file diff --git a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileEnvelopeTest.kt b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileEnvelopeTest.kt index 8db2bf62..ab96c324 100644 --- a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileEnvelopeTest.kt +++ b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileEnvelopeTest.kt @@ -29,7 +29,7 @@ class FileEnvelopeTest { val tmpPath = Files.createTempFile("dataforge_test", ".df") writeEnvelopeFile(tmpPath, envelope) println(tmpPath.toUri()) - val restored: Envelope = readEnvelopeFile(tmpPath)!! + val restored: Envelope = readEnvelopeFile(tmpPath) assertTrue { envelope.contentEquals(restored) } } } @@ -40,7 +40,7 @@ class FileEnvelopeTest { val tmpPath = Files.createTempFile("dataforge_test_tagless", ".df") writeEnvelopeFile(tmpPath, envelope, envelopeFormat = TaglessEnvelopeFormat) println(tmpPath.toUri()) - val restored: Envelope = readEnvelopeFile(tmpPath)!! + val restored: Envelope = readEnvelopeFile(tmpPath) assertTrue { envelope.contentEquals(restored) } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 96a64daf..c97b21a8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,8 +10,8 @@ pluginManagement { maven("https://dl.bintray.com/mipt-npm/dev") } - val toolsVersion = "0.7.4" - val kotlinVersion = "1.4.30-RC" + val toolsVersion = "0.7.5" + val kotlinVersion = "1.4.30" plugins { id("ru.mipt.npm.project") version toolsVersion From 81fb064d38ff7ef0ae345eba941c2182d53c64e5 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 12:46:15 +0300 Subject: [PATCH 53/59] Maturity declarations. --- build.gradle.kts | 6 +----- dataforge-context/build.gradle.kts | 4 ++++ dataforge-io/build.gradle.kts | 4 ++++ dataforge-io/dataforge-io-yaml/build.gradle.kts | 7 +++++-- .../hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt | 0 .../kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt | 0 .../kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt | 0 dataforge-meta/build.gradle.kts | 6 +++++- dataforge-scripting/build.gradle.kts | 4 ++++ dataforge-tables/build.gradle.kts | 4 ++++ dataforge-workspace/build.gradle.kts | 4 ++++ settings.gradle.kts | 2 +- 12 files changed, 32 insertions(+), 9 deletions(-) rename dataforge-io/dataforge-io-yaml/src/{jvmMain => commonMain}/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt (100%) rename dataforge-io/dataforge-io-yaml/src/{jvmMain => commonMain}/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt (100%) rename dataforge-io/dataforge-io-yaml/src/{jvmTest => commonTest}/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt (100%) diff --git a/build.gradle.kts b/build.gradle.kts index acdfeb81..cc74ad20 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.project") } -val dataforgeVersion by extra("0.3.0-dev-3") +val dataforgeVersion by extra("0.3.0") val bintrayRepo by extra("dataforge") val githubProject by extra("dataforge-core") @@ -13,10 +13,6 @@ allprojects { version = dataforgeVersion apply() - - repositories { - mavenLocal() - } } apiValidation{ diff --git a/dataforge-context/build.gradle.kts b/dataforge-context/build.gradle.kts index 01a7e3bb..84785ce8 100644 --- a/dataforge-context/build.gradle.kts +++ b/dataforge-context/build.gradle.kts @@ -28,4 +28,8 @@ kotlin { } } } +} + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT } \ No newline at end of file diff --git a/dataforge-io/build.gradle.kts b/dataforge-io/build.gradle.kts index d2e3a41a..01f749cb 100644 --- a/dataforge-io/build.gradle.kts +++ b/dataforge-io/build.gradle.kts @@ -22,4 +22,8 @@ kotlin { } } } +} + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE } \ No newline at end of file diff --git a/dataforge-io/dataforge-io-yaml/build.gradle.kts b/dataforge-io/dataforge-io-yaml/build.gradle.kts index d325a8c8..4ee1029a 100644 --- a/dataforge-io/dataforge-io-yaml/build.gradle.kts +++ b/dataforge-io/dataforge-io-yaml/build.gradle.kts @@ -7,16 +7,19 @@ description = "YAML meta IO" kscience { useSerialization{ - yamlKt() + yamlKt("0.9.0-dev-1") } } +repositories{ + maven("https://dl.bintray.com/mamoe/yamlkt") +} + kotlin { sourceSets { commonMain{ dependencies { api(project(":dataforge-io")) - //api("net.mamoe.yamlkt:yamlkt:${ru.mipt.npm.gradle.KScienceVersions.Serialization.yamlKtVersion}") } } } diff --git a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt b/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt similarity index 100% rename from dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt rename to dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt diff --git a/dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt b/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt similarity index 100% rename from dataforge-io/dataforge-io-yaml/src/jvmMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt rename to dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt diff --git a/dataforge-io/dataforge-io-yaml/src/jvmTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt b/dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt similarity index 100% rename from dataforge-io/dataforge-io-yaml/src/jvmTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt rename to dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt diff --git a/dataforge-meta/build.gradle.kts b/dataforge-meta/build.gradle.kts index 980924a7..45ca68f3 100644 --- a/dataforge-meta/build.gradle.kts +++ b/dataforge-meta/build.gradle.kts @@ -9,4 +9,8 @@ kscience { } } -description = "Meta definition and basic operations on meta" \ No newline at end of file +description = "Meta definition and basic operations on meta" + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT +} \ No newline at end of file diff --git a/dataforge-scripting/build.gradle.kts b/dataforge-scripting/build.gradle.kts index cb16b66e..e0c47057 100644 --- a/dataforge-scripting/build.gradle.kts +++ b/dataforge-scripting/build.gradle.kts @@ -22,4 +22,8 @@ kotlin { } } } +} + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE } \ No newline at end of file diff --git a/dataforge-tables/build.gradle.kts b/dataforge-tables/build.gradle.kts index e88f70d1..d0f008c3 100644 --- a/dataforge-tables/build.gradle.kts +++ b/dataforge-tables/build.gradle.kts @@ -12,4 +12,8 @@ kotlin { } } } +} + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE } \ No newline at end of file diff --git a/dataforge-workspace/build.gradle.kts b/dataforge-workspace/build.gradle.kts index 2bb03cec..ae83b062 100644 --- a/dataforge-workspace/build.gradle.kts +++ b/dataforge-workspace/build.gradle.kts @@ -13,4 +13,8 @@ kotlin { } } } +} + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index c97b21a8..defbc0e5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,7 +10,7 @@ pluginManagement { maven("https://dl.bintray.com/mipt-npm/dev") } - val toolsVersion = "0.7.5" + val toolsVersion = "0.7.6" val kotlinVersion = "1.4.30" plugins { From 1970243785e67cb583e7be5b513f3e42089f145e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 18:15:54 +0300 Subject: [PATCH 54/59] Remove KClass from dataforge-data --- .../hep/dataforge/context/ContextBuilder.kt | 11 ++++- .../kotlin/hep/dataforge/context/resolve.kt | 2 +- .../dataforge/properties/ConfigProperty.kt | 2 +- .../hep/dataforge/properties/Property.kt | 2 +- .../hep/dataforge/properties/bindings.kt | 2 +- .../hep/dataforge/descriptors/annotations.kt | 1 - .../kotlin/hep/dataforge/provider/dfType.kt | 2 +- .../kotlin/hep/dataforge/actions/Action.kt | 3 +- .../kotlin/hep/dataforge/actions/MapAction.kt | 17 +++++-- .../hep/dataforge/actions/ReduceAction.kt | 34 ++++++++------ .../hep/dataforge/actions/SplitAction.kt | 22 ++++++++- .../hep/dataforge/data/CachingAction.kt | 1 - .../hep/dataforge/data/CoroutineMonitor.kt | 2 +- .../kotlin/hep/dataforge/data/Data.kt | 23 +++++++--- .../kotlin/hep/dataforge/data/DataSet.kt | 5 +- .../hep/dataforge/data/DataSetBuilder.kt | 26 ++++++----- .../kotlin/hep/dataforge/data/DataTree.kt | 15 +++--- .../kotlin/hep/dataforge/data/Goal.kt | 2 +- .../kotlin/hep/dataforge/data/GroupRule.kt | 10 ++-- .../dataforge/{actions => data}/NamedData.kt | 4 +- .../kotlin/hep/dataforge/data/dataFilter.kt | 12 ++--- .../hep/dataforge/data/dataTransform.kt | 46 ++++++++----------- .../kotlin/hep/dataforge/data/select.kt | 4 +- .../kotlin/hep/dataforge/data/ActionsTest.kt | 17 +++---- .../hep/dataforge/data/DataTreeBuilderTest.kt | 19 ++++---- .../io/yaml/FrontMatterEnvelopeFormat.kt | 2 +- .../hep/dataforge/io/yaml/YamlMetaFormat.kt | 1 + .../dataforge/io/yaml/YamlMetaFormatTest.kt | 2 +- .../kotlin/hep/dataforge/io/Consumer.kt | 2 +- .../kotlin/hep/dataforge/io/MultipartTest.kt | 2 +- .../jvmMain/kotlin/hep/dataforge/io/fileIO.kt | 2 +- .../kotlin/hep/dataforge/io/FileBinaryTest.kt | 2 +- .../hep/dataforge/io/FileEnvelopeTest.kt | 2 +- .../kotlin/hep/dataforge/meta/Configurable.kt | 1 + .../kotlin/hep/dataforge/meta/MetaBuilder.kt | 1 + .../kotlin/hep/dataforge/meta/MutableMeta.kt | 1 + .../kotlin/hep/dataforge/meta/annotations.kt | 11 ----- .../meta/descriptors/ItemDescriptor.kt | 1 + .../kotlin/hep/dataforge/meta/mapMeta.kt | 1 + .../transformations/MetaTransformation.kt | 1 + .../kotlin/hep/dataforge/misc/annotations.kt | 21 +++++++++ .../kotlin/hep/dataforge/names/Name.kt | 3 +- .../kotlin/hep/dataforge/names/nameMatcher.kt | 2 +- .../kotlin/hep/dataforge/meta/MetaTest.kt | 1 + .../hep/dataforge/names/NameMatchTest.kt | 2 +- .../dataforge/tables/io/textTableEnvelope.kt | 1 + .../hep/dataforge/tables/io/TextRowsTest.kt | 2 +- .../kotlin/hep/dataforge/workspace/Task.kt | 12 +++-- .../hep/dataforge/workspace/TaskData.kt | 2 +- .../dataforge/workspace/WorkspaceBuilder.kt | 15 ++---- .../hep/dataforge/workspace/envelopeData.kt | 7 ++- .../hep/dataforge/workspace/fileData.kt | 7 +-- .../hep/dataforge/workspace/FileDataTest.kt | 2 +- .../workspace/SimpleWorkspaceTest.kt | 2 +- 54 files changed, 223 insertions(+), 172 deletions(-) rename dataforge-data/src/commonMain/kotlin/hep/dataforge/{actions => data}/NamedData.kt (89%) delete mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/annotations.kt create mode 100644 dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/annotations.kt diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt index 69a68026..f124d3d2 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt @@ -1,7 +1,16 @@ package hep.dataforge.context -import hep.dataforge.meta.* +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.seal +import hep.dataforge.misc.DFBuilder +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.toName +import kotlin.collections.HashMap +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.forEach +import kotlin.collections.set /** * A convenience builder for context diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt index bfc4e22d..b41e11fd 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt @@ -1,6 +1,6 @@ package hep.dataforge.context -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.Name import hep.dataforge.names.plus import hep.dataforge.provider.Provider diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/ConfigProperty.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/ConfigProperty.kt index 98a43b69..86c6bcde 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/ConfigProperty.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/ConfigProperty.kt @@ -1,12 +1,12 @@ package hep.dataforge.properties import hep.dataforge.meta.Config -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.get import hep.dataforge.meta.set import hep.dataforge.meta.transformations.MetaConverter import hep.dataforge.meta.transformations.nullableItemToObject import hep.dataforge.meta.transformations.nullableObjectToMetaItem +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.Name @DFExperimental diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/Property.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/Property.kt index 987cfe4c..44e97ea9 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/Property.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/properties/Property.kt @@ -1,6 +1,6 @@ package hep.dataforge.properties -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow diff --git a/dataforge-context/src/jsMain/kotlin/hep/dataforge/properties/bindings.kt b/dataforge-context/src/jsMain/kotlin/hep/dataforge/properties/bindings.kt index 7ef8d72d..86296e39 100644 --- a/dataforge-context/src/jsMain/kotlin/hep/dataforge/properties/bindings.kt +++ b/dataforge-context/src/jsMain/kotlin/hep/dataforge/properties/bindings.kt @@ -1,6 +1,6 @@ package hep.dataforge.properties -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import org.w3c.dom.HTMLInputElement @DFExperimental diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/descriptors/annotations.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/descriptors/annotations.kt index cadd4231..f88cee99 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/descriptors/annotations.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/descriptors/annotations.kt @@ -16,7 +16,6 @@ package hep.dataforge.descriptors -import hep.dataforge.meta.DFExperimental import hep.dataforge.values.ValueType import kotlin.reflect.KClass diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt index 65817b64..6f7855a9 100644 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt @@ -2,7 +2,7 @@ package hep.dataforge.provider import hep.dataforge.context.Context import hep.dataforge.context.gather -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import hep.dataforge.misc.Type import hep.dataforge.names.Name import kotlin.reflect.KClass diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/Action.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/Action.kt index a7c51e8f..85ec2977 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/Action.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/Action.kt @@ -1,10 +1,9 @@ package hep.dataforge.actions import hep.dataforge.data.DataSet -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta +import hep.dataforge.misc.DFExperimental import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* /** * A simple data transformation on a data node. Actions should avoid doing actual dependency evaluation in [execute]. diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt index 77acc6a4..23731621 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/MapAction.kt @@ -1,7 +1,13 @@ package hep.dataforge.actions import hep.dataforge.data.* -import hep.dataforge.meta.* +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.seal +import hep.dataforge.meta.toMutableMeta +import hep.dataforge.misc.DFBuilder +import hep.dataforge.misc.DFExperimental +import hep.dataforge.misc.DFInternal import hep.dataforge.names.Name import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collect @@ -34,7 +40,6 @@ public class MapActionBuilder(public var name: Name, public var meta: Meta } } - @PublishedApi internal class MapAction( private val outputType: KType, @@ -63,7 +68,9 @@ internal class MapAction( //getting new meta val newMeta = builder.meta.seal() - val newData = data.map(outputType, meta = newMeta) { builder.result(env, it) } + @OptIn(DFInternal::class) val newData = Data(outputType, newMeta, dependencies = listOf(data)) { + builder.result(env, data.await()) + } //setting the data node return newData.named(newName) } @@ -85,6 +92,10 @@ internal class MapAction( } +/** + * A one-to-one mapping action + */ +@DFExperimental @Suppress("FunctionName") public inline fun Action.Companion.map( noinline builder: MapActionBuilder.() -> Unit, diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt index f3cf9fdc..8e2781b5 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/ReduceAction.kt @@ -1,20 +1,21 @@ package hep.dataforge.actions import hep.dataforge.data.* -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder +import hep.dataforge.misc.DFBuilder +import hep.dataforge.misc.DFExperimental +import hep.dataforge.misc.DFInternal import hep.dataforge.names.Name import hep.dataforge.names.toName import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.fold -import kotlin.reflect.KClass import kotlin.reflect.KType +import kotlin.reflect.typeOf -@DFExperimental public class JoinGroup(public var name: String, internal val set: DataSet) { public var meta: MetaBuilder = MetaBuilder() @@ -27,9 +28,9 @@ public class JoinGroup(public var name: String, internal val s } -@DFExperimental +@DFBuilder public class ReduceGroupBuilder( - private val inputType: KClass, + private val inputType: KType, private val scope: CoroutineScope, public val actionMeta: Meta, ) { @@ -40,7 +41,7 @@ public class ReduceGroupBuilder( */ public fun byValue(tag: String, defaultTag: String = "@default", action: JoinGroup.() -> Unit) { groupRules += { node -> - GroupRule.byMetaValue(scope, tag, defaultTag).gather(inputType, node).map { + GroupRule.byMetaValue(scope, tag, defaultTag).gather(node).map { JoinGroup(it.key, it.value).apply(action) } } @@ -73,16 +74,17 @@ public class ReduceGroupBuilder( } -@DFExperimental -public class ReduceAction( - private val inputType: KClass, +@PublishedApi +internal class ReduceAction( + private val inputType: KType, outputType: KType, private val action: ReduceGroupBuilder.() -> Unit, ) : CachingAction(outputType) { //TODO optimize reduction. Currently the whole action recalculates on push + override fun CoroutineScope.transform(set: DataSet, meta: Meta, key: Name): Flow> = flow { - ReduceGroupBuilder(inputType,this@transform, meta).apply(action).buildGroups(set).forEach { group -> + ReduceGroupBuilder(inputType, this@transform, meta).apply(action).buildGroups(set).forEach { group -> val dataFlow: Map> = group.set.flow().fold(HashMap()) { acc, value -> acc.apply { acc[value.name] = value.data @@ -94,8 +96,7 @@ public class ReduceAction( val groupMeta = group.meta val env = ActionEnv(groupName.toName(), groupMeta, meta) - - val res: LazyData = dataFlow.reduceToData( + @OptIn(DFInternal::class) val res: Data = dataFlow.reduceToData( outputType, meta = groupMeta ) { group.result.invoke(env, it) } @@ -105,4 +106,11 @@ public class ReduceAction( } } -public operator fun Map.get(name: String): T? = get(name.toName()) +/** + * A one-to-one mapping action + */ +@DFExperimental +@Suppress("FunctionName") +public inline fun Action.Companion.reduce( + noinline builder: ReduceGroupBuilder.() -> Unit, +): Action = ReduceAction(typeOf(), typeOf(), builder) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt index c659d5a1..37b8f734 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/SplitAction.kt @@ -5,13 +5,17 @@ import hep.dataforge.meta.Laminate import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.toMutableMeta +import hep.dataforge.misc.DFExperimental +import hep.dataforge.misc.DFInternal import hep.dataforge.names.Name import hep.dataforge.names.toName import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlin.collections.set import kotlin.reflect.KType +import kotlin.reflect.typeOf public class SplitBuilder(public val name: Name, public val meta: Meta) { @@ -39,11 +43,13 @@ public class SplitBuilder(public val name: Name, public val me /** * Action that splits each incoming element into a number of fragments defined in builder */ +@PublishedApi internal class SplitAction( private val outputType: KType, private val action: SplitBuilder.() -> Unit, ) : Action { + @OptIn(FlowPreview::class) override suspend fun execute( dataSet: DataSet, meta: Meta, @@ -59,7 +65,10 @@ internal class SplitAction( // apply individual fragment rules to result return split.fragments.entries.asFlow().map { (fragmentName, rule) -> val env = SplitBuilder.FragmentRule(fragmentName, laminate.toMutableMeta()).apply(rule) - data.map(outputType, meta = env.meta) { env.result(it) }.named(fragmentName) + //data.map(outputType, meta = env.meta) { env.result(it) }.named(fragmentName) + @OptIn(DFInternal::class) Data(outputType, meta = env.meta, dependencies = listOf(data)) { + env.result(data.await()) + }.named(fragmentName) } } @@ -75,4 +84,13 @@ internal class SplitAction( } } } -} \ No newline at end of file +} + +/** + * Action that splits each incoming element into a number of fragments defined in builder + */ +@DFExperimental +@Suppress("FunctionName") +public inline fun Action.Companion.split( + noinline builder: SplitBuilder.() -> Unit, +): Action = SplitAction(typeOf(), builder) \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt index c4fb4f90..7911cb1f 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CachingAction.kt @@ -1,7 +1,6 @@ package hep.dataforge.data import hep.dataforge.actions.Action -import hep.dataforge.actions.NamedData import hep.dataforge.meta.Meta import hep.dataforge.names.Name import hep.dataforge.names.startsWith diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CoroutineMonitor.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CoroutineMonitor.kt index d1c0d55e..60bf5775 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CoroutineMonitor.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/CoroutineMonitor.kt @@ -1,6 +1,6 @@ package hep.dataforge.data -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlin.coroutines.CoroutineContext diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index 0a768ee4..6702de4d 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -3,6 +3,7 @@ package hep.dataforge.data import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.isEmpty +import hep.dataforge.misc.DFInternal import hep.dataforge.misc.Type import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext @@ -35,17 +36,21 @@ public interface Data : Goal, MetaRepr { public companion object { public const val TYPE: String = "data" + /** + * The type that can't have any subtypes + */ + internal val TYPE_OF_NOTHING: KType = typeOf() + public inline fun static( value: T, meta: Meta = Meta.EMPTY, - ): Data = StaticData(typeOf(),value, meta) + ): Data = StaticData(typeOf(), value, meta) /** * An empty data containing only meta */ public fun empty(meta: Meta): Data = object : Data { - private val nothing: Nothing get() = error("this is nothing") - override val type: KType = this::nothing.returnType + override val type: KType = TYPE_OF_NOTHING override val meta: Meta = meta override val dependencies: Collection> = emptyList() override val deferred: Deferred @@ -59,13 +64,17 @@ public interface Data : Goal, MetaRepr { } } -public class LazyData( +/** + * A lazily computed variant of [Data] based on [LazyGoal] + * One must ensure that proper [type] is used so this method should not be used + */ +private class LazyData( override val type: KType, override val meta: Meta = Meta.EMPTY, - context: CoroutineContext = EmptyCoroutineContext, + additionalContext: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), block: suspend () -> T, -) : Data, LazyGoal(context, dependencies, block) +) : Data, LazyGoal(additionalContext, dependencies, block) public class StaticData( override val type: KType, @@ -74,6 +83,7 @@ public class StaticData( ) : Data, StaticGoal(value) @Suppress("FunctionName") +@DFInternal public fun Data( type: KType, meta: Meta = Meta.EMPTY, @@ -82,6 +92,7 @@ public fun Data( block: suspend () -> T, ): Data = LazyData(type, meta, context, dependencies, block) +@OptIn(DFInternal::class) @Suppress("FunctionName") public inline fun Data( meta: Meta = Meta.EMPTY, diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt index 129f657f..f2a4bf88 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSet.kt @@ -1,7 +1,6 @@ package hep.dataforge.data -import hep.dataforge.actions.NamedData -import hep.dataforge.actions.named +import hep.dataforge.data.Data.Companion.TYPE_OF_NOTHING import hep.dataforge.meta.Meta import hep.dataforge.meta.set import hep.dataforge.names.* @@ -43,7 +42,7 @@ public interface DataSet { * An empty [DataSet] that suits all types */ public val EMPTY: DataSet = object : DataSet { - override val dataType: KType = this::nothing.returnType + override val dataType: KType = TYPE_OF_NOTHING private val nothing: Nothing get() = error("this is nothing") diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt index f001c4ad..95e9fd06 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataSetBuilder.kt @@ -1,17 +1,19 @@ package hep.dataforge.data -import hep.dataforge.actions.NamedData -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.Name import hep.dataforge.names.plus import hep.dataforge.names.toName import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect +import kotlin.reflect.KType public interface DataSetBuilder { + public val dataType: KType + /** * Remove all data items starting with [name] */ @@ -50,8 +52,12 @@ public interface DataSetBuilder { public suspend infix fun String.put(block: suspend DataSetBuilder.() -> Unit): Unit = emit(toName(), block) } -private class SubSetBuilder(private val parent: DataSetBuilder, private val branch: Name) : - DataSetBuilder { +private class SubSetBuilder( + private val parent: DataSetBuilder, + private val branch: Name, +) : DataSetBuilder { + override val dataType: KType get() = parent.dataType + override suspend fun remove(name: Name) { parent.remove(branch + name) } @@ -101,7 +107,7 @@ public suspend inline fun DataSetBuilder.produce( name: Name, meta: Meta = Meta.EMPTY, noinline producer: suspend () -> T, -){ +) { val data = Data(meta, block = producer) emit(name, data) } @@ -109,19 +115,17 @@ public suspend inline fun DataSetBuilder.produce( /** * Emit a static data with the fixed value */ -public suspend fun DataSetBuilder.static(name: String, data: T, meta: Meta = Meta.EMPTY): Unit = +public suspend inline fun DataSetBuilder.static(name: String, data: T, meta: Meta = Meta.EMPTY): Unit = emit(name, Data.static(data, meta)) -public suspend fun DataSetBuilder.static(name: Name, data: T, meta: Meta = Meta.EMPTY): Unit = +public suspend inline fun DataSetBuilder.static(name: Name, data: T, meta: Meta = Meta.EMPTY): Unit = emit(name, Data.static(data, meta)) -public suspend fun DataSetBuilder.static( +public suspend inline fun DataSetBuilder.static( name: String, data: T, metaBuilder: MetaBuilder.() -> Unit, -) { - emit(name.toName(), Data.static(data, Meta(metaBuilder))) -} +): Unit = emit(name.toName(), Data.static(data, Meta(metaBuilder))) /** * Update data with given node data and meta with node meta. diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt index 0354b161..5676eeed 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataTree.kt @@ -1,22 +1,21 @@ package hep.dataforge.data -import hep.dataforge.actions.NamedData -import hep.dataforge.actions.named -import hep.dataforge.meta.* import hep.dataforge.misc.Type import hep.dataforge.names.* -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map import kotlin.collections.component1 import kotlin.collections.component2 -import kotlin.reflect.KClass +import kotlin.reflect.KType public sealed class DataTreeItem { public class Node(public val tree: DataTree) : DataTreeItem() public class Leaf(public val data: Data) : DataTreeItem() } -public val DataTreeItem.type: KClass +public val DataTreeItem.type: KType get() = when (this) { is DataTreeItem.Node -> tree.dataType is DataTreeItem.Leaf -> data.type @@ -91,7 +90,7 @@ public fun DataTree.itemFlow(): Flow>> = * The difference from similar method for [DataSet] is that internal logic is more simple and the return value is a [DataTree] */ public fun DataTree.branch(branchName: Name): DataTree = object : DataTree { - override val dataType: KClass get() = this@branch.dataType + override val dataType: KType get() = this@branch.dataType override suspend fun items(): Map> = getItem(branchName).tree?.items() ?: emptyMap() } diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt index eef9e7cb..7763c3ce 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt @@ -1,6 +1,6 @@ package hep.dataforge.data -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt index 414fa17e..fcaa0658 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt @@ -20,10 +20,9 @@ import hep.dataforge.meta.string import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import kotlin.reflect.KClass public interface GroupRule { - public suspend fun gather(dataType: KClass, set: DataSet): Map> + public suspend fun gather(set: DataSet): Map> public companion object { /** @@ -41,21 +40,20 @@ public interface GroupRule { ): GroupRule = object : GroupRule { override suspend fun gather( - dataType: KClass, set: DataSet, ): Map> { val map = HashMap>() set.flow().collect { data -> val tagValue = data.meta[key]?.string ?: defaultTagValue - map.getOrPut(tagValue) { ActiveDataTree(dataType) }.emit(data.name, data.data) + map.getOrPut(tagValue) { ActiveDataTree(set.dataType) }.emit(data.name, data.data) } scope.launch { set.updates.collect { name -> val data = set.getData(name) val tagValue = data?.meta[key]?.string ?: defaultTagValue - map.getOrPut(tagValue) { ActiveDataTree(dataType) }.emit(name, data) + map.getOrPut(tagValue) { ActiveDataTree(set.dataType) }.emit(name, data) } } @@ -63,4 +61,4 @@ public interface GroupRule { } } } -} +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/NamedData.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt similarity index 89% rename from dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/NamedData.kt rename to dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt index 126c972b..aa5afcdc 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/actions/NamedData.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/NamedData.kt @@ -1,7 +1,5 @@ -package hep.dataforge.actions +package hep.dataforge.data -import hep.dataforge.data.Data -import hep.dataforge.data.StaticData import hep.dataforge.meta.isEmpty import hep.dataforge.misc.Named import hep.dataforge.names.Name diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt index aff59e61..8f38d100 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataFilter.kt @@ -1,14 +1,12 @@ package hep.dataforge.data -import hep.dataforge.actions.NamedData -import hep.dataforge.actions.named -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull -import kotlin.reflect.KClass +import kotlin.reflect.KType /** @@ -17,7 +15,7 @@ import kotlin.reflect.KClass public fun DataSet.filter( predicate: suspend (Name, Data) -> Boolean, ): ActiveDataSet = object : ActiveDataSet { - override val dataType: KClass get() = this@filter.dataType + override val dataType: KType get() = this@filter.dataType override fun flow(): Flow> = this@filter.flow().filter { predicate(it.name, it.data) } @@ -37,7 +35,7 @@ public fun DataSet.filter( */ public fun DataSet.withNamePrefix(prefix: Name): DataSet = if (prefix.isEmpty()) this else object : ActiveDataSet { - override val dataType: KClass get() = this@withNamePrefix.dataType + override val dataType: KType get() = this@withNamePrefix.dataType override fun flow(): Flow> = this@withNamePrefix.flow().map { it.data.named(prefix + it.name) } @@ -53,7 +51,7 @@ else object : ActiveDataSet { public fun DataSet.branch(branchName: Name): DataSet = if (branchName.isEmpty()) { this } else object : ActiveDataSet { - override val dataType: KClass get() = this@branch.dataType + override val dataType: KType get() = this@branch.dataType override fun flow(): Flow> = this@branch.flow().mapNotNull { it.name.removeHeadOrNull(branchName)?.let { name -> diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt index fdc24c1c..8b0c7787 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataTransform.kt @@ -1,11 +1,10 @@ package hep.dataforge.data -import hep.dataforge.actions.NamedData -import hep.dataforge.actions.named import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.seal import hep.dataforge.meta.toMutableMeta +import hep.dataforge.misc.DFInternal import kotlinx.coroutines.flow.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -20,23 +19,11 @@ import kotlin.reflect.typeOf * @param meta for the resulting data. By default equals input data. * @param block the transformation itself */ -private fun Data.map( - outputType: KType, - coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = this.meta, - block: suspend (T) -> R, -): LazyData = LazyData(outputType, meta, coroutineContext, listOf(this)) { - block(await()) -} - -/** - * See [map] - */ public inline fun Data.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, crossinline block: suspend (T) -> R, -): LazyData = LazyData(typeOf(), meta, coroutineContext, listOf(this)) { +): Data = Data(meta, coroutineContext, listOf(this)) { block(await()) } @@ -48,7 +35,7 @@ public inline fun Data.combine( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, crossinline block: suspend (left: T1, right: T2) -> R, -): LazyData = LazyData(typeOf(), meta, coroutineContext, listOf(this, other)) { +): Data = Data(meta, coroutineContext, listOf(this, other)) { block(await(), other.await()) } @@ -62,8 +49,7 @@ public inline fun Collection>.reduceToData( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, crossinline block: suspend (Collection) -> R, -): LazyData = LazyData( - typeOf(), +): Data = Data( meta, coroutineContext, this @@ -71,12 +57,13 @@ public inline fun Collection>.reduceToData( block(map { it.await() }) } +@DFInternal public fun Map>.reduceToData( outputType: KType, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, block: suspend (Map) -> R, -): LazyData = LazyData( +): Data = Data( outputType, meta, coroutineContext, @@ -96,8 +83,7 @@ public inline fun Map>.reduceToData( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, noinline block: suspend (Map) -> R, -): LazyData = LazyData( - typeOf(), +): Data = Data( meta, coroutineContext, this.values @@ -110,12 +96,13 @@ public inline fun Map>.reduceToData( /** * Transform a [Flow] of [NamedData] to a single [Data]. */ +@DFInternal public suspend fun Flow>.reduceToData( outputType: KType, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, transformation: suspend (Flow>) -> R, -): LazyData = LazyData( +): Data = Data( outputType, meta, coroutineContext, @@ -124,11 +111,12 @@ public suspend fun Flow>.reduceToData( transformation(this) } +@OptIn(DFInternal::class) public suspend inline fun Flow>.reduceToData( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, noinline transformation: suspend (Flow>) -> R, -): LazyData = reduceToData(typeOf(), coroutineContext, meta) { +): Data = reduceToData(typeOf(), coroutineContext, meta) { transformation(it) } @@ -140,7 +128,7 @@ public suspend inline fun Flow>.foldToDa coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, noinline block: suspend (result: R, data: NamedData) -> R, -): LazyData = reduceToData( +): Data = reduceToData( coroutineContext, meta ) { it.fold(initial, block) @@ -148,6 +136,7 @@ public suspend inline fun Flow>.foldToDa //DataSet operations +@DFInternal public suspend fun DataSet.map( outputType: KType, coroutineContext: CoroutineContext = EmptyCoroutineContext, @@ -157,11 +146,14 @@ public suspend fun DataSet.map( populate( flow().map { val newMeta = it.meta.toMutableMeta().apply(metaTransform).seal() - it.map(outputType, coroutineContext, newMeta, block).named(it.name) + Data(outputType, newMeta, coroutineContext, listOf(it)) { + block(it.await()) + }.named(it.name) } ) } +@OptIn(DFInternal::class) public suspend inline fun DataSet.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, noinline metaTransform: MetaBuilder.() -> Unit = {}, @@ -179,11 +171,11 @@ public suspend inline fun DataSet.reduceToData( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, noinline transformation: suspend (Flow>) -> R, -): LazyData = flow().reduceToData(coroutineContext, meta, transformation) +): Data = flow().reduceToData(coroutineContext, meta, transformation) public suspend inline fun DataSet.foldToData( initial: R, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = Meta.EMPTY, noinline block: suspend (result: R, data: NamedData) -> R, -): LazyData = flow().foldToData(initial, coroutineContext, meta, block) \ No newline at end of file +): Data = flow().foldToData(initial, coroutineContext, meta, block) \ No newline at end of file diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt index def3133a..9df89f84 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt @@ -1,8 +1,6 @@ package hep.dataforge.data -import hep.dataforge.actions.NamedData -import hep.dataforge.actions.named -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.Name import hep.dataforge.names.matches import hep.dataforge.names.toName diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt index 81049612..0da9c2c2 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/ActionsTest.kt @@ -1,15 +1,12 @@ package hep.dataforge.data -import hep.dataforge.actions.MapAction +import hep.dataforge.actions.Action +import hep.dataforge.actions.map import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Test import kotlin.test.assertEquals -/** - * Block the thread and get data content - */ -public fun Data.value(): T = runBlocking { await() } - +@Suppress("EXPERIMENTAL_API_USAGE") class ActionsTest { val data: DataTree = runBlocking { DataTree { @@ -21,23 +18,23 @@ class ActionsTest { @Test fun testStaticMapAction() { - val plusOne = MapAction { + val plusOne = Action.map { result { it + 1 } } runBlocking { val result = plusOne.execute(data) - assertEquals(2, result.getData("1")?.value()) + assertEquals(2, result.getData("1")?.await()) } } @Test fun testDynamicMapAction() { - val plusOne = MapAction { + val plusOne = Action.map { result { it + 1 } } val datum = runBlocking { val result = plusOne.execute(data, scope = this) - result.getData("1")?.value() + result.getData("1")?.await() } assertEquals(2, datum) } diff --git a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt index cf34cbb8..0225e9cf 100644 --- a/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/hep/dataforge/data/DataTreeBuilderTest.kt @@ -18,10 +18,12 @@ internal class DataTreeBuilderTest { static("c.d", "c.d") static("c.f", "c.f") } - assertEquals("a", node.getData("primary.a")?.value()) - assertEquals("b", node.getData("primary.b")?.value()) - assertEquals("c.d", node.getData("c.d")?.value()) - assertEquals("c.f", node.getData("c.f")?.value()) + runBlocking { + assertEquals("a", node.getData("primary.a")?.await()) + assertEquals("b", node.getData("primary.b")?.await()) + assertEquals("c.d", node.getData("c.d")?.await()) + assertEquals("c.f", node.getData("c.f")?.await()) + } } @Test @@ -42,9 +44,10 @@ internal class DataTreeBuilderTest { populate(updateData) } - - assertEquals("a", node.getData("update.a")?.value()) - assertEquals("a", node.getData("primary.a")?.value()) + runBlocking { + assertEquals("a", node.getData("update.a")?.await()) + assertEquals("a", node.getData("primary.a")?.await()) + } } @Test @@ -76,7 +79,7 @@ internal class DataTreeBuilderTest { } } updateJob.join() - assertEquals(9, rootNode.getData("sub.value")?.value()) + assertEquals(9, rootNode.getData("sub.value")?.await()) cancel() } } catch (t: Throwable) { diff --git a/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt b/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt index 69100422..9a6d38f8 100644 --- a/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt @@ -4,8 +4,8 @@ import hep.dataforge.context.Context import hep.dataforge.io.* import hep.dataforge.io.IOFormat.Companion.META_KEY import hep.dataforge.io.IOFormat.Companion.NAME_KEY -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta +import hep.dataforge.misc.DFExperimental import kotlinx.io.* import kotlinx.io.text.readUtf8Line import kotlinx.io.text.writeUtf8String diff --git a/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt b/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt index 160683a6..9f5cdda3 100644 --- a/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/commonMain/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt @@ -8,6 +8,7 @@ import hep.dataforge.io.MetaFormatFactory import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.ItemDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.NameToken import hep.dataforge.names.withIndex import hep.dataforge.values.ListValue diff --git a/dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt b/dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt index 83f6d3a6..d63da939 100644 --- a/dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt +++ b/dataforge-io/dataforge-io-yaml/src/commonTest/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt @@ -2,10 +2,10 @@ package hep.dataforge.io.yaml import hep.dataforge.io.parse import hep.dataforge.io.toString -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.meta.get import hep.dataforge.meta.seal +import hep.dataforge.misc.DFExperimental import kotlin.test.Test import kotlin.test.assertEquals diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Consumer.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Consumer.kt index 0ef5d327..51ec6250 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Consumer.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/Consumer.kt @@ -1,6 +1,6 @@ package hep.dataforge.io -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental /** * A fire-and-forget consumer of messages diff --git a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MultipartTest.kt b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MultipartTest.kt index 1b239edd..e511a472 100644 --- a/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MultipartTest.kt +++ b/dataforge-io/src/commonTest/kotlin/hep/dataforge/io/MultipartTest.kt @@ -1,9 +1,9 @@ package hep.dataforge.io import hep.dataforge.context.Global -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.get import hep.dataforge.meta.int +import hep.dataforge.misc.DFExperimental import kotlinx.io.text.writeUtf8String import kotlin.test.Test import kotlin.test.assertEquals diff --git a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt index 2f644878..54cf6b2c 100644 --- a/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt +++ b/dataforge-io/src/jvmMain/kotlin/hep/dataforge/io/fileIO.kt @@ -1,9 +1,9 @@ package hep.dataforge.io -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.isEmpty +import hep.dataforge.misc.DFExperimental import kotlinx.io.* import java.nio.file.Files import java.nio.file.Path diff --git a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileBinaryTest.kt b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileBinaryTest.kt index 4971bf6c..8bfefd6d 100644 --- a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileBinaryTest.kt +++ b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileBinaryTest.kt @@ -1,7 +1,7 @@ package hep.dataforge.io import hep.dataforge.context.Global -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import kotlinx.io.asBinary import kotlinx.io.toByteArray import kotlinx.io.writeDouble diff --git a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileEnvelopeTest.kt b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileEnvelopeTest.kt index ab96c324..fba3ed8e 100644 --- a/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileEnvelopeTest.kt +++ b/dataforge-io/src/jvmTest/kotlin/hep/dataforge/io/FileEnvelopeTest.kt @@ -1,7 +1,7 @@ package hep.dataforge.io import hep.dataforge.context.Global -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import kotlinx.io.writeDouble import java.nio.file.Files import kotlin.test.Test diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Configurable.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Configurable.kt index 764ee1ea..429ac360 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Configurable.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Configurable.kt @@ -1,5 +1,6 @@ package hep.dataforge.meta +import hep.dataforge.misc.DFBuilder import hep.dataforge.names.Name import kotlin.properties.ReadWriteProperty diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt index f2016b80..4fe5de9b 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MetaBuilder.kt @@ -1,5 +1,6 @@ package hep.dataforge.meta +import hep.dataforge.misc.DFBuilder import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.values.EnumValue diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt index f5353cbd..4524f11e 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/MutableMeta.kt @@ -1,5 +1,6 @@ package hep.dataforge.meta +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.* public interface MutableMeta> : TypedMeta, MutableItemProvider { diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/annotations.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/annotations.kt deleted file mode 100644 index 69ffa8aa..00000000 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/annotations.kt +++ /dev/null @@ -1,11 +0,0 @@ -package hep.dataforge.meta - -/** - * General marker for dataforge builders - */ -@DslMarker -public annotation class DFBuilder - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Retention(AnnotationRetention.BINARY) -public annotation class DFExperimental \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt index e03c832a..33343fe1 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/descriptors/ItemDescriptor.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta.descriptors import hep.dataforge.meta.* +import hep.dataforge.misc.DFBuilder import hep.dataforge.names.* import hep.dataforge.values.* diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt index d223ce99..371a67a0 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/mapMeta.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.toName import hep.dataforge.values.ListValue import hep.dataforge.values.Value diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt index 710998ef..0e41b016 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/transformations/MetaTransformation.kt @@ -1,6 +1,7 @@ package hep.dataforge.meta.transformations import hep.dataforge.meta.* +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.Name /** diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/annotations.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/annotations.kt new file mode 100644 index 00000000..7b271a4b --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/misc/annotations.kt @@ -0,0 +1,21 @@ +package hep.dataforge.misc + +/** + * General marker for dataforge builders + */ +@DslMarker +public annotation class DFBuilder + +/** + * The declaration is experimental and could be changed in future + */ +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) +@Retention(AnnotationRetention.BINARY) +public annotation class DFExperimental + +/** + * The declaration is internal to the DataForge and could use unsafe or unstable features. + */ +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) +@Retention(AnnotationRetention.BINARY) +public annotation class DFInternal \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt index 5d9cc7bb..3bb24ab1 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/Name.kt @@ -1,6 +1,6 @@ package hep.dataforge.names -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.PrimitiveKind @@ -186,7 +186,6 @@ public fun Name.withIndex(index: String): Name { * Fast [String]-based accessor for item map */ public operator fun Map.get(body: String, query: String? = null): T? = get(NameToken(body, query)) - public operator fun Map.get(name: String): T? = get(name.toName()) public operator fun MutableMap.set(name: String, value: T): Unit = set(name.toName(), value) diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/nameMatcher.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/nameMatcher.kt index 363109f1..5b786917 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/nameMatcher.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/names/nameMatcher.kt @@ -1,6 +1,6 @@ package hep.dataforge.names -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental /** diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaTest.kt index 12569bc3..89786ea1 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/meta/MetaTest.kt @@ -1,5 +1,6 @@ package hep.dataforge.meta +import hep.dataforge.misc.DFExperimental import hep.dataforge.values.NumberValue import hep.dataforge.values.True import hep.dataforge.values.Value diff --git a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameMatchTest.kt b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameMatchTest.kt index b91d94a6..1ff4381f 100644 --- a/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameMatchTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/hep/dataforge/names/NameMatchTest.kt @@ -1,6 +1,6 @@ package hep.dataforge.names -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import kotlin.test.Test import kotlin.test.assertFails import kotlin.test.assertFalse diff --git a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/textTableEnvelope.kt b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/textTableEnvelope.kt index 4deb84e6..b6be63e0 100644 --- a/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/textTableEnvelope.kt +++ b/dataforge-tables/src/commonMain/kotlin/hep/dataforge/tables/io/textTableEnvelope.kt @@ -2,6 +2,7 @@ package hep.dataforge.tables.io import hep.dataforge.io.Envelope import hep.dataforge.meta.* +import hep.dataforge.misc.DFExperimental import hep.dataforge.tables.SimpleColumnHeader import hep.dataforge.tables.Table import hep.dataforge.values.Value diff --git a/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt b/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt index d97484ed..90fa3a01 100644 --- a/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt +++ b/dataforge-tables/src/jvmTest/kotlin/hep/dataforge/tables/io/TextRowsTest.kt @@ -1,6 +1,6 @@ package hep.dataforge.tables.io -import hep.dataforge.meta.DFExperimental +import hep.dataforge.misc.DFExperimental import hep.dataforge.tables.Table import hep.dataforge.tables.get import hep.dataforge.tables.row diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt index df3c16af..6e6c590e 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Task.kt @@ -6,11 +6,13 @@ import hep.dataforge.data.GoalExecutionRestriction import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.Described import hep.dataforge.meta.descriptors.ItemDescriptor +import hep.dataforge.misc.DFInternal import hep.dataforge.misc.Type import hep.dataforge.names.Name import hep.dataforge.workspace.Task.Companion.TYPE import kotlinx.coroutines.withContext -import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf @Type(TYPE) public interface Task : Described { @@ -42,8 +44,9 @@ public class TaskResultBuilder( * Data dependency cycles are not allowed. */ @Suppress("FunctionName") +@DFInternal public fun Task( - resultType: KClass, + resultType: KType, descriptor: ItemDescriptor? = null, builder: suspend TaskResultBuilder.() -> Unit, ): Task = object : Task { @@ -56,15 +59,16 @@ public fun Task( taskMeta: Meta, ): TaskResult = withContext(GoalExecutionRestriction() + workspace.goalLogger) { //TODO use safe builder and check for external data on add and detects cycles - val dataset = DataTree(resultType) { + val dataset = DataTree(resultType) { TaskResultBuilder(workspace,taskName, taskMeta, this).apply { builder() } } workspace.internalize(dataset, taskName, taskMeta) } } +@OptIn(DFInternal::class) @Suppress("FunctionName") public inline fun Task( descriptor: ItemDescriptor? = null, noinline builder: suspend TaskResultBuilder.() -> Unit, -): Task = Task(T::class, descriptor, builder) \ No newline at end of file +): Task = Task(typeOf(), descriptor, builder) \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt index 37862a48..f317b00a 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/TaskData.kt @@ -1,7 +1,7 @@ package hep.dataforge.workspace -import hep.dataforge.actions.NamedData import hep.dataforge.data.Data +import hep.dataforge.data.NamedData import hep.dataforge.meta.Meta import hep.dataforge.names.Name diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt index aa3519a3..cd3e6d92 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspaceBuilder.kt @@ -7,16 +7,15 @@ import hep.dataforge.data.ActiveDataTree import hep.dataforge.data.DataSet import hep.dataforge.data.DataSetBuilder import hep.dataforge.data.DataTree -import hep.dataforge.meta.DFBuilder -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.misc.DFBuilder +import hep.dataforge.misc.DFExperimental import hep.dataforge.names.Name import hep.dataforge.names.toName import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KClass public data class TaskReference(public val taskName: Name, public val task: Task) @@ -24,25 +23,19 @@ public interface TaskContainer { public fun registerTask(taskName: Name, task: Task<*>) } -public fun TaskContainer.registerTask( - resultType: KClass, - name: String, - descriptorBuilder: NodeDescriptor.() -> Unit = {}, - builder: suspend TaskResultBuilder.() -> Unit, -): Unit = registerTask(name.toName(), Task(resultType, NodeDescriptor(descriptorBuilder), builder)) public inline fun TaskContainer.registerTask( name: String, noinline descriptorBuilder: NodeDescriptor.() -> Unit = {}, noinline builder: suspend TaskResultBuilder.() -> Unit, -): Unit = registerTask(T::class, name, descriptorBuilder, builder) +): Unit = registerTask(name.toName(), Task(NodeDescriptor(descriptorBuilder), builder)) public inline fun TaskContainer.task( noinline descriptorBuilder: NodeDescriptor.() -> Unit = {}, noinline builder: suspend TaskResultBuilder.() -> Unit, ): PropertyDelegateProvider>> = PropertyDelegateProvider { _, property -> val taskName = property.name.toName() - val task = Task(T::class, NodeDescriptor(descriptorBuilder), builder) + val task = Task(NodeDescriptor(descriptorBuilder), builder) registerTask(taskName, task) ReadOnlyProperty { _, _ -> TaskReference(taskName, task) } } diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt index c659d2c5..cebba975 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt @@ -3,15 +3,14 @@ package hep.dataforge.workspace import hep.dataforge.data.Data import hep.dataforge.data.await import hep.dataforge.io.* -import kotlin.reflect.KClass +import hep.dataforge.misc.DFInternal /** * Convert an [Envelope] to a data via given format. The actual parsing is done lazily. */ +@OptIn(DFInternal::class) public fun Envelope.toData(format: IOFormat): Data { - @Suppress("UNCHECKED_CAST") - val kclass: KClass = format.type.classifier as? KClass ?: error("IOFormat type is not a class") - return Data(kclass, meta) { + return Data(format.type, meta) { data?.readWith(format) ?: error("Can't convert envelope without data to Data") } } diff --git a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt index 29ff0c62..a8378046 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/hep/dataforge/workspace/fileData.kt @@ -4,6 +4,7 @@ package hep.dataforge.workspace import hep.dataforge.data.* import hep.dataforge.io.* import hep.dataforge.meta.* +import hep.dataforge.misc.DFExperimental import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -15,7 +16,6 @@ import java.nio.file.StandardOpenOption import java.nio.file.spi.FileSystemProvider import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream -import kotlin.reflect.KClass import kotlin.reflect.KType import kotlin.reflect.typeOf import kotlin.streams.toList @@ -36,9 +36,6 @@ internal inline fun IOPlugin.formatResolver(): FileFormatResol resolveIOFormat() ?: error("Can't resolve IO format for ${T::class}") } -private val FileFormatResolver.kClass: KClass - get() = type.classifier as? KClass ?: error("Format resolver actual type does not correspond to type parameter") - private fun newZFS(path: Path): FileSystem { val fsProvider = FileSystemProvider.installedProviders().find { it.scheme == "jar" } ?: error("Zip file system provider not found") @@ -110,7 +107,7 @@ public suspend fun IOPlugin.readDataDirectory( return readDataDirectory(fs.rootDirectories.first(), formatResolver) } if (!Files.isDirectory(path)) error("Provided path $path is not a directory") - return DataTree(formatResolver.kClass) { + return DataTree(formatResolver.type) { Files.list(path).toList().forEach { path -> val fileName = path.fileName.toString() if (fileName.startsWith(IOPlugin.META_FILE_NAME)) { diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt index e089e448..df8e2673 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt @@ -4,8 +4,8 @@ import hep.dataforge.context.Global import hep.dataforge.data.* import hep.dataforge.io.IOFormat import hep.dataforge.io.io -import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.Meta +import hep.dataforge.misc.DFExperimental import kotlinx.coroutines.runBlocking import kotlinx.io.Input import kotlinx.io.Output diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt index b6bc9a1c..5ee08ea5 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/SimpleWorkspaceTest.kt @@ -1,9 +1,9 @@ package hep.dataforge.workspace -import hep.dataforge.actions.get import hep.dataforge.context.* import hep.dataforge.data.* import hep.dataforge.meta.* +import hep.dataforge.names.get import hep.dataforge.names.plus import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.single From 7d3df24568688a21d3de3894e6117415b9e24167 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 19:36:41 +0300 Subject: [PATCH 55/59] Remove KClass from dataforge-data --- .../kotlin/hep/dataforge/data/select.kt | 40 +++---------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt index 9df89f84..02672c37 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/select.kt @@ -12,44 +12,15 @@ import kotlin.reflect.full.isSubtypeOf import kotlin.reflect.typeOf -/** - * Check if data could be safely cast to given class - */ -private fun Data<*>.canCast(type: KType): Boolean = this.type.isSubtypeOf(type) - /** * Cast the node to given type if the cast is possible or return null */ @Suppress("UNCHECKED_CAST") private fun Data<*>.castOrNull(type: KType): Data? = - if (!canCast(type)) null else object : Data by (this as Data) { + if (!this.type.isSubtypeOf(type)) null else object : Data by (this as Data) { override val type: KType = type } -/** - * Unsafe cast of data node - */ -private fun Data<*>.cast(type: KType): Data = - castOrNull(type) ?: error("Can't cast ${this.type} to $type") - -private inline fun Data<*>.cast(): Data = cast(typeOf()) - -@Suppress("UNCHECKED_CAST") -private fun DataSet<*>.castOrNull(type: KType): DataSet? = - if (!canCast(type)) null else object : DataSet by (this as DataSet) { - override val dataType: KType = type - } - - -private fun DataSet<*>.cast(type: KType): DataSet = - castOrNull(type) ?: error("Can't cast ${this.dataType} to $type") - -/** - * Check that node is compatible with given type meaning that each element could be cast to the type - */ -private fun DataSet<*>.canCast(type: KType): Boolean = - type.isSubtypeOf(this.dataType) - /** * Select all data matching given type and filters. Does not modify paths */ @@ -61,10 +32,11 @@ internal fun DataSet<*>.select( ): ActiveDataSet = object : ActiveDataSet { override val dataType = type - @Suppress("UNCHECKED_CAST") - override fun flow(): Flow> = this@select.flow().filter { - it.type.isSubtypeOf(type) && (namePattern == null || it.name.matches(namePattern)) + + override fun flow(): Flow> = this@select.flow().filter { datum -> + datum.type.isSubtypeOf(type) && (namePattern == null || datum.name.matches(namePattern)) }.map { + @Suppress("UNCHECKED_CAST") it as NamedData } @@ -72,7 +44,7 @@ internal fun DataSet<*>.select( override val updates: Flow = this@select.updates.filter { val datum = this@select.getData(it) - datum?.canCast(type) ?: false + datum?.type?.isSubtypeOf(type) ?: false } } From 5d02520904ed5fbc831440a583a9367925231a25 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 20:23:33 +0300 Subject: [PATCH 56/59] turn on API validation --- CHANGELOG.md | 1 + build.gradle.kts | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f9f236d..0faac561 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - \[Major breaking change\] Moved `NodeItem` and `ValueItem` to a top level - Plugins are removed from Context constructor and added lazily in ContextBuilder - \[Major breaking change\] Full refactor of DataTree/DataSource +- \[Major Breaking change\] Replace KClass with KType in data. Remove direct access to constructors with types. ### Deprecated diff --git a/build.gradle.kts b/build.gradle.kts index cc74ad20..27917005 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,14 +15,6 @@ allprojects { apply() } -apiValidation{ - validationDisabled = true -} - subprojects { apply(plugin = "ru.mipt.npm.publish") -} - -apiValidation{ - ignoredProjects.add("dataforge-tables") } \ No newline at end of file From 2b945d4a78ea9cbee4f6b092cea106b43cf1dcc9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 20:49:48 +0300 Subject: [PATCH 57/59] Update CHANGELOG.md --- CHANGELOG.md | 13 +++ dataforge-context/api/dataforge-context.api | 49 ++++----- dataforge-meta/api/dataforge-meta.api | 115 ++++++++++++-------- 3 files changed, 105 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0faac561..ccbd72a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ ## [Unreleased] ### Added + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + +## [0.3.0] +### Added - Yaml meta format based on yaml.kt - `Path` builders - Special ValueType for lists diff --git a/dataforge-context/api/dataforge-context.api b/dataforge-context/api/dataforge-context.api index 1831f5ce..d2d736d9 100644 --- a/dataforge-context/api/dataforge-context.api +++ b/dataforge-context/api/dataforge-context.api @@ -4,15 +4,15 @@ public abstract class hep/dataforge/context/AbstractPlugin : hep/dataforge/conte public synthetic fun (Lhep/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun attach (Lhep/dataforge/context/Context;)V public fun content (Ljava/lang/String;)Ljava/util/Map; - public synthetic fun dependsOn ()Ljava/util/Collection; - public final fun dependsOn ()Ljava/util/List; + public final fun dependsOn ()Ljava/util/Map; public fun detach ()V public fun getContext ()Lhep/dataforge/context/Context; public fun getDefaultChainTarget ()Ljava/lang/String; public fun getDefaultTarget ()Ljava/lang/String; public fun getMeta ()Lhep/dataforge/meta/Meta; public fun getName ()Lhep/dataforge/names/Name; - protected final fun require (Lhep/dataforge/context/PluginFactory;)Lkotlin/properties/ReadOnlyProperty; + protected final fun require (Lhep/dataforge/context/PluginFactory;Lhep/dataforge/meta/Meta;)Lkotlin/properties/ReadOnlyProperty; + public static synthetic fun require$default (Lhep/dataforge/context/AbstractPlugin;Lhep/dataforge/context/PluginFactory;Lhep/dataforge/meta/Meta;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; public fun toMeta ()Lhep/dataforge/meta/Meta; } @@ -35,10 +35,9 @@ public final class hep/dataforge/context/ClassLoaderPluginKt { public static final fun getClassLoaderPlugin (Lhep/dataforge/context/Context;)Lhep/dataforge/context/ClassLoaderPlugin; } -public class hep/dataforge/context/Context : hep/dataforge/context/Named, hep/dataforge/meta/MetaRepr, hep/dataforge/provider/Provider, kotlinx/coroutines/CoroutineScope { +public class hep/dataforge/context/Context : hep/dataforge/meta/MetaRepr, hep/dataforge/misc/Named, hep/dataforge/provider/Provider, kotlinx/coroutines/CoroutineScope { public static final field Companion Lhep/dataforge/context/Context$Companion; public static final field PROPERTY_TARGET Ljava/lang/String; - public fun (Lhep/dataforge/names/Name;Lhep/dataforge/context/Context;Lhep/dataforge/meta/Meta;)V public fun close ()V public fun content (Ljava/lang/String;)Ljava/util/Map; public final fun content (Ljava/lang/String;Z)Ljava/util/Map; @@ -74,6 +73,11 @@ public final class hep/dataforge/context/ContextBuilder { public final fun setName (Ljava/lang/String;)V } +public final class hep/dataforge/context/ContextKt { + public static final fun Context (Ljava/lang/String;Lhep/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/context/Context; + public static synthetic fun Context$default (Ljava/lang/String;Lhep/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/context/Context; +} + public abstract interface class hep/dataforge/context/Factory { public abstract fun invoke (Lhep/dataforge/meta/Meta;Lhep/dataforge/context/Context;)Ljava/lang/Object; } @@ -96,24 +100,11 @@ public final class hep/dataforge/context/LoggingKt { public static final fun getLogger (Lhep/dataforge/context/ContextAware;)Lmu/KLogger; } -public abstract interface class hep/dataforge/context/Named { - public static final field Companion Lhep/dataforge/context/Named$Companion; - public abstract fun getName ()Lhep/dataforge/names/Name; -} - -public final class hep/dataforge/context/Named$Companion { - public final fun nameOf (Ljava/lang/Object;)Lhep/dataforge/names/Name; -} - -public final class hep/dataforge/context/NamedKt { - public static final fun isAnonymous (Lhep/dataforge/context/Named;)Z -} - -public abstract interface class hep/dataforge/context/Plugin : hep/dataforge/context/ContextAware, hep/dataforge/context/Named, hep/dataforge/meta/MetaRepr, hep/dataforge/provider/Provider { +public abstract interface class hep/dataforge/context/Plugin : hep/dataforge/context/ContextAware, hep/dataforge/meta/MetaRepr, hep/dataforge/misc/Named, hep/dataforge/provider/Provider { public static final field Companion Lhep/dataforge/context/Plugin$Companion; public static final field TARGET Ljava/lang/String; public abstract fun attach (Lhep/dataforge/context/Context;)V - public abstract fun dependsOn ()Ljava/util/Collection; + public abstract fun dependsOn ()Ljava/util/Map; public abstract fun detach ()V public abstract fun getMeta ()Lhep/dataforge/meta/Meta; public abstract fun getName ()Lhep/dataforge/names/Name; @@ -146,9 +137,9 @@ public final class hep/dataforge/context/PluginFactory$Companion { public final class hep/dataforge/context/PluginManager : hep/dataforge/context/ContextAware, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { public fun (Lhep/dataforge/context/Context;)V - public final fun fetch (Lhep/dataforge/context/PluginFactory;ZLhep/dataforge/meta/Meta;)Lhep/dataforge/context/Plugin; + public final fun fetch (Lhep/dataforge/context/PluginFactory;Lhep/dataforge/meta/Meta;Z)Lhep/dataforge/context/Plugin; public final fun fetch (Lhep/dataforge/context/PluginFactory;ZLkotlin/jvm/functions/Function1;)Lhep/dataforge/context/Plugin; - public static synthetic fun fetch$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginFactory;ZLhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/context/Plugin; + public static synthetic fun fetch$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginFactory;Lhep/dataforge/meta/Meta;ZILjava/lang/Object;)Lhep/dataforge/context/Plugin; public static synthetic fun fetch$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginFactory;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/context/Plugin; public final fun find (ZLkotlin/jvm/functions/Function1;)Lhep/dataforge/context/Plugin; public static synthetic fun find$default (Lhep/dataforge/context/PluginManager;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lhep/dataforge/context/Plugin; @@ -262,9 +253,6 @@ public final class hep/dataforge/provider/Path : java/lang/Iterable, kotlin/jvm/ public fun equals (Ljava/lang/Object;)Z public static fun equals-impl (Ljava/util/List;Ljava/lang/Object;)Z public static final fun equals-impl0 (Ljava/util/List;Ljava/util/List;)Z - public static final fun getHead-impl (Ljava/util/List;)Lhep/dataforge/provider/PathToken; - public static final fun getLength-impl (Ljava/util/List;)I - public static final fun getTail-e2ET3QM (Ljava/util/List;)Ljava/util/List; public final fun getTokens ()Ljava/util/List; public fun hashCode ()I public static fun hashCode-impl (Ljava/util/List;)I @@ -276,7 +264,7 @@ public final class hep/dataforge/provider/Path : java/lang/Iterable, kotlin/jvm/ } public final class hep/dataforge/provider/Path$Companion { - public final fun parse-IN54j3k (Ljava/lang/String;)Ljava/util/List; + public final fun parse-AnEnhig (Ljava/lang/String;)Ljava/util/List; } public final class hep/dataforge/provider/PathKt { @@ -285,7 +273,10 @@ public final class hep/dataforge/provider/PathKt { public static final fun asPath (Lhep/dataforge/names/Name;Ljava/lang/String;)Ljava/util/List; public static final fun asPath (Lhep/dataforge/provider/PathToken;)Ljava/util/List; public static synthetic fun asPath$default (Lhep/dataforge/names/Name;Ljava/lang/String;ILjava/lang/Object;)Ljava/util/List; - public static final fun plus-MQiGgVU (Ljava/util/List;Ljava/util/List;)Ljava/util/List; + public static final fun getHead-Ipm_iiw (Ljava/util/List;)Lhep/dataforge/provider/PathToken; + public static final fun getLength-Ipm_iiw (Ljava/util/List;)I + public static final fun getTail-Ipm_iiw (Ljava/util/List;)Ljava/util/List; + public static final fun plus-BlTXZnM (Ljava/util/List;Ljava/util/List;)Ljava/util/List; } public final class hep/dataforge/provider/PathToken { @@ -321,8 +312,8 @@ public final class hep/dataforge/provider/Provider$DefaultImpls { } public final class hep/dataforge/provider/ProviderKt { - public static final fun provide-0Dbucg0 (Lhep/dataforge/provider/Provider;Ljava/util/List;Ljava/lang/String;)Ljava/lang/Object; - public static synthetic fun provide-0Dbucg0$default (Lhep/dataforge/provider/Provider;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun provide-mzxrFLw (Lhep/dataforge/provider/Provider;Ljava/util/List;Ljava/lang/String;)Ljava/lang/Object; + public static synthetic fun provide-mzxrFLw$default (Lhep/dataforge/provider/Provider;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/Object; public static final fun top (Lhep/dataforge/provider/Provider;Ljava/lang/String;Lkotlin/reflect/KClass;)Ljava/util/Map; } diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 113bf7e1..06901639 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -10,7 +10,6 @@ public abstract class hep/dataforge/meta/AbstractMutableMeta : hep/dataforge/met public abstract class hep/dataforge/meta/AbstractTypedMeta : hep/dataforge/meta/MetaBase, hep/dataforge/meta/TypedMeta { public fun ()V public fun getItem (Lhep/dataforge/names/Name;)Lhep/dataforge/meta/TypedMetaItem; - public fun toMeta ()Lhep/dataforge/meta/Meta; } public final class hep/dataforge/meta/Config : hep/dataforge/meta/AbstractMutableMeta, hep/dataforge/meta/ObservableItemProvider { @@ -49,12 +48,6 @@ public final class hep/dataforge/meta/ConfigurableKt { public static final fun configure (Lhep/dataforge/meta/Configurable;Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Configurable; } -public abstract interface annotation class hep/dataforge/meta/DFBuilder : java/lang/annotation/Annotation { -} - -public abstract interface annotation class hep/dataforge/meta/DFExperimental : java/lang/annotation/Annotation { -} - public final class hep/dataforge/meta/ItemDelegateKt { public static final fun boolean (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; public static final fun boolean (Lhep/dataforge/meta/ItemProvider;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadOnlyProperty; @@ -250,12 +243,12 @@ public final class hep/dataforge/meta/MetaBuilder : hep/dataforge/meta/AbstractM public final class hep/dataforge/meta/MetaBuilderKt { public static final fun Meta (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MetaBuilder; - public static final fun builder (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MetaBuilder; + public static final fun toMutableMeta (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MetaBuilder; } public final class hep/dataforge/meta/MetaItemKt { - public static final fun asMetaItem (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/NodeItem; - public static final fun asMetaItem (Lhep/dataforge/values/Value;)Lhep/dataforge/meta/ValueItem; + public static final fun asMetaItem (Lhep/dataforge/meta/Meta;)Lhep/dataforge/meta/MetaItemNode; + public static final fun asMetaItem (Lhep/dataforge/values/Value;)Lhep/dataforge/meta/MetaItemValue; public static final fun getBoolean (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Boolean; public static final fun getDouble (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Double; public static final fun getFloat (Lhep/dataforge/meta/TypedMetaItem;)Ljava/lang/Float; @@ -269,6 +262,19 @@ public final class hep/dataforge/meta/MetaItemKt { public static final fun getValue (Lhep/dataforge/meta/TypedMetaItem;)Lhep/dataforge/values/Value; } +public final class hep/dataforge/meta/MetaItemNode : hep/dataforge/meta/TypedMetaItem { + public static final field Companion Lhep/dataforge/meta/MetaItemNode$Companion; + public fun (Lhep/dataforge/meta/Meta;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getNode ()Lhep/dataforge/meta/Meta; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class hep/dataforge/meta/MetaItemNode$Companion { + public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; +} + public final class hep/dataforge/meta/MetaItemSerializer : kotlinx/serialization/KSerializer { public static final field INSTANCE Lhep/dataforge/meta/MetaItemSerializer; public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lhep/dataforge/meta/TypedMetaItem; @@ -278,6 +284,19 @@ public final class hep/dataforge/meta/MetaItemSerializer : kotlinx/serialization public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } +public final class hep/dataforge/meta/MetaItemValue : hep/dataforge/meta/TypedMetaItem { + public static final field Companion Lhep/dataforge/meta/MetaItemValue$Companion; + public fun (Lhep/dataforge/values/Value;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getValue ()Lhep/dataforge/values/Value; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class hep/dataforge/meta/MetaItemValue$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class hep/dataforge/meta/MetaKt { public static final fun get (Lhep/dataforge/meta/Meta;Lhep/dataforge/names/NameToken;)Lhep/dataforge/meta/TypedMetaItem; public static final fun isEmpty (Lhep/dataforge/meta/Meta;)Z @@ -401,19 +420,6 @@ public final class hep/dataforge/meta/MutableMetaKt { public static final fun edit (Lhep/dataforge/meta/AbstractMutableMeta;Lhep/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)V } -public final class hep/dataforge/meta/NodeItem : hep/dataforge/meta/TypedMetaItem { - public static final field Companion Lhep/dataforge/meta/NodeItem$Companion; - public fun (Lhep/dataforge/meta/Meta;)V - public fun equals (Ljava/lang/Object;)Z - public final fun getNode ()Lhep/dataforge/meta/Meta; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class hep/dataforge/meta/NodeItem$Companion { - public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; -} - public abstract interface class hep/dataforge/meta/ObservableItemProvider : hep/dataforge/meta/ItemProvider { public abstract fun onChange (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V public abstract fun removeListener (Ljava/lang/Object;)V @@ -457,7 +463,6 @@ public class hep/dataforge/meta/SchemeSpec : hep/dataforge/meta/Specification, h public synthetic fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; public fun getDescriptor ()Lhep/dataforge/meta/descriptors/NodeDescriptor; public synthetic fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/ItemProvider; - public synthetic fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/MutableItemProvider; public final fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/Scheme; public synthetic fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/ItemProvider; public fun read (Lhep/dataforge/meta/ItemProvider;)Lhep/dataforge/meta/Scheme; @@ -518,19 +523,6 @@ public final class hep/dataforge/meta/TypedMetaKt { public static final fun get (Lhep/dataforge/meta/TypedMeta;Ljava/lang/String;)Lhep/dataforge/meta/TypedMetaItem; } -public final class hep/dataforge/meta/ValueItem : hep/dataforge/meta/TypedMetaItem { - public static final field Companion Lhep/dataforge/meta/ValueItem$Companion; - public fun (Lhep/dataforge/values/Value;)V - public fun equals (Ljava/lang/Object;)Z - public final fun getValue ()Lhep/dataforge/values/Value; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class hep/dataforge/meta/ValueItem$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - public abstract interface class hep/dataforge/meta/descriptors/Described { public static final field Companion Lhep/dataforge/meta/descriptors/Described$Companion; public abstract fun getDescriptor ()Lhep/dataforge/meta/descriptors/ItemDescriptor; @@ -566,6 +558,7 @@ public final class hep/dataforge/meta/descriptors/ItemDescriptor$Companion { } public final class hep/dataforge/meta/descriptors/ItemDescriptorKt { + public static final fun NodeDescriptor (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/descriptors/NodeDescriptor; public static final fun attributes (Lhep/dataforge/meta/descriptors/ItemDescriptor;Lkotlin/jvm/functions/Function1;)V public static final fun get (Lhep/dataforge/meta/descriptors/ItemDescriptor;Lhep/dataforge/names/Name;)Lhep/dataforge/meta/descriptors/ItemDescriptor; public static final fun get (Lhep/dataforge/meta/descriptors/ItemDescriptor;Ljava/lang/String;)Lhep/dataforge/meta/descriptors/ItemDescriptor; @@ -596,7 +589,6 @@ public final class hep/dataforge/meta/descriptors/NodeDescriptor : hep/dataforge } public final class hep/dataforge/meta/descriptors/NodeDescriptor$Companion { - public final fun invoke (Lkotlin/jvm/functions/Function1;)Lhep/dataforge/meta/descriptors/NodeDescriptor; } public final class hep/dataforge/meta/descriptors/ValueDescriptor : hep/dataforge/meta/descriptors/ItemDescriptor { @@ -681,7 +673,7 @@ public final class hep/dataforge/meta/transformations/MetaTransformation { } public final class hep/dataforge/meta/transformations/MetaTransformation$Companion { - public final fun make-S5KKvQA (Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; + public final fun make-edBMxVg (Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; } public final class hep/dataforge/meta/transformations/MetaTransformationBuilder { @@ -736,6 +728,32 @@ public final class hep/dataforge/meta/transformations/TransformationRule$Default public static fun selectItems (Lhep/dataforge/meta/transformations/TransformationRule;Lhep/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; } +public abstract interface annotation class hep/dataforge/misc/DFBuilder : java/lang/annotation/Annotation { +} + +public abstract interface annotation class hep/dataforge/misc/DFExperimental : java/lang/annotation/Annotation { +} + +public abstract interface annotation class hep/dataforge/misc/DFInternal : java/lang/annotation/Annotation { +} + +public abstract interface class hep/dataforge/misc/Named { + public static final field Companion Lhep/dataforge/misc/Named$Companion; + public abstract fun getName ()Lhep/dataforge/names/Name; +} + +public final class hep/dataforge/misc/Named$Companion { + public final fun nameOf (Ljava/lang/Object;)Lhep/dataforge/names/Name; +} + +public final class hep/dataforge/misc/NamedKt { + public static final fun isAnonymous (Lhep/dataforge/misc/Named;)Z +} + +public abstract interface annotation class hep/dataforge/misc/Type : java/lang/annotation/Annotation { + public abstract fun id ()Ljava/lang/String; +} + public final class hep/dataforge/names/Name { public static final field Companion Lhep/dataforge/names/Name$Companion; public static final field NAME_SEPARATOR Ljava/lang/String; @@ -751,6 +769,8 @@ public final class hep/dataforge/names/Name$Companion : kotlinx/serialization/KS public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; public final fun getEMPTY ()Lhep/dataforge/names/Name; + public final fun getMATCH_ALL_TOKEN ()Lhep/dataforge/names/NameToken; + public final fun getMATCH_ANY_TOKEN ()Lhep/dataforge/names/NameToken; public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lhep/dataforge/names/Name;)V public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V public final fun serializer ()Lkotlinx/serialization/KSerializer; @@ -776,6 +796,7 @@ public final class hep/dataforge/names/NameKt { public static final fun plus (Lhep/dataforge/names/Name;Lhep/dataforge/names/NameToken;)Lhep/dataforge/names/Name; public static final fun plus (Lhep/dataforge/names/Name;Ljava/lang/String;)Lhep/dataforge/names/Name; public static final fun plus (Lhep/dataforge/names/NameToken;Lhep/dataforge/names/Name;)Lhep/dataforge/names/Name; + public static final fun removeHeadOrNull (Lhep/dataforge/names/Name;Lhep/dataforge/names/Name;)Lhep/dataforge/names/Name; public static final fun set (Ljava/util/Map;Ljava/lang/String;Ljava/lang/Object;)V public static final fun startsWith (Lhep/dataforge/names/Name;Lhep/dataforge/names/Name;)Z public static final fun startsWith (Lhep/dataforge/names/Name;Lhep/dataforge/names/NameToken;)Z @@ -783,6 +804,12 @@ public final class hep/dataforge/names/NameKt { public static final fun withIndex (Lhep/dataforge/names/Name;Ljava/lang/String;)Lhep/dataforge/names/Name; } +public final class hep/dataforge/names/NameMatcherKt { + public static final fun matches (Lhep/dataforge/names/Name;Lhep/dataforge/names/Name;)Z + public static final fun matches (Lhep/dataforge/names/Name;Ljava/lang/String;)Z + public static final fun matches (Lhep/dataforge/names/NameToken;Lhep/dataforge/names/NameToken;)Z +} + public final class hep/dataforge/names/NameToken { public static final field Companion Lhep/dataforge/names/NameToken$Companion; public fun (Ljava/lang/String;Ljava/lang/String;)V @@ -812,10 +839,6 @@ public final class hep/dataforge/names/NameTokenKt { public static final fun withIndex (Lhep/dataforge/names/NameToken;Ljava/lang/String;)Lhep/dataforge/names/NameToken; } -public abstract interface annotation class hep/dataforge/type/Type : java/lang/annotation/Annotation { - public abstract fun id ()Ljava/lang/String; -} - public final class hep/dataforge/values/DoubleArrayValue : hep/dataforge/values/Value, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { public fun ([D)V public fun equals (Ljava/lang/Object;)Z @@ -866,6 +889,7 @@ public final class hep/dataforge/values/LazyParsedValue : hep/dataforge/values/V } public final class hep/dataforge/values/ListValue : hep/dataforge/values/Value, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { + public static final field Companion Lhep/dataforge/values/ListValue$Companion; public fun (Ljava/util/List;)V public fun equals (Ljava/lang/Object;)Z public fun getList ()Ljava/util/List; @@ -877,6 +901,10 @@ public final class hep/dataforge/values/ListValue : hep/dataforge/values/Value, public fun toString ()Ljava/lang/String; } +public final class hep/dataforge/values/ListValue$Companion { + public final fun getEMPTY ()Lhep/dataforge/values/ListValue; +} + public final class hep/dataforge/values/Null : hep/dataforge/values/Value { public static final field INSTANCE Lhep/dataforge/values/Null; public fun equals (Ljava/lang/Object;)Z @@ -992,6 +1020,7 @@ public final class hep/dataforge/values/ValueType : java/lang/Enum { public final class hep/dataforge/values/ValueType$$serializer : kotlinx/serialization/internal/GeneratedSerializer { public static final field INSTANCE Lhep/dataforge/values/ValueType$$serializer; + public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; public fun childSerializers ()[Lkotlinx/serialization/KSerializer; public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lhep/dataforge/values/ValueType; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; From db4ed02f9d7fba756d23a44b52eb6f449946ee33 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 20:58:19 +0300 Subject: [PATCH 58/59] Update Readme --- README.md | 102 ---------------------------- build.gradle.kts | 14 +++- docs/templates/ARTIFACT-TEMPLATE.md | 32 +++++++++ docs/templates/README-TEMPLATE.md | 8 +++ 4 files changed, 51 insertions(+), 105 deletions(-) delete mode 100644 README.md create mode 100644 docs/templates/ARTIFACT-TEMPLATE.md create mode 100644 docs/templates/README-TEMPLATE.md diff --git a/README.md b/README.md deleted file mode 100644 index 4bd6df9f..00000000 --- a/README.md +++ /dev/null @@ -1,102 +0,0 @@ -[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) -[![DOI](https://zenodo.org/badge/148831678.svg)](https://zenodo.org/badge/latestdoi/148831678) - -![Gradle build](https://github.com/mipt-npm/dataforge-core/workflows/Gradle%20build/badge.svg) - -[ ![Download](https://api.bintray.com/packages/mipt-npm/dataforge/dataforge-meta/images/download.svg) ](https://bintray.com/mipt-npm/dataforge/dataforge-meta/_latestVersion) - - - -# Questions and Answers # - -In this section we will try to cover DataForge main ideas in the form of questions and answers. - -## General ## - -**Q:** I have a lot of data to analyze. The analysis process is complicated, requires a lot of stages and data flow is not always obvious. To top it the data size is huge, so I don't want to perform operation I don't need (calculate something I won't need or calculate something twice). And yes, I need it to be performed in parallel and probably on remote computer. By the way, I am sick and tired of scripts that modify other scripts that control scripts. Could you help me? - -**A:** Yes, that is the precisely the problem DataForge was made to solve. It allows to perform some automated data manipulations with automatic optimization and parallelization. The important thing that data processing recipes are made in the declarative way, so it is quite easy to perform computations on a remote station. Also DataForge guarantees reproducibility of analysis results. -


    - -**Q:** How does it work? - -**A:** At the core of DataForge lies the idea of **metadata processor**. It utilizes the statement that in order to analyze something you need data itself and some additional information about what does that data represent and what does user want as a result. This additional information is called metadata and could be organized in a regular structure (a tree of values not unlike XML or JSON). The important thing is that this distinction leaves no place for user instructions (or scripts). Indeed, the idea of DataForge logic is that one do not need imperative commands. The framework configures itself according to input meta-data and decides what operations should be performed in the most efficient way. -
    - -**Q:** But where does it take algorithms to use? - -**A:** Of course algorithms must be written somewhere. No magic here. The logic is written in specialized modules. Some modules are provided out of the box at the system core, some need to be developed for specific problem. -
    - -**Q:** So I still need to write the code? What is the difference then? - -**A:** Yes, someone still need to write the code. But not necessary you. Simple operations could be performed using provided core logic. Also your group can have one programmer writing the logic and all other using it without any real programming expertise. Also the framework organized in a such way that one writes some additional logic, he do not need to thing about complicated thing like parallel computing, resource handling, logging, caching etc. Most of the things are done by the DataForge. -
    - -## Platform ## - -**Q:** Which platform does DataForge use? Which operation system is it working on? - -**A:** The DataForge is mostly written in Java and utilizes JVM as a platform. It works on any system that supports JVM (meaning almost any modern system excluding some mobile platforms). -
    - - **Q:** But Java... it is slow! - - **A:** [It is not](https://stackoverflow.com/questions/2163411/is-java-really-slow/2163570#2163570). It lacks some hardware specific optimizations and requires some additional time to start (due to JIT nature), but otherwise it is at least as fast as other languages traditionally used in science. More importantly, the memory safety, tooling support and vast ecosystem makes it №1 candidate for data analysis framework. - -
    - - **Q:** Can I use my C++/Fortran/Python code in DataForge? - - **A:** Yes, as long as the code could be called from Java. Most of common languages have a bridge for Java access. There are completely no problems with compiled C/Fortran libraries. Python code could be called via one of existing python-java interfaces. It is also planned to implement remote method invocation for common languages, so your Python, or, say, Julia, code could run in its native environment. The metadata processor paradigm makes it much easier to do so. - -
    - -## Features ## - -**Q:** What other features does DataForge provide? - -**A:** Alongside metadata processing (and a lot of tools for metadata manipulation and layering), DataForge has two additional important concepts: - -* **Modularisation**. Contrary to lot other frameworks, DataForge is intrinsically modular. The mandatory part is a rather tiny core module. Everything else could be customized. - -* **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. - - - - -
    - - - -
    - -## Misc ## - -**Q:** So everything looks great, can I replace my ROOT / other data analysis framework with DataForge? - -**A:** One must note, that DataForge is made for analysis, not for visualisation. The visualisation and user interaction capabilities of DataForge are rather limited compared to frameworks like ROOT, JAS3 or DataMelt. The idea is to provide reliable API and core functionality. In fact JAS3 and DataMelt could be used as a frontend for DataForge mechanics. It is planned to add an interface to ROOT via JFreeHep AIDA. - -
    - -**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. - - -
    - -**Q:** Can I use DataForge on a mobile platform? - -**A:** DataForge is modular. Core and the most of api are pretty compact, so it could be used in Android applications. Some modules are designed for PC and could not be used on other platforms. IPhone does not support Java and therefore could use only client-side DataForge applications. diff --git a/build.gradle.kts b/build.gradle.kts index 27917005..21633df0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,9 +4,7 @@ plugins { val dataforgeVersion by extra("0.3.0") -val bintrayRepo by extra("dataforge") -val githubProject by extra("dataforge-core") -val spaceRepo by extra("https://maven.jetbrains.space/mipt-npm/p/df/maven") + allprojects { group = "hep.dataforge" @@ -17,4 +15,14 @@ allprojects { subprojects { apply(plugin = "ru.mipt.npm.publish") +} + +readme { + readmeTemplate = file("docs/templates/README-TEMPLATE.md") +} + +ksciencePublish { + bintrayRepo = "dataforge" + githubProject = "dataforge-core" + spaceRepo = "https://maven.jetbrains.space/mipt-npm/p/df/maven" } \ No newline at end of file diff --git a/docs/templates/ARTIFACT-TEMPLATE.md b/docs/templates/ARTIFACT-TEMPLATE.md new file mode 100644 index 00000000..410c6cad --- /dev/null +++ b/docs/templates/ARTIFACT-TEMPLATE.md @@ -0,0 +1,32 @@ +> #### Artifact: +> +> This module artifact: `${group}:${name}:${version}`. +> +> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dataforge/${name}/images/download.svg) ](https://bintray.com/mipt-npm/kscience/${name}/_latestVersion) +> +> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/${name}/images/download.svg) ](https://bintray.com/mipt-npm/dev/${name}/_latestVersion) +> +> **Gradle:** +> +> ```gradle +> repositories { +> maven { url 'https://dl.bintray.com/mipt-npm/dataforge' } +> maven { url 'https://dl.bintray.com/mipt-npm/dev' } +> } +> +> dependencies { +> implementation '${group}:${name}:${version}' +> } +> ``` +> **Gradle Kotlin DSL:** +> +> ```kotlin +> repositories { +> maven("https://dl.bintray.com/mipt-npm/kscience") +> maven("https://dl.bintray.com/mipt-npm/dev") +> } +> +> dependencies { +> implementation("${group}:${name}:${version}") +> } +> ``` \ No newline at end of file diff --git a/docs/templates/README-TEMPLATE.md b/docs/templates/README-TEMPLATE.md new file mode 100644 index 00000000..e0d7bb23 --- /dev/null +++ b/docs/templates/README-TEMPLATE.md @@ -0,0 +1,8 @@ +[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) +[![DOI](https://zenodo.org/badge/148831678.svg)](https://zenodo.org/badge/latestdoi/148831678) + +![Gradle build](https://github.com/mipt-npm/dataforge-core/workflows/Gradle%20build/badge.svg) + +[ ![Download](https://api.bintray.com/packages/mipt-npm/dataforge/dataforge-meta/images/download.svg) ](https://bintray.com/mipt-npm/dataforge/dataforge-meta/_latestVersion) + +$modules From a72630764106d35bebc78983c4533169b626f836 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 7 Feb 2021 20:58:43 +0300 Subject: [PATCH 59/59] Update Readme --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..10b32c35 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) +[![DOI](https://zenodo.org/badge/148831678.svg)](https://zenodo.org/badge/latestdoi/148831678) + +![Gradle build](https://github.com/mipt-npm/dataforge-core/workflows/Gradle%20build/badge.svg) + +[ ![Download](https://api.bintray.com/packages/mipt-npm/dataforge/dataforge-meta/images/download.svg) ](https://bintray.com/mipt-npm/dataforge/dataforge-meta/_latestVersion) + +
    + +* ### [dataforge-context](dataforge-context) +> +> +> **Maturity**: DEVELOPMENT +
    + +* ### [dataforge-data](dataforge-data) +> +> +> **Maturity**: EXPERIMENTAL +
    + +* ### [dataforge-io](dataforge-io) +> +> +> **Maturity**: PROTOTYPE +
    + +* ### [dataforge-meta](dataforge-meta) +> +> +> **Maturity**: DEVELOPMENT +
    + +* ### [dataforge-scripting](dataforge-scripting) +> +> +> **Maturity**: PROTOTYPE +
    + +* ### [dataforge-tables](dataforge-tables) +> +> +> **Maturity**: PROTOTYPE +
    + +* ### [dataforge-workspace](dataforge-workspace) +> +> +> **Maturity**: EXPERIMENTAL +
    + +* ### [dataforge-io-yaml](dataforge-io-yaml) +> YAML meta converters and Front Matter envelope format +> +> **Maturity**: PROTOTYPE +
    +