diff --git a/.gitignore b/.gitignore index 17a319a4..53b55cd4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,7 @@ out/ .gradle build/ +.kotlin + !gradle-wrapper.jar \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e32cc6bf..6c856ac5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,17 +3,82 @@ ## Unreleased ### Added +- Coroutine exception logging in context ### Changed +- Simplify inheritance logic in `MutableTypedMeta` ### Deprecated +- MetaProvider `spec` is replaced by `readable`. `listOfSpec` replaced with `listOfReadable` ### Removed ### Fixed +- Fixed NameToken parsing. +- Top level string list meta conversion. ### Security +## 0.9.0 - 2024-06-04 + +### Added + +- Custom CoroutineContext during `Context` creation. + +### Changed + +- Kotlin 2.0 +- `MetaSpec` renamed to `MetaReader`. MetaSpec is now reserved for builder-based generation of meta descriptors. +- Add self-type for Meta. Remove unsafe cast method for meta instances. + +### Removed + +- Automatic descriptors for schema. It is not possible to implement them without heavy reflection. + +## 0.8.2 - 2024-04-27 + +### Added + +- Name index comparator +- Specialized ByteArrayValue + +### Changed + +- DataSink `branch` is replaced with `putAll` to avoid confusion with DataTree methods +- Meta delegate now uses a specific class that has a descriptor + +### Fixed + +- `listOfScheme` and `listOfConvertable` delegates provides correct items order. +- Scheme meta setter works with proper sub-branch. +- NameToken.parse improper work with indices. +- Proper data handling for cache. + +## 0.8.0 - 2024-02-03 + +### Added + +- Wasm artifacts +- Add automatic MetaConverter for serializeable objects +- Add Meta and MutableMeta delegates for convertable and serializeable +- Meta mapping for data. + +### Changed + +- Descriptor `children` renamed to `nodes` +- `MetaConverter` now inherits `MetaSpec` (former `Specifiction`). So `MetaConverter` could be used more universally. +- Meta copy and modification now use lightweight non-observable meta builders. +- Full refactor of Data API. DataTree now works similar to Meta: contains optional anonymous root element and data items. Updates are available for `ObservaleDataSource` and `ObservableDataTree` variants. + +### Deprecated + +- `node(key,converter)` in favor of `serializable` delegate + +### Fixed + +- Partially fixed a bug with `MutableMeta` observable wrappers. +- `valueSequence` now include root value. So `meta.update` works properly. + ## 0.7.0 - 2023-11-26 ### Added diff --git a/build.gradle.kts b/build.gradle.kts index 4b5fe48c..9f9632de 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,20 +3,21 @@ import space.kscience.gradle.useApache2Licence import space.kscience.gradle.useSPCTeam plugins { - id("space.kscience.gradle.project") + alias(spclibs.plugins.kscience.project) + alias(spclibs.plugins.kotlinx.kover) } allprojects { group = "space.kscience" - version = "0.7.0" + version = "0.9.1-dev-1" } subprojects { apply(plugin = "maven-publish") tasks.withType { - kotlinOptions { - freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers" + compilerOptions { + freeCompilerArgs.add("-Xcontext-receivers") } } } @@ -30,8 +31,8 @@ ksciencePublish { useApache2Licence() useSPCTeam() } - repository("spc","https://maven.sciprog.center/kscience") - sonatype() + repository("spc", "https://maven.sciprog.center/kscience") + sonatype("https://oss.sonatype.org") } apiValidation { diff --git a/dataforge-context/README.md b/dataforge-context/README.md index 905171b1..2cd53fd1 100644 --- a/dataforge-context/README.md +++ b/dataforge-context/README.md @@ -6,18 +6,16 @@ Context and provider definitions ## Artifact: -The Maven coordinates of this project are `space.kscience:dataforge-context:0.7.0`. +The Maven coordinates of this project are `space.kscience:dataforge-context:0.9.0-dev-1`. **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - //uncomment to access development builds - //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:dataforge-context:0.7.0") + implementation("space.kscience:dataforge-context:0.9.0-dev-1") } ``` diff --git a/dataforge-context/api/dataforge-context.api b/dataforge-context/api/dataforge-context.api index eac8e52c..67cd0cba 100644 --- a/dataforge-context/api/dataforge-context.api +++ b/dataforge-context/api/dataforge-context.api @@ -57,6 +57,7 @@ public abstract interface class space/kscience/dataforge/context/ContextAware { public final class space/kscience/dataforge/context/ContextBuilder { public final fun build ()Lspace/kscience/dataforge/context/Context; + public final fun coroutineContext (Lkotlin/coroutines/CoroutineContext;)V public final fun getName ()Lspace/kscience/dataforge/names/Name; public final fun plugin (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V public final fun plugin (Lspace/kscience/dataforge/context/Plugin;)V @@ -69,9 +70,6 @@ public final class space/kscience/dataforge/context/ContextBuilder { public final fun properties (Lkotlin/jvm/functions/Function1;)V } -public final class space/kscience/dataforge/context/ContextBuilderKt { -} - public final class space/kscience/dataforge/context/DefaultLogManager : space/kscience/dataforge/context/AbstractPlugin, space/kscience/dataforge/context/LogManager { public static final field Companion Lspace/kscience/dataforge/context/DefaultLogManager$Companion; public fun ()V @@ -212,14 +210,14 @@ public final class space/kscience/dataforge/context/PluginTag : space/kscience/d public fun toString ()Ljava/lang/String; } -public final class space/kscience/dataforge/context/PluginTag$$serializer : kotlinx/serialization/internal/GeneratedSerializer { +public synthetic class space/kscience/dataforge/context/PluginTag$$serializer : kotlinx/serialization/internal/GeneratedSerializer { public static final field INSTANCE Lspace/kscience/dataforge/context/PluginTag$$serializer; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/context/PluginTag; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/context/PluginTag; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/context/PluginTag;)V + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/context/PluginTag;)V public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } @@ -249,13 +247,19 @@ public final class space/kscience/dataforge/context/SlfLogManager$Companion : sp public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; } -public final class space/kscience/dataforge/properties/PropertyKt { +public abstract interface annotation class space/kscience/dataforge/descriptors/Description : java/lang/annotation/Annotation { + public abstract fun value ()Ljava/lang/String; } -public final class space/kscience/dataforge/properties/SchemePropertyKt { +public abstract interface annotation class space/kscience/dataforge/descriptors/DescriptorResource : java/lang/annotation/Annotation { + public abstract fun resourceName ()Ljava/lang/String; } -public final class space/kscience/dataforge/provider/DfTypeKt { +public abstract interface annotation class space/kscience/dataforge/descriptors/DescriptorUrl : java/lang/annotation/Annotation { + public abstract fun url ()Ljava/lang/String; +} + +public abstract interface annotation class space/kscience/dataforge/descriptors/Multiple : java/lang/annotation/Annotation { } public final class space/kscience/dataforge/provider/Path : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { diff --git a/dataforge-context/build.gradle.kts b/dataforge-context/build.gradle.kts index be9036d0..72a41693 100644 --- a/dataforge-context/build.gradle.kts +++ b/dataforge-context/build.gradle.kts @@ -8,12 +8,14 @@ kscience { jvm() js() native() + wasm() useCoroutines() useSerialization() - dependencies { - api(project(":dataforge-meta")) + commonMain { + api(projects.dataforgeMeta) + api(spclibs.atomicfu) } - dependencies(jvmMain){ + jvmMain{ api(kotlin("reflect")) api("org.slf4j:slf4j-api:1.7.30") } diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Context.kt index bb74d605..457c39fc 100644 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Context.kt @@ -1,5 +1,6 @@ package space.kscience.dataforge.context +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob @@ -10,6 +11,7 @@ import space.kscience.dataforge.misc.ThreadSafe import space.kscience.dataforge.names.Name import space.kscience.dataforge.provider.Provider import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top. @@ -26,6 +28,7 @@ public open class Context internal constructor( public val parent: Context?, plugins: Set, // set of unattached plugins meta: Meta, + coroutineContext: CoroutineContext = EmptyCoroutineContext, ) : Named, MetaRepr, Provider, CoroutineScope { /** @@ -65,7 +68,9 @@ public open class Context internal constructor( override val coroutineContext: CoroutineContext by lazy { (parent ?: Global).coroutineContext.let { parenContext -> - parenContext + SupervisorJob(parenContext[Job]) + parenContext + coroutineContext + SupervisorJob(parenContext[Job]) + CoroutineExceptionHandler { _, throwable -> + logger.error(throwable) { "Exception in context $name" } + } } } diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/ContextBuilder.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/ContextBuilder.kt index c0db4314..894c5f15 100644 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/ContextBuilder.kt +++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/ContextBuilder.kt @@ -13,6 +13,8 @@ import space.kscience.dataforge.names.plus import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.set +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** * A convenience builder for context @@ -59,8 +61,15 @@ public class ContextBuilder internal constructor( plugin(DeFactoPluginFactory(plugin)) } + private var coroutineContext: CoroutineContext = EmptyCoroutineContext + + public fun coroutineContext(coroutineContext: CoroutineContext) { + this.coroutineContext = coroutineContext + } + + public fun build(): Context { - val contextName = name ?: NameToken("@auto",hashCode().toUInt().toString(16)).asName() + val contextName = name ?: NameToken("@auto", hashCode().toUInt().toString(16)).asName() val plugins = HashMap() fun addPlugin(factory: PluginFactory<*>, meta: Meta) { @@ -86,7 +95,7 @@ public class ContextBuilder internal constructor( addPlugin(factory, meta) } - return Context(contextName, parent, plugins.values.toSet(), meta.seal()) + return Context(contextName, parent, plugins.values.toSet(), meta.seal(), coroutineContext) } } diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Plugin.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Plugin.kt index 152f5a76..6c5648a6 100644 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Plugin.kt +++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Plugin.kt @@ -3,7 +3,7 @@ package space.kscience.dataforge.context import space.kscience.dataforge.context.Plugin.Companion.TARGET import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaRepr -import space.kscience.dataforge.misc.DfId +import space.kscience.dataforge.misc.DfType import space.kscience.dataforge.misc.Named import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.parseAsName @@ -18,7 +18,7 @@ import space.kscience.dataforge.provider.Provider * * create - configure - attach - detach - destroy */ -@DfId(TARGET) +@DfType(TARGET) public interface Plugin : Named, ContextAware, Provider, MetaRepr { /** diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/PluginFactory.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/PluginFactory.kt index 0273d327..9cc67168 100644 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/PluginFactory.kt +++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/PluginFactory.kt @@ -1,9 +1,9 @@ package space.kscience.dataforge.context import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DfId +import space.kscience.dataforge.misc.DfType -@DfId(PluginFactory.TYPE) +@DfType(PluginFactory.TYPE) public interface PluginFactory : Factory { public val tag: PluginTag diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/MetaProperty.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/MetaProperty.kt deleted file mode 100644 index e79ce931..00000000 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/MetaProperty.kt +++ /dev/null @@ -1,35 +0,0 @@ -package space.kscience.dataforge.properties - - -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.ObservableMutableMeta -import space.kscience.dataforge.meta.transformations.MetaConverter -import space.kscience.dataforge.meta.transformations.nullableMetaToObject -import space.kscience.dataforge.meta.transformations.nullableObjectToMeta -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.startsWith - -@DFExperimental -public class MetaProperty( - public val meta: ObservableMutableMeta, - public val name: Name, - public val converter: MetaConverter, -) : Property { - - override var value: T? - get() = converter.nullableMetaToObject(meta[name]) - set(value) { - meta[name] = converter.nullableObjectToMeta(value) ?: Meta.EMPTY - } - - override fun onChange(owner: Any?, callback: (T?) -> Unit) { - meta.onChange(owner) { name -> - if (name.startsWith(this@MetaProperty.name)) callback(converter.nullableMetaToObject(this[name])) - } - } - - override fun removeChangeListener(owner: Any?) { - meta.removeListener(owner) - } -} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/Property.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/Property.kt deleted file mode 100644 index 7b0280eb..00000000 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/Property.kt +++ /dev/null @@ -1,47 +0,0 @@ -package space.kscience.dataforge.properties - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import space.kscience.dataforge.misc.DFExperimental - -@DFExperimental -public interface Property { - public var value: T - - public fun onChange(owner: Any? = null, callback: (T) -> Unit) - public fun removeChangeListener(owner: Any? = null) -} - -@DFExperimental -@OptIn(ExperimentalCoroutinesApi::class) -public fun Property.toFlow(): StateFlow = MutableStateFlow(value).also { stateFlow -> - onChange { - stateFlow.value = it - } -} - -/** - * Reflect all changes in the [source] property onto this property. Does not reflect changes back. - * - * @return a mirroring job - */ -@DFExperimental -public fun Property.mirror(source: Property) { - source.onChange(this) { - this.value = it - } -} - -/** - * Bi-directional connection between properties - */ -@DFExperimental -public fun Property.bind(other: Property) { - onChange(other) { - other.value = it - } - other.onChange { - this.value = it - } -} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt new file mode 100644 index 00000000..da539fcb --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt @@ -0,0 +1,51 @@ +package space.kscience.dataforge.properties + + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import space.kscience.dataforge.meta.* +import space.kscience.dataforge.misc.DFExperimental + +@DFExperimental +public fun ObservableMeta.asFlow(converter: MetaReader): Flow = callbackFlow { + onChange(this){ + trySend(converter.read(this)) + } + + awaitClose{ + removeListener(this) + } +} + +@DFExperimental +public fun MutableMeta.listenTo( + scope: CoroutineScope, + converter: MetaConverter, + flow: Flow, +): Job = flow.onEach { + update(converter.convert(it)) +}.launchIn(scope) + +@DFExperimental +public fun ObservableMutableMeta.bind( + scope: CoroutineScope, + converter: MetaConverter, + flow: MutableSharedFlow, +): Job = scope.launch{ + listenTo(this, converter,flow) + onChange(flow){ + launch { + flow.emit(converter.read(this@onChange)) + } + } + flow.onCompletion { + removeListener(flow) + } +}.also { + it.invokeOnCompletion { + removeListener(flow) + } +} diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/schemeProperty.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/schemeProperty.kt deleted file mode 100644 index 3b4d948d..00000000 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/schemeProperty.kt +++ /dev/null @@ -1,31 +0,0 @@ -package space.kscience.dataforge.properties - - -import space.kscience.dataforge.meta.Scheme -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.parseAsName -import space.kscience.dataforge.names.startsWith -import kotlin.reflect.KMutableProperty1 - -@DFExperimental -public fun S.property(property: KMutableProperty1): Property = - object : Property { - override var value: T? - get() = property.get(this@property) - set(value) { - property.set(this@property, value) - } - - override fun onChange(owner: Any?, callback: (T?) -> Unit) { - this@property.meta.onChange(this) { name -> - if (name.startsWith(property.name.parseAsName(true))) { - callback(property.get(this@property)) - } - } - } - - override fun removeChangeListener(owner: Any?) { - this@property.meta.removeListener(this@property) - } - - } \ No newline at end of file diff --git a/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/properties/MetaPropertiesTest.kt b/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/properties/MetaPropertiesTest.kt deleted file mode 100644 index 00b71673..00000000 --- a/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/properties/MetaPropertiesTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -package space.kscience.dataforge.properties - -import space.kscience.dataforge.meta.Scheme -import space.kscience.dataforge.meta.SchemeSpec -import space.kscience.dataforge.meta.int -import space.kscience.dataforge.misc.DFExperimental -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class TestScheme : Scheme() { - var a by int() - var b by int() - companion object : SchemeSpec(::TestScheme) -} - -@DFExperimental -class MetaPropertiesTest { - @Test - fun testBinding() { - val scheme = TestScheme.empty() - val a = scheme.property(TestScheme::a) - val b = scheme.property(TestScheme::b) - a.bind(b) - scheme.a = 2 - assertEquals(2, scheme.b) - assertEquals(2, b.value) - } -} \ No newline at end of file diff --git a/dataforge-context/src/jsMain/kotlin/space/kscience/dataforge/properties/bindings.kt b/dataforge-context/src/jsMain/kotlin/space/kscience/dataforge/properties/bindings.kt deleted file mode 100644 index b4451c97..00000000 --- a/dataforge-context/src/jsMain/kotlin/space/kscience/dataforge/properties/bindings.kt +++ /dev/null @@ -1,32 +0,0 @@ -package space.kscience.dataforge.properties - -import org.w3c.dom.HTMLInputElement -import space.kscience.dataforge.misc.DFExperimental - -@DFExperimental -public fun HTMLInputElement.bindValue(property: Property) { - if (this.onchange != null) error("Input element already bound") - this.onchange = { - property.value = this.value - Unit - } - property.onChange(this) { - if (value != it) { - value = it - } - } -} - -@DFExperimental -public fun HTMLInputElement.bindChecked(property: Property) { - if (this.onchange != null) error("Input element already bound") - this.onchange = { - property.value = this.checked - Unit - } - property.onChange(this) { - if (checked != it) { - checked = it - } - } -} \ No newline at end of file diff --git a/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/annotations.kt b/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/annotations.kt deleted file mode 100644 index f97cb28d..00000000 --- a/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/annotations.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2018 Alexander Nozik. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package space.kscience.dataforge.descriptors - -//@MustBeDocumented -//annotation class Attribute( -// val key: String, -// val value: String -//) -// -//@MustBeDocumented -//annotation class Attributes( -// val attrs: Array -//) -// -//@MustBeDocumented -//annotation class ItemDef( -// val info: String = "", -// val multiple: Boolean = false, -// val required: Boolean = false -//) -// -//@Target(AnnotationTarget.PROPERTY) -//@MustBeDocumented -//annotation class ValueDef( -// val type: Array = [ValueType.STRING], -// val def: String = "", -// val allowed: Array = [], -// val enumeration: KClass<*> = Any::class -//) - -///** -// * Description text for meta property, node or whole object -// */ -//@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) -//@Retention(AnnotationRetention.RUNTIME) -//@MustBeDocumented -//annotation class Description(val value: String) -// -///** -// * Annotation for value property which states that lists are expected -// */ -//@Target(AnnotationTarget.PROPERTY) -//@Retention(AnnotationRetention.RUNTIME) -//@MustBeDocumented -//annotation class Multiple -// -///** -// * Descriptor target -// * The DataForge path to the resource containing the description. Following targets are supported: -// * 1. resource -// * 1. file -// * 1. class -// * 1. method -// * 1. property -// * -// * -// * Does not work if [type] is provided -// */ -//@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) -//@Retention(AnnotationRetention.RUNTIME) -//@MustBeDocumented -//annotation class Descriptor(val value: String) -// -// -///** -// * Aggregator class for descriptor nodes -// */ -//@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER) -//@Retention(AnnotationRetention.RUNTIME) -//@MustBeDocumented -//annotation class DescriptorNodes(vararg val nodes: NodeDef) -// -///** -// * Aggregator class for descriptor values -// */ -//@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER) -//@Retention(AnnotationRetention.RUNTIME) -//@MustBeDocumented -//annotation class DescriptorValues(vararg val nodes: ValueDef) -// -///** -// * Alternative name for property descriptor declaration -// */ -//@Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) -//@Retention(AnnotationRetention.RUNTIME) -//@MustBeDocumented -//annotation class DescriptorName(val name: String) -// -//@Target(AnnotationTarget.PROPERTY) -//@Retention(AnnotationRetention.RUNTIME) -//@MustBeDocumented -//annotation class DescriptorValue(val def: ValueDef) -////TODO enter fields directly? -// -//@Target(AnnotationTarget.PROPERTY) -//@Retention(AnnotationRetention.RUNTIME) -//@MustBeDocumented -//annotation class ValueProperty( -// val name: String = "", -// val type: Array = arrayOf(ValueType.STRING), -// val multiple: Boolean = false, -// val def: String = "", -// val enumeration: KClass<*> = Any::class, -// val tags: Array = emptyArray() -//) -// -// -//@Target(AnnotationTarget.PROPERTY) -//@Retention(AnnotationRetention.RUNTIME) -//@MustBeDocumented -//annotation class NodeProperty(val name: String = "") diff --git a/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/reflectiveDescriptors.kt b/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/reflectiveDescriptors.kt index 9de5e280..590324d7 100644 --- a/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/reflectiveDescriptors.kt +++ b/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/reflectiveDescriptors.kt @@ -1,53 +1,132 @@ package space.kscience.dataforge.descriptors +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream +import org.slf4j.LoggerFactory +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder +import space.kscience.dataforge.misc.DFExperimental +import java.net.URL +import kotlin.reflect.KAnnotatedElement +import kotlin.reflect.KProperty -//inline fun T.buildDescriptor(): NodeDescriptor = NodeDescriptor { -// T::class.apply { -// findAnnotation()?.let { def -> -// info = def.info -// required = def.required -// multiple = def.multiple -// } -// findAnnotation()?.let { attr -> -// attributes { -// this[attr.key] = attr.value.parseValue() -// } -// } -// findAnnotation()?.attrs?.forEach { attr -> -// attributes { -// this[attr.key] = attr.value.parseValue() -// } -// } -// } -// T::class.memberProperties.forEach { property -> -// val delegate = property.getDelegate(this@buildDescriptor) +/** + * Description text for meta property, node or whole object + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +public annotation class Description(val value: String) + +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +public annotation class Multiple() + +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +public annotation class DescriptorResource(val resourceName: String) + +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +public annotation class DescriptorUrl(val url: String) + + +@OptIn(ExperimentalSerializationApi::class) +private fun MetaDescriptorBuilder.loadDescriptorFromUrl(url: URL) { + url.openStream().use { + from(Json.decodeFromStream(MetaDescriptor.serializer(), it)) + } +} + +private fun MetaDescriptorBuilder.loadDescriptorFromResource(resource: DescriptorResource) { + val url = {}.javaClass.getResource(resource.resourceName) + if (url != null) { + loadDescriptorFromUrl(url) + } else { + LoggerFactory.getLogger("System") + .error("Can't find descriptor resource with name ${resource.resourceName}") + } +} + +@DFExperimental +public fun MetaDescriptorBuilder.forAnnotatedElement(element: KAnnotatedElement) { + element.annotations.forEach { + when (it) { + is Description -> description = it.value + + is DescriptorResource -> loadDescriptorFromResource(it) + + is DescriptorUrl -> loadDescriptorFromUrl(URL(it.url)) + } + } +} + +@DFExperimental +public fun MetaDescriptorBuilder.forProperty(property: KProperty<*>) { + property.annotations.forEach { + when (it) { + is Description -> description = it.value + + is DescriptorResource -> loadDescriptorFromResource(it) + + is DescriptorUrl -> loadDescriptorFromUrl(URL(it.url)) + } + } +} // -// val descriptor: ItemDescriptor = when (delegate) { -// is ConfigurableDelegate -> buildPropertyDescriptor(property, delegate) -// is ReadWriteDelegateWrapper<*, *> -> { -// if (delegate.delegate is ConfigurableDelegate) { -// buildPropertyDescriptor(property, delegate.delegate as ConfigurableDelegate) -// } else { -// return@forEach +//@DFExperimental +//public fun MetaDescriptor.Companion.forScheme( +// spec: SchemeSpec, +// mod: MetaDescriptorBuilder.() -> Unit = {}, +//): MetaDescriptor = MetaDescriptor { +// val scheme = spec.empty() +// val kClass: KClass = scheme::class as KClass +// when { +// kClass.isSubclassOf(Number::class) -> valueType(ValueType.NUMBER) +// kClass == String::class -> ValueType.STRING +// kClass == Boolean::class -> ValueType.BOOLEAN +// kClass == DoubleArray::class -> ValueType.LIST +// kClass == ByteArray::class -> ValueType.LIST +// } +// +// forAnnotatedElement(kClass) +// kClass.memberProperties.forEach { property -> +// node(property.name) { +// +// (property.getDelegate(scheme) as? MetaDelegate<*>)?.descriptor?.let { +// from(it) +// } +// +// property.annotations.forEach { +// when (it) { +// is Description -> { +// description = it.value +// } +// +// is Multiple -> { +// multiple = true +// } +// +// is DescriptorResource -> { +// loadDescriptorFromResource(it) +// } +// +// is DescriptorUrl -> { +// loadDescriptorFromUrl(URL(it.url)) +// } // } // } -// else -> return@forEach -// } -// defineItem(property.name, descriptor) -// } -//} - -//inline fun buildPropertyDescriptor( -// property: KProperty1, -// delegate: ConfigurableDelegate -//): ItemDescriptor { -// when { -// V::class.isSubclassOf(Scheme::class) -> NodeDescriptor { -// default = delegate.default.node -// } -// V::class.isSubclassOf(Meta::class) -> NodeDescriptor { -// default = delegate.default.node // } // // } +// mod() //} +// +//@DFExperimental +//public inline fun SchemeSpec.autoDescriptor( +// noinline mod: MetaDescriptorBuilder.() -> Unit = {}, +//): MetaDescriptor = MetaDescriptor.forScheme(this, mod) \ No newline at end of file diff --git a/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/provider/dfType.kt b/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/provider/dfType.kt index ab34ea50..cccc3efb 100644 --- a/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/provider/dfType.kt +++ b/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/provider/dfType.kt @@ -4,7 +4,7 @@ import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.PluginBuilder import space.kscience.dataforge.context.gather import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.misc.DfId +import space.kscience.dataforge.misc.DfType import space.kscience.dataforge.misc.Named import space.kscience.dataforge.names.Name import kotlin.reflect.KClass @@ -12,21 +12,21 @@ import kotlin.reflect.full.findAnnotation @DFExperimental -public val KClass<*>.dfId: String - get() = findAnnotation()?.id ?: simpleName ?: "" +public val KClass<*>.dfType: String + get() = findAnnotation()?.id ?: simpleName ?: "" /** - * Provide an object with given name inferring target from its type using [DfId] annotation + * Provide an object with given name inferring target from its type using [DfType] annotation */ @DFExperimental public inline fun Provider.provideByType(name: String): T? { - val target = T::class.dfId + val target = T::class.dfType return provide(target, name) } @DFExperimental public inline fun Provider.top(): Map { - val target = T::class.dfId + val target = T::class.dfType return top(target) } @@ -35,15 +35,15 @@ public inline fun Provider.top(): Map { */ @DFExperimental public inline fun Context.gather(inherit: Boolean = true): Map = - gather(T::class.dfId, inherit) + gather(T::class.dfType, inherit) @DFExperimental public inline fun PluginBuilder.provides(items: Map) { - provides(T::class.dfId, items) + provides(T::class.dfType, items) } @DFExperimental public inline fun PluginBuilder.provides(vararg items: Named) { - provides(T::class.dfId, *items) + provides(T::class.dfType, *items) } diff --git a/dataforge-context/src/jvmTest/kotlin/space/kscience/dataforge/descriptors/TestAutoDescriptors.kt b/dataforge-context/src/jvmTest/kotlin/space/kscience/dataforge/descriptors/TestAutoDescriptors.kt new file mode 100644 index 00000000..3b1fce3d --- /dev/null +++ b/dataforge-context/src/jvmTest/kotlin/space/kscience/dataforge/descriptors/TestAutoDescriptors.kt @@ -0,0 +1,29 @@ +@file:OptIn(DFExperimental::class) + +package space.kscience.dataforge.descriptors + +import space.kscience.dataforge.misc.DFExperimental + +// +//class TestScheme : Scheme() { +// +// @Description("A") +// val a by string() +// +// @Description("B") +// val b by int() +// +// val c by int() +// +// companion object : SchemeSpec(::TestScheme) { +// override val descriptor: MetaDescriptor = autoDescriptor() +// } +//} +// +//class TestAutoDescriptors { +// @Test +// fun autoDescriptor() { +// val autoDescriptor = MetaDescriptor.forScheme(TestScheme) +// println(Json { prettyPrint = true }.encodeToString(autoDescriptor)) +// } +//} \ No newline at end of file diff --git a/dataforge-context/src/wasmJsMain/kotlin/space/kscience/dataforge/context/loggingWasm.kt b/dataforge-context/src/wasmJsMain/kotlin/space/kscience/dataforge/context/loggingWasm.kt new file mode 100644 index 00000000..740957b4 --- /dev/null +++ b/dataforge-context/src/wasmJsMain/kotlin/space/kscience/dataforge/context/loggingWasm.kt @@ -0,0 +1,3 @@ +package space.kscience.dataforge.context + +internal actual fun getGlobalLoggerFactory(): PluginFactory = DefaultLogManager \ No newline at end of file diff --git a/dataforge-data/README.md b/dataforge-data/README.md index 5935af6e..35aaa4e6 100644 --- a/dataforge-data/README.md +++ b/dataforge-data/README.md @@ -6,18 +6,16 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:dataforge-data:0.7.0`. +The Maven coordinates of this project are `space.kscience:dataforge-data:0.9.0-dev-1`. **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - //uncomment to access development builds - //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:dataforge-data:0.7.0") + implementation("space.kscience:dataforge-data:0.9.0-dev-1") } ``` diff --git a/dataforge-data/build.gradle.kts b/dataforge-data/build.gradle.kts index 9f96604a..99314ea3 100644 --- a/dataforge-data/build.gradle.kts +++ b/dataforge-data/build.gradle.kts @@ -6,9 +6,12 @@ kscience{ jvm() js() native() + wasm() useCoroutines() dependencies { - api(project(":dataforge-meta")) + api(spclibs.atomicfu) + api(projects.dataforgeMeta) + //Remove after subtype moved to stdlib api(kotlin("reflect")) } } diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/AbstractAction.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/AbstractAction.kt index e7bbe6f6..4ed5f8df 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/AbstractAction.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/AbstractAction.kt @@ -1,9 +1,14 @@ package space.kscience.dataforge.actions -import kotlinx.coroutines.launch -import space.kscience.dataforge.data.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.onEach +import space.kscience.dataforge.data.DataSink +import space.kscience.dataforge.data.DataTree +import space.kscience.dataforge.data.DataUpdate +import space.kscience.dataforge.data.launchUpdate import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DFInternal +import space.kscience.dataforge.misc.UnsafeKType import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.startsWith import kotlin.reflect.KType @@ -19,47 +24,47 @@ internal fun MutableMap.removeWhatStartsWith(name: Name) { /** * An action that caches results on-demand and recalculates them on source push */ -public abstract class AbstractAction( +public abstract class AbstractAction( public val outputType: KType, ) : Action { /** * Generate initial content of the output */ - protected abstract fun DataSetBuilder.generate( - data: DataSet, + protected abstract fun DataSink.generate( + source: DataTree, meta: Meta, ) /** - * Update part of the data set when given [updateKey] is triggered by the source + * Update part of the data set using provided data + * + * @param source the source data tree in case we need several data items to update + * @param meta the metadata used for the whole data tree + * @param updatedData an updated item */ - protected open fun DataSourceBuilder.update( - dataSet: DataSet, + protected open suspend fun DataSink.update( + source: DataTree, meta: Meta, - updateKey: Name, + updatedData: DataUpdate, ) { - // By default, recalculate the whole dataset - generate(dataSet, meta) + //by default regenerate the whole data set + generate(source, meta) } - @OptIn(DFInternal::class) + @OptIn(UnsafeKType::class) override fun execute( - dataSet: DataSet, + source: DataTree, meta: Meta, - ): DataSet = if (dataSet is DataSource) { - DataSource(outputType, dataSet){ - generate(dataSet, meta) - - launch { - dataSet.updates.collect { name -> - update(dataSet, meta, name) - } - } - } - } else { - DataTree(outputType) { - generate(dataSet, meta) + updatesScope: CoroutineScope + ): DataTree = DataTree(outputType) { + generate(source, meta) + //propagate updates + launchUpdate(updatesScope) { + source.updates.onEach { update -> + update(source, meta, update) + }.collect() } } } + diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/Action.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/Action.kt index 4fed8e51..80898aa8 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/Action.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/Action.kt @@ -1,40 +1,50 @@ package space.kscience.dataforge.actions -import space.kscience.dataforge.data.DataSet +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import space.kscience.dataforge.data.DataTree import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.misc.DFExperimental /** * A simple data transformation on a data node. Actions should avoid doing actual dependency evaluation in [execute]. */ -public interface Action { +public fun 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. */ - public fun execute(dataSet: DataSet, meta: Meta = Meta.EMPTY): DataSet + public fun execute(source: DataTree, meta: Meta, updatesScope: CoroutineScope): DataTree public companion object } +/** + * A convenience method to transform data using given [action] + */ +@OptIn(DelicateCoroutinesApi::class) +public fun DataTree.transform( + action: Action, + meta: Meta = Meta.EMPTY, + updateScope: CoroutineScope = GlobalScope, +): DataTree = action.execute(this, meta, updateScope) + /** * Action composition. The result is terminal if one of its parts is terminal */ -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 execute( - dataSet: DataSet, - meta: Meta, - ): DataSet = action.execute(this@then.execute(dataSet, meta), meta) - } +public infix fun Action.then(action: Action): Action = Action { dataSet, meta, scope -> + action.execute(this@then.execute(dataSet, meta, scope), meta, scope) } @DFExperimental -public operator fun Action.invoke( - dataSet: DataSet, +@OptIn(DelicateCoroutinesApi::class) +public operator fun Action.invoke( + dataSet: DataTree, meta: Meta = Meta.EMPTY, -): DataSet = execute(dataSet, meta) + updateScope: CoroutineScope = GlobalScope, +): DataTree = execute(dataSet, meta, updateScope) + + diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/MapAction.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/MapAction.kt index 883b3928..08bf08e9 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/MapAction.kt @@ -6,8 +6,7 @@ import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.seal import space.kscience.dataforge.meta.toMutableMeta import space.kscience.dataforge.misc.DFBuilder -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.misc.DFInternal +import space.kscience.dataforge.misc.UnsafeKType import space.kscience.dataforge.names.Name import kotlin.reflect.KType import kotlin.reflect.typeOf @@ -29,6 +28,7 @@ public class MapActionBuilder( public var name: Name, public var meta: MutableMeta, public val actionMeta: Meta, + public val dataType: KType, @PublishedApi internal var outputType: KType, ) { @@ -45,19 +45,21 @@ public class MapActionBuilder( /** * Calculate the result of goal */ - public inline fun result(noinline f: suspend ActionEnv.(T) -> R1) { - outputType = typeOf() - result = f; - } + public inline fun result(noinline f: suspend ActionEnv.(T) -> R1): Unit = result(typeOf(), f) } -@PublishedApi -internal class MapAction( +@UnsafeKType +public class MapAction( outputType: KType, private val block: MapActionBuilder.() -> Unit, ) : AbstractAction(outputType) { - private fun DataSetBuilder.mapOne(name: Name, data: Data, meta: Meta) { + private fun DataSink.mapOne(name: Name, data: Data?, meta: Meta) { + //fast return for null data + if (data == null) { + put(name, null) + return + } // Creating a new environment for action using **old** name, old meta and task meta val env = ActionEnv(name, data.meta, meta) @@ -66,6 +68,7 @@ internal class MapAction( name, data.meta.toMutableMeta(), // using data meta meta, + data.type, outputType ).apply(block) @@ -75,21 +78,25 @@ internal class MapAction( //getting new meta val newMeta = builder.meta.seal() - @OptIn(DFInternal::class) val newData = Data(builder.outputType, newMeta, dependencies = listOf(data)) { builder.result(env, data.await()) } //setting the data node - data(newName, newData) + put(newName, newData) } - override fun DataSetBuilder.generate(data: DataSet, meta: Meta) { - data.forEach { mapOne(it.name, it.data, meta) } + override fun DataSink.generate(source: DataTree, meta: Meta) { + source.forEach { mapOne(it.name, it.data, meta) } } - override fun DataSourceBuilder.update(dataSet: DataSet, meta: Meta, updateKey: Name) { - remove(updateKey) - dataSet[updateKey]?.let { mapOne(updateKey, it, meta) } + + + override suspend fun DataSink.update( + source: DataTree, + meta: Meta, + updatedData: DataUpdate, + ) { + mapOne(updatedData.name, updatedData.data, meta) } } @@ -97,9 +104,9 @@ internal class MapAction( /** * A one-to-one mapping action */ -@DFExperimental -@Suppress("FunctionName") -public inline fun Action.Companion.map( + +@OptIn(UnsafeKType::class) +public inline fun Action.Companion.mapping( noinline builder: MapActionBuilder.() -> Unit, ): Action = MapAction(typeOf(), builder) diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/ReduceAction.kt index fe823bd7..93278442 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/ReduceAction.kt @@ -4,17 +4,16 @@ import space.kscience.dataforge.data.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.misc.DFBuilder -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.misc.DFInternal +import space.kscience.dataforge.misc.UnsafeKType import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.parseAsName import kotlin.reflect.KType import kotlin.reflect.typeOf -public class JoinGroup( +public class JoinGroup( public var name: String, - internal val set: DataSet, + internal val set: DataTree, @PublishedApi internal var outputType: KType, ) { @@ -22,12 +21,12 @@ public class JoinGroup( public lateinit var result: suspend ActionEnv.(Map>) -> R - internal fun result(outputType: KType, f: suspend ActionEnv.(Map>) -> R1) { + internal fun result(outputType: KType, f: suspend ActionEnv.(Map>) -> R1) { this.outputType = outputType this.result = f; } - public inline fun result(noinline f: suspend ActionEnv.(Map>) -> R1) { + public inline fun result(noinline f: suspend ActionEnv.(Map>) -> R1) { outputType = typeOf() this.result = f; } @@ -35,11 +34,11 @@ public class JoinGroup( } @DFBuilder -public class ReduceGroupBuilder( +public class ReduceGroupBuilder( public val actionMeta: Meta, private val outputType: KType, ) { - private val groupRules: MutableList<(DataSet) -> List>> = ArrayList(); + private val groupRules: MutableList<(DataTree) -> List>> = ArrayList(); /** * introduce grouping by meta value @@ -54,12 +53,12 @@ public class ReduceGroupBuilder( public fun group( groupName: String, - predicate: (Name, Meta) -> Boolean, + predicate: DataFilter, action: JoinGroup.() -> Unit, ) { groupRules += { source -> listOf( - JoinGroup(groupName, source.filter(predicate), outputType).apply(action) + JoinGroup(groupName, source.filterData(predicate), outputType).apply(action) ) } } @@ -67,26 +66,26 @@ public class ReduceGroupBuilder( /** * Apply transformation to the whole node */ - public fun result(resultName: String, f: suspend ActionEnv.(Map>) -> R) { + public fun result(resultName: String, f: suspend ActionEnv.(Map>) -> R) { groupRules += { node -> listOf(JoinGroup(resultName, node, outputType).apply { result(outputType, f) }) } } - internal fun buildGroups(input: DataSet): List> = + internal fun buildGroups(input: DataTree): List> = groupRules.flatMap { it.invoke(input) } } @PublishedApi -internal class ReduceAction( +internal class ReduceAction( outputType: KType, private val action: ReduceGroupBuilder.() -> Unit, ) : AbstractAction(outputType) { //TODO optimize reduction. Currently, the whole action recalculates on push - override fun DataSetBuilder.generate(data: DataSet, meta: Meta) { - ReduceGroupBuilder(meta, outputType).apply(action).buildGroups(data).forEach { group -> + override fun DataSink.generate(source: DataTree, meta: Meta) { + ReduceGroupBuilder(meta, outputType).apply(action).buildGroups(source).forEach { group -> val dataFlow: Map> = group.set.asSequence().fold(HashMap()) { acc, value -> acc.apply { acc[value.name] = value.data @@ -98,12 +97,12 @@ internal class ReduceAction( val groupMeta = group.meta val env = ActionEnv(groupName.parseAsName(), groupMeta, meta) - @OptIn(DFInternal::class) val res: Data = dataFlow.reduceToData( + @OptIn(UnsafeKType::class) val res: Data = dataFlow.reduceToData( group.outputType, meta = groupMeta ) { group.result.invoke(env, it) } - data(env.name, res) + put(env.name, res) } } } @@ -111,7 +110,6 @@ internal class ReduceAction( /** * A one-to-one mapping action */ -@DFExperimental -public inline fun Action.Companion.reduce( +public inline fun Action.Companion.reducing( noinline builder: ReduceGroupBuilder.() -> Unit, ): Action = ReduceAction(typeOf(), builder) diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/SplitAction.kt index 24745929..2268b0fa 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/SplitAction.kt @@ -5,7 +5,6 @@ import space.kscience.dataforge.meta.Laminate import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MutableMeta import space.kscience.dataforge.meta.toMutableMeta -import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.parseAsName import kotlin.collections.set @@ -13,9 +12,9 @@ import kotlin.reflect.KType import kotlin.reflect.typeOf -public class SplitBuilder(public val name: Name, public val meta: Meta) { +public class SplitBuilder(public val name: Name, public val meta: Meta) { - public class FragmentRule( + public class FragmentRule( public val name: Name, public var meta: MutableMeta, @PublishedApi internal var outputType: KType, @@ -44,15 +43,15 @@ 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( +internal class SplitAction( outputType: KType, private val action: SplitBuilder.() -> Unit, ) : AbstractAction(outputType) { - private fun DataSetBuilder.splitOne(name: Name, data: Data, meta: Meta) { - val laminate = Laminate(data.meta, meta) + private fun DataSink.splitOne(name: Name, data: Data?, meta: Meta) { + val laminate = Laminate(data?.meta, meta) - val split = SplitBuilder(name, data.meta).apply(action) + val split = SplitBuilder(name, data?.meta ?: Meta.EMPTY).apply(action) // apply individual fragment rules to result @@ -64,29 +63,36 @@ internal class SplitAction( ).apply(rule) //data.map(outputType, meta = env.meta) { env.result(it) }.named(fragmentName) - data( - fragmentName, - @Suppress("OPT_IN_USAGE") Data(outputType, meta = env.meta, dependencies = listOf(data)) { - env.result(data.await()) - } - ) + if (data == null) { + put(fragmentName, null) + } else { + put( + fragmentName, + @Suppress("OPT_IN_USAGE") Data(outputType, meta = env.meta, dependencies = listOf(data)) { + env.result(data.await()) + } + ) + } } } - override fun DataSetBuilder.generate(data: DataSet, meta: Meta) { - data.forEach { splitOne(it.name, it.data, meta) } + override fun DataSink.generate(source: DataTree, meta: Meta) { + source.forEach { splitOne(it.name, it.data, meta) } } - override fun DataSourceBuilder.update(dataSet: DataSet, meta: Meta, updateKey: Name) { - remove(updateKey) - dataSet[updateKey]?.let { splitOne(updateKey, it, meta) } + override suspend fun DataSink.update( + source: DataTree, + meta: Meta, + updatedData: DataUpdate, + ) { + splitOne(updatedData.name, updatedData.data, meta) } } /** * Action that splits each incoming element into a number of fragments defined in builder */ -@DFExperimental -public inline fun Action.Companion.split( + +public inline fun Action.Companion.splitting( noinline builder: SplitBuilder.() -> Unit, ): Action = SplitAction(typeOf(), builder) \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Data.kt index 984582e5..b9946a48 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Data.kt @@ -4,8 +4,8 @@ import kotlinx.coroutines.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaRepr import space.kscience.dataforge.meta.isEmpty -import space.kscience.dataforge.misc.DFInternal -import space.kscience.dataforge.misc.DfId +import space.kscience.dataforge.misc.DfType +import space.kscience.dataforge.misc.UnsafeKType import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.reflect.KType @@ -14,7 +14,7 @@ import kotlin.reflect.typeOf /** * A data element characterized by its meta */ -@DfId(Data.TYPE) +@DfType(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. @@ -41,7 +41,7 @@ public interface Data : Goal, MetaRepr { */ internal val TYPE_OF_NOTHING: KType = typeOf() - public inline fun static( + public inline fun wrapValue( value: T, meta: Meta = Meta.EMPTY, ): Data = StaticData(typeOf(), value, meta) @@ -50,10 +50,10 @@ public interface Data : Goal, MetaRepr { * An empty data containing only meta */ @OptIn(DelicateCoroutinesApi::class) - public fun empty(meta: Meta): Data = object : Data { - override val type: KType = TYPE_OF_NOTHING + public fun buildEmpty(meta: Meta): Data = object : Data { + override val type: KType get() = TYPE_OF_NOTHING override val meta: Meta = meta - override val dependencies: Collection> = emptyList() + override val dependencies: Collection> get() = emptyList() override val deferred: Deferred get() = GlobalScope.async(start = CoroutineStart.LAZY) { error("The Data is empty and could not be computed") @@ -62,6 +62,8 @@ public interface Data : Goal, MetaRepr { override fun async(coroutineScope: CoroutineScope): Deferred = deferred override fun reset() {} } + + public val EMPTY: Data = buildEmpty(Meta.EMPTY) } } @@ -69,39 +71,37 @@ public interface Data : Goal, MetaRepr { * 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( +private class LazyData( override val type: KType, override val meta: Meta = Meta.EMPTY, additionalContext: CoroutineContext = EmptyCoroutineContext, - dependencies: Collection> = emptyList(), + dependencies: Iterable> = emptyList(), block: suspend () -> T, ) : Data, LazyGoal(additionalContext, dependencies, block) -public class StaticData( +public class StaticData( override val type: KType, value: T, override val meta: Meta = Meta.EMPTY, ) : Data, StaticGoal(value) @Suppress("FunctionName") -public inline fun Data(value: T, meta: Meta = Meta.EMPTY): StaticData = +public inline fun Data(value: T, meta: Meta = Meta.EMPTY): StaticData = StaticData(typeOf(), value, meta) -@Suppress("FunctionName") -@DFInternal -public fun Data( +@UnsafeKType +public fun Data( type: KType, meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, - dependencies: Collection> = emptyList(), + dependencies: Iterable> = emptyList(), block: suspend () -> T, ): Data = LazyData(type, meta, context, dependencies, block) -@OptIn(DFInternal::class) -@Suppress("FunctionName") -public inline fun Data( +@OptIn(UnsafeKType::class) +public inline fun Data( meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, - dependencies: Collection> = emptyList(), + dependencies: Iterable> = emptyList(), noinline block: suspend () -> T, ): Data = Data(typeOf(), meta, context, dependencies, block) diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt new file mode 100644 index 00000000..38174e50 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt @@ -0,0 +1,110 @@ +package space.kscience.dataforge.data + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.NameToken +import space.kscience.dataforge.names.plus +import kotlin.reflect.KType + +public fun interface DataFilter { + + public fun accepts(name: Name, meta: Meta?, type: KType): Boolean + + public companion object { + public val EMPTY: DataFilter = DataFilter { _, _, _ -> true } + } +} + + +public fun DataFilter.accepts(update: DataUpdate<*>): Boolean = accepts(update.name, update.data?.meta, update.type) + +public fun > Sequence.filterData(predicate: DataFilter): Sequence = filter { data -> + predicate.accepts(data) +} + +public fun > Flow.filterData(predicate: DataFilter): Flow = filter { data -> + predicate.accepts(data) +} + +public fun DataSource.filterData( + predicate: DataFilter, +): DataSource = object : DataSource { + override val dataType: KType get() = this@filterData.dataType + + override fun read(name: Name): Data? = + this@filterData.read(name)?.takeIf { predicate.accepts(name, it.meta, it.type) } +} + +/** + * Stateless filtered [ObservableDataSource] + */ +public fun ObservableDataSource.filterData( + predicate: DataFilter, +): ObservableDataSource = object : ObservableDataSource { + override val updates: Flow> + get() = this@filterData.updates.filter { predicate.accepts(it) } + + override val dataType: KType get() = this@filterData.dataType + + override fun read(name: Name): Data? = + this@filterData.read(name)?.takeIf { predicate.accepts(name, it.meta, it.type) } +} + +internal class FilteredDataTree( + val source: DataTree, + val filter: DataFilter, + val branch: Name, + override val dataType: KType = source.dataType, +) : DataTree { + + override val data: Data? + get() = source[branch].takeIf { + filter.accepts(Name.EMPTY, it?.meta, it?.type ?: dataType) + } + + override val items: Map> + get() = source.branch(branch)?.items + ?.mapValues { FilteredDataTree(source, filter, branch + it.key) } + ?.filter { !it.value.isEmpty() } + ?: emptyMap() + + override val updates: Flow> + get() = source.updates.filter { filter.accepts(it) } +} + + +public fun DataTree.filterData( + predicate: DataFilter, +): DataTree = FilteredDataTree(this, predicate, Name.EMPTY) + + +///** +// * Generate a wrapper data set with a given name prefix appended to all names +// */ +//public fun DataTree.withNamePrefix(prefix: Name): DataSet = if (prefix.isEmpty()) { +// this +//} else object : DataSource { +// +// override val dataType: KType get() = this@withNamePrefix.dataType +// +// override val coroutineContext: CoroutineContext +// get() = (this@withNamePrefix as? DataSource)?.coroutineContext ?: EmptyCoroutineContext +// +// override val meta: Meta get() = this@withNamePrefix.meta +// +// +// override fun iterator(): Iterator> = iterator { +// for (d in this@withNamePrefix) { +// yield(d.data.named(prefix + d.name)) +// } +// } +// +// override fun get(name: Name): Data? = +// name.removeFirstOrNull(name)?.let { this@withNamePrefix.get(it) } +// +// override val updates: Flow get() = this@withNamePrefix.updates.map { prefix + it } +//} +// + diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSet.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSet.kt deleted file mode 100644 index 44639653..00000000 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSet.kt +++ /dev/null @@ -1,124 +0,0 @@ -package space.kscience.dataforge.data - -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.mapNotNull -import space.kscience.dataforge.data.Data.Companion.TYPE_OF_NOTHING -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.asName -import space.kscience.dataforge.names.endsWith -import space.kscience.dataforge.names.parseAsName -import kotlin.reflect.KType - -public interface DataSet { - - /** - * The minimal common ancestor to all data in the node - */ - public val dataType: KType - - /** - * Meta-data associated with this node. If no meta is provided, returns [Meta.EMPTY]. - */ - public val meta: Meta - - /** - * Traverse this [DataSet] returning named data instances. The order is not guaranteed. - */ - public operator fun iterator(): Iterator> - - /** - * Get data with given name. - */ - public operator fun get(name: Name): Data? - - 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: KType = TYPE_OF_NOTHING - override val meta: Meta get() = Meta.EMPTY - - override fun iterator(): Iterator> = emptySequence>().iterator() - - override fun get(name: Name): Data? = null - } - } -} - -public fun DataSet.asSequence(): Sequence> = object : Sequence> { - override fun iterator(): Iterator> = this@asSequence.iterator() -} - -/** - * Return a single [Data] in this [DataSet]. Throw error if it is not single. - */ -public fun DataSet.single(): NamedData = asSequence().single() - -public fun DataSet.asIterable(): Iterable> = object : Iterable> { - override fun iterator(): Iterator> = this@asIterable.iterator() -} - -public operator fun DataSet.get(name: String): Data? = get(name.parseAsName()) - -/** - * A [DataSet] with propagated updates. - */ -public interface DataSource : DataSet, CoroutineScope { - - /** - * 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 - - /** - * Stop generating updates from this [DataSource] - */ - public fun close() { - coroutineContext[Job]?.cancel() - } -} - -public val DataSet.updates: Flow get() = if (this is DataSource) updates else emptyFlow() -// -///** -// * Flow all data nodes with names starting with [branchName] -// */ -//public fun DataSet.children(branchName: Name): Sequence> = -// this@children.asSequence().filter { -// it.name.startsWith(branchName) -// } - -/** - * 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 { - asIterable().map { - it.launch(this@launch) - }.joinAll() -} - -public suspend fun DataSet.computeAndJoinAll(): Unit = coroutineScope { startAll(this).join() } - -public fun DataSet<*>.toMeta(): Meta = Meta { - forEach { - if (it.name.endsWith(DataSet.META_KEY)) { - set(it.name, it.meta) - } else { - it.name put { - "type" put it.type.toString() - "meta" put it.meta - } - } - } -} - -public val DataSet.updatesWithData: Flow> get() = updates.mapNotNull { get(it)?.named(it) } \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSetBuilder.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSetBuilder.kt deleted file mode 100644 index f9f14f37..00000000 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSetBuilder.kt +++ /dev/null @@ -1,165 +0,0 @@ -package space.kscience.dataforge.data - -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.isEmpty -import space.kscience.dataforge.names.plus -import kotlin.reflect.KType - -public interface DataSetBuilder { - public val dataType: KType - - /** - * Remove all data items starting with [name] - */ - public fun remove(name: Name) - - public fun data(name: Name, data: Data?) - - /** - * Set a current state of given [dataSet] into a branch [name]. Does not propagate updates - */ - public fun node(name: Name, dataSet: DataSet) { - //remove previous items - if (name != Name.EMPTY) { - remove(name) - } - - //Set new items - dataSet.forEach { - data(name + it.name, it.data) - } - } - - /** - * Set meta for the given node - */ - public fun meta(name: Name, meta: Meta) - -} - -/** - * Define meta in this [DataSet] - */ -public fun DataSetBuilder.meta(value: Meta): Unit = meta(Name.EMPTY, value) - -/** - * Define meta in this [DataSet] - */ -public fun DataSetBuilder.meta(mutableMeta: MutableMeta.() -> Unit): Unit = meta(Meta(mutableMeta)) - -@PublishedApi -internal class SubSetBuilder( - private val parent: DataSetBuilder, - private val branch: Name, -) : DataSetBuilder { - override val dataType: KType get() = parent.dataType - - override fun remove(name: Name) { - parent.remove(branch + name) - } - - override fun data(name: Name, data: Data?) { - parent.data(branch + name, data) - } - - override fun node(name: Name, dataSet: DataSet) { - parent.node(branch + name, dataSet) - } - - override fun meta(name: Name, meta: Meta) { - parent.meta(branch + name, meta) - } -} - -public inline fun DataSetBuilder.node( - name: Name, - crossinline block: DataSetBuilder.() -> Unit, -) { - if (name.isEmpty()) block() else SubSetBuilder(this, name).block() -} - - -public fun DataSetBuilder.data(name: String, value: Data) { - data(Name.parse(name), value) -} - -public fun DataSetBuilder.node(name: String, set: DataSet) { - node(Name.parse(name), set) -} - -public inline fun DataSetBuilder.node( - name: String, - crossinline block: DataSetBuilder.() -> Unit, -): Unit = node(Name.parse(name), block) - -public fun DataSetBuilder.set(value: NamedData) { - data(value.name, value.data) -} - -/** - * Produce lazy [Data] and emit it into the [DataSetBuilder] - */ -public inline fun DataSetBuilder.produce( - name: String, - meta: Meta = Meta.EMPTY, - noinline producer: suspend () -> T, -) { - val data = Data(meta, block = producer) - data(name, data) -} - -public inline fun DataSetBuilder.produce( - name: Name, - meta: Meta = Meta.EMPTY, - noinline producer: suspend () -> T, -) { - val data = Data(meta, block = producer) - data(name, data) -} - -/** - * Emit a static data with the fixed value - */ -public inline fun DataSetBuilder.static( - name: String, - data: T, - meta: Meta = Meta.EMPTY, -): Unit = data(name, Data.static(data, meta)) - -public inline fun DataSetBuilder.static( - name: Name, - data: T, - meta: Meta = Meta.EMPTY, -): Unit = data(name, Data.static(data, meta)) - -public inline fun DataSetBuilder.static( - name: String, - data: T, - mutableMeta: MutableMeta.() -> Unit, -): Unit = data(Name.parse(name), Data.static(data, Meta(mutableMeta))) - -/** - * Update data with given node data and meta with node meta. - */ -@DFExperimental -public fun DataSetBuilder.populateFrom(tree: DataSet): Unit { - tree.forEach { - //TODO check if the place is occupied - data(it.name, it.data) - } -} - -//public fun DataSetBuilder.populateFrom(flow: Flow>) { -// flow.collect { -// data(it.name, it.data) -// } -//} - -public fun DataSetBuilder.populateFrom(sequence: Sequence>) { - sequence.forEach { - data(it.name, it.data) - } -} diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSink.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSink.kt new file mode 100644 index 00000000..6daeae98 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSink.kt @@ -0,0 +1,159 @@ +package space.kscience.dataforge.data + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.launch +import space.kscience.dataforge.misc.UnsafeKType +import space.kscience.dataforge.names.* +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +public interface DataSink { + /** + * Put data without notification + */ + public fun put(name: Name, data: Data?) + + /** + * Put data and propagate changes downstream + */ + public suspend fun update(name: Name, data: Data?) +} + +/** + * Launch continuous update using + */ +public fun DataSink.launchUpdate( + scope: CoroutineScope, + updater: suspend DataSink.() -> Unit, +): Job = scope.launch { + object : DataSink { + override fun put(name: Name, data: Data?) { + launch { + this@launchUpdate.update(name, data) + } + } + + override suspend fun update(name: Name, data: Data?) { + this@launchUpdate.update(name, data) + } + }.updater() +} + +/** + * A mutable version of [DataTree] + */ +public interface MutableDataTree : DataTree, DataSink { + override var data: Data? + + override val items: Map> + + public fun getOrCreateItem(token: NameToken): MutableDataTree + + public operator fun set(token: NameToken, data: Data?) + + override fun put(name: Name, data: Data?): Unit = set(name, data) +} + +public tailrec operator fun MutableDataTree.set(name: Name, data: Data?): Unit { + when (name.length) { + 0 -> this.data = data + 1 -> set(name.first(), data) + else -> getOrCreateItem(name.first())[name.cutFirst()] = data + } +} + +/** + * Provide a mutable subtree if it exists + */ +public tailrec fun MutableDataTree.branch(name: Name): MutableDataTree? = + when (name.length) { + 0 -> this + 1 -> items[name.first()] + else -> items[name.first()]?.branch(name.cutFirst()) + } + +private class MutableDataTreeRoot( + override val dataType: KType, +) : MutableDataTree { + + override val updates = MutableSharedFlow>(100, onBufferOverflow = BufferOverflow.DROP_LATEST) + + + inner class MutableDataTreeBranch(val branchName: Name) : MutableDataTree { + + override var data: Data? = null + + override val items = HashMap>() + + override val updates: Flow> = this@MutableDataTreeRoot.updates.mapNotNull { update -> + update.name.removeFirstOrNull(branchName)?.let { + DataUpdate(update.data?.type ?: dataType, it, update.data) + } + } + override val dataType: KType get() = this@MutableDataTreeRoot.dataType + + + override fun getOrCreateItem(token: NameToken): MutableDataTree = + items.getOrPut(token) { MutableDataTreeBranch(branchName + token) } + + + override fun set(token: NameToken, data: Data?) { + val subTree = getOrCreateItem(token) + subTree.data = data + } + + override suspend fun update(name: Name, data: Data?) { + if (name.isEmpty()) { + this.data = data + this@MutableDataTreeRoot.updates.emit(DataUpdate(data?.type ?: dataType, branchName + name, data)) + } else { + getOrCreateItem(name.first()).update(name.cutFirst(), data) + } + } + + } + + + override var data: Data? = null + + override val items = HashMap>() + + override fun getOrCreateItem(token: NameToken): MutableDataTree = items.getOrPut(token) { + MutableDataTreeBranch(token.asName()) + } + + override fun set(token: NameToken, data: Data?) { + val subTree = getOrCreateItem(token) + subTree.data = data + } + + override suspend fun update(name: Name, data: Data?) { + if (name.isEmpty()) { + this.data = data + updates.emit(DataUpdate(data?.type ?: dataType, name, data)) + } else { + getOrCreateItem(name.first()).update(name.cutFirst(), data) + } + } +} + +/** + * Create a new [MutableDataTree] + */ +@UnsafeKType +public fun MutableDataTree( + type: KType, +): MutableDataTree = MutableDataTreeRoot(type) + +/** + * Create and initialize a observable mutable data tree. + */ +@OptIn(UnsafeKType::class) +public inline fun MutableDataTree( + generator: MutableDataTree.() -> Unit = {}, +): MutableDataTree = MutableDataTree(typeOf()).apply { generator() } \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSource.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSource.kt new file mode 100644 index 00000000..a9756be7 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSource.kt @@ -0,0 +1,131 @@ +package space.kscience.dataforge.data + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.names.* +import kotlin.contracts.contract +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +/** + * A generic data provider + */ +public interface DataSource { + + /** + * The minimal common ancestor to all data in the node + */ + public val dataType: KType + + /** + * Get data with given name. Or null if it is not present + */ + public fun read(name: Name): Data? +} + +/** + * A data provider with possible dynamic updates + */ +public interface ObservableDataSource : DataSource { + + /** + * Flow updates made to the data + */ + public val updates: Flow> +} + +/** + * A tree like structure for data holding + */ +public interface DataTree : ObservableDataSource { + + public val data: Data? + public val items: Map> + + override fun read(name: Name): Data? = when (name.length) { + 0 -> data + else -> items[name.first()]?.read(name.cutFirst()) + } + + /** + * Flow updates made to the data + */ + override val updates: Flow> + + public companion object { + private object EmptyDataTree : + DataTree { + override val data: Data? = null + override val items: Map = emptyMap() + override val dataType: KType = typeOf() + + override fun read(name: Name): Data? = null + override val updates: Flow> get() = emptyFlow() + } + + public val EMPTY: DataTree = EmptyDataTree + } +} + +/** + * An alias for easier access to tree values + */ +public operator fun DataTree.get(name: Name): Data? = read(name) + +public operator fun DataTree.get(name: String): Data? = read(name.parseAsName()) + +/** + * Return a sequence of all data items in this tree. + * This method does not take updates into account. + */ +public fun DataTree.asSequence( + namePrefix: Name = Name.EMPTY, +): Sequence> = sequence { + data?.let { yield(it.named(namePrefix)) } + items.forEach { (token, tree) -> + yieldAll(tree.asSequence(namePrefix + token)) + } +} + +/** + * Walk the data tree depth-first. + * + * @return a [Sequence] of pairs [Name]-[DataTree] for all nodes including the root one. + */ +public fun DataTree.walk( + namePrefix: Name = Name.EMPTY, +): Sequence>> = sequence { + yield(namePrefix to this@walk) + items.forEach { (token, tree) -> + yieldAll(tree.walk(namePrefix + token)) + } +} + +public val DataTree<*>.meta: Meta? get() = data?.meta + +/** + * Provide subtree if it exists + */ +public tailrec fun DataTree.branch(name: Name): DataTree? = + when (name.length) { + 0 -> this + 1 -> items[name.first()] + else -> items[name.first()]?.branch(name.cutFirst()) + } + +public fun DataTree.branch(name: String): DataTree? = + branch(name.parseAsName()) + +public fun DataTree<*>.isEmpty(): Boolean = data == null && items.isEmpty() + +/** + * Check if the [DataTree] is observable + */ +public fun DataSource.isObservable(): Boolean { + contract { + returns(true) implies (this@isObservable is ObservableDataSource) + } + return this is ObservableDataSource +} + diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTree.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTree.kt deleted file mode 100644 index bafcbea2..00000000 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTree.kt +++ /dev/null @@ -1,119 +0,0 @@ -package space.kscience.dataforge.data - -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DFInternal -import space.kscience.dataforge.misc.DfId -import space.kscience.dataforge.names.* -import kotlin.collections.component1 -import kotlin.collections.component2 -import kotlin.reflect.KType -import kotlin.reflect.typeOf - -public sealed class DataTreeItem { - - public abstract val meta: Meta - - public class Node(public val tree: DataTree) : DataTreeItem() { - override val meta: Meta get() = tree.meta - } - - public class Leaf(public val data: Data) : DataTreeItem() { - override val meta: Meta get() = data.meta - } -} - -public val DataTreeItem.type: KType - 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 - */ -@DfId(DataTree.TYPE) -public interface DataTree : DataSet { - - /** - * Top-level children items of this [DataTree] - */ - public val items: Map> - - override val meta: Meta get() = items[META_ITEM_NAME_TOKEN]?.meta ?: Meta.EMPTY - - override fun iterator(): Iterator> = iterator { - items.forEach { (token, childItem: DataTreeItem) -> - if (!token.body.startsWith("@")) { - when (childItem) { - is DataTreeItem.Leaf -> yield(childItem.data.named(token.asName())) - is DataTreeItem.Node -> yieldAll(childItem.tree.asSequence().map { it.named(token + it.name) }) - } - } - } - } - - override fun get(name: Name): Data? = when (name.length) { - 0 -> null - 1 -> items[name.firstOrNull()!!].data - else -> items[name.firstOrNull()!!].tree?.get(name.cutFirst()) - } - - public companion object { - public const val TYPE: String = "dataTree" - - /** - * A name token used to designate tree node meta - */ - public val META_ITEM_NAME_TOKEN: NameToken = NameToken("@meta") - - @DFInternal - public fun emptyWithType(type: KType, meta: Meta = Meta.EMPTY): DataTree = object : DataTree { - override val items: Map> get() = emptyMap() - override val dataType: KType get() = type - override val meta: Meta get() = meta - } - - @OptIn(DFInternal::class) - public inline fun empty(meta: Meta = Meta.EMPTY): DataTree = - emptyWithType(typeOf(), meta) - } -} - -public fun DataTree.listChildren(prefix: Name): List = - getItem(prefix).tree?.items?.keys?.map { prefix + it } ?: emptyList() - -/** - * Get a [DataTreeItem] with given [name] or null if the item does not exist - */ -public tailrec 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 - -/** - * A [Sequence] of all children including nodes - */ -public fun DataTree.traverseItems(): Sequence>> = sequence { - items.forEach { (head, item) -> - yield(head.asName() to item) - if (item is DataTreeItem.Node) { - val subSequence = item.tree.traverseItems() - .map { (name, data) -> (head.asName() + name) to data } - yieldAll(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] - */ -@OptIn(DFInternal::class) -public fun DataTree.branch(branchName: Name): DataTree = - getItem(branchName)?.tree ?: DataTree.emptyWithType(dataType) - -public fun DataTree.branch(branchName: String): DataTree = branch(branchName.parseAsName()) diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTreeBuilder.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTreeBuilder.kt deleted file mode 100644 index 303ba44e..00000000 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataTreeBuilder.kt +++ /dev/null @@ -1,127 +0,0 @@ -package space.kscience.dataforge.data - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.launch -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DFInternal -import space.kscience.dataforge.misc.ThreadSafe -import space.kscience.dataforge.names.* -import kotlin.collections.set -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.coroutineContext -import kotlin.reflect.KType -import kotlin.reflect.typeOf - -public interface DataSourceBuilder : DataSetBuilder, DataSource { - override val updates: MutableSharedFlow -} - -/** - * A mutable [DataTree] that propagates updates - */ -public class DataTreeBuilder( - override val dataType: KType, - coroutineContext: CoroutineContext, -) : DataTree, DataSourceBuilder { - - override val coroutineContext: CoroutineContext = - coroutineContext + Job(coroutineContext[Job]) + GoalExecutionRestriction() - - private val treeItems = HashMap>() - - override val items: Map> - get() = treeItems.filter { !it.key.body.startsWith("@") } - - override val updates: MutableSharedFlow = MutableSharedFlow() - - @ThreadSafe - private fun remove(token: NameToken) { - if (treeItems.remove(token) != null) { - launch { - updates.emit(token.asName()) - } - } - } - - override fun remove(name: Name) { - if (name.isEmpty()) error("Can't remove the root node") - (getItem(name.cutLast()).tree as? DataTreeBuilder)?.remove(name.lastOrNull()!!) - } - - @ThreadSafe - private fun set(token: NameToken, data: Data) { - treeItems[token] = DataTreeItem.Leaf(data) - } - - @ThreadSafe - private fun set(token: NameToken, node: DataTree) { - treeItems[token] = DataTreeItem.Node(node) - } - - private fun getOrCreateNode(token: NameToken): DataTreeBuilder = - (treeItems[token] as? DataTreeItem.Node)?.tree as? DataTreeBuilder - ?: DataTreeBuilder(dataType, coroutineContext).also { set(token, it) } - - private fun getOrCreateNode(name: Name): DataTreeBuilder = when (name.length) { - 0 -> this - 1 -> getOrCreateNode(name.firstOrNull()!!) - else -> getOrCreateNode(name.firstOrNull()!!).getOrCreateNode(name.cutFirst()) - } - - override fun data(name: Name, data: Data?) { - if (data == null) { - remove(name) - } else { - 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) - } - } - launch { - updates.emit(name) - } - } - - override fun meta(name: Name, meta: Meta) { - val item = getItem(name) - if (item is DataTreeItem.Leaf) error("TODO: Can't change meta of existing leaf item.") - data(name + DataTree.META_ITEM_NAME_TOKEN, Data.empty(meta)) - } -} - -/** - * Create a dynamic [DataSource]. Initial data is placed synchronously. - */ -@DFInternal -@Suppress("FunctionName") -public fun DataSource( - type: KType, - parent: CoroutineScope, - block: DataSourceBuilder.() -> Unit, -): DataTreeBuilder = DataTreeBuilder(type, parent.coroutineContext).apply(block) - -@Suppress("OPT_IN_USAGE", "FunctionName") -public inline fun DataSource( - parent: CoroutineScope, - crossinline block: DataSourceBuilder.() -> Unit, -): DataTreeBuilder = DataSource(typeOf(), parent) { block() } - -@Suppress("FunctionName") -public suspend inline fun DataSource( - crossinline block: DataSourceBuilder.() -> Unit = {}, -): DataTreeBuilder = DataTreeBuilder(typeOf(), coroutineContext).apply { block() } - -public inline fun DataSourceBuilder.emit( - name: Name, - parent: CoroutineScope, - noinline block: DataSourceBuilder.() -> Unit, -): Unit = node(name, DataSource(parent, block)) - -public inline fun DataSourceBuilder.emit( - name: String, - parent: CoroutineScope, - noinline block: DataSourceBuilder.() -> Unit, -): Unit = node(Name.parse(name), DataSource(parent, block)) \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Goal.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Goal.kt index 678711c1..95ddbbf7 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Goal.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Goal.kt @@ -9,7 +9,7 @@ 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> + public val dependencies: Iterable> /** * Returns current running coroutine if the goal is started. Null if the computation is not started. @@ -54,7 +54,7 @@ public open class StaticGoal(public val value: T) : Goal { */ public open class LazyGoal( private val coroutineContext: CoroutineContext = EmptyCoroutineContext, - override val dependencies: Collection> = emptyList(), + override val dependencies: Iterable> = emptyList(), public val block: suspend () -> T, ) : Goal { @@ -82,8 +82,8 @@ public open class LazyGoal( } log?.emit { "Starting dependencies computation for ${this@LazyGoal}" } - val startedDependencies = this.dependencies.map { goal -> - goal.run { async(coroutineScope) } + val startedDependencies = dependencies.map { goal -> + goal.async(coroutineScope) } return deferred ?: coroutineScope.async( coroutineContext diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/GroupRule.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/GroupRule.kt index 189087a3..6dd3caa9 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/GroupRule.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/GroupRule.kt @@ -15,13 +15,12 @@ */ package space.kscience.dataforge.data -import kotlinx.coroutines.launch import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string -import space.kscience.dataforge.misc.DFInternal +import space.kscience.dataforge.misc.UnsafeKType public interface GroupRule { - public fun gather(set: DataSet): Map> + public fun gather(set: DataTree): Map> public companion object { /** @@ -32,42 +31,20 @@ public interface GroupRule { * @param defaultTagValue * @return */ - @OptIn(DFInternal::class) + @OptIn(UnsafeKType::class) public fun byMetaValue( key: String, defaultTagValue: String, ): GroupRule = object : GroupRule { - override fun gather( - set: DataSet, - ): Map> { - val map = HashMap>() + override fun gather( + set: DataTree, + ): Map> { + val map = HashMap>() - if (set is DataSource) { - set.forEach { data -> - val tagValue: String = data.meta[key]?.string ?: defaultTagValue - (map.getOrPut(tagValue) { DataTreeBuilder(set.dataType, set.coroutineContext) } as DataTreeBuilder) - .data(data.name, data.data) - - set.launch { - set.updates.collect { name -> - val dataUpdate = set[name] - - val updateTagValue = dataUpdate?.meta?.get(key)?.string ?: defaultTagValue - map.getOrPut(updateTagValue) { - DataSource(set.dataType, this) { - data(name, dataUpdate) - } - } - } - } - } - } else { - set.forEach { data -> - val tagValue: String = data.meta[key]?.string ?: defaultTagValue - (map.getOrPut(tagValue) { StaticDataTree(set.dataType) } as StaticDataTree) - .data(data.name, data.data) - } + set.forEach { data -> + val tagValue: String = data.meta[key]?.string ?: defaultTagValue + map.getOrPut(tagValue) { MutableDataTree(set.dataType) }.put(data.name, data.data) } diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/MetaMaskData.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/MetaMaskData.kt new file mode 100644 index 00000000..85f0b2f9 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/MetaMaskData.kt @@ -0,0 +1,23 @@ +package space.kscience.dataforge.data + +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.meta.copy + + +private class MetaMaskData(val origin: Data, override val meta: Meta) : Data by origin + +/** + * A data with overriden meta. It reflects original data computed state. + */ +public fun Data.withMeta(newMeta: Meta): Data = if (this is MetaMaskData) { + MetaMaskData(origin, newMeta) +} else { + MetaMaskData(this, newMeta) +} + +/** + * Create a new [Data] with the same computation, but different meta. The meta is created by applying [block] to + * the existing data meta. + */ +public inline fun Data.withMeta(block: MutableMeta.() -> Unit): Data = withMeta(meta.copy(block)) \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/NamedData.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/NamedData.kt index 4c9d4bb3..9cb4c2d9 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/NamedData.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/NamedData.kt @@ -3,16 +3,36 @@ package space.kscience.dataforge.data import space.kscience.dataforge.meta.isEmpty import space.kscience.dataforge.misc.Named import space.kscience.dataforge.names.Name +import kotlin.reflect.KType -public interface NamedData : Named, Data { +/** + * An interface implementing a data update event. + * + * If [data] is null, then corresponding element should be removed. + */ +public interface DataUpdate : Named { + public val type: KType override val name: Name - public val data: Data + public val data: Data? +} + +public fun DataUpdate(type: KType, name: Name, data: Data?): DataUpdate = object : DataUpdate { + override val type: KType = type + override val name: Name = name + override val data: Data? = data +} + +/** + * A data coupled to a name. + */ +public interface NamedData : DataUpdate, Data { + override val data: Data } public operator fun NamedData<*>.component1(): Name = name -public operator fun NamedData.component2(): Data = data +public operator fun NamedData.component2(): Data = data -private class NamedDataImpl( +private class NamedDataImpl( override val name: Name, override val data: Data, ) : Data by data, NamedData { @@ -28,8 +48,10 @@ private class NamedDataImpl( } } -public fun Data.named(name: Name): NamedData = if (this is NamedData) { +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 +} + +public fun NamedData(name: Name, data: Data): NamedData = data.named(name) \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/StaticDataTree.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/StaticDataTree.kt deleted file mode 100644 index 4f0f455e..00000000 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/StaticDataTree.kt +++ /dev/null @@ -1,82 +0,0 @@ -package space.kscience.dataforge.data - -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.* -import kotlin.reflect.KType -import kotlin.reflect.typeOf - -@PublishedApi -internal class StaticDataTree( - override val dataType: KType, -) : DataSetBuilder, DataTree { - - private val _items: MutableMap> = HashMap() - - override val items: Map> - get() = _items.filter { !it.key.body.startsWith("@") } - - 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()) - } - } - - private 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 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 data(name: Name, data: Data?) { - set(name, data?.let { DataTreeItem.Leaf(it) }) - } - - override fun node(name: Name, dataSet: DataSet) { - if (dataSet is StaticDataTree) { - set(name, DataTreeItem.Node(dataSet)) - } else { - dataSet.forEach { - data(name + it.name, it.data) - } - } - } - - override fun meta(name: Name, meta: Meta) { - val item = getItem(name) - if (item is DataTreeItem.Leaf) TODO("Can't change meta of existing leaf item.") - data(name + DataTree.META_ITEM_NAME_TOKEN, Data.empty(meta)) - } -} - -@Suppress("FunctionName") -public inline fun DataTree( - dataType: KType, - block: DataSetBuilder.() -> Unit, -): DataTree = StaticDataTree(dataType).apply { block() } - -@Suppress("FunctionName") -public inline fun DataTree( - noinline block: DataSetBuilder.() -> Unit, -): DataTree = DataTree(typeOf(), block) - -@OptIn(DFExperimental::class) -public fun DataSet.seal(): DataTree = DataTree(dataType) { - populateFrom(this@seal) -} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataBuilders.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataBuilders.kt new file mode 100644 index 00000000..649cfd19 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataBuilders.kt @@ -0,0 +1,134 @@ +package space.kscience.dataforge.data + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MutableMeta +import space.kscience.dataforge.names.* + + +public fun DataSink.put(value: NamedData) { + put(value.name, value.data) +} + +public inline fun DataSink.putAll( + prefix: Name, + block: DataSink.() -> Unit, +) { + if (prefix.isEmpty()) { + apply(block) + } else { + val proxyDataSink = object :DataSink{ + override fun put(name: Name, data: Data?) { + this@putAll.put(prefix + name, data) + } + + override suspend fun update(name: Name, data: Data?) { + this@putAll.update(prefix + name, data) + } + + } + + proxyDataSink.apply(block) + } +} + + +public inline fun DataSink.putAll( + prefix: String, + block: DataSink.() -> Unit, +): Unit = putAll(prefix.asName(), block) + + +public fun DataSink.put(name: String, value: Data) { + put(Name.parse(name), value) +} + +public fun DataSink.putAll(name: Name, tree: DataTree) { + putAll(name) { putAll(tree.asSequence()) } +} + + +public fun DataSink.putAll(name: String, tree: DataTree) { + putAll(Name.parse(name)) { putAll(tree.asSequence()) } +} + +/** + * Produce lazy [Data] and emit it into the [MutableDataTree] + */ +public inline fun DataSink.putValue( + name: String, + meta: Meta = Meta.EMPTY, + noinline producer: suspend () -> T, +) { + val data = Data(meta, block = producer) + put(name, data) +} + +public inline fun DataSink.putValue( + name: Name, + meta: Meta = Meta.EMPTY, + noinline producer: suspend () -> T, +) { + val data = Data(meta, block = producer) + put(name, data) +} + +/** + * Emit static data with the fixed value + */ +public inline fun DataSink.putValue( + name: Name, + value: T, + meta: Meta = Meta.EMPTY, +): Unit = put(name, Data.wrapValue(value, meta)) + +public inline fun DataSink.putValue( + name: String, + value: T, + meta: Meta = Meta.EMPTY, +): Unit = put(name, Data.wrapValue(value, meta)) + +public inline fun DataSink.putValue( + name: String, + value: T, + metaBuilder: MutableMeta.() -> Unit, +): Unit = put(Name.parse(name), Data.wrapValue(value, Meta(metaBuilder))) + +public suspend inline fun DataSink.updateValue( + name: Name, + value: T, + meta: Meta = Meta.EMPTY, +): Unit = update(name, Data.wrapValue(value, meta)) + +public suspend inline fun DataSink.updateValue( + name: String, + value: T, + meta: Meta = Meta.EMPTY, +): Unit = update(name.parseAsName(), Data.wrapValue(value, meta)) + +public fun DataSink.putAll(sequence: Sequence>) { + sequence.forEach { + put(it.name, it.data) + } +} + +public fun DataSink.putAll(tree: DataTree) { + putAll(tree.asSequence()) +} + +/** + * Copy given data set and mirror its changes to this [DataSink] in [this@setAndObserve]. Returns an update [Job] + */ +public fun DataSink.putAllAndWatch( + scope: CoroutineScope, + branchName: Name = Name.EMPTY, + source: DataTree, +): Job { + putAll(branchName, source) + return source.updates.onEach { + update(branchName + it.name, it.data) + }.launchIn(scope) +} \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataFilter.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataFilter.kt deleted file mode 100644 index 3d2b6537..00000000 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataFilter.kt +++ /dev/null @@ -1,105 +0,0 @@ -package space.kscience.dataforge.data - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.* -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext -import kotlin.reflect.KType - - -/** - * A stateless filtered [DataSet] - */ -public fun DataSet.filter( - predicate: (Name, Meta) -> Boolean, -): DataSource = object : DataSource { - - override val dataType: KType get() = this@filter.dataType - - override val coroutineContext: CoroutineContext - get() = (this@filter as? DataSource)?.coroutineContext ?: EmptyCoroutineContext - - - override val meta: Meta get() = this@filter.meta - - override fun iterator(): Iterator> = iterator { - for (d in this@filter) { - if (predicate(d.name, d.meta)) { - yield(d) - } - } - } - - override fun get(name: Name): Data? = this@filter.get(name)?.takeIf { - predicate(name, it.meta) - } - - override val updates: Flow = this@filter.updates.filter flowFilter@{ name -> - val theData = this@filter[name] ?: return@flowFilter false - predicate(name, theData.meta) - } -} - -/** - * 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 : DataSource { - - override val dataType: KType get() = this@withNamePrefix.dataType - - override val coroutineContext: CoroutineContext - get() = (this@withNamePrefix as? DataSource)?.coroutineContext ?: EmptyCoroutineContext - - override val meta: Meta get() = this@withNamePrefix.meta - - - override fun iterator(): Iterator> = iterator { - for (d in this@withNamePrefix) { - yield(d.data.named(prefix + d.name)) - } - } - - override fun get(name: Name): Data? = - name.removeFirstOrNull(name)?.let { this@withNamePrefix.get(it) } - - override val updates: Flow get() = this@withNamePrefix.updates.map { prefix + it } -} - -/** - * Get a subset of data starting with a given [branchName] - */ -public fun DataSet.branch(branchName: Name): DataSet = if (branchName.isEmpty()) { - this -} else object : DataSource { - override val dataType: KType get() = this@branch.dataType - - override val coroutineContext: CoroutineContext - get() = (this@branch as? DataSource)?.coroutineContext ?: EmptyCoroutineContext - - override val meta: Meta get() = this@branch.meta - - override fun iterator(): Iterator> = iterator { - for (d in this@branch) { - d.name.removeFirstOrNull(branchName)?.let { name -> - yield(d.data.named(name)) - } - } - } - - override fun get(name: Name): Data? = this@branch.get(branchName + name) - - override val updates: Flow get() = this@branch.updates.mapNotNull { it.removeFirstOrNull(branchName) } -} - -public fun DataSet.branch(branchName: String): DataSet = this@branch.branch(branchName.parseAsName()) - -@DFExperimental -public suspend fun DataSet.rootData(): Data? = get(Name.EMPTY) - diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTransform.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTransform.kt index 76577346..c0d92e9e 100644 --- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTransform.kt +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTransform.kt @@ -1,24 +1,40 @@ package space.kscience.dataforge.data -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.meta.seal -import space.kscience.dataforge.meta.toMutableMeta -import space.kscience.dataforge.misc.DFInternal +import space.kscience.dataforge.meta.* +import space.kscience.dataforge.misc.UnsafeKType import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.NameToken import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.reflect.KType import kotlin.reflect.typeOf -public data class ValueWithMeta(val meta: Meta, val value: T) +public data class ValueWithMeta(val value: T, val meta: Meta) -public suspend fun Data.awaitWithMeta(): ValueWithMeta = ValueWithMeta(meta, await()) +public suspend fun Data.awaitWithMeta(): ValueWithMeta = ValueWithMeta(await(), meta) -public data class NamedValueWithMeta(val name: Name, val meta: Meta, val value: T) +public data class NamedValueWithMeta(val name: Name, val value: T, val meta: Meta) + +public suspend fun NamedData.awaitWithMeta(): NamedValueWithMeta = + NamedValueWithMeta(name, await(), meta) + +/** + * Lazily transform this data to another data. By convention [block] should not use external data (be pure). + * @param type explicit type of the resulting [Data] + * @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 + */ +@UnsafeKType +public fun Data.transform( + type: KType, + meta: Meta = this.meta, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + block: suspend (T) -> R, +): Data = Data(type, meta, coroutineContext, listOf(this)) { + block(await()) +} -public suspend fun NamedData.awaitWithMeta(): NamedValueWithMeta = - NamedValueWithMeta(name, meta, await()) /** @@ -27,9 +43,9 @@ public suspend fun NamedData.awaitWithMeta(): NamedValueWithMeta * @param meta for the resulting data. By default equals input data. * @param block the transformation itself */ -public inline fun Data.map( - coroutineContext: CoroutineContext = EmptyCoroutineContext, +public inline fun Data.transform( meta: Meta = this.meta, + coroutineContext: CoroutineContext = EmptyCoroutineContext, crossinline block: suspend (T) -> R, ): Data = Data(meta, coroutineContext, listOf(this)) { block(await()) @@ -38,10 +54,10 @@ public inline fun Data.map( /** * Combine this data with the other data using [block]. See [Data::map] for other details */ -public inline fun Data.combine( +public inline fun Data.combine( other: Data, - coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, + coroutineContext: CoroutineContext = EmptyCoroutineContext, crossinline block: suspend (left: T1, right: T2) -> R, ): Data = Data(meta, coroutineContext, listOf(this, other)) { block(await(), other.await()) @@ -50,26 +66,31 @@ public inline fun Data.combine( //data collection operations -/** - * Lazily reduce a collection of [Data] to a single data. - */ -public inline fun Collection>.reduceToData( - coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, - crossinline block: suspend (List>) -> R, -): Data = Data( - meta, - coroutineContext, - this -) { - block(map { it.awaitWithMeta() }) +@PublishedApi +internal fun Iterable>.joinMeta(): Meta = Meta { + var counter = 0 + forEach { data -> + val inputIndex = (data as? NamedData)?.name?.toString() ?: (counter++).toString() + val token = NameToken("data", inputIndex) + set(token, data.meta) + } } -@DFInternal -public fun Map>.reduceToData( + + +@PublishedApi +internal fun Map<*, Data<*>>.joinMeta(): Meta = Meta { + forEach { (key, data) -> + val token = NameToken("data", key.toString()) + set(token, data.meta) + } +} + +@UnsafeKType +public fun Map>.reduceToData( outputType: KType, + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, block: suspend (Map>) -> R, ): Data = Data( outputType, @@ -86,9 +107,9 @@ public fun Map>.reduceToData( * @param T type of the input goal * @param R type of the result goal */ -public inline fun Map>.reduceToData( +public inline fun Map>.reduceToData( + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, crossinline block: suspend (Map>) -> R, ): Data = Data( meta, @@ -100,11 +121,11 @@ public inline fun Map>.reduceToData( //Iterable operations -@DFInternal -public inline fun Iterable>.reduceToData( +@UnsafeKType +public inline fun Iterable>.reduceToData( outputType: KType, + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, crossinline transformation: suspend (Collection>) -> R, ): Data = Data( outputType, @@ -115,22 +136,22 @@ public inline fun Iterable>.reduceToData( transformation(map { it.awaitWithMeta() }) } -@OptIn(DFInternal::class) -public inline fun Iterable>.reduceToData( +@OptIn(UnsafeKType::class) +public inline fun Iterable>.reduceToData( + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, crossinline transformation: suspend (Collection>) -> R, -): Data = reduceToData(typeOf(), coroutineContext, meta) { +): Data = reduceToData(typeOf(), meta, coroutineContext) { transformation(it) } -public inline fun Iterable>.foldToData( +public inline fun Iterable>.foldToData( initial: R, + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, crossinline block: suspend (result: R, data: ValueWithMeta) -> R, ): Data = reduceToData( - coroutineContext, meta + meta, coroutineContext ) { it.fold(initial) { acc, t -> block(acc, t) } } @@ -138,11 +159,11 @@ public inline fun Iterable>.foldToData( /** * Transform an [Iterable] of [NamedData] to a single [Data]. */ -@DFInternal -public inline fun Iterable>.reduceNamedToData( +@UnsafeKType +public inline fun Iterable>.reduceNamedToData( outputType: KType, + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, crossinline transformation: suspend (Collection>) -> R, ): Data = Data( outputType, @@ -153,69 +174,79 @@ public inline fun Iterable>.reduceNamedToData( transformation(map { it.awaitWithMeta() }) } -@OptIn(DFInternal::class) -public inline fun Iterable>.reduceNamedToData( +@OptIn(UnsafeKType::class) +public inline fun Iterable>.reduceNamedToData( + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, crossinline transformation: suspend (Collection>) -> R, -): Data = reduceNamedToData(typeOf(), coroutineContext, meta) { +): Data = reduceNamedToData(typeOf(), meta, coroutineContext) { transformation(it) } /** * Fold a [Iterable] of named data into a single [Data] */ -public inline fun Iterable>.foldNamedToData( +public inline fun Iterable>.foldNamedToData( initial: R, + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, crossinline block: suspend (result: R, data: NamedValueWithMeta) -> R, ): Data = reduceNamedToData( - coroutineContext, meta + meta, coroutineContext ) { it.fold(initial) { acc, t -> block(acc, t) } } //DataSet operations -@DFInternal -public suspend fun DataSet.map( + +@UnsafeKType +public suspend fun DataTree.transform( outputType: KType, - coroutineContext: CoroutineContext = EmptyCoroutineContext, metaTransform: MutableMeta.() -> Unit = {}, + coroutineContext: CoroutineContext = EmptyCoroutineContext, block: suspend (NamedValueWithMeta) -> R, -): DataTree = DataTree(outputType) { - forEach { - val newMeta = it.meta.toMutableMeta().apply(metaTransform).seal() - val d = Data(outputType, newMeta, coroutineContext, listOf(it)) { - block(it.awaitWithMeta()) +): DataTree = DataTree(outputType){ + //quasi-synchronous processing of elements in the tree + asSequence().forEach { namedData: NamedData -> + val newMeta = namedData.meta.toMutableMeta().apply(metaTransform).seal() + val d = Data(outputType, newMeta, coroutineContext, listOf(namedData)) { + block(namedData.awaitWithMeta()) } - data(it.name, d) + put(namedData.name, d) } } -@OptIn(DFInternal::class) -public suspend inline fun DataSet.map( - coroutineContext: CoroutineContext = EmptyCoroutineContext, +@OptIn(UnsafeKType::class) +public suspend inline fun DataTree.transform( noinline metaTransform: MutableMeta.() -> Unit = {}, + coroutineContext: CoroutineContext = EmptyCoroutineContext, noinline block: suspend (NamedValueWithMeta) -> R, -): DataTree = map(typeOf(), coroutineContext, metaTransform, block) +): DataTree = this@transform.transform(typeOf(), metaTransform, coroutineContext, block) -public inline fun DataSet.forEach(block: (NamedData) -> Unit) { - for (d in this) { - block(d) +public inline fun DataTree.forEach(block: (NamedData) -> Unit) { + asSequence().forEach(block) +} + +// DataSet reduction + +@PublishedApi +internal fun DataTree<*>.joinMeta(): Meta = Meta { + asSequence().forEach { + val token = NameToken("data", it.name.toString()) + set(token, it.meta) } } -public inline fun DataSet.reduceToData( +public inline fun DataTree.reduceToData( + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, crossinline transformation: suspend (Iterable>) -> R, -): Data = asIterable().reduceNamedToData(coroutineContext, meta, transformation) +): Data = asSequence().asIterable().reduceNamedToData(meta, coroutineContext, transformation) -public inline fun DataSet.foldToData( +public inline fun DataTree.foldToData( initial: R, + meta: Meta = joinMeta(), coroutineContext: CoroutineContext = EmptyCoroutineContext, - meta: Meta = Meta.EMPTY, crossinline block: suspend (result: R, data: NamedValueWithMeta) -> R, -): Data = asIterable().foldNamedToData(initial, coroutineContext, meta, block) \ No newline at end of file +): Data = asSequence().asIterable().foldNamedToData(initial, meta, coroutineContext, block) \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTreeBuilder.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTreeBuilder.kt new file mode 100644 index 00000000..ac0492f0 --- /dev/null +++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTreeBuilder.kt @@ -0,0 +1,112 @@ +package space.kscience.dataforge.data + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import space.kscience.dataforge.misc.UnsafeKType +import space.kscience.dataforge.names.* +import kotlin.reflect.KType +import kotlin.reflect.typeOf + + +private class FlatDataTree( + override val dataType: KType, + private val dataSet: Map>, + private val sourceUpdates: Flow>, + private val prefix: Name, +) : DataTree { + override val data: Data? get() = dataSet[prefix] + override val items: Map> + get() = dataSet.keys + .filter { it.startsWith(prefix) && it.length > prefix.length } + .map { it.tokens[prefix.length] } + .associateWith { FlatDataTree(dataType, dataSet, sourceUpdates, prefix + it) } + + override fun read(name: Name): Data? = dataSet[prefix + name] + + override val updates: Flow> = + sourceUpdates.mapNotNull { update -> + update.name.removeFirstOrNull(prefix)?.let { DataUpdate(dataType, it, update.data) } + } +} + +/** + * A builder for static [DataTree]. + */ +private class DataTreeBuilder( + private val type: KType, + initialData: Map> = emptyMap(), +) : DataSink { + + private val map = HashMap>(initialData) + + private val mutex = Mutex() + + private val updatesFlow = MutableSharedFlow>() + + override fun put(name: Name, data: Data?) { + if (data == null) { + map.remove(name) + } else { + map[name] = data + } + } + + override suspend fun update(name: Name, data: Data?) { + mutex.withLock { + if (data == null) { + map.remove(name) + } else { + map.put(name, data) + } + } + updatesFlow.emit(DataUpdate(data?.type ?: type, name, data)) + } + + public fun build(): DataTree = FlatDataTree(type, map, updatesFlow, Name.EMPTY) +} + +/** + * Create a static [DataTree] + */ +@UnsafeKType +public fun DataTree( + dataType: KType, + generator: DataSink.() -> Unit, +): DataTree = DataTreeBuilder(dataType).apply(generator).build() + +/** + * Create and a data tree. + */ +@OptIn(UnsafeKType::class) +public inline fun DataTree( + noinline generator: DataSink.() -> Unit, +): DataTree = DataTree(typeOf(), generator) + + +/** + * Represent this flat data map as a [DataTree] without copying it + */ +@UnsafeKType +public fun Map>.asTree(type: KType): DataTree = + DataTreeBuilder(type, this).build() + +/** + * Represent this flat data map as a [DataTree] without copying it + */ +@OptIn(UnsafeKType::class) +public inline fun Map>.asTree(): DataTree = asTree(typeOf()) + + +@UnsafeKType +public fun Sequence>.toTree(type: KType): DataTree = + DataTreeBuilder(type, associate { it.name to it.data }).build() + + +/** + * Collect a sequence of [NamedData] to a [DataTree] + */ +@OptIn(UnsafeKType::class) +public inline fun Sequence>.toTree(): DataTree = toTree(typeOf()) diff --git a/dataforge-data/src/commonTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/commonTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt new file mode 100644 index 00000000..760aeec2 --- /dev/null +++ b/dataforge-data/src/commonTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt @@ -0,0 +1,73 @@ +package space.kscience.dataforge.data + +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.test.runTest +import space.kscience.dataforge.names.asName +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.time.Duration.Companion.milliseconds + + +internal class DataTreeBuilderTest { + @Test + fun testTreeBuild() = runTest(timeout = 500.milliseconds) { + val node = DataTree { + putAll("primary") { + putValue("a", "a") + putValue("b", "b") + } + putValue("c.d", "c.d") + putValue("c.f", "c.f") + } + assertEquals("a", node["primary.a"]?.await()) + assertEquals("b", node["primary.b"]?.await()) + assertEquals("c.d", node["c.d"]?.await()) + assertEquals("c.f", node["c.f"]?.await()) + + } + + @Test + fun testDataUpdate() = runTest(timeout = 500.milliseconds) { + val updateData = DataTree { + putAll("update") { + put("a", Data.wrapValue("a")) + put("b", Data.wrapValue("b")) + } + } + + val node = DataTree { + putAll("primary") { + putValue("a", "a") + putValue("b", "b") + } + putValue("root", "root") + putAll(updateData) + } + + assertEquals("a", node["update.a"]?.await()) + assertEquals("a", node["primary.a"]?.await()) + } + + @Test + fun testDynamicUpdates() = runTest(timeout = 500.milliseconds) { + var job: Job? = null + + val subNode = MutableDataTree() + + val rootNode = MutableDataTree() { + job = putAllAndWatch(this@runTest, "sub".asName(), subNode) + } + + repeat(10) { + subNode.updateValue("value[$it]", it) + } + + rootNode.updates.take(10).collect() + assertEquals(9, rootNode["sub.value[9]"]?.await()) + assertEquals(8, rootNode["sub.value[8]"]?.await()) + + job?.cancel() + } +} \ No newline at end of file diff --git a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/actionInContext.kt b/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/actionInContext.kt deleted file mode 100644 index 33731a95..00000000 --- a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/actionInContext.kt +++ /dev/null @@ -1,2 +0,0 @@ -package space.kscience.dataforge.data - diff --git a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataFilterJvm.kt b/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataFilterJvm.kt index 74d67d9d..0cc81f7a 100644 --- a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataFilterJvm.kt +++ b/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataFilterJvm.kt @@ -2,11 +2,8 @@ package space.kscience.dataforge.data import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.misc.DFInternal import space.kscience.dataforge.names.Name -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext import kotlin.reflect.KType import kotlin.reflect.full.isSubtypeOf import kotlin.reflect.typeOf @@ -16,7 +13,7 @@ import kotlin.reflect.typeOf * Cast the node to given type if the cast is possible or return null */ @Suppress("UNCHECKED_CAST") -private fun Data<*>.castOrNull(type: KType): Data? = +private fun Data<*>.castOrNull(type: KType): Data? = if (!this.type.isSubtypeOf(type)) { null } else { @@ -25,61 +22,51 @@ private fun Data<*>.castOrNull(type: KType): Data? = } } +@Suppress("UNCHECKED_CAST") +@DFInternal +public fun Sequence>.filterByDataType(type: KType): Sequence> = + filter { it.type.isSubtypeOf(type) } as Sequence> + +@Suppress("UNCHECKED_CAST") +@DFInternal +public fun Flow>.filterByDataType(type: KType): Flow> = + filter { it.type.isSubtypeOf(type) } as Flow> + /** * Select all data matching given type and filters. Does not modify paths * - * @param predicate addition filtering condition based on item name and meta. By default, accepts all + * @param filter additional filtering condition based on item name and meta. By default, accepts all */ -@OptIn(DFExperimental::class) -public fun DataSet<*>.filterByType( +@Suppress("UNCHECKED_CAST") +@DFInternal +public fun DataTree<*>.filterByType( type: KType, - predicate: (name: Name, meta: Meta) -> Boolean = { _, _ -> true }, -): DataSource = object : DataSource { - override val dataType = type - - override val coroutineContext: CoroutineContext - get() = (this@filterByType as? DataSource)?.coroutineContext ?: EmptyCoroutineContext - - override val meta: Meta get() = this@filterByType.meta - - private fun checkDatum(name: Name, datum: Data<*>): Boolean = datum.type.isSubtypeOf(type) - && predicate(name, datum.meta) - - override fun iterator(): Iterator> = iterator { - for(d in this@filterByType){ - if(checkDatum(d.name,d.data)){ - @Suppress("UNCHECKED_CAST") - yield(d as NamedData) - } - } - } - - override fun get(name: Name): Data? = this@filterByType[name]?.let { datum -> - if (checkDatum(name, datum)) datum.castOrNull(type) else null - } - - override val updates: Flow = this@filterByType.updates.filter { name -> - get(name)?.let { datum -> - checkDatum(name, datum) - } ?: false + branch: Name = Name.EMPTY, + filter: DataFilter = DataFilter.EMPTY, +): DataTree { + val filterWithType = DataFilter { name, meta, dataType -> + filter.accepts(name, meta, dataType) && dataType.isSubtypeOf(type) } + return FilteredDataTree(this, filterWithType, branch, type) as DataTree } /** * Select a single datum of the appropriate type */ -public inline fun DataSet<*>.filterByType( - noinline predicate: (name: Name, meta: Meta) -> Boolean = { _, _ -> true }, -): DataSet = filterByType(typeOf(), predicate) +@OptIn(DFInternal::class) +public inline fun DataTree<*>.filterByType( + branch: Name = Name.EMPTY, + filter: DataFilter = DataFilter.EMPTY, +): DataTree = filterByType(typeOf(), branch, filter = filter) /** * Select a single datum if it is present and of given [type] */ -public fun DataSet<*>.getByType(type: KType, name: Name): NamedData? = +public fun DataTree<*>.getByType(type: KType, name: Name): NamedData? = get(name)?.castOrNull(type)?.named(name) -public inline fun DataSet<*>.getByType(name: Name): NamedData? = +public inline fun DataTree<*>.getByType(name: Name): NamedData? = this@getByType.getByType(typeOf(), name) -public inline fun DataSet<*>.getByType(name: String): NamedData? = - this@getByType.getByType(typeOf(), Name.parse(name)) \ No newline at end of file +public inline fun DataTree<*>.getByType(name: String): NamedData? = + this@getByType.getByType(typeOf(), Name.parse(name)) diff --git a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataSetBuilderInContext.kt b/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataSetBuilderInContext.kt index cb222ea0..8ce50a22 100644 --- a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataSetBuilderInContext.kt +++ b/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataSetBuilderInContext.kt @@ -1,40 +1,27 @@ package space.kscience.dataforge.data -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.plus /** * Append data to node */ -context(DataSetBuilder) public infix fun String.put(data: Data): Unit = - data(Name.parse(this), data) +context(DataSink) +public infix fun String.put(data: Data): Unit = + put(Name.parse(this), data) /** * Append node */ -context(DataSetBuilder) public infix fun String.put(dataSet: DataSet): Unit = - node(Name.parse(this), dataSet) +context(DataSink) +public infix fun String.putAll(dataSet: DataTree): Unit = + putAll(this, dataSet) /** * Build and append node */ -context(DataSetBuilder) public infix fun String.put( - block: DataSetBuilder.() -> Unit, -): Unit = node(Name.parse(this), block) +context(DataSink) +public infix fun String.putAll( + block: DataSink.() -> Unit, +): Unit = putAll(Name.parse(this), block) -/** - * Copy given data set and mirror its changes to this [DataTreeBuilder] in [this@setAndObserve]. Returns an update [Job] - */ -context(DataSetBuilder) public fun CoroutineScope.setAndWatch( - name: Name, - dataSet: DataSet, -): Job = launch { - node(name, dataSet) - dataSet.updates.collect { nameInBranch -> - data(name + nameInBranch, dataSet.get(nameInBranch)) - } -} \ No newline at end of file diff --git a/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/ActionsTest.kt b/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/ActionsTest.kt index 3987cd19..ee4b05be 100644 --- a/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/ActionsTest.kt +++ b/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/ActionsTest.kt @@ -1,50 +1,61 @@ package space.kscience.dataforge.data +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Test +import kotlinx.coroutines.withContext import space.kscience.dataforge.actions.Action import space.kscience.dataforge.actions.invoke -import space.kscience.dataforge.actions.map +import space.kscience.dataforge.actions.mapping import space.kscience.dataforge.misc.DFExperimental +import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.time.Duration.Companion.milliseconds @OptIn(DFExperimental::class, ExperimentalCoroutinesApi::class) internal class ActionsTest { @Test - fun testStaticMapAction() = runTest { + fun testStaticMapAction() = runTest(timeout = 500.milliseconds) { + val plusOne = Action.mapping { + result { it + 1 } + } + val data: DataTree = DataTree { repeat(10) { - static(it.toString(), it) + putValue(it.toString(), it) } } - val plusOne = Action.map { - result { it + 1 } - } val result = plusOne(data) + + advanceUntilIdle() assertEquals(2, result["1"]?.await()) } @Test - fun testDynamicMapAction() = runTest { - val data: DataSourceBuilder = DataSource() - - val plusOne = Action.map { + fun testDynamicMapAction() = runTest(timeout = 500.milliseconds) { + val plusOne = Action.mapping { result { it + 1 } } - val result = plusOne(data) + val source: MutableDataTree = MutableDataTree() - repeat(10) { - data.static(it.toString(), it) + val result = plusOne(source) + + + withContext(Dispatchers.Default) { + repeat(10) { + source.updateValue(it.toString(), it) + } + + delay(50) } - delay(20) +// result.updates.take(10).onEach { println(it.name) }.collect() assertEquals(2, result["1"]?.await()) - data.close() } } \ No newline at end of file diff --git a/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt deleted file mode 100644 index b77f7ea2..00000000 --- a/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -package space.kscience.dataforge.data - -import kotlinx.coroutines.* -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.asName -import kotlin.test.Test -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") - } - runBlocking { - assertEquals("a", node["primary.a"]?.await()) - assertEquals("b", node["primary.b"]?.await()) - assertEquals("c.d", node["c.d"]?.await()) - assertEquals("c.f", node["c.f"]?.await()) - } - } - - @OptIn(DFExperimental::class) - @Test - fun testDataUpdate() = runBlocking { - val updateData: DataTree = DataTree { - "update" put { - "a" put Data.static("a") - "b" put Data.static("b") - } - } - - val node = DataTree { - "primary" put { - static("a", "a") - static("b", "b") - } - static("root", "root") - populateFrom(updateData) - } - - runBlocking { - assertEquals("a", node["update.a"]?.await()) - assertEquals("a", node["primary.a"]?.await()) - } - } - - @Test - fun testDynamicUpdates() = runBlocking { - try { - lateinit var updateJob: Job - supervisorScope { - val subNode = DataSource { - updateJob = launch { - repeat(10) { - delay(10) - static("value", it) - } - delay(10) - } - } - launch { - subNode.updatesWithData.collect { - println(it) - } - } - val rootNode = DataSource { - setAndWatch("sub".asName(), subNode) - } - - launch { - rootNode.updatesWithData.collect { - println(it) - } - } - updateJob.join() - assertEquals(9, rootNode["sub.value"]?.await()) - cancel() - } - } catch (t: Throwable) { - if (t !is CancellationException) throw t - } - - } -} \ No newline at end of file diff --git a/dataforge-io/README.md b/dataforge-io/README.md index ec431a04..5a9979a9 100644 --- a/dataforge-io/README.md +++ b/dataforge-io/README.md @@ -6,18 +6,16 @@ IO module ## Artifact: -The Maven coordinates of this project are `space.kscience:dataforge-io:0.7.0`. +The Maven coordinates of this project are `space.kscience:dataforge-io:0.9.0-dev-1`. **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - //uncomment to access development builds - //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:dataforge-io:0.7.0") + implementation("space.kscience:dataforge-io:0.9.0-dev-1") } ``` diff --git a/dataforge-io/build.gradle.kts b/dataforge-io/build.gradle.kts index 6d3c888c..16a6fbfc 100644 --- a/dataforge-io/build.gradle.kts +++ b/dataforge-io/build.gradle.kts @@ -4,12 +4,13 @@ plugins { description = "IO module" -val ioVersion = "0.2.1" +val ioVersion = "0.4.0" kscience { jvm() js() native() + wasm() useSerialization() useSerialization(sourceSet = space.kscience.gradle.DependencySourceSet.TEST) { cbor() diff --git a/dataforge-io/dataforge-io-proto/build.gradle.kts b/dataforge-io/dataforge-io-proto/build.gradle.kts new file mode 100644 index 00000000..a9099cb5 --- /dev/null +++ b/dataforge-io/dataforge-io-proto/build.gradle.kts @@ -0,0 +1,33 @@ +plugins { + id("space.kscience.gradle.mpp") + id("com.squareup.wire") version "4.9.9" +} + +description = "ProtoBuf meta IO" + +kscience { + jvm() +// js() + dependencies { + api(projects.dataforgeIo) + api("com.squareup.wire:wire-runtime:4.9.9") + } + useSerialization { + protobuf() + } +} + +wire { + kotlin { + sourcePath { + srcDir("src/commonMain/proto") + } + } +} + +readme { + maturity = space.kscience.gradle.Maturity.PROTOTYPE + description = """ + ProtoBuf Meta representation + """.trimIndent() +} diff --git a/dataforge-io/dataforge-io-proto/src/commonMain/kotlin/ProtoEnvelopeFormat.kt b/dataforge-io/dataforge-io-proto/src/commonMain/kotlin/ProtoEnvelopeFormat.kt new file mode 100644 index 00000000..d60539f5 --- /dev/null +++ b/dataforge-io/dataforge-io-proto/src/commonMain/kotlin/ProtoEnvelopeFormat.kt @@ -0,0 +1,32 @@ +package pace.kscience.dataforge.io.proto + +import kotlinx.io.Sink +import kotlinx.io.Source +import kotlinx.io.readByteArray +import okio.ByteString +import okio.ByteString.Companion.toByteString +import space.kscience.dataforge.io.Envelope +import space.kscience.dataforge.io.EnvelopeFormat +import space.kscience.dataforge.io.asBinary +import space.kscience.dataforge.io.proto.ProtoEnvelope +import space.kscience.dataforge.io.toByteArray +import space.kscience.dataforge.meta.Meta + + +public object ProtoEnvelopeFormat : EnvelopeFormat { + override fun readFrom(source: Source): Envelope { + val protoEnvelope = ProtoEnvelope.ADAPTER.decode(source.readByteArray()) + return Envelope( + meta = protoEnvelope.meta?.let { ProtoMetaWrapper(it) } ?: Meta.EMPTY, + data = protoEnvelope.dataBytes.toByteArray().asBinary() + ) + } + + override fun writeTo(sink: Sink, obj: Envelope) { + val protoEnvelope = ProtoEnvelope( + obj.meta.toProto(), + obj.data?.toByteArray()?.toByteString() ?: ByteString.EMPTY + ) + sink.write(ProtoEnvelope.ADAPTER.encode(protoEnvelope)) + } +} \ No newline at end of file diff --git a/dataforge-io/dataforge-io-proto/src/commonMain/kotlin/ProtoMetaFormat.kt b/dataforge-io/dataforge-io-proto/src/commonMain/kotlin/ProtoMetaFormat.kt new file mode 100644 index 00000000..8857832a --- /dev/null +++ b/dataforge-io/dataforge-io-proto/src/commonMain/kotlin/ProtoMetaFormat.kt @@ -0,0 +1,76 @@ +package pace.kscience.dataforge.io.proto + +import kotlinx.io.Sink +import kotlinx.io.Source +import kotlinx.io.readByteArray +import space.kscience.dataforge.io.MetaFormat +import space.kscience.dataforge.io.proto.ProtoMeta +import space.kscience.dataforge.meta.* +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.names.NameToken + +internal class ProtoMetaWrapper(private val proto: ProtoMeta) : Meta { + + private fun ProtoMeta.ProtoValue.toValue(): Value? = when { + stringValue != null -> stringValue.asValue() + booleanValue != null -> booleanValue.asValue() + doubleValue != null -> doubleValue.asValue() + floatValue != null -> floatValue.asValue() + int32Value != null -> int32Value.asValue() + int64Value != null -> int64Value.asValue() + bytesValue != null -> bytesValue.toByteArray().asValue() + listValue != null -> listValue.values.mapNotNull { it.toValue() }.asValue() + float64ListValue != null -> float64ListValue.values.map { it.asValue() }.asValue() + else -> null + } + + override val value: Value? + get() = proto.protoValue?.toValue() + + + override val items: Map + get() = proto.items.entries.associate { NameToken.parse(it.key) to ProtoMetaWrapper(it.value) } + + override fun toString(): String = Meta.toString(this) + + override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) + + override fun hashCode(): Int = Meta.hashCode(this) +} + +internal fun Meta.toProto(): ProtoMeta { + + + fun Value.toProto(): ProtoMeta.ProtoValue = when (type) { + ValueType.NULL -> ProtoMeta.ProtoValue() + + ValueType.NUMBER -> when (value) { + is Int, is Short, is Byte -> ProtoMeta.ProtoValue(int32Value = int) + is Long -> ProtoMeta.ProtoValue(int64Value = long) + is Float -> ProtoMeta.ProtoValue(floatValue = float) + else -> { +// LoggerFactory.getLogger(ProtoMeta::class.java) +// .warn("Unknown number type ${value} encoded as Double") + ProtoMeta.ProtoValue(doubleValue = double) + } + } + + ValueType.STRING -> ProtoMeta.ProtoValue(stringValue = string) + ValueType.BOOLEAN -> ProtoMeta.ProtoValue(booleanValue = boolean) + ValueType.LIST -> ProtoMeta.ProtoValue(listValue = ProtoMeta.ProtoValueList(list.map { it.toProto() })) + } + + return ProtoMeta( + protoValue = value?.toProto(), + items.entries.associate { it.key.toString() to it.value.toProto() } + ) +} + +public object ProtoMetaFormat : MetaFormat { + override fun writeMeta(sink: Sink, meta: Meta, descriptor: MetaDescriptor?) { + sink.write(ProtoMeta.ADAPTER.encode(meta.toProto())) + } + + override fun readMeta(source: Source, descriptor: MetaDescriptor?): Meta = + ProtoMetaWrapper(ProtoMeta.ADAPTER.decode(source.readByteArray())) +} \ No newline at end of file diff --git a/dataforge-io/dataforge-io-proto/src/commonMain/proto/meta.proto b/dataforge-io/dataforge-io-proto/src/commonMain/proto/meta.proto new file mode 100644 index 00000000..c6233872 --- /dev/null +++ b/dataforge-io/dataforge-io-proto/src/commonMain/proto/meta.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; +package space.kscience.dataforge.io.proto; + +message ProtoMeta { + message ProtoValue { + oneof value { + string stringValue = 2; + bool booleanValue = 3; + double doubleValue = 4; + float floatValue = 5; + int32 int32Value = 6; + int64 int64Value = 7; + bytes bytesValue = 8; + ProtoValueList listValue = 9; + Float64List float64ListValue = 10; + } + } + + message ProtoValueList{ + repeated ProtoValue values = 1; + } + + message Float64List{ + repeated double values = 1 [packed=true]; + } + + ProtoValue protoValue = 1; + + map items = 2; +} + +message ProtoEnvelope{ + ProtoMeta meta = 1; + bytes dataBytes = 2; +} \ No newline at end of file diff --git a/dataforge-io/dataforge-io-proto/src/commonTest/kotlin/ProtoBufTest.kt b/dataforge-io/dataforge-io-proto/src/commonTest/kotlin/ProtoBufTest.kt new file mode 100644 index 00000000..f8abef24 --- /dev/null +++ b/dataforge-io/dataforge-io-proto/src/commonTest/kotlin/ProtoBufTest.kt @@ -0,0 +1,83 @@ +package pace.kscience.dataforge.io.proto + +import kotlinx.io.writeString +import space.kscience.dataforge.io.Envelope +import space.kscience.dataforge.io.toByteArray +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.asValue +import space.kscience.dataforge.meta.get +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals + +class ProtoBufTest { + + @Test + fun testProtoBufMetaFormat(){ + val meta = Meta { + "a" put 22 + "node" put { + "b" put "DDD" + "c" put 11.1 + "d" put { + "d1" put { + "d11" put "aaa" + "d12" put "bbb" + } + "d2" put 2 + } + "array" put doubleArrayOf(1.0, 2.0, 3.0) + "array2d" put listOf( + doubleArrayOf(1.0, 2.0, 3.0).asValue(), + doubleArrayOf(1.0, 2.0, 3.0).asValue() + ).asValue() + } + } + val buffer = kotlinx.io.Buffer() + ProtoMetaFormat.writeTo(buffer,meta) + val result = ProtoMetaFormat.readFrom(buffer) + +// println(result["a"]?.value) + + meta.items.keys.forEach { + assertEquals(meta[it],result[it],"${meta[it]} != ${result[it]}") + } + + assertEquals(meta, result) + } + + @Test + fun testProtoBufEnvelopeFormat(){ + val envelope = Envelope{ + meta { + "a" put 22 + "node" put { + "b" put "DDD" + "c" put 11.1 + "d" put { + "d1" put { + "d11" put "aaa" + "d12" put "bbb" + } + "d2" put 2 + } + "array" put doubleArrayOf(1.0, 2.0, 3.0) + "array2d" put listOf( + doubleArrayOf(1.0, 2.0, 3.0).asValue(), + doubleArrayOf(1.0, 2.0, 3.0).asValue() + ).asValue() + } + } + data { + writeString("Hello world!") + } + } + + val buffer = kotlinx.io.Buffer() + ProtoEnvelopeFormat.writeTo(buffer,envelope) + val result = ProtoEnvelopeFormat.readFrom(buffer) + + assertEquals(envelope.meta, result.meta) + assertContentEquals(envelope.data?.toByteArray(), result.data?.toByteArray()) + } +} \ No newline at end of file diff --git a/dataforge-io/dataforge-io-proto/src/jvmMain/kotlin/performanceComparison.kt b/dataforge-io/dataforge-io-proto/src/jvmMain/kotlin/performanceComparison.kt new file mode 100644 index 00000000..74939882 --- /dev/null +++ b/dataforge-io/dataforge-io-proto/src/jvmMain/kotlin/performanceComparison.kt @@ -0,0 +1,51 @@ +package pace.kscience.dataforge.io.proto + +import kotlinx.io.writeString +import space.kscience.dataforge.io.Envelope +import space.kscience.dataforge.meta.asValue +import kotlin.concurrent.thread +import kotlin.time.measureTime + +public fun main() { + val envelope = Envelope { + meta { + "a" put 22 + "node" put { + "b" put "DDD" + "c" put 11.1 + "d" put { + "d1" put { + "d11" put "aaa" + "d12" put "bbb" + } + "d2" put 2 + } + "array" put doubleArrayOf(1.0, 2.0, 3.0) + "array2d" put listOf( + doubleArrayOf(1.0, 2.0, 3.0).asValue(), + doubleArrayOf(1.0, 2.0, 3.0).asValue() + ).asValue() + } + } + data { + writeString("Hello world!") + } + } + + val format = ProtoEnvelopeFormat + + measureTime { + val threads = List(100) { + thread { + repeat(100000) { + val buffer = kotlinx.io.Buffer() + format.writeTo(buffer, envelope) +// println(buffer.size) + val r = format.readFrom(buffer) + } + } + } + + threads.forEach { it.join() } + }.also { println(it) } +} \ No newline at end of file diff --git a/dataforge-io/dataforge-io-yaml/README.md b/dataforge-io/dataforge-io-yaml/README.md index 06a0efb4..f70a1490 100644 --- a/dataforge-io/dataforge-io-yaml/README.md +++ b/dataforge-io/dataforge-io-yaml/README.md @@ -6,18 +6,16 @@ YAML meta IO ## Artifact: -The Maven coordinates of this project are `space.kscience:dataforge-io-yaml:0.7.0`. +The Maven coordinates of this project are `space.kscience:dataforge-io-yaml:0.9.0-dev-1`. **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - //uncomment to access development builds - //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:dataforge-io-yaml:0.7.0") + implementation("space.kscience:dataforge-io-yaml:0.9.0-dev-1") } ``` diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/Binary.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/Binary.kt index f85dd98e..de365519 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/Binary.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/Binary.kt @@ -71,6 +71,11 @@ internal class ByteArrayBinary( override fun view(offset: Int, binarySize: Int): ByteArrayBinary = ByteArrayBinary(array, start + offset, binarySize) + + override fun toString(): String = + "ByteArrayBinary(array=$array, start=$start, size=$size)" + + } public fun ByteArray.asBinary(): Binary = ByteArrayBinary(this) diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/Envelope.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/Envelope.kt index 728a0e69..43df520e 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/Envelope.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/Envelope.kt @@ -2,7 +2,6 @@ package space.kscience.dataforge.io import space.kscience.dataforge.meta.Laminate import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName @@ -34,7 +33,9 @@ public interface Envelope { } } -internal class SimpleEnvelope(override val meta: Meta, override val data: Binary?) : Envelope +internal class SimpleEnvelope(override val meta: Meta, override val data: Binary?) : Envelope{ + override fun toString(): String = "Envelope(meta=$meta, data=$data)" +} public fun Envelope(meta: Meta, data: Binary?): Envelope = SimpleEnvelope(meta, data) diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeFormat.kt index 0df5ab27..27a192d7 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeFormat.kt @@ -4,20 +4,17 @@ import kotlinx.io.Source import space.kscience.dataforge.context.Context import space.kscience.dataforge.io.EnvelopeFormatFactory.Companion.ENVELOPE_FORMAT_TYPE import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DfId +import space.kscience.dataforge.misc.DfType import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import kotlin.reflect.KType import kotlin.reflect.typeOf -public interface EnvelopeFormat : IOFormat { - - override val type: KType get() = typeOf() -} +public interface EnvelopeFormat : IOFormat public fun EnvelopeFormat.read(input: Source): Envelope = readFrom(input) -@DfId(ENVELOPE_FORMAT_TYPE) +@DfType(ENVELOPE_FORMAT_TYPE) public interface EnvelopeFormatFactory : IOFormatFactory, EnvelopeFormat { override val type: KType get() = typeOf() diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeParts.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeParts.kt index 183e7b03..88231899 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeParts.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeParts.kt @@ -11,6 +11,7 @@ import space.kscience.dataforge.io.PartDescriptor.Companion.PARTS_KEY import space.kscience.dataforge.io.PartDescriptor.Companion.SEPARATOR_KEY import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.asName +import space.kscience.dataforge.names.getIndexedList import space.kscience.dataforge.names.plus private class PartDescriptor : Scheme() { @@ -84,7 +85,7 @@ public fun EnvelopeBuilder.envelopes( public fun Envelope.parts(): EnvelopeParts { if (data == null) return emptyList() //TODO add zip folder reader - val parts = meta.getIndexed(PARTS_KEY).values.map { + val parts = meta.getIndexedList(PARTS_KEY).map { PartDescriptor.read(it) } return if (parts.isEmpty()) { diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOFormat.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOFormat.kt index ffcadf1a..39fa2be1 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOFormat.kt @@ -7,7 +7,7 @@ import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.io.IOFormatFactory.Companion.IO_FORMAT_TYPE import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DfId +import space.kscience.dataforge.misc.DfType import space.kscience.dataforge.misc.Named import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName @@ -17,11 +17,7 @@ import kotlin.reflect.typeOf /** * Reader of a custom object from input */ -public interface IOReader { - /** - * The type of object being read - */ - public val type: KType +public fun interface IOReader { public fun readFrom(source: Source): T @@ -32,7 +28,6 @@ public interface IOReader { * no-op reader for binaries. */ public val binary: IOReader = object : IOReader { - override val type: KType = typeOf() override fun readFrom(source: Source): Binary = source.readByteArray().asBinary() @@ -42,8 +37,6 @@ public interface IOReader { } public inline fun IOReader(crossinline read: Source.() -> T): IOReader = object : IOReader { - override val type: KType = typeOf() - override fun readFrom(source: Source): T = source.read() } @@ -56,24 +49,24 @@ public fun interface IOWriter { */ public interface IOFormat : IOReader, IOWriter -public fun Source.readWith(format: IOReader): T = format.readFrom(this) +public fun Source.readWith(format: IOReader): T = format.readFrom(this) /** * Read given binary as an object using given format */ -public fun Binary.readWith(format: IOReader): T = read { - readWith(format) +public fun Binary.readWith(format: IOReader): T = read { + this.readWith(format) } /** * Write an object to the [Sink] with given [format] */ -public fun Sink.writeWith(format: IOWriter, obj: T): Unit = +public fun Sink.writeWith(format: IOWriter, obj: T): Unit = format.writeTo(this, obj) -@DfId(IO_FORMAT_TYPE) -public interface IOFormatFactory : Factory>, Named { +@DfType(IO_FORMAT_TYPE) +public interface IOFormatFactory : Factory>, Named { /** * Explicit type for dynamic type checks */ @@ -86,7 +79,7 @@ public interface IOFormatFactory : Factory>, Named { } } -public fun Binary(obj: T, format: IOWriter): Binary = Binary { format.writeTo(this, obj) } +public fun Binary(obj: T, format: IOWriter): Binary = Binary { format.writeTo(this, obj) } public object FloatIOFormat : IOFormat, IOFormatFactory { override fun build(context: Context, meta: Meta): IOFormat = this diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOPlugin.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOPlugin.kt index c3248021..f431a731 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOPlugin.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOPlugin.kt @@ -5,9 +5,8 @@ import space.kscience.dataforge.io.EnvelopeFormatFactory.Companion.ENVELOPE_FORM import space.kscience.dataforge.io.IOFormatFactory.Companion.IO_FORMAT_TYPE import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string -import space.kscience.dataforge.misc.DFInternal +import space.kscience.dataforge.misc.UnsafeKType import space.kscience.dataforge.names.Name import kotlin.reflect.KType import kotlin.reflect.typeOf @@ -20,12 +19,12 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { } @Suppress("UNCHECKED_CAST") - @DFInternal - public fun resolveIOFormat(type: KType, meta: Meta): IOFormat? = + @UnsafeKType + public fun resolveIOFormat(type: KType, meta: Meta): IOFormat? = ioFormatFactories.singleOrNull { it.type == type }?.build(context, meta) as? IOFormat - @OptIn(DFInternal::class) - public inline fun resolveIOFormat(meta: Meta = Meta.EMPTY): IOFormat? = + @OptIn(UnsafeKType::class) + public inline fun resolveIOFormat(meta: Meta = Meta.EMPTY): IOFormat? = resolveIOFormat(typeOf(), meta) diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/MetaFormat.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/MetaFormat.kt index cadf87ca..b36ddc2a 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/MetaFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/MetaFormat.kt @@ -9,7 +9,7 @@ import space.kscience.dataforge.context.Global import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.misc.DfId +import space.kscience.dataforge.misc.DfType import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus @@ -21,8 +21,6 @@ import kotlin.reflect.typeOf */ public interface MetaFormat : IOFormat { - override val type: KType get() = typeOf() - override fun writeTo(sink: Sink, obj: Meta) { writeMeta(sink, obj, null) } @@ -38,7 +36,7 @@ public interface MetaFormat : IOFormat { public fun readMeta(source: Source, descriptor: MetaDescriptor? = null): Meta } -@DfId(META_FORMAT_TYPE) +@DfType(META_FORMAT_TYPE) public interface MetaFormatFactory : IOFormatFactory, MetaFormat { public val shortName: String diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/ioMisc.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/ioMisc.kt index 93fd210a..b5e56dfe 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/ioMisc.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/ioMisc.kt @@ -5,7 +5,6 @@ import kotlinx.io.bytestring.ByteString import kotlinx.io.bytestring.decodeToString import kotlinx.io.bytestring.encodeToByteString import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DFExperimental import kotlin.math.min /** @@ -52,7 +51,6 @@ public fun IOPlugin.peekBinaryEnvelopeFormat(binary: Binary): EnvelopeFormat? { /** * A zero-copy read from */ -@DFExperimental public fun IOPlugin.readEnvelope( binary: Binary, readNonEnvelopes: Boolean = false, @@ -62,7 +60,6 @@ public fun IOPlugin.readEnvelope( Envelope(Meta.EMPTY, binary) } else error("Can't infer format for $binary") -@DFExperimental public fun IOPlugin.readEnvelope( string: String, readNonEnvelopes: Boolean = false, diff --git a/dataforge-io/src/jvmMain/kotlin/space/kscience/dataforge/io/fileIO.kt b/dataforge-io/src/jvmMain/kotlin/space/kscience/dataforge/io/fileIO.kt index c15280f3..7df23eb5 100644 --- a/dataforge-io/src/jvmMain/kotlin/space/kscience/dataforge/io/fileIO.kt +++ b/dataforge-io/src/jvmMain/kotlin/space/kscience/dataforge/io/fileIO.kt @@ -77,14 +77,12 @@ public fun Path.rewrite(block: Sink.() -> Unit): Unit { stream.asSink().buffered().use(block) } -@DFExperimental public fun EnvelopeFormat.readFile(path: Path): Envelope = readFrom(path.asBinary()) /** * Resolve IOFormat based on type */ @Suppress("UNCHECKED_CAST") -@DFExperimental public inline fun IOPlugin.resolveIOFormat(): IOFormat? = ioFormatFactories.find { it.type.isSupertypeOf(typeOf()) } as IOFormat? @@ -192,7 +190,6 @@ public fun IOPlugin.peekFileEnvelopeFormat(path: Path): EnvelopeFormat? { * * Return null otherwise. */ -@DFExperimental public fun IOPlugin.readEnvelopeFile( path: Path, readNonEnvelopes: Boolean = false, diff --git a/dataforge-meta/README.md b/dataforge-meta/README.md index 5f214640..e4fcacb1 100644 --- a/dataforge-meta/README.md +++ b/dataforge-meta/README.md @@ -6,18 +6,16 @@ Meta definition and basic operations on meta ## Artifact: -The Maven coordinates of this project are `space.kscience:dataforge-meta:0.7.0`. +The Maven coordinates of this project are `space.kscience:dataforge-meta:0.9.0-dev-1`. **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - //uncomment to access development builds - //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:dataforge-meta:0.7.0") + implementation("space.kscience:dataforge-meta:0.9.0-dev-1") } ``` diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index 7daa7540..0bb4a908 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -1,3 +1,15 @@ +public final class space/kscience/dataforge/meta/ByteArrayValue : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker, space/kscience/dataforge/meta/Value { + public fun ([B)V + public fun equals (Ljava/lang/Object;)Z + public fun getList ()Ljava/util/List; + public fun getType ()Lspace/kscience/dataforge/meta/ValueType; + public synthetic fun getValue ()Ljava/lang/Object; + public fun getValue ()[B + public fun hashCode ()I + public fun iterator ()Ljava/util/Iterator; + public fun toString ()Ljava/lang/String; +} + public abstract interface class space/kscience/dataforge/meta/Configurable { public abstract fun getMeta ()Lspace/kscience/dataforge/meta/MutableMeta; } @@ -30,7 +42,20 @@ public final class space/kscience/dataforge/meta/EnumValue : space/kscience/data } public final class space/kscience/dataforge/meta/ExoticValuesKt { + public static final fun asValue ([B)Lspace/kscience/dataforge/meta/Value; public static final fun asValue ([D)Lspace/kscience/dataforge/meta/Value; + public static final fun byteArray (Lspace/kscience/dataforge/meta/MetaProvider;[BLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; + public static final fun byteArray (Lspace/kscience/dataforge/meta/MutableMetaProvider;[BLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun byteArray$default (Lspace/kscience/dataforge/meta/MetaProvider;[BLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; + public static synthetic fun byteArray$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[BLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun doubleArray (Lspace/kscience/dataforge/meta/MetaProvider;[DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; + public static final fun doubleArray (Lspace/kscience/dataforge/meta/MutableMetaProvider;[DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun doubleArray$default (Lspace/kscience/dataforge/meta/MetaProvider;[DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; + public static synthetic fun doubleArray$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun getByteArray (Lspace/kscience/dataforge/meta/Meta;)[B + public static final fun getByteArray (Lspace/kscience/dataforge/meta/Value;)[B + public static final fun getDoubleArray (Lspace/kscience/dataforge/meta/Meta;)[D + public static final fun getDoubleArray (Lspace/kscience/dataforge/meta/Value;)[D public static final fun lazyParseValue (Ljava/lang/String;)Lspace/kscience/dataforge/meta/LazyParsedValue; } @@ -56,6 +81,20 @@ public final class space/kscience/dataforge/meta/JsonMetaKt { public static final fun toValue (Lkotlinx/serialization/json/JsonPrimitive;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)Lspace/kscience/dataforge/meta/Value; } +public final class space/kscience/dataforge/meta/KeepTransformationRule : space/kscience/dataforge/meta/TransformationRule { + public fun (Lkotlin/jvm/functions/Function1;)V + public final fun component1 ()Lkotlin/jvm/functions/Function1; + public final fun copy (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/KeepTransformationRule; + public static synthetic fun copy$default (Lspace/kscience/dataforge/meta/KeepTransformationRule;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/KeepTransformationRule; + public fun equals (Ljava/lang/Object;)Z + public final fun getSelector ()Lkotlin/jvm/functions/Function1; + public fun hashCode ()I + public fun matches (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;)Z + public fun selectItems (Lspace/kscience/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; + public fun toString ()Ljava/lang/String; + public fun transformItem (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MutableMeta;)V +} + public final class space/kscience/dataforge/meta/Laminate : space/kscience/dataforge/meta/TypedMeta { public static final field Companion Lspace/kscience/dataforge/meta/Laminate$Companion; public fun equals (Ljava/lang/Object;)Z @@ -64,6 +103,8 @@ public final class space/kscience/dataforge/meta/Laminate : space/kscience/dataf public synthetic fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/TypedMeta; public fun getItems ()Ljava/util/Map; public final fun getLayers ()Ljava/util/List; + public fun getSelf ()Lspace/kscience/dataforge/meta/Laminate; + public synthetic fun getSelf ()Lspace/kscience/dataforge/meta/TypedMeta; public fun getValue ()Lspace/kscience/dataforge/meta/Value; public fun hashCode ()I public final fun merge ()Lspace/kscience/dataforge/meta/SealedMeta; @@ -159,49 +200,82 @@ public final class space/kscience/dataforge/meta/MetaBuilder : space/kscience/da public abstract interface annotation class space/kscience/dataforge/meta/MetaBuilderMarker : java/lang/annotation/Annotation { } +public abstract interface class space/kscience/dataforge/meta/MetaConverter : space/kscience/dataforge/meta/MetaReader { + public static final field Companion Lspace/kscience/dataforge/meta/MetaConverter$Companion; + public abstract fun convert (Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; + public fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; + public fun read (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public abstract fun readOrNull (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; +} + +public final class space/kscience/dataforge/meta/MetaConverter$Companion { + public final fun getBoolean ()Lspace/kscience/dataforge/meta/MetaConverter; + public final fun getDouble ()Lspace/kscience/dataforge/meta/MetaConverter; + public final fun getFloat ()Lspace/kscience/dataforge/meta/MetaConverter; + public final fun getInt ()Lspace/kscience/dataforge/meta/MetaConverter; + public final fun getLong ()Lspace/kscience/dataforge/meta/MetaConverter; + public final fun getMeta ()Lspace/kscience/dataforge/meta/MetaConverter; + public final fun getNumber ()Lspace/kscience/dataforge/meta/MetaConverter; + public final fun getString ()Lspace/kscience/dataforge/meta/MetaConverter; + public final fun getValue ()Lspace/kscience/dataforge/meta/MetaConverter; + public final fun valueList (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/MetaConverter; + public static synthetic fun valueList$default (Lspace/kscience/dataforge/meta/MetaConverter$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaConverter; +} + +public final class space/kscience/dataforge/meta/MetaConverterKt { + public static final fun convertNullable (Lspace/kscience/dataforge/meta/MetaConverter;Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; +} + +public abstract interface class space/kscience/dataforge/meta/MetaDelegate : kotlin/properties/ReadOnlyProperty, space/kscience/dataforge/meta/descriptors/Described { +} + public final class space/kscience/dataforge/meta/MetaDelegateKt { - public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadOnlyProperty; - public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;ZLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;ZLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static final fun double (Lspace/kscience/dataforge/meta/MetaProvider;DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun double (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MetaProvider;DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static final fun float (Lspace/kscience/dataforge/meta/MetaProvider;FLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun float (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MetaProvider;FLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static final fun int (Lspace/kscience/dataforge/meta/MetaProvider;ILspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun int (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MetaProvider;ILspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static final fun long (Lspace/kscience/dataforge/meta/MetaProvider;JLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun long (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MetaProvider;JLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static final fun node (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun node (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/transformations/MetaConverter;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/transformations/MetaConverter;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static final fun value (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty; - public static final fun value (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; - public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; + public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;ZLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;ZLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun double (Lspace/kscience/dataforge/meta/MetaProvider;DLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun double (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MetaProvider;DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun float (Lspace/kscience/dataforge/meta/MetaProvider;FLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun float (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MetaProvider;FLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun int (Lspace/kscience/dataforge/meta/MetaProvider;ILspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun int (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MetaProvider;ILspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun listOfSpec (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun listOfSpec$default (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun long (Lspace/kscience/dataforge/meta/MetaProvider;JLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun long (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MetaProvider;JLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun node (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaReader;)Lkotlin/properties/ReadOnlyProperty; + public static final fun node (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaReader;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; + public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun spec (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun spec$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun value (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static final fun value (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; + public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate; } public final class space/kscience/dataforge/meta/MetaKt { @@ -222,7 +296,6 @@ public final class space/kscience/dataforge/meta/MetaKt { public static final fun getLong (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Long; public static final synthetic fun getNonNullable (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/NameToken;)Lspace/kscience/dataforge/meta/Meta; public static final fun getNumber (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Number; - public static final fun getSelf (Lspace/kscience/dataforge/meta/TypedMeta;)Lspace/kscience/dataforge/meta/TypedMeta; public static final fun getShort (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Short; public static final fun getString (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/String; public static final fun getStringList (Lspace/kscience/dataforge/meta/Meta;)Ljava/util/List; @@ -239,6 +312,16 @@ public abstract interface class space/kscience/dataforge/meta/MetaProvider : spa public fun getValue (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/Value; } +public abstract interface class space/kscience/dataforge/meta/MetaReader : space/kscience/dataforge/meta/descriptors/Described { + public fun read (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public abstract fun readOrNull (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; +} + +public final class space/kscience/dataforge/meta/MetaReaderKt { + public static final fun readNullable (Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public static final fun readValue (Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/meta/Value;)Ljava/lang/Object; +} + public abstract interface class space/kscience/dataforge/meta/MetaRepr { public abstract fun toMeta ()Lspace/kscience/dataforge/meta/Meta; } @@ -252,6 +335,37 @@ public final class space/kscience/dataforge/meta/MetaSerializer : kotlinx/serial public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/Meta;)V } +public final class space/kscience/dataforge/meta/MetaTransformation { + public static final field Companion Lspace/kscience/dataforge/meta/MetaTransformation$Companion; + public static final fun apply-impl (Ljava/util/Collection;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/Meta; + public static final fun bind-impl (Ljava/util/Collection;Lspace/kscience/dataforge/meta/ObservableMeta;Lspace/kscience/dataforge/meta/MutableMeta;)V + public static final synthetic fun box-impl (Ljava/util/Collection;)Lspace/kscience/dataforge/meta/MetaTransformation; + public static fun constructor-impl (Ljava/util/Collection;)Ljava/util/Collection; + 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;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/Meta; + public fun hashCode ()I + public static fun hashCode-impl (Ljava/util/Collection;)I + public fun toString ()Ljava/lang/String; + public static fun toString-impl (Ljava/util/Collection;)Ljava/lang/String; + public final synthetic fun unbox-impl ()Ljava/util/Collection; +} + +public final class space/kscience/dataforge/meta/MetaTransformation$Companion { + public final fun make--mWxz5M (Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; +} + +public final class space/kscience/dataforge/meta/MetaTransformationBuilder { + public fun ()V + public final fun build-m6Fha10 ()Ljava/util/Collection; + public final fun keep (Ljava/lang/String;)V + public final fun keep (Lkotlin/jvm/functions/Function1;)V + public final fun keep (Lspace/kscience/dataforge/names/Name;)V + public final fun move (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)V + public static synthetic fun move$default (Lspace/kscience/dataforge/meta/MetaTransformationBuilder;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V +} + public abstract interface class space/kscience/dataforge/meta/MutableMeta : space/kscience/dataforge/meta/Meta, space/kscience/dataforge/meta/MutableMetaProvider { public static final field Companion Lspace/kscience/dataforge/meta/MutableMeta$Companion; public synthetic fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/Meta; @@ -267,6 +381,7 @@ public abstract interface class space/kscience/dataforge/meta/MutableMeta : spac public fun put (Ljava/lang/String;Lspace/kscience/dataforge/meta/MetaRepr;)V public fun put (Ljava/lang/String;Lspace/kscience/dataforge/meta/Value;)V public fun put (Ljava/lang/String;Z)V + public fun put (Ljava/lang/String;[B)V public fun put (Ljava/lang/String;[D)V public fun put (Lspace/kscience/dataforge/names/Name;Ljava/lang/Enum;)V public fun put (Lspace/kscience/dataforge/names/Name;Ljava/lang/Number;)V @@ -286,64 +401,70 @@ public final class space/kscience/dataforge/meta/MutableMeta$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public abstract interface class space/kscience/dataforge/meta/MutableMetaDelegate : kotlin/properties/ReadWriteProperty, space/kscience/dataforge/meta/descriptors/Described { +} + public final class space/kscience/dataforge/meta/MutableMetaDelegateKt { - public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadWriteProperty; - public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;ZLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;ZLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun double (Lspace/kscience/dataforge/meta/MutableMetaProvider;DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun double (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun doubleArray (Lspace/kscience/dataforge/meta/MutableMetaProvider;[DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun doubleArray$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun float (Lspace/kscience/dataforge/meta/MutableMetaProvider;FLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun float (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;FLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun int (Lspace/kscience/dataforge/meta/MutableMetaProvider;ILspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun int (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;ILspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun listValue (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun listValue$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun long (Lspace/kscience/dataforge/meta/MutableMetaProvider;JLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun long (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;JLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun node (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun node (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/transformations/MetaConverter;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/transformations/MetaConverter;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun numberList (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun numberList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun stringList (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun stringList (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun stringList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun stringList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun value (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun value (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;ZLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;ZLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun convertable (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun convertable$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun double (Lspace/kscience/dataforge/meta/MutableMetaProvider;DLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun double (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun float (Lspace/kscience/dataforge/meta/MutableMetaProvider;FLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun float (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;FLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun int (Lspace/kscience/dataforge/meta/MutableMetaProvider;ILspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun int (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;ILspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun listOfConvertable (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun listOfConvertable$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun listValue (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun listValue$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun long (Lspace/kscience/dataforge/meta/MutableMetaProvider;JLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun long (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;JLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun node (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaConverter;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun node (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaConverter;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun numberList (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun numberList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun stringList (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun stringList (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun stringList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun stringList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun value (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static final fun value (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; + public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate; } public final class space/kscience/dataforge/meta/MutableMetaKt { public static final fun ObservableMutableMeta ()Lspace/kscience/dataforge/meta/ObservableMutableMeta; public static final fun ObservableMutableMeta (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/ObservableMutableMeta; + public static final fun ObservableMutableMeta (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/ObservableMutableMeta; public static synthetic fun ObservableMutableMeta$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/ObservableMutableMeta; public static final fun append (Lspace/kscience/dataforge/meta/MutableMeta;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;)V public static final fun append (Lspace/kscience/dataforge/meta/MutableMeta;Ljava/lang/String;Lspace/kscience/dataforge/meta/Value;)V @@ -357,6 +478,7 @@ public final class space/kscience/dataforge/meta/MutableMetaKt { public static final fun getOrCreate (Lspace/kscience/dataforge/meta/MutableTypedMeta;Ljava/lang/String;)Lspace/kscience/dataforge/meta/MutableTypedMeta; public static final fun remove (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;)V public static final fun remove (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)V + public static final fun reset (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/Meta;)V public static final fun set (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Ljava/lang/Iterable;)V public static final fun set (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;)V public static final fun set (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Ljava/lang/Iterable;)V @@ -365,7 +487,7 @@ public final class space/kscience/dataforge/meta/MutableMetaKt { public static final fun set (Lspace/kscience/dataforge/meta/MutableValueProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Value;)V public static final fun setIndexed (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)V public static synthetic fun setIndexed$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V - public static final fun toMutableMeta (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/ObservableMutableMeta; + public static final fun toMutableMeta (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/MutableMeta; public static final fun update (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/Meta;)V public static final fun withDefault (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/MetaProvider;)Lspace/kscience/dataforge/meta/MutableMeta; } @@ -419,11 +541,6 @@ public abstract interface class space/kscience/dataforge/meta/ObservableMeta : s public abstract fun removeListener (Ljava/lang/Object;)V } -public final class space/kscience/dataforge/meta/ObservableMetaKt { - public static final fun useProperty (Lspace/kscience/dataforge/meta/Scheme;Lkotlin/reflect/KProperty1;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V - public static synthetic fun useProperty$default (Lspace/kscience/dataforge/meta/Scheme;Lkotlin/reflect/KProperty1;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V -} - public final class space/kscience/dataforge/meta/ObservableMetaWrapperKt { public static final fun asObservable (Lspace/kscience/dataforge/meta/MutableMeta;)Lspace/kscience/dataforge/meta/ObservableMutableMeta; } @@ -435,16 +552,29 @@ public abstract interface class space/kscience/dataforge/meta/ObservableMutableM public fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/ObservableMutableMeta; public synthetic fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/TypedMeta; public abstract fun getOrCreate (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/ObservableMutableMeta; + public fun getSelf ()Lspace/kscience/dataforge/meta/ObservableMutableMeta; + public synthetic fun getSelf ()Lspace/kscience/dataforge/meta/TypedMeta; } -public abstract interface class space/kscience/dataforge/meta/ReadOnlySpecification : space/kscience/dataforge/meta/descriptors/Described { - public abstract fun empty ()Ljava/lang/Object; - public fun invoke (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public abstract fun read (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; +public final class space/kscience/dataforge/meta/RegexItemTransformationRule : space/kscience/dataforge/meta/TransformationRule { + public fun (Lkotlin/text/Regex;Lkotlin/jvm/functions/Function4;)V + public final fun component1 ()Lkotlin/text/Regex; + public final fun component2 ()Lkotlin/jvm/functions/Function4; + public final fun copy (Lkotlin/text/Regex;Lkotlin/jvm/functions/Function4;)Lspace/kscience/dataforge/meta/RegexItemTransformationRule; + public static synthetic fun copy$default (Lspace/kscience/dataforge/meta/RegexItemTransformationRule;Lkotlin/text/Regex;Lkotlin/jvm/functions/Function4;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/RegexItemTransformationRule; + public fun equals (Ljava/lang/Object;)Z + public final fun getFrom ()Lkotlin/text/Regex; + public final fun getTransform ()Lkotlin/jvm/functions/Function4; + public fun hashCode ()I + public fun matches (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;)Z + public fun toString ()Ljava/lang/String; + public fun transformItem (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MutableMeta;)V } public class space/kscience/dataforge/meta/Scheme : space/kscience/dataforge/meta/Configurable, space/kscience/dataforge/meta/MetaRepr, space/kscience/dataforge/meta/MutableMetaProvider, space/kscience/dataforge/meta/descriptors/Described { public fun ()V + public fun (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V + public synthetic fun (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/Meta; public fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMeta; public final fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; @@ -454,6 +584,7 @@ public class space/kscience/dataforge/meta/Scheme : space/kscience/dataforge/met public fun setValue (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Value;)V public fun toMeta ()Lspace/kscience/dataforge/meta/Laminate; public synthetic fun toMeta ()Lspace/kscience/dataforge/meta/Meta; + public fun toString ()Ljava/lang/String; public fun validate (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;)Z } @@ -461,20 +592,34 @@ public final class space/kscience/dataforge/meta/SchemeKt { public static final fun copy (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/Scheme; public static synthetic fun copy$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/Scheme; public static final fun invoke (Lspace/kscience/dataforge/meta/Scheme;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/Scheme; - public static final fun retarget (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/MutableMeta;)Lspace/kscience/dataforge/meta/Scheme; + public static final fun listOfScheme (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static final fun listOfScheme (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun listOfScheme$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun listOfScheme$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun scheme (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static final fun scheme (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun scheme$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun scheme$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun schemeOrNull (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static final fun schemeOrNull (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun schemeOrNull$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun schemeOrNull$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun updateWith (Lspace/kscience/dataforge/meta/Configurable;Lspace/kscience/dataforge/meta/SchemeSpec;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/Scheme; + public static final fun updateWith (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/Scheme; + public static final fun useProperty (Lspace/kscience/dataforge/meta/Scheme;Lkotlin/reflect/KProperty1;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V + public static synthetic fun useProperty$default (Lspace/kscience/dataforge/meta/Scheme;Lkotlin/reflect/KProperty1;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V } -public class space/kscience/dataforge/meta/SchemeSpec : space/kscience/dataforge/meta/Specification { +public class space/kscience/dataforge/meta/SchemeSpec : space/kscience/dataforge/meta/MetaConverter { public fun (Lkotlin/jvm/functions/Function0;)V - public synthetic fun empty ()Ljava/lang/Object; - public fun empty ()Lspace/kscience/dataforge/meta/Scheme; + public synthetic fun convert (Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; + public fun convert (Lspace/kscience/dataforge/meta/Scheme;)Lspace/kscience/dataforge/meta/Meta; + public final fun empty ()Lspace/kscience/dataforge/meta/Scheme; public fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; - public synthetic fun invoke (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public final fun invoke (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/Scheme; - public synthetic fun read (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; - public fun read (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/Scheme; - public synthetic fun write (Lspace/kscience/dataforge/meta/MutableMeta;)Ljava/lang/Object; - public fun write (Lspace/kscience/dataforge/meta/MutableMeta;)Lspace/kscience/dataforge/meta/Scheme; + public synthetic fun readOrNull (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun readOrNull (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/Scheme; + public final fun write (Lspace/kscience/dataforge/meta/MutableMeta;)Lspace/kscience/dataforge/meta/Scheme; } public final class space/kscience/dataforge/meta/SealedMeta : space/kscience/dataforge/meta/TypedMeta { @@ -482,19 +627,21 @@ public final class space/kscience/dataforge/meta/SealedMeta : space/kscience/dat public fun (Lspace/kscience/dataforge/meta/Value;Ljava/util/Map;)V public fun equals (Ljava/lang/Object;)Z public fun getItems ()Ljava/util/Map; + public fun getSelf ()Lspace/kscience/dataforge/meta/SealedMeta; + public synthetic fun getSelf ()Lspace/kscience/dataforge/meta/TypedMeta; public fun getValue ()Lspace/kscience/dataforge/meta/Value; public fun hashCode ()I public fun toString ()Ljava/lang/String; } -public final class space/kscience/dataforge/meta/SealedMeta$$serializer : kotlinx/serialization/internal/GeneratedSerializer { +public synthetic class space/kscience/dataforge/meta/SealedMeta$$serializer : kotlinx/serialization/internal/GeneratedSerializer { public static final field INSTANCE Lspace/kscience/dataforge/meta/SealedMeta$$serializer; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/meta/SealedMeta; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/meta/SealedMeta; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/SealedMeta;)V + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/SealedMeta;)V public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } @@ -515,21 +662,20 @@ public final class space/kscience/dataforge/meta/SealedMetaKt { public static final fun seal (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/SealedMeta; } -public abstract interface class space/kscience/dataforge/meta/Specification : space/kscience/dataforge/meta/ReadOnlySpecification { - public abstract fun write (Lspace/kscience/dataforge/meta/MutableMeta;)Ljava/lang/Object; -} - -public final class space/kscience/dataforge/meta/SpecificationKt { - public static final fun spec (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/Specification;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun spec (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/Specification;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun spec$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/Specification;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun spec$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/Specification;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun specOrNull (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/Specification;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static final fun specOrNull (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/Specification;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun specOrNull$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/Specification;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static synthetic fun specOrNull$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/Specification;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; - public static final fun updateWith (Lspace/kscience/dataforge/meta/Configurable;Lspace/kscience/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static final fun updateWith (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/Specification;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +public final class space/kscience/dataforge/meta/SingleItemTransformationRule : space/kscience/dataforge/meta/TransformationRule { + public fun (Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function3;)V + public final fun component1 ()Lspace/kscience/dataforge/names/Name; + public final fun component2 ()Lkotlin/jvm/functions/Function3; + public final fun copy (Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function3;)Lspace/kscience/dataforge/meta/SingleItemTransformationRule; + public static synthetic fun copy$default (Lspace/kscience/dataforge/meta/SingleItemTransformationRule;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/SingleItemTransformationRule; + public fun equals (Ljava/lang/Object;)Z + public final fun getFrom ()Lspace/kscience/dataforge/names/Name; + public final fun getTransform ()Lkotlin/jvm/functions/Function3; + public fun hashCode ()I + public fun matches (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;)Z + public fun selectItems (Lspace/kscience/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; + public fun toString ()Ljava/lang/String; + public fun transformItem (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MutableMeta;)V } public final class space/kscience/dataforge/meta/StringValue : space/kscience/dataforge/meta/Value { @@ -550,6 +696,12 @@ public final class space/kscience/dataforge/meta/StringValue : space/kscience/da public final synthetic fun unbox-impl ()Ljava/lang/String; } +public abstract interface class space/kscience/dataforge/meta/TransformationRule { + public abstract fun matches (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;)Z + public fun selectItems (Lspace/kscience/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; + public abstract fun transformItem (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MutableMeta;)V +} + public final class space/kscience/dataforge/meta/True : space/kscience/dataforge/meta/Value { public static final field INSTANCE Lspace/kscience/dataforge/meta/True; public fun equals (Ljava/lang/Object;)Z @@ -563,6 +715,7 @@ public abstract interface class space/kscience/dataforge/meta/TypedMeta : space/ public synthetic fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/Meta; public fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/TypedMeta; public abstract fun getItems ()Ljava/util/Map; + public abstract fun getSelf ()Lspace/kscience/dataforge/meta/TypedMeta; public fun toMeta ()Lspace/kscience/dataforge/meta/Meta; } @@ -587,7 +740,6 @@ public final class space/kscience/dataforge/meta/Value$Companion { public final class space/kscience/dataforge/meta/ValueExtensionsKt { public static final fun getBoolean (Lspace/kscience/dataforge/meta/Value;)Z public static final fun getDouble (Lspace/kscience/dataforge/meta/Value;)D - public static final fun getDoubleArray (Lspace/kscience/dataforge/meta/Value;)[D public static final fun getFloat (Lspace/kscience/dataforge/meta/Value;)F public static final fun getInt (Lspace/kscience/dataforge/meta/Value;)I public static final fun getLong (Lspace/kscience/dataforge/meta/Value;)J @@ -606,7 +758,6 @@ public final class space/kscience/dataforge/meta/ValueKt { public static final fun asValue (Ljava/lang/Number;)Lspace/kscience/dataforge/meta/Value; public static final fun asValue (Ljava/lang/String;)Lspace/kscience/dataforge/meta/Value; public static final fun asValue (Z)Lspace/kscience/dataforge/meta/Value; - public static final fun asValue ([B)Lspace/kscience/dataforge/meta/Value; public static final fun asValue ([F)Lspace/kscience/dataforge/meta/Value; public static final fun asValue ([I)Lspace/kscience/dataforge/meta/Value; public static final fun asValue ([J)Lspace/kscience/dataforge/meta/Value; @@ -692,20 +843,21 @@ public final class space/kscience/dataforge/meta/descriptors/MetaDescriptor { public final fun getDescription ()Ljava/lang/String; public final fun getIndexKey ()Ljava/lang/String; public final fun getMultiple ()Z + public final fun getNodes ()Ljava/util/Map; public final fun getValueRestriction ()Lspace/kscience/dataforge/meta/descriptors/ValueRestriction; public final fun getValueTypes ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } -public final class space/kscience/dataforge/meta/descriptors/MetaDescriptor$$serializer : kotlinx/serialization/internal/GeneratedSerializer { +public synthetic class space/kscience/dataforge/meta/descriptors/MetaDescriptor$$serializer : kotlinx/serialization/internal/GeneratedSerializer { public static final field INSTANCE Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor$$serializer; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } @@ -720,25 +872,23 @@ public final class space/kscience/dataforge/meta/descriptors/MetaDescriptorBuild public final fun attributes (Lkotlin/jvm/functions/Function1;)V public final fun build ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; public final fun default (Ljava/lang/Object;)V + public final fun from (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V public final fun getAllowedValues ()Ljava/util/List; public final fun getAttributes ()Lspace/kscience/dataforge/meta/MutableMeta; public final fun getChildren ()Ljava/util/Map; public final fun getDefault ()Lspace/kscience/dataforge/meta/Value; + public final fun getDescription ()Ljava/lang/String; public final fun getIndexKey ()Ljava/lang/String; - public final fun getInfo ()Ljava/lang/String; public final fun getMultiple ()Z public final fun getValueRestriction ()Lspace/kscience/dataforge/meta/descriptors/ValueRestriction; public final fun getValueTypes ()Ljava/util/List; - public final fun item (Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; - public static synthetic fun item$default (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; - public final fun node (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; - public static synthetic fun node$default (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; + public final fun node (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V public final fun setAllowedValues (Ljava/util/List;)V public final fun setAttributes (Lspace/kscience/dataforge/meta/MutableMeta;)V public final fun setChildren (Ljava/util/Map;)V public final fun setDefault (Lspace/kscience/dataforge/meta/Value;)V + public final fun setDescription (Ljava/lang/String;)V public final fun setIndexKey (Ljava/lang/String;)V - public final fun setInfo (Ljava/lang/String;)V public final fun setMultiple (Z)V public final fun setValueRestriction (Lspace/kscience/dataforge/meta/descriptors/ValueRestriction;)V public final fun setValueTypes (Ljava/util/List;)V @@ -749,16 +899,16 @@ public final class space/kscience/dataforge/meta/descriptors/MetaDescriptorBuild public static final fun MetaDescriptor (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; public static final fun copy (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; public static synthetic fun copy$default (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; - public static final fun item (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; public static final fun node (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V public static final fun node (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Ljava/lang/String;Lspace/kscience/dataforge/meta/descriptors/Described;Lkotlin/jvm/functions/Function1;)V - public static final fun node (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; + public static final fun node (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Ljava/lang/String;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V + public static final fun node (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)V public static synthetic fun node$default (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Ljava/lang/String;Lspace/kscience/dataforge/meta/descriptors/Described;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static final fun required (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;)V - public static final fun value (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Ljava/lang/String;Lspace/kscience/dataforge/meta/ValueType;[Lspace/kscience/dataforge/meta/ValueType;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; - public static final fun value (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/ValueType;[Lspace/kscience/dataforge/meta/ValueType;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; - public static synthetic fun value$default (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Ljava/lang/String;Lspace/kscience/dataforge/meta/ValueType;[Lspace/kscience/dataforge/meta/ValueType;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; - public static synthetic fun value$default (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/ValueType;[Lspace/kscience/dataforge/meta/ValueType;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder; + public static final fun value (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Ljava/lang/String;Lspace/kscience/dataforge/meta/ValueType;[Lspace/kscience/dataforge/meta/ValueType;Lkotlin/jvm/functions/Function1;)V + public static final fun value (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/ValueType;[Lspace/kscience/dataforge/meta/ValueType;Lkotlin/jvm/functions/Function1;)V + public static synthetic fun value$default (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Ljava/lang/String;Lspace/kscience/dataforge/meta/ValueType;[Lspace/kscience/dataforge/meta/ValueType;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public static synthetic fun value$default (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/ValueType;[Lspace/kscience/dataforge/meta/ValueType;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V } public final class space/kscience/dataforge/meta/descriptors/MetaDescriptorKt { @@ -772,6 +922,7 @@ public final class space/kscience/dataforge/meta/descriptors/MetaDescriptorKt { public final class space/kscience/dataforge/meta/descriptors/ValueRestriction : java/lang/Enum { public static final field ABSENT Lspace/kscience/dataforge/meta/descriptors/ValueRestriction; + public static final field Companion Lspace/kscience/dataforge/meta/descriptors/ValueRestriction$Companion; public static final field NONE Lspace/kscience/dataforge/meta/descriptors/ValueRestriction; public static final field REQUIRED Lspace/kscience/dataforge/meta/descriptors/ValueRestriction; public static fun getEntries ()Lkotlin/enums/EnumEntries; @@ -779,119 +930,8 @@ public final class space/kscience/dataforge/meta/descriptors/ValueRestriction : public static fun values ()[Lspace/kscience/dataforge/meta/descriptors/ValueRestriction; } -public final class space/kscience/dataforge/meta/transformations/KeepTransformationRule : space/kscience/dataforge/meta/transformations/TransformationRule { - public fun (Lkotlin/jvm/functions/Function1;)V - public final fun component1 ()Lkotlin/jvm/functions/Function1; - public final fun copy (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/transformations/KeepTransformationRule; - public static synthetic fun copy$default (Lspace/kscience/dataforge/meta/transformations/KeepTransformationRule;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/transformations/KeepTransformationRule; - public fun equals (Ljava/lang/Object;)Z - public final fun getSelector ()Lkotlin/jvm/functions/Function1; - public fun hashCode ()I - public fun matches (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;)Z - public fun selectItems (Lspace/kscience/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; - public fun toString ()Ljava/lang/String; - public fun transformItem (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MutableMeta;)V -} - -public abstract interface class space/kscience/dataforge/meta/transformations/MetaConverter { - public static final field Companion Lspace/kscience/dataforge/meta/transformations/MetaConverter$Companion; - public fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; - public abstract fun getType ()Lkotlin/reflect/KType; - public fun metaToObject (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; - public abstract fun metaToObjectOrNull (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; - public abstract fun objectToMeta (Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; -} - -public final class space/kscience/dataforge/meta/transformations/MetaConverter$Companion { - public final fun getBoolean ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public final fun getDouble ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public final fun getFloat ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public final fun getInt ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public final fun getLong ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public final fun getMeta ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public final fun getNumber ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public final fun getString ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public final fun getValue ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public final fun valueList (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/transformations/MetaConverter; - public static synthetic fun valueList$default (Lspace/kscience/dataforge/meta/transformations/MetaConverter$Companion;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/transformations/MetaConverter; -} - -public final class space/kscience/dataforge/meta/transformations/MetaConverterKt { - public static final fun nullableMetaToObject (Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; - public static final fun nullableObjectToMeta (Lspace/kscience/dataforge/meta/transformations/MetaConverter;Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; - public static final fun valueToObject (Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lspace/kscience/dataforge/meta/Value;)Ljava/lang/Object; -} - -public final class space/kscience/dataforge/meta/transformations/MetaTransformation { - public static final field Companion Lspace/kscience/dataforge/meta/transformations/MetaTransformation$Companion; - public static final fun apply-impl (Ljava/util/Collection;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/Meta; - public static final fun bind-impl (Ljava/util/Collection;Lspace/kscience/dataforge/meta/ObservableMeta;Lspace/kscience/dataforge/meta/MutableMeta;)V - public static final synthetic fun box-impl (Ljava/util/Collection;)Lspace/kscience/dataforge/meta/transformations/MetaTransformation; - public static fun constructor-impl (Ljava/util/Collection;)Ljava/util/Collection; - 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;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/Meta; - public fun hashCode ()I - public static fun hashCode-impl (Ljava/util/Collection;)I - public fun toString ()Ljava/lang/String; - public static fun toString-impl (Ljava/util/Collection;)Ljava/lang/String; - public final synthetic fun unbox-impl ()Ljava/util/Collection; -} - -public final class space/kscience/dataforge/meta/transformations/MetaTransformation$Companion { - public final fun make-XNaMui4 (Lkotlin/jvm/functions/Function1;)Ljava/util/Collection; -} - -public final class space/kscience/dataforge/meta/transformations/MetaTransformationBuilder { - public fun ()V - public final fun build-050menU ()Ljava/util/Collection; - public final fun keep (Ljava/lang/String;)V - public final fun keep (Lkotlin/jvm/functions/Function1;)V - public final fun keep (Lspace/kscience/dataforge/names/Name;)V - public final fun move (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun move$default (Lspace/kscience/dataforge/meta/transformations/MetaTransformationBuilder;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V -} - -public final class space/kscience/dataforge/meta/transformations/RegexItemTransformationRule : space/kscience/dataforge/meta/transformations/TransformationRule { - public fun (Lkotlin/text/Regex;Lkotlin/jvm/functions/Function4;)V - public final fun component1 ()Lkotlin/text/Regex; - public final fun component2 ()Lkotlin/jvm/functions/Function4; - public final fun copy (Lkotlin/text/Regex;Lkotlin/jvm/functions/Function4;)Lspace/kscience/dataforge/meta/transformations/RegexItemTransformationRule; - public static synthetic fun copy$default (Lspace/kscience/dataforge/meta/transformations/RegexItemTransformationRule;Lkotlin/text/Regex;Lkotlin/jvm/functions/Function4;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/transformations/RegexItemTransformationRule; - public fun equals (Ljava/lang/Object;)Z - public final fun getFrom ()Lkotlin/text/Regex; - public final fun getTransform ()Lkotlin/jvm/functions/Function4; - public fun hashCode ()I - public fun matches (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;)Z - public fun toString ()Ljava/lang/String; - public fun transformItem (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MutableMeta;)V -} - -public final class space/kscience/dataforge/meta/transformations/SingleItemTransformationRule : space/kscience/dataforge/meta/transformations/TransformationRule { - public fun (Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function3;)V - public final fun component1 ()Lspace/kscience/dataforge/names/Name; - public final fun component2 ()Lkotlin/jvm/functions/Function3; - public final fun copy (Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function3;)Lspace/kscience/dataforge/meta/transformations/SingleItemTransformationRule; - public static synthetic fun copy$default (Lspace/kscience/dataforge/meta/transformations/SingleItemTransformationRule;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/transformations/SingleItemTransformationRule; - public fun equals (Ljava/lang/Object;)Z - public final fun getFrom ()Lspace/kscience/dataforge/names/Name; - public final fun getTransform ()Lkotlin/jvm/functions/Function3; - public fun hashCode ()I - public fun matches (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;)Z - public fun selectItems (Lspace/kscience/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; - public fun toString ()Ljava/lang/String; - public fun transformItem (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MutableMeta;)V -} - -public abstract interface class space/kscience/dataforge/meta/transformations/TransformationRule { - public abstract fun matches (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;)Z - public fun selectItems (Lspace/kscience/dataforge/meta/Meta;)Lkotlin/sequences/Sequence; - public abstract fun transformItem (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MutableMeta;)V -} - -public final class space/kscience/dataforge/misc/CastJvmKt { - public static final fun unsafeCast (Ljava/lang/Object;)Ljava/lang/Object; +public final class space/kscience/dataforge/meta/descriptors/ValueRestriction$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; } public abstract interface annotation class space/kscience/dataforge/misc/DFBuilder : java/lang/annotation/Annotation { @@ -903,7 +943,7 @@ public abstract interface annotation class space/kscience/dataforge/misc/DFExper public abstract interface annotation class space/kscience/dataforge/misc/DFInternal : java/lang/annotation/Annotation { } -public abstract interface annotation class space/kscience/dataforge/misc/DfId : java/lang/annotation/Annotation { +public abstract interface annotation class space/kscience/dataforge/misc/DfType : java/lang/annotation/Annotation { public abstract fun id ()Ljava/lang/String; } @@ -920,6 +960,9 @@ public final class space/kscience/dataforge/misc/NamedKt { public static final fun isAnonymous (Lspace/kscience/dataforge/misc/Named;)Z } +public abstract interface annotation class space/kscience/dataforge/misc/UnsafeKType : java/lang/annotation/Annotation { +} + public final class space/kscience/dataforge/names/Name { public static final field Companion Lspace/kscience/dataforge/names/Name$Companion; public static final field NAME_SEPARATOR Ljava/lang/String; @@ -937,6 +980,16 @@ public final class space/kscience/dataforge/names/Name$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class space/kscience/dataforge/names/NameIndexComparator : java/util/Comparator { + public static final field INSTANCE Lspace/kscience/dataforge/names/NameIndexComparator; + public synthetic fun compare (Ljava/lang/Object;Ljava/lang/Object;)I + public fun compare (Ljava/lang/String;Ljava/lang/String;)I +} + +public final class space/kscience/dataforge/names/NameIndexComparatorKt { + public static final fun getIndexedList (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;)Ljava/util/List; +} + public final class space/kscience/dataforge/names/NameKt { public static final fun appendFirst (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;)Lspace/kscience/dataforge/names/Name; public static final fun appendLeft (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;)Lspace/kscience/dataforge/names/Name; @@ -944,6 +997,7 @@ public final class space/kscience/dataforge/names/NameKt { public static final fun asName (Lspace/kscience/dataforge/names/NameToken;)Lspace/kscience/dataforge/names/Name; public static final fun cutFirst (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/names/Name; public static final fun cutLast (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/names/Name; + public static final fun endsWith (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;)Z public static final fun endsWith (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;)Z public static final fun endsWith (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/NameToken;)Z public static final fun first (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/names/NameToken; @@ -966,8 +1020,10 @@ public final class space/kscience/dataforge/names/NameKt { public static final fun removeHeadOrNull (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/names/Name; public static final fun replaceLast (Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/names/Name; public static final fun set (Ljava/util/Map;Ljava/lang/String;Ljava/lang/Object;)V + public static final fun startsWith (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;)Z public static final fun startsWith (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;)Z public static final fun startsWith (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/NameToken;)Z + public static final fun toStringUnescaped (Lspace/kscience/dataforge/names/Name;)Ljava/lang/String; public static final fun withIndex (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;)Lspace/kscience/dataforge/names/Name; } diff --git a/dataforge-meta/build.gradle.kts b/dataforge-meta/build.gradle.kts index 51b07113..d150ef98 100644 --- a/dataforge-meta/build.gradle.kts +++ b/dataforge-meta/build.gradle.kts @@ -6,6 +6,7 @@ kscience { jvm() js() native() + wasm() useSerialization{ json() } diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/JsonMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/JsonMeta.kt index 36373582..1817ba64 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/JsonMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/JsonMeta.kt @@ -31,12 +31,12 @@ private fun Meta.toJsonWithIndex(descriptor: MetaDescriptor?, index: String?): J val pairs: MutableList> = items.entries.groupBy { it.key.body }.mapTo(ArrayList()) { (body, list) -> - val childDescriptor = descriptor?.children?.get(body) + val childDescriptor = descriptor?.nodes?.get(body) if (list.size == 1) { val (token, element) = list.first() - //do not add an empty element - val child: JsonElement = element.toJsonWithIndex(childDescriptor, token.index) - if(token.index == null) { + //do not add an empty element + val child: JsonElement = element.toJsonWithIndex(childDescriptor, token.index) + if (token.index == null) { body to child } else { body to JsonArray(listOf(child)) @@ -106,7 +106,7 @@ private fun JsonElement.toValueOrNull(descriptor: MetaDescriptor?): Value? = whe private fun MutableMap.addJsonElement( key: String, element: JsonElement, - descriptor: MetaDescriptor? + descriptor: MetaDescriptor?, ) { when (element) { is JsonPrimitive -> put(NameToken(key), Meta(element.toValue(descriptor))) @@ -117,8 +117,11 @@ private fun MutableMap.addJsonElement( } else { val indexKey = descriptor?.indexKey ?: Meta.INDEX_KEY element.forEachIndexed { serial, childElement -> - val index = (childElement as? JsonObject)?.get(indexKey)?.jsonPrimitive?.content + + val index = (childElement as? JsonObject) + ?.get(indexKey)?.jsonPrimitive?.content ?: serial.toString() + val child: SealedMeta = when (childElement) { is JsonObject -> childElement.toMeta(descriptor) is JsonArray -> { @@ -133,12 +136,14 @@ private fun MutableMap.addJsonElement( Meta(childValue) } } + is JsonPrimitive -> Meta(childElement.toValue(null)) } put(NameToken(key, index), child) } } } + is JsonObject -> { val indexKey = descriptor?.indexKey ?: Meta.INDEX_KEY val index = element[indexKey]?.jsonPrimitive?.content @@ -160,11 +165,15 @@ public fun JsonObject.toMeta(descriptor: MetaDescriptor? = null): SealedMeta { public fun JsonElement.toMeta(descriptor: MetaDescriptor? = null): SealedMeta = when (this) { is JsonPrimitive -> Meta(toValue(descriptor)) is JsonObject -> toMeta(descriptor) - is JsonArray -> SealedMeta(null, - linkedMapOf().apply { - addJsonElement(Meta.JSON_ARRAY_KEY, this@toMeta, null) - } - ) + is JsonArray -> if (all { it is JsonPrimitive }) { + Meta(map { it.toValueOrNull(descriptor) ?: error("Unreachable: should not contain objects") }.asValue()) + } else { + SealedMeta(null, + linkedMapOf().apply { + addJsonElement(Meta.JSON_ARRAY_KEY, this@toMeta, null) + } + ) + } } // diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt index 87284107..0ae84bb6 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt @@ -9,6 +9,8 @@ import space.kscience.dataforge.names.NameToken */ public class Laminate internal constructor(public val layers: List) : TypedMeta { + override val self: Laminate get() = this + override val value: Value? = layers.firstNotNullOfOrNull { it.value } override val items: Map by lazy { diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt index 420625ca..fd953085 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt @@ -2,8 +2,7 @@ package space.kscience.dataforge.meta import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json -import space.kscience.dataforge.misc.DfId -import space.kscience.dataforge.misc.unsafeCast +import space.kscience.dataforge.misc.DfType import space.kscience.dataforge.names.* import kotlin.jvm.JvmName @@ -31,7 +30,7 @@ public fun interface MetaProvider : ValueProvider { * TODO add documentation * Same name siblings are supported via elements with the same [Name] but different indices. */ -@DfId(Meta.TYPE) +@DfType(Meta.TYPE) @Serializable(MetaSerializer::class) public interface Meta : MetaRepr, MetaProvider { public val value: Value? @@ -151,6 +150,8 @@ public interface TypedMeta> : Meta { override val items: Map + public val self: M + override fun get(name: Name): M? { tailrec fun M.find(name: Name): M? = if (name.isEmpty()) { this @@ -164,11 +165,6 @@ public interface TypedMeta> : Meta { override fun toMeta(): Meta = this } -/** - * Access self as a recursive type instance - */ -public inline val > TypedMeta.self: M get() = unsafeCast() - //public typealias Meta = TypedMeta<*> public operator fun > TypedMeta?.get(token: NameToken): M? = this?.items?.get(token) @@ -188,10 +184,12 @@ public operator fun > M?.get(key: String): M? = this?.get(key.p /** - * Get a sequence of [Name]-[Value] pairs using top-down traversal of the tree + * Get a sequence of [Name]-[Value] pairs using top-down traversal of the tree. + * The sequence includes root value with empty name */ public fun Meta.valueSequence(): Sequence> = sequence { items.forEach { (key, item) -> + value?.let { yield(Name.EMPTY to it) } item.value?.let { itemValue -> yield(key.asName() to itemValue) } @@ -248,7 +246,7 @@ public inline fun > Meta?.enum(): E? = this?.value?.let { } } -public val Meta.stringList: List? get() = value?.list?.map { it.string } +public val Meta?.stringList: List? get() = this?.value?.list?.map { it.string } /** * Create a provider that uses given provider for default values if those are not found in this provider diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaConverter.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaConverter.kt new file mode 100644 index 00000000..0f0e8efe --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaConverter.kt @@ -0,0 +1,165 @@ +package space.kscience.dataforge.meta + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.serializer +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.misc.DFExperimental + + +/** + * A converter of generic object to and from [Meta] + */ +public interface MetaConverter : MetaReader { + + /** + * A descriptor for resulting meta + */ + override val descriptor: MetaDescriptor? get() = null + + /** + * Attempt conversion of [source] to an object or return null if conversion failed + */ + override fun readOrNull(source: Meta): T? + + override fun read(source: Meta): T = + readOrNull(source) ?: error("Meta $source could not be interpreted by $this") + + public fun convert(obj: T): Meta + + public companion object { + + public val meta: MetaConverter = object : MetaConverter { + override fun readOrNull(source: Meta): Meta = source + override fun convert(obj: Meta): Meta = obj + } + + public val value: MetaConverter = object : MetaConverter { + override fun readOrNull(source: Meta): Value? = source.value + override fun convert(obj: Value): Meta = Meta(obj) + } + + public val string: MetaConverter = object : MetaConverter { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.STRING) + } + + + override fun readOrNull(source: Meta): String? = source.string + override fun convert(obj: String): Meta = Meta(obj.asValue()) + } + + public val boolean: MetaConverter = object : MetaConverter { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.BOOLEAN) + } + + override fun readOrNull(source: Meta): Boolean? = source.boolean + override fun convert(obj: Boolean): Meta = Meta(obj.asValue()) + } + + public val number: MetaConverter = object : MetaConverter { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.NUMBER) + } + + override fun readOrNull(source: Meta): Number? = source.number + override fun convert(obj: Number): Meta = Meta(obj.asValue()) + } + + public val double: MetaConverter = object : MetaConverter { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.NUMBER) + } + + override fun readOrNull(source: Meta): Double? = source.double + override fun convert(obj: Double): Meta = Meta(obj.asValue()) + } + + public val float: MetaConverter = object : MetaConverter { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.NUMBER) + } + + override fun readOrNull(source: Meta): Float? = source.float + override fun convert(obj: Float): Meta = Meta(obj.asValue()) + } + + public val int: MetaConverter = object : MetaConverter { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.NUMBER) + } + + override fun readOrNull(source: Meta): Int? = source.int + override fun convert(obj: Int): Meta = Meta(obj.asValue()) + } + + public val long: MetaConverter = object : MetaConverter { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.NUMBER) + } + + override fun readOrNull(source: Meta): Long? = source.long + override fun convert(obj: Long): Meta = Meta(obj.asValue()) + } + + public inline fun > enum(): MetaConverter = object : MetaConverter { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.STRING) + allowedValues(enumValues()) + } + + @Suppress("USELESS_CAST") + override fun readOrNull(source: Meta): E = source.enum() as? E ?: error("The Item is not a Enum") + + override fun convert(obj: E): Meta = Meta(obj.asValue()) + } + + public val stringList: MetaConverter> = object : MetaConverter> { + override fun convert(obj: List): Meta = Meta(obj.map { it.asValue() }.asValue()) + + override fun readOrNull(source: Meta): List? = source.stringList + } + + public fun valueList( + writer: (T) -> Value = { Value.of(it) }, + reader: (Value) -> T, + ): MetaConverter> = object : MetaConverter> { + override val descriptor: MetaDescriptor = MetaDescriptor { + valueType(ValueType.LIST) + } + + override fun readOrNull(source: Meta): List? = source.value?.list?.map(reader) + + override fun convert(obj: List): Meta = Meta(obj.map(writer).asValue()) + } + + /** + * Automatically generate [MetaConverter] for a class using its serializer and optional [descriptor] + */ + @DFExperimental + public inline fun serializable( + descriptor: MetaDescriptor? = null, + jsonEncoder: Json = Json, + ): MetaConverter = object : MetaConverter { + private val serializer: KSerializer = serializer() + + override fun readOrNull(source: Meta): T? { + val json = source.toJson(descriptor) + return jsonEncoder.decodeFromJsonElement(serializer, json) + } + + override fun convert(obj: T): Meta { + val json = jsonEncoder.encodeToJsonElement(obj) + return json.toMeta(descriptor) + } + + } + + } +} + +public fun MetaConverter.convertNullable(obj: T?): Meta? = obj?.let { convert(it) } + + diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaDelegate.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaDelegate.kt index 73923d56..2fa0f528 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaDelegate.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaDelegate.kt @@ -1,84 +1,190 @@ package space.kscience.dataforge.meta -import space.kscience.dataforge.meta.transformations.MetaConverter +import space.kscience.dataforge.meta.descriptors.Described +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty /* Meta delegates */ -public fun MetaProvider.node(key: Name? = null): ReadOnlyProperty = ReadOnlyProperty { _, property -> - get(key ?: property.name.asName()) +public interface MetaDelegate : ReadOnlyProperty, Described + + +public fun MetaProvider.node( + key: Name? = null, + descriptor: MetaDescriptor? = null, +): MetaDelegate = object : MetaDelegate { + override val descriptor: MetaDescriptor? = descriptor + + override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? { + return get(key ?: property.name.asName()) + } } + +/** + * Use [reader] to read the Meta node + */ +public fun MetaProvider.readable( + reader: MetaReader, + key: Name? = null, +): MetaDelegate = object : MetaDelegate { + override val descriptor: MetaDescriptor? get() = reader.descriptor + + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + return get(key ?: property.name.asName())?.let { reader.read(it) } + } +} + +/** + * Use [reader] to read the Meta node or return [default] if node does not exist + */ +public fun MetaProvider.readable( + reader: MetaReader, + default: T, + key: Name? = null, +): MetaDelegate = object : MetaDelegate { + override val descriptor: MetaDescriptor? get() = reader.descriptor + + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return get(key ?: property.name.asName())?.let { reader.read(it) } ?: default + } +} + +/** + * Use [reader] to read the Meta node + */ +@Deprecated("Replace with reading", ReplaceWith("reading(metaReader, key)")) +public fun MetaProvider.spec( + reader: MetaReader, + key: Name? = null, +): MetaDelegate = readable(reader, key) + +/** + * Use object serializer to transform it to Meta and back + */ +@DFExperimental +public inline fun MetaProvider.serializable( + key: Name? = null, + descriptor: MetaDescriptor? = null, +): MetaDelegate = readable(MetaConverter.serializable(descriptor), key) + +@DFExperimental +public inline fun MetaProvider.serializable( + key: Name? = null, + default: T, + descriptor: MetaDescriptor? = null, +): MetaDelegate = readable(MetaConverter.serializable(descriptor), default, key) + +@Deprecated("Use convertable", ReplaceWith("convertable(converter, key)")) public fun MetaProvider.node( key: Name? = null, - converter: MetaConverter -): ReadOnlyProperty = ReadOnlyProperty { _, property -> - get(key ?: property.name.asName())?.let { converter.metaToObject(it) } + converter: MetaReader, +): ReadOnlyProperty = readable(converter, key) + +/** + * Use [reader] to convert a list of same name siblings meta to object + */ +public fun Meta.listOfReadable( + reader: MetaReader, + key: Name? = null, +): MetaDelegate> = object : MetaDelegate> { + override fun getValue(thisRef: Any?, property: KProperty<*>): List { + val name = key ?: property.name.asName() + return getIndexed(name).values.map { reader.read(it) } + } + + override val descriptor: MetaDescriptor? = reader.descriptor?.copy(multiple = true) } + +/** + * Use [converter] to convert a list of same name siblings meta to object + */ +@Deprecated("Replace with readingList", ReplaceWith("readingList(converter, key)")) +public fun Meta.listOfSpec( + converter: MetaReader, + key: Name? = null, +): MetaDelegate> = listOfReadable(converter, key) + +@DFExperimental +public inline fun Meta.listOfSerializable( + key: Name? = null, + descriptor: MetaDescriptor? = null, +): MetaDelegate> = listOfReadable(MetaConverter.serializable(descriptor), key) + /** * A property delegate that uses custom key */ -public fun MetaProvider.value(key: Name? = null): ReadOnlyProperty = ReadOnlyProperty { _, property -> - get(key ?: property.name.asName())?.value +public fun MetaProvider.value( + key: Name? = null, + descriptor: MetaDescriptor? = null, +): MetaDelegate = object : MetaDelegate { + override fun getValue(thisRef: Any?, property: KProperty<*>): Value? = get(key ?: property.name.asName())?.value + + override val descriptor: MetaDescriptor? = descriptor } public fun MetaProvider.value( key: Name? = null, - reader: (Value?) -> R -): ReadOnlyProperty = ReadOnlyProperty { _, property -> - reader(get(key ?: property.name.asName())?.value) + descriptor: MetaDescriptor? = null, + reader: (Value?) -> R, +): MetaDelegate = object : MetaDelegate { + override fun getValue(thisRef: Any?, property: KProperty<*>): R = reader(get(key ?: property.name.asName())?.value) + + override val descriptor: MetaDescriptor? = descriptor } //TODO add caching for sealed nodes /* Read-only delegates for [Meta] */ -public fun MetaProvider.string(key: Name? = null): ReadOnlyProperty = value(key) { it?.string } +public fun MetaProvider.string(key: Name? = null): MetaDelegate = value(key = key) { it?.string } -public fun MetaProvider.boolean(key: Name? = null): ReadOnlyProperty = value(key) { it?.boolean } +public fun MetaProvider.boolean(key: Name? = null): MetaDelegate = value(key = key) { it?.boolean } -public fun MetaProvider.number(key: Name? = null): ReadOnlyProperty = value(key) { it?.numberOrNull } +public fun MetaProvider.number(key: Name? = null): MetaDelegate = value(key = key) { it?.numberOrNull } -public fun MetaProvider.double(key: Name? = null): ReadOnlyProperty = value(key) { it?.double } +public fun MetaProvider.double(key: Name? = null): MetaDelegate = value(key = key) { it?.double } -public fun MetaProvider.float(key: Name? = null): ReadOnlyProperty = value(key) { it?.float } +public fun MetaProvider.float(key: Name? = null): MetaDelegate = value(key = key) { it?.float } -public fun MetaProvider.int(key: Name? = null): ReadOnlyProperty = value(key) { it?.int } +public fun MetaProvider.int(key: Name? = null): MetaDelegate = value(key = key) { it?.int } -public fun MetaProvider.long(key: Name? = null): ReadOnlyProperty = value(key) { it?.long } +public fun MetaProvider.long(key: Name? = null): MetaDelegate = value(key = key) { it?.long } -public fun MetaProvider.string(default: String, key: Name? = null): ReadOnlyProperty = - value(key) { it?.string ?: default } +public fun MetaProvider.string(default: String, key: Name? = null): MetaDelegate = + value(key = key) { it?.string ?: default } -public fun MetaProvider.boolean(default: Boolean, key: Name? = null): ReadOnlyProperty = - value(key) { it?.boolean ?: default } +public fun MetaProvider.boolean(default: Boolean, key: Name? = null): MetaDelegate = + value(key = key) { it?.boolean ?: default } -public fun MetaProvider.number(default: Number, key: Name? = null): ReadOnlyProperty = - value(key) { it?.numberOrNull ?: default } +public fun MetaProvider.number(default: Number, key: Name? = null): MetaDelegate = + value(key = key) { it?.numberOrNull ?: default } -public fun MetaProvider.double(default: Double, key: Name? = null): ReadOnlyProperty = - value(key) { it?.double ?: default } +public fun MetaProvider.double(default: Double, key: Name? = null): MetaDelegate = + value(key = key) { it?.double ?: default } -public fun MetaProvider.float(default: Float, key: Name? = null): ReadOnlyProperty = - value(key) { it?.float ?: default } +public fun MetaProvider.float(default: Float, key: Name? = null): MetaDelegate = + value(key = key) { it?.float ?: default } -public fun MetaProvider.int(default: Int, key: Name? = null): ReadOnlyProperty = - value(key) { it?.int ?: default } +public fun MetaProvider.int(default: Int, key: Name? = null): MetaDelegate = + value(key = key) { it?.int ?: default } -public fun MetaProvider.long(default: Long, key: Name? = null): ReadOnlyProperty = - value(key) { it?.long ?: default } +public fun MetaProvider.long(default: Long, key: Name? = null): MetaDelegate = + value(key = key) { it?.long ?: default } -public inline fun > MetaProvider.enum(default: E, key: Name? = null): ReadOnlyProperty = - value(key) { it?.enum() ?: default } +public inline fun > MetaProvider.enum(default: E, key: Name? = null): MetaDelegate = + value(key = key) { it?.enum() ?: default } -public fun MetaProvider.string(key: Name? = null, default: () -> String): ReadOnlyProperty = - value(key) { it?.string ?: default() } +public fun MetaProvider.string(key: Name? = null, default: () -> String): MetaDelegate = + value(key = key) { it?.string ?: default() } -public fun MetaProvider.boolean(key: Name? = null, default: () -> Boolean): ReadOnlyProperty = - value(key) { it?.boolean ?: default() } +public fun MetaProvider.boolean(key: Name? = null, default: () -> Boolean): MetaDelegate = + value(key = key) { it?.boolean ?: default() } -public fun MetaProvider.number(key: Name? = null, default: () -> Number): ReadOnlyProperty = - value(key) { it?.numberOrNull ?: default() } +public fun MetaProvider.number(key: Name? = null, default: () -> Number): MetaDelegate = + value(key = key) { it?.numberOrNull ?: default() } diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaReader.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaReader.kt new file mode 100644 index 00000000..a8514d63 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaReader.kt @@ -0,0 +1,21 @@ +package space.kscience.dataforge.meta + +import space.kscience.dataforge.meta.descriptors.Described + +public interface MetaReader : Described { + + /** + * Read the source meta into an object and return null if Meta could not be interpreted as a target type + */ + public fun readOrNull(source: Meta): T? + + /** + * Read generic read-only meta with this [MetaReader] producing instance of the desired type. + * Throws an error if conversion could not be done. + */ + public fun read(source: Meta): T = readOrNull(source) ?: error("Meta $source could not be interpreted by $this") +} + + +public fun MetaReader.readNullable(item: Meta?): T? = item?.let { read(it) } +public fun MetaReader.readValue(value: Value): T? = read(Meta(value)) diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaRef.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaRef.kt new file mode 100644 index 00000000..2e6f3452 --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaRef.kt @@ -0,0 +1,221 @@ +package space.kscience.dataforge.meta + +import kotlinx.serialization.json.Json +import space.kscience.dataforge.meta.descriptors.Described +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder +import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.asName +import space.kscience.dataforge.names.startsWith +import kotlin.properties.PropertyDelegateProvider +import kotlin.properties.ReadOnlyProperty + + +/** + * A reference to a read-only value of type [T] inside [MetaProvider] or writable value in [MutableMetaProvider] + */ +@DFExperimental +public data class MetaRef( + public val name: Name, + public val converter: MetaConverter, + override val descriptor: MetaDescriptor? = converter.descriptor, +) : Described + +/** + * Get a value from provider by [ref] or return null if node with given name is missing + */ +@DFExperimental +public operator fun MetaProvider.get(ref: MetaRef): T? = get(ref.name)?.let { ref.converter.readOrNull(it) } + +/** + * Set a value in a mutable provider by [ref] + */ +@DFExperimental +public operator fun MutableMetaProvider.set(ref: MetaRef, value: T) { + set(ref.name, ref.converter.convert(value)) +} + +/** + * Observe changes to specific property via given [ref]. + * + * This listener should be removed in a same way as [ObservableMeta.onChange]. + * + * @param callback an action to be performed on each change of item. Null means that the item is not present or malformed. + */ +@DFExperimental +public fun ObservableMeta.onValueChange(owner: Any?, ref: MetaRef, callback: (T?) -> Unit) { + onChange(owner) { name -> + if (name.startsWith(ref.name)) { + get(name)?.let { value -> + callback(ref.converter.readOrNull(value)) + } + } + } +} + +/** + * Remove a node corresponding to [ref] from a mutable provider if it exists + */ +@DFExperimental +public fun MutableMetaProvider.remove(ref: MetaRef<*>) { + remove(ref.name) +} + +/** + * Base storage of [MetaRef] + */ +@OptIn(DFExperimental::class) +public interface MetaRefStore : Described { + public val refs: List> +} + +/** + * A base class for [Meta] specification that stores references to meta nodes. + */ +@DFExperimental +public abstract class MetaSpec : MetaRefStore { + private val _refs: MutableList> = mutableListOf() + override val refs: List> get() = _refs + + /** + * Register a ref in this specification + */ + protected fun registerRef(ref: MetaRef<*>) { + _refs.add(ref) + } + + /** + * Create and register a ref by property name and provided converter. + * By default, uses descriptor from the converter + */ + public fun item( + converter: MetaConverter, + key: Name? = null, + descriptor: MetaDescriptor? = converter.descriptor, + ): PropertyDelegateProvider>> = + PropertyDelegateProvider { _, property -> + val ref = MetaRef(key ?: property.name.asName(), converter, descriptor) + registerRef(ref) + ReadOnlyProperty { _, _ -> + ref + } + } + + /** + * Override to provide custom [MetaDescriptor] + */ + protected open fun MetaDescriptorBuilder.buildDescriptor(): Unit = Unit + + override val descriptor: MetaDescriptor by lazy { + MetaDescriptor { + refs.forEach { ref -> + ref.descriptor?.let { + node(ref.name, ref.descriptor) + } + } + buildDescriptor() + } + } +} + +/** + * Register an item using a [descriptorBuilder] to customize descriptor + */ +@DFExperimental +public fun MetaSpec.item( + converter: MetaConverter, + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = item(converter, key, MetaDescriptor { + converter.descriptor?.let { from(it) } + descriptorBuilder() +}) + +//utility methods to add different nodes + +@DFExperimental +public fun MetaSpec.metaItem( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.meta, key, descriptorBuilder) + +@DFExperimental +public fun MetaSpec.string( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.string, key, descriptorBuilder) + +@DFExperimental +public fun MetaSpec.boolean( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.boolean, key, descriptorBuilder) + +@DFExperimental +public fun MetaSpec.stringList( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>>> = + item(MetaConverter.stringList, key, descriptorBuilder) + +@DFExperimental +public fun MetaSpec.float( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.float, key, descriptorBuilder) + +@DFExperimental +public fun MetaSpec.double( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.double, key, descriptorBuilder) + +@DFExperimental +public fun MetaSpec.int( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.int, key, descriptorBuilder) + +@DFExperimental +public fun MetaSpec.long( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.long, key, descriptorBuilder) + + +@DFExperimental +public fun MetaSpec.doubleArray( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.doubleArray, key, descriptorBuilder) + +@DFExperimental +public fun MetaSpec.byteArray( + key: Name? = null, + descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.byteArray, key, descriptorBuilder) + +@DFExperimental +public inline fun > MetaSpec.enum( + key: Name? = null, + noinline descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.enum(), key, descriptorBuilder) + +@DFExperimental +public inline fun MetaSpec.serializable( + key: Name? = null, + jsonEncoder: Json = Json, + noinline descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + item(MetaConverter.serializable(jsonEncoder = jsonEncoder), key, descriptorBuilder) \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaTransformation.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaTransformation.kt similarity index 98% rename from dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaTransformation.kt rename to dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaTransformation.kt index d41365a6..dbf94f0f 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaTransformation.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaTransformation.kt @@ -1,6 +1,5 @@ -package space.kscience.dataforge.meta.transformations +package space.kscience.dataforge.meta -import space.kscience.dataforge.meta.* import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import kotlin.jvm.JvmInline diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt index 7e05d215..274671bf 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt @@ -19,6 +19,10 @@ public annotation class MetaBuilderMarker public interface MutableMetaProvider : MetaProvider, MutableValueProvider { override fun get(name: Name): MutableMeta? public operator fun set(name: Name, node: Meta?) + + /** + * Set value with the given name. Does nothing if value is not changed. + */ override fun setValue(name: Name, value: Value?) } @@ -48,11 +52,13 @@ public interface MutableMeta : Meta, MutableMetaProvider { } override fun setValue(name: Name, value: Value?) { - getOrCreate(name).value = value + if (value != getValue(name)) { + getOrCreate(name).value = value + } } /** - * Get existing node or create a new one + * Get an existing node or create a new one */ public fun getOrCreate(name: Name): MutableMeta @@ -122,6 +128,10 @@ public interface MutableMeta : Meta, MutableMetaProvider { setValue(Name.parse(this), array.asValue()) } + public infix fun String.put(array: ByteArray) { + setValue(Name.parse(this), array.asValue()) + } + public infix fun String.put(repr: MetaRepr) { set(Name.parse(this), repr.toMeta()) } @@ -149,7 +159,17 @@ public interface MutableTypedMeta> : TypedMeta, Mutab */ @DFExperimental public fun attach(name: Name, node: M) - override fun get(name: Name): M? + + override fun get(name: Name): M? { + tailrec fun M.find(name: Name): M? = if (name.isEmpty()) { + self + } else { + items[name.firstOrNull()!!]?.find(name.cutFirst()) + } + + return self.find(name) + } + override fun getOrCreate(name: Name): M } @@ -165,7 +185,7 @@ public fun MutableMetaProvider.remove(key: String) { // node setters -public operator fun MutableMetaProvider.set(Key: NameToken, value: Meta): Unit = set(Key.asName(), value) +public operator fun MutableMetaProvider.set(key: NameToken, value: Meta): Unit = set(key.asName(), value) public operator fun MutableMetaProvider.set(key: String, value: Meta): Unit = set(Name.parse(key), value) @@ -198,10 +218,8 @@ public operator fun MutableMetaProvider.set(key: String, metas: Iterable): /** - * 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 + * Update the existing mutable node with another node. + * Values that are present in the current provider and are missing in [meta] are kept. */ public fun MutableMetaProvider.update(meta: Meta) { meta.valueSequence().forEach { (name, value) -> @@ -222,7 +240,7 @@ public fun > MutableTypedMeta.edit(name: Name, builde getOrCreate(name).apply(builder) /** - * Set a value at a given [name]. If node does not exist, create it. + * Set a value at a given [name]. If a node does not exist, create it. */ public operator fun > MutableTypedMeta.set(name: Name, value: Value?) { edit(name) { @@ -245,6 +263,9 @@ private class MutableMetaImpl( value: Value?, children: Map = emptyMap(), ) : AbstractObservableMeta(), ObservableMutableMeta { + + override val self get() = this + override var value = value @ThreadSafe set(value) { val oldValue = field @@ -324,8 +345,6 @@ private class MutableMetaImpl( //remove child and invalidate if argument is null if (node == null) { children.remove(token)?.removeListener(this) - // old item is not null otherwise we can't be here - invalidate(name) } else { val newNode = wrapItem(node) newNode.adoptBy(this, token) @@ -335,7 +354,7 @@ private class MutableMetaImpl( else -> { val token = name.firstOrNull()!! - //get existing or create new node. + //get an existing node or create a new node. if (items[token] == null) { val newNode = MutableMetaImpl(null) newNode.adoptBy(this, token) @@ -369,16 +388,37 @@ public fun MutableMeta.append(name: Name, value: Value): Unit = append(name, Met public fun MutableMeta.append(key: String, value: Value): Unit = append(Name.parse(key), value) +/** + * Update all items that exist in the [newMeta] and remove existing items that are missing in [newMeta]. + * This produces the same result as clearing all items and updating blank meta with a [newMeta], but does not + * produce unnecessary invalidation events (if they are supported). + */ +public fun MutableMeta.reset(newMeta: Meta) { + //remove old items + (items.keys - newMeta.items.keys).forEach { + remove(it.asName()) + } + newMeta.items.forEach { (token, item) -> + set(token, item) + } +} + /** * Create a mutable copy of this meta. The copy is created even if the Meta is already mutable */ -public fun Meta.toMutableMeta(): ObservableMutableMeta = MutableMetaImpl(value, items) +public fun Meta.toMutableMeta(): MutableMeta = + MutableMeta { update(this@toMutableMeta) } //MutableMetaImpl(value, items) public fun Meta.asMutableMeta(): MutableMeta = (this as? MutableMeta) ?: toMutableMeta() @JsName("newObservableMutableMeta") public fun ObservableMutableMeta(): ObservableMutableMeta = MutableMetaImpl(null) +/** + * Create a pre-filled [ObservableMutableMeta] + */ +public fun ObservableMutableMeta(content: Meta): ObservableMutableMeta = ObservableMutableMeta { update(content) } + /** * Build a [MutableMeta] using given transformation */ @@ -387,12 +427,14 @@ public inline fun ObservableMutableMeta(builder: MutableMeta.() -> Unit = {}): O /** - * Create a copy of this [Meta], optionally applying the given [block]. - * The listeners of the original Config are not retained. + * Create a read-only copy of this [Meta]. [modification] is an optional modification applied to [Meta] on copy. + * + * The copy does not reflect changes of the initial Meta. */ -public inline fun Meta.copy(block: MutableMeta.() -> Unit = {}): Meta = - toMutableMeta().apply(block) - +public inline fun Meta.copy(modification: MutableMeta.() -> Unit = {}): Meta = Meta { + update(this@copy) + modification() +} private class MutableMetaWithDefault( val source: MutableMeta, val default: MetaProvider, val rootName: Name, diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaDelegate.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaDelegate.kt index 0f28523c..28a550d8 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaDelegate.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaDelegate.kt @@ -1,52 +1,146 @@ package space.kscience.dataforge.meta -import space.kscience.dataforge.meta.transformations.MetaConverter +import space.kscience.dataforge.meta.descriptors.Described +import space.kscience.dataforge.meta.descriptors.MetaDescriptor +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName +import space.kscience.dataforge.names.getIndexedList import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty + /* Read-write delegates */ -public fun MutableMetaProvider.node(key: Name? = null): ReadWriteProperty = - object : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? { - return get(key ?: property.name.asName()) - } +public interface MutableMetaDelegate : ReadWriteProperty, Described - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) { - val name = key ?: property.name.asName() - set(name, value) - } +public fun MutableMetaProvider.node( + key: Name? = null, + descriptor: MetaDescriptor? = null, +): MutableMetaDelegate = object : MutableMetaDelegate { + + override val descriptor: MetaDescriptor? = descriptor + + override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? { + return get(key ?: property.name.asName()) } -public fun MutableMetaProvider.node(key: Name? = null, converter: MetaConverter): ReadWriteProperty = - object : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T? { - return get(key ?: property.name.asName())?.let { converter.metaToObject(it) } - } + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) { + val name = key ?: property.name.asName() + set(name, value) + } +} - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { - val name = key ?: property.name.asName() - set(name, value?.let { converter.objectToMeta(it) }) - } +/** + * Use [converter] to transform an object to Meta and back. + * Note that mutation of the object does not change Meta. + */ +public fun MutableMetaProvider.convertable( + converter: MetaConverter, + key: Name? = null, +): MutableMetaDelegate = object : MutableMetaDelegate { + + override val descriptor: MetaDescriptor? get() = converter.descriptor + + + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + val name = key ?: property.name.asName() + return get(name)?.let { converter.read(it) } } -public fun MutableMetaProvider.value(key: Name? = null): ReadWriteProperty = - object : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Value? = - get(key ?: property.name.asName())?.value - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) { - setValue(key ?: property.name.asName(), value) - } + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + val name = key ?: property.name.asName() + set(name, value?.let { converter.convert(it) }) } +} + +public fun MutableMetaProvider.convertable( + converter: MetaConverter, + default: T, + key: Name? = null, +): MutableMetaDelegate = object : MutableMetaDelegate { + + override val descriptor: MetaDescriptor? get() = converter.descriptor + + + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + val name = key ?: property.name.asName() + return get(name)?.let { converter.read(it) } ?: default + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + val name = key ?: property.name.asName() + set(name, value?.let { converter.convert(it) }) + } +} + +/** + * Use object serializer to transform it to Meta and back. + * Note that mutation of the object does not change Meta. + */ +@DFExperimental +public inline fun MutableMetaProvider.serializable( + descriptor: MetaDescriptor? = null, + key: Name? = null, +): MutableMetaDelegate = convertable(MetaConverter.serializable(descriptor), key) + +@DFExperimental +public inline fun MutableMetaProvider.serializable( + descriptor: MetaDescriptor? = null, + default: T, + key: Name? = null, +): MutableMetaDelegate = convertable(MetaConverter.serializable(descriptor), default, key) + +/** + * Use [converter] to convert a list of same name siblings meta to object and back. + * Note that mutation of the object does not change Meta. + */ +public fun MutableMeta.listOfConvertable( + converter: MetaConverter, + key: Name? = null, +): MutableMetaDelegate> = object : MutableMetaDelegate> { + override val descriptor: MetaDescriptor? = converter.descriptor?.copy(multiple = true) + + override fun getValue(thisRef: Any?, property: KProperty<*>): List { + val name = key ?: property.name.asName() + return getIndexedList(name).map { converter.read(it) } + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: List) { + val name = key ?: property.name.asName() + setIndexed(name, value.map { converter.convert(it) }) + } +} + +@DFExperimental +public inline fun MutableMeta.listOfSerializable( + key: Name? = null, + descriptor: MetaDescriptor? = null, +): MutableMetaDelegate> = listOfConvertable(MetaConverter.serializable(descriptor), key) + + +public fun MutableMetaProvider.value( + key: Name? = null, + descriptor: MetaDescriptor? = null, +): MutableMetaDelegate = object : MutableMetaDelegate { + override val descriptor: MetaDescriptor? = descriptor + + override fun getValue(thisRef: Any?, property: KProperty<*>): Value? = + get(key ?: property.name.asName())?.value + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) { + setValue(key ?: property.name.asName(), value) + } +} public fun MutableMetaProvider.value( key: Name? = null, writer: (T) -> Value? = { Value.of(it) }, - reader: (Value?) -> T -): ReadWriteProperty = object : ReadWriteProperty { + descriptor: MetaDescriptor? = null, + reader: (Value?) -> T, +): MutableMetaDelegate = object : MutableMetaDelegate { + override val descriptor: MetaDescriptor? = descriptor + override fun getValue(thisRef: Any?, property: KProperty<*>): T = reader(get(key ?: property.name.asName())?.value) @@ -57,65 +151,65 @@ public fun MutableMetaProvider.value( /* Read-write delegates for [MutableItemProvider] */ -public fun MutableMetaProvider.string(key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.string(key: Name? = null): MutableMetaDelegate = value(key) { it?.string } -public fun MutableMetaProvider.boolean(key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.boolean(key: Name? = null): MutableMetaDelegate = value(key) { it?.boolean } -public fun MutableMetaProvider.number(key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.number(key: Name? = null): MutableMetaDelegate = value(key) { it?.number } -public fun MutableMetaProvider.string(default: String, key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.string(default: String, key: Name? = null): MutableMetaDelegate = value(key) { it?.string ?: default } -public fun MutableMetaProvider.boolean(default: Boolean, key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.boolean(default: Boolean, key: Name? = null): MutableMetaDelegate = value(key) { it?.boolean ?: default } -public fun MutableMetaProvider.number(default: Number, key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.number(default: Number, key: Name? = null): MutableMetaDelegate = value(key) { it?.number ?: default } -public fun MutableMetaProvider.string(key: Name? = null, default: () -> String): ReadWriteProperty = +public fun MutableMetaProvider.string(key: Name? = null, default: () -> String): MutableMetaDelegate = value(key) { it?.string ?: default() } -public fun MutableMetaProvider.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty = +public fun MutableMetaProvider.boolean(key: Name? = null, default: () -> Boolean): MutableMetaDelegate = value(key) { it?.boolean ?: default() } -public fun MutableMetaProvider.number(key: Name? = null, default: () -> Number): ReadWriteProperty = +public fun MutableMetaProvider.number(key: Name? = null, default: () -> Number): MutableMetaDelegate = value(key) { it?.number ?: default() } public inline fun > MutableMetaProvider.enum( default: E, key: Name? = null, -): ReadWriteProperty = value(key) { value -> value?.string?.let { enumValueOf(it) } ?: default } +): MutableMetaDelegate = value(key) { value -> value?.string?.let { enumValueOf(it) } ?: default } /* Number delegates */ -public fun MutableMetaProvider.int(key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.int(key: Name? = null): MutableMetaDelegate = value(key) { it?.int } -public fun MutableMetaProvider.double(key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.double(key: Name? = null): MutableMetaDelegate = value(key) { it?.double } -public fun MutableMetaProvider.long(key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.long(key: Name? = null): MutableMetaDelegate = value(key) { it?.long } -public fun MutableMetaProvider.float(key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.float(key: Name? = null): MutableMetaDelegate = value(key) { it?.float } /* Safe number delegates*/ -public fun MutableMetaProvider.int(default: Int, key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.int(default: Int, key: Name? = null): MutableMetaDelegate = value(key) { it?.int ?: default } -public fun MutableMetaProvider.double(default: Double, key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.double(default: Double, key: Name? = null): MutableMetaDelegate = value(key) { it?.double ?: default } -public fun MutableMetaProvider.long(default: Long, key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.long(default: Long, key: Name? = null): MutableMetaDelegate = value(key) { it?.long ?: default } -public fun MutableMetaProvider.float(default: Float, key: Name? = null): ReadWriteProperty = +public fun MutableMetaProvider.float(default: Float, key: Name? = null): MutableMetaDelegate = value(key) { it?.float ?: default } @@ -124,7 +218,7 @@ public fun MutableMetaProvider.float(default: Float, key: Name? = null): ReadWri public fun MutableMetaProvider.stringList( vararg default: String, key: Name? = null, -): ReadWriteProperty> = value( +): MutableMetaDelegate> = value( key, writer = { list -> list.map { str -> str.asValue() }.asValue() }, reader = { it?.stringList ?: listOf(*default) }, @@ -132,7 +226,7 @@ public fun MutableMetaProvider.stringList( public fun MutableMetaProvider.stringList( key: Name? = null, -): ReadWriteProperty?> = value( +): MutableMetaDelegate?> = value( key, writer = { it -> it?.map { str -> str.asValue() }?.asValue() }, reader = { it?.stringList }, @@ -141,29 +235,18 @@ public fun MutableMetaProvider.stringList( public fun MutableMetaProvider.numberList( vararg default: Number, key: Name? = null, -): ReadWriteProperty> = value( +): MutableMetaDelegate> = value( key, writer = { it.map { num -> num.asValue() }.asValue() }, reader = { it?.list?.map { value -> value.numberOrNull ?: Double.NaN } ?: listOf(*default) }, ) -/* A special delegate for double arrays */ - - -public fun MutableMetaProvider.doubleArray( - vararg default: Double, - key: Name? = null, -): ReadWriteProperty = value( - key, - writer = { DoubleArrayValue(it) }, - reader = { it?.doubleArray ?: doubleArrayOf(*default) }, -) public fun MutableMetaProvider.listValue( key: Name? = null, writer: (T) -> Value = { Value.of(it) }, reader: (Value) -> T, -): ReadWriteProperty?> = value( +): MutableMetaDelegate?> = value( key, writer = { it?.map(writer)?.asValue() }, reader = { it?.list?.map(reader) } diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt index 5a2b05f7..20388a92 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt @@ -1,8 +1,7 @@ package space.kscience.dataforge.meta import space.kscience.dataforge.misc.ThreadSafe -import space.kscience.dataforge.names.* -import kotlin.reflect.KProperty1 +import space.kscience.dataforge.names.Name internal data class MetaListener( @@ -15,12 +14,15 @@ internal data class MetaListener( */ public interface ObservableMeta : Meta { /** - * Add change listener to this meta. Owner is declared to be able to remove listeners later. Listener without owner could not be removed + * Add change listener to this meta. The Owner is declared to be able to remove listeners later. + * Listeners without an owner could be only removed all together. + * + * `this` object in the listener represents the current state of this meta. The name points to a changed node */ public fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) /** - * Remove all listeners belonging to given owner + * Remove all listeners belonging to the given [owner]. Passing null removes all listeners. */ public fun removeListener(owner: Any?) @@ -34,17 +36,7 @@ public interface ObservableMeta : Meta { * A [Meta] which is both observable and mutable */ public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta { - override fun getOrCreate(name: Name): ObservableMutableMeta - - override fun get(name: Name): ObservableMutableMeta? { - tailrec fun ObservableMutableMeta.find(name: Name): ObservableMutableMeta? = if (name.isEmpty()) { - this - } else { - items[name.firstOrNull()!!]?.find(name.cutFirst()) - } - - return find(name) - } + override val self: ObservableMutableMeta get() = this } internal abstract class AbstractObservableMeta : ObservableMeta { @@ -67,24 +59,4 @@ internal abstract class AbstractObservableMeta : ObservableMeta { override fun toString(): String = Meta.toString(this) override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) override fun hashCode(): Int = Meta.hashCode(this) -} - -/** - * Use the value of the property in a [callBack]. - * The callback is called once immediately after subscription to pass the initial value. - * - * Optional [owner] property is used for - */ -public fun S.useProperty( - property: KProperty1, - owner: Any? = null, - callBack: S.(T) -> Unit, -) { - //Pass initial value. - callBack(property.get(this)) - meta.onChange(owner) { name -> - if (name.startsWith(property.name.asName())) { - callBack(property.get(this@useProperty)) - } - } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt index 76645d83..7c62f692 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt @@ -6,60 +6,74 @@ import space.kscience.dataforge.names.* /** * A class that takes [MutableMeta] provider and adds obsevability on top of that + * + * TODO rewrite to properly work with detached nodes */ private class ObservableMetaWrapper( val root: MutableMeta, - val absoluteName: Name, + val nodeName: Name, val listeners: MutableSet, ) : ObservableMutableMeta { - override val items: Map - get() = root.items.keys.associateWith { - ObservableMetaWrapper(root, absoluteName + it, listeners) - } - override fun get(name: Name): ObservableMutableMeta? = - root.get(name)?.let { ObservableMetaWrapper(root, this.absoluteName + name, listeners) } + override val self get() = this + + override val items: Map + get() = root[nodeName]?.items?.keys?.associateWith { + ObservableMetaWrapper(root, nodeName + it, listeners) + } ?: emptyMap() + + override fun get(name: Name): ObservableMutableMeta? = if (root[nodeName + name] == null) { + null + } else { + ObservableMetaWrapper(root, nodeName + name, listeners) + } @ThreadSafe override fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) { listeners.add( - MetaListener(Pair(owner, absoluteName)) { name -> - if (name.startsWith(absoluteName)) { - (this[absoluteName] ?: Meta.EMPTY).callback(name.removeFirstOrNull(absoluteName)!!) + MetaListener(Pair(owner, nodeName)) { fullName -> + if (fullName.startsWith(nodeName)) { + root[nodeName]?.callback(fullName.removeFirstOrNull(nodeName)!!) } } ) } override fun removeListener(owner: Any?) { - listeners.removeAll { it.owner === Pair(owner, absoluteName) } + listeners.removeAll { it.owner === Pair(owner, nodeName) } } override fun invalidate(name: Name) { - listeners.forEach { it.callback(this, name) } + listeners.forEach { it.callback(this, nodeName + name) } } override var value: Value? - get() = root.value + get() = root[nodeName]?.value set(value) { - root.value = value + root.getOrCreate(nodeName).value = value invalidate(Name.EMPTY) } override fun getOrCreate(name: Name): ObservableMutableMeta = - ObservableMetaWrapper(root, this.absoluteName + name, listeners) + ObservableMetaWrapper(root, nodeName + name, listeners) - override fun set(name: Name, node: Meta?) { + fun removeNode(name: Name): Meta? { val oldMeta = get(name) //don't forget to remove listener oldMeta?.removeListener(this) - root.set(absoluteName + name, node) + + return oldMeta + } + + override fun set(name: Name, node: Meta?) { + val oldMeta = removeNode(name) + root[nodeName + name] = node if (oldMeta != node) { invalidate(name) } } - override fun toMeta(): Meta = root[absoluteName]?.toMeta() ?: Meta.EMPTY + override fun toMeta(): Meta = root[nodeName]?.toMeta() ?: Meta.EMPTY override fun toString(): String = Meta.toString(this) override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt index 90473286..bc05cb5d 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt @@ -7,36 +7,45 @@ import space.kscience.dataforge.meta.descriptors.validate import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.ThreadSafe import space.kscience.dataforge.names.* +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty +import kotlin.reflect.KProperty1 /** - * A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification]. - * Default item provider and [MetaDescriptor] are optional + * A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [MetaReader]. + * + * @param prototype default values provided by this scheme */ -public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurable { +public open class Scheme( + private var prototype: Meta? = null, + descriptor: MetaDescriptor? = null, +) : Described, MetaRepr, MutableMetaProvider, Configurable { /** - * Meta to be mutated by this schme + * Meta to be mutated by this scheme */ - private var targetMeta: MutableMeta = MutableMeta() + internal var target: MutableMeta = MutableMeta() /** - * Default values provided by this scheme + * A descriptor of this scheme */ - private var defaultMeta: Meta? = null + final override var descriptor: MetaDescriptor? = descriptor + private set + final override val meta: ObservableMutableMeta = SchemeMeta(Name.EMPTY) - final override var descriptor: MetaDescriptor? = null - internal set - - internal fun wrap( - newMeta: MutableMeta, - preserveDefault: Boolean = false, + /** + * This method must be called before the scheme could be used + */ + internal fun initialize( + target: MutableMeta, + prototype: Meta, + descriptor: MetaDescriptor?, ) { - if (preserveDefault) { - defaultMeta = targetMeta.seal() - } - targetMeta = newMeta + this.target = target + this.prototype = prototype + this.descriptor = descriptor } /** @@ -47,11 +56,11 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl return descriptor?.validate(meta) ?: true } - override fun get(name: Name): MutableMeta? = meta.get(name) + override fun get(name: Name): MutableMeta? = meta[name] override fun set(name: Name, node: Meta?) { if (validate(name, meta)) { - meta.set(name, node) + meta[name] = node } else { error("Validation failed for node $node at $name") } @@ -68,14 +77,19 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl private val listeners: MutableList = mutableListOf() + override fun toString(): String = meta.toString() + private inner class SchemeMeta(val pathName: Name) : ObservableMutableMeta { + + override val self get() = this + override var value: Value? - get() = targetMeta[pathName]?.value - ?: defaultMeta?.get(pathName)?.value + get() = target[pathName]?.value + ?: prototype?.get(pathName)?.value ?: descriptor?.get(pathName)?.defaultValue set(value) { - val oldValue = targetMeta[pathName]?.value - targetMeta[pathName] = value + val oldValue = target[pathName]?.value + target[pathName] = value if (oldValue != value) { invalidate(Name.EMPTY) } @@ -83,8 +97,8 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl override val items: Map get() { - val targetKeys = targetMeta[pathName]?.items?.keys ?: emptySet() - val defaultKeys = defaultMeta?.get(pathName)?.items?.keys ?: emptySet() + val targetKeys = target[pathName]?.items?.keys ?: emptySet() + val defaultKeys = prototype?.get(pathName)?.items?.keys ?: emptySet() return (targetKeys + defaultKeys).associateWith { SchemeMeta(pathName + it) } } @@ -111,7 +125,7 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl override fun hashCode(): Int = Meta.hashCode(this) override fun set(name: Name, node: Meta?) { - targetMeta.set(name, node) + target[pathName + name] = node invalidate(name) } @@ -119,7 +133,6 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl @DFExperimental override fun attach(name: Name, node: ObservableMutableMeta) { - //TODO implement zero-copy attachment set(name, node) node.onChange(this) { changeName -> set(name + changeName, this[changeName]) @@ -131,10 +144,11 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl /** * Relocate scheme target onto given [MutableMeta]. Old provider does not get updates anymore. - * Current state of the scheme used as a default. + * The Current state of the scheme that os used as a default. */ +@DFExperimental public fun T.retarget(provider: MutableMeta): T = apply { - wrap(provider, true) + initialize(provider, meta.seal(), descriptor) } /** @@ -151,26 +165,151 @@ public inline fun T.copy(spec: SchemeSpec, block: T.() -> Unit = /** * A specification for simplified generation of wrappers */ -public open class SchemeSpec( +public open class SchemeSpec( private val builder: () -> T, -) : Specification { +) : MetaConverter { - override fun read(source: Meta): T = builder().also { - it.wrap(MutableMeta().withDefault(source)) - } - - override fun write(target: MutableMeta): T = empty().also { - it.wrap(target) - } - - //TODO Generate descriptor from Scheme class override val descriptor: MetaDescriptor? get() = null - override fun empty(): T = builder().also { - it.descriptor = descriptor + override fun readOrNull(source: Meta): T = builder().also { + it.initialize(MutableMeta(), source, descriptor) } - @Suppress("OVERRIDE_BY_INLINE") - final override inline operator fun invoke(action: T.() -> Unit): T = empty().apply(action) + /** + * Write changes made to the [Scheme] to target [MutableMeta]. If the empty [Scheme] contains any data it is copied to the target. + */ + public fun write(target: MutableMeta): T = empty().also { + target.update(it.meta) + it.initialize(target, Meta.EMPTY, descriptor) + } + /** + * Generate a blank object. The object could contain some elements if they are defined in a constructor + */ + public fun empty(): T = builder().also { + it.initialize(MutableMeta(), it.target, descriptor) + } + + override fun convert(obj: T): Meta = obj.meta + + /** + * A convenience method to use specifications in builders + */ + public inline operator fun invoke(action: T.() -> Unit): T = empty().apply(action) + +} + + +/** + * Update a [MutableMeta] using given specification + */ +public fun MutableMeta.updateWith( + spec: SchemeSpec, + action: T.() -> Unit, +): T = spec.write(this).apply(action) + + +/** + * Update configuration using given specification + */ +public fun Configurable.updateWith( + spec: SchemeSpec, + action: T.() -> Unit, +): T = spec.write(meta).apply(action) + + +/** + * A delegate that uses a [MetaReader] to wrap a child of this provider + */ +public fun MutableMetaProvider.scheme( + spec: SchemeSpec, + key: Name? = null, +): ReadWriteProperty = object : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + val name = key ?: property.name.asName() + val node = get(name)?: MutableMeta().also { set(name,it) } + return spec.write(node) + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + val name = key ?: property.name.asName() + set(name, value.toMeta()) + } +} + +public fun Scheme.scheme( + spec: SchemeSpec, + key: Name? = null, +): ReadWriteProperty = meta.scheme(spec, key) + +/** + * A delegate that uses a [MetaReader] to wrap a child of this provider. + * Returns null if meta with given name does not exist. + */ +public fun MutableMeta.schemeOrNull( + spec: SchemeSpec, + key: Name? = null, +): ReadWriteProperty = object : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + val name = key ?: property.name.asName() + return if (get(name) == null) null else spec.write(getOrCreate(name)) + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + val name = key ?: property.name.asName() + if (value == null) remove(name) + else set(name, value.toMeta()) + } +} + +public fun Scheme.schemeOrNull( + spec: SchemeSpec, + key: Name? = null, +): ReadWriteProperty = meta.schemeOrNull(spec, key) + +/** + * A delegate that uses a [MetaReader] to wrap a list of child providers. + * If children are mutable, the changes in list elements are reflected on them. + * The list is a snapshot of children state, so change in structure is not reflected on its composition. + */ +public fun MutableMeta.listOfScheme( + spec: SchemeSpec, + key: Name? = null, +): ReadWriteProperty> = object : ReadWriteProperty> { + override fun getValue(thisRef: Any?, property: KProperty<*>): List { + val name = key ?: property.name.asName() + return getIndexedList(name).map { spec.write(it as MutableMeta) } + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: List) { + val name = key ?: property.name.asName() + setIndexed(name, value.map { it.toMeta() }) + } +} + + +public fun Scheme.listOfScheme( + spec: SchemeSpec, + key: Name? = null, +): ReadWriteProperty> = meta.listOfScheme(spec, key) + + +/** + * Use the value of the property in a [callBack]. + * The callback is called once immediately after subscription to pass the initial value. + * + * Optional [owner] property is used for + */ +public fun S.useProperty( + property: KProperty1, + owner: Any? = null, + callBack: S.(T) -> Unit, +) { + //Pass initial value. + callBack(property.get(this)) + meta.onChange(owner) { name -> + if (name.startsWith(property.name.asName())) { + callBack(property.get(this@useProperty)) + } + } } \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt index 217a6a04..b218fad6 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt @@ -13,6 +13,9 @@ public class SealedMeta( override val value: Value?, override val items: Map, ) : TypedMeta { + + override val self: SealedMeta get() = this + override fun toString(): String = Meta.toString(this) override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta) @@ -76,6 +79,8 @@ internal class MetaBuilder( override fun set(name: Name, node: Meta?) { + //skip setting if value has not changed + if(node == get(name)) return when (name.length) { 0 -> error("Can't set a meta with empty name") 1 -> { @@ -89,7 +94,7 @@ internal class MetaBuilder( } else -> { - getOrCreate(name.first().asName()).set(name.cutFirst(), node) + getOrCreate(name.first().asName())[name.cutFirst()] = node } } } @@ -101,11 +106,6 @@ internal class MetaBuilder( override fun hashCode(): Int = Meta.hashCode(this) } -/** - * Create a read-only meta. - */ -public inline fun Meta(builder: MutableMeta.() -> Unit): Meta = - MetaBuilder().apply(builder).seal() /** * Create an immutable meta. @@ -113,6 +113,11 @@ public inline fun Meta(builder: MutableMeta.() -> Unit): Meta = public inline fun SealedMeta(builder: MutableMeta.() -> Unit): SealedMeta = MetaBuilder().apply(builder).seal() +/** + * Create a read-only meta. + */ +public inline fun Meta(builder: MutableMeta.() -> Unit): Meta = SealedMeta(builder) + /** * Create an empty meta mutable meta. */ diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Specification.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Specification.kt deleted file mode 100644 index 6d3afbea..00000000 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Specification.kt +++ /dev/null @@ -1,130 +0,0 @@ -package space.kscience.dataforge.meta - -import space.kscience.dataforge.meta.descriptors.Described -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.asName -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KProperty - -public interface ReadOnlySpecification: Described { - - /** - * Read generic read-only meta with this [Specification] producing instance of desired type. - * The source is not mutated even if it is in theory mutable - */ - public fun read(source: Meta): T - - /** - * Generate an empty object - */ - public fun empty(): T - - /** - * A convenience method to use specifications in builders - */ - 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 [MutableMeta], using it as inner storage (changes to [Specification] are reflected on [MutableMeta] - */ - public fun write(target: MutableMeta): T -} - -/** - * Update a [MutableMeta] using given specification - */ -public fun MutableMeta.updateWith( - spec: Specification, - action: T.() -> Unit, -): T = spec.write(this).apply(action) - - -/** - * Update configuration using given specification - */ -public fun Configurable.updateWith( - spec: Specification, - action: T.() -> Unit, -): T = spec.write(meta).apply(action) - -// -//public fun > MutableMeta.withSpec(spec: Specification): M? = -// spec.write(it) - -/** - * A delegate that uses a [Specification] to wrap a child of this provider - */ -public fun MutableMeta.spec( - spec: Specification, - key: Name? = null, -): ReadWriteProperty = object : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T { - val name = key ?: property.name.asName() - return spec.write(getOrCreate(name)) - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - val name = key ?: property.name.asName() - set(name, value.toMeta()) - } -} - -public fun Scheme.spec( - spec: Specification, - key: Name? = null, -): ReadWriteProperty = meta.spec(spec, key) - -/** - * A delegate that uses a [Specification] to wrap a child of this provider. - * Returns null if meta with given name does not exist. - */ -public fun MutableMeta.specOrNull( - spec: Specification, - key: Name? = null, -): ReadWriteProperty = object : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T? { - val name = key ?: property.name.asName() - return if (get(name) == null) null else spec.write(getOrCreate(name)) - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { - val name = key ?: property.name.asName() - if (value == null) remove(name) - else set(name, value.toMeta()) - } -} - -public fun Scheme.specOrNull( - spec: Specification, - key: Name? = null, -): ReadWriteProperty = meta.specOrNull(spec, key) - -/** - * A delegate that uses a [Specification] to wrap a list of child providers. - * If children are mutable, the changes in list elements are reflected on them. - * The list is a snapshot of children state, so change in structure is not reflected on its composition. - */ -@DFExperimental -public fun MutableMeta.listOfSpec( - spec: Specification, - key: Name? = null, -): ReadWriteProperty> = object : ReadWriteProperty> { - override fun getValue(thisRef: Any?, property: KProperty<*>): List { - val name = key ?: property.name.asName() - return getIndexed(name).values.map { spec.write(it as MutableMeta) } - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: List) { - val name = key ?: property.name.asName() - setIndexed(name, value.map { it.toMeta() }) - } -} diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Value.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Value.kt index 66e14c86..2ab7b9ee 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Value.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Value.kt @@ -256,8 +256,6 @@ public fun ShortArray.asValue(): Value = if (isEmpty()) Null else ListValue(map public fun FloatArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) }) -public fun ByteArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) }) - public fun > E.asValue(): Value = EnumValue(this) diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ValueSerializer.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ValueSerializer.kt index dc13ef4c..0379187f 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ValueSerializer.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ValueSerializer.kt @@ -8,6 +8,9 @@ import kotlinx.serialization.descriptors.element import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +/** + * A serializer for [Value] + */ public object ValueSerializer : KSerializer { private val listSerializer by lazy { ListSerializer(ValueSerializer) } diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptor.kt index 742b89ed..45954985 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptor.kt @@ -7,6 +7,7 @@ import space.kscience.dataforge.names.* /** * Restrictions on value in the node */ +@Serializable public enum class ValueRestriction { /** * No restrictions @@ -27,7 +28,7 @@ public enum class ValueRestriction { /** * The descriptor for a meta * @param description description text - * @param children child descriptors for this node + * @param nodes child descriptors for this node * @param multiple True if same name siblings with this name are allowed * @param valueRestriction The requirements for node content * @param valueTypes list of allowed types for [Meta.value], null if all values are allowed. @@ -39,7 +40,7 @@ public enum class ValueRestriction { @Serializable public data class MetaDescriptor( public val description: String? = null, - public val children: Map = emptyMap(), + public val nodes: Map = emptyMap(), public val multiple: Boolean = false, public val valueRestriction: ValueRestriction = ValueRestriction.NONE, public val valueTypes: List? = null, @@ -47,6 +48,9 @@ public data class MetaDescriptor( public val defaultValue: Value? = null, public val attributes: Meta = Meta.EMPTY, ) { + @Deprecated("Replace by nodes", ReplaceWith("nodes")) + public val children: Map get() = nodes + /** * A node constructed of default values for this descriptor and its children */ @@ -55,7 +59,7 @@ public data class MetaDescriptor( defaultValue?.let { defaultValue -> this.value = defaultValue } - children.forEach { (key, descriptor) -> + nodes.forEach { (key, descriptor) -> set(key, descriptor.defaultNode) } } @@ -67,13 +71,13 @@ public data class MetaDescriptor( } } -public val MetaDescriptor.required: Boolean get() = valueRestriction == ValueRestriction.REQUIRED || children.values.any { required } +public val MetaDescriptor.required: Boolean get() = valueRestriction == ValueRestriction.REQUIRED || nodes.values.any { required } public val MetaDescriptor.allowedValues: List? get() = attributes[MetaDescriptor.ALLOWED_VALUES_KEY]?.value?.list public operator fun MetaDescriptor.get(name: Name): MetaDescriptor? = when (name.length) { 0 -> this - 1 -> children[name.firstOrNull()!!.toString()] + 1 -> nodes[name.firstOrNull()!!.toString()] else -> get(name.firstOrNull()!!.asName())?.get(name.cutFirst()) } @@ -95,7 +99,7 @@ public fun MetaDescriptor.validate(item: Meta?): Boolean { if (item == null) return !required if (!validate(item.value)) return false - children.forEach { (key, childDescriptor) -> + nodes.forEach { (key, childDescriptor) -> if (!childDescriptor.validate(item[key])) return false } return true diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder.kt index ae6c171a..2590273e 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder.kt @@ -9,7 +9,8 @@ import space.kscience.dataforge.names.length import kotlin.collections.set public class MetaDescriptorBuilder @PublishedApi internal constructor() { - public var info: String? = null + public var description: String? = null + public var children: MutableMap = linkedMapOf() public var multiple: Boolean = false public var valueRestriction: ValueRestriction = ValueRestriction.NONE @@ -40,38 +41,28 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() { attributes.apply(block) } - public fun item(name: Name, block: MetaDescriptorBuilder.() -> Unit = {}): MetaDescriptorBuilder { - return when (name.length) { - 0 -> apply(block) + internal fun node( + name: Name, + descriptorBuilder: MetaDescriptorBuilder, + ): Unit { + when (name.length) { + 0 -> error("Can't set descriptor to root") + 1 -> { - val target = MetaDescriptorBuilder().apply(block) - children[name.first().body] = target - target + children[name.first().body] = descriptorBuilder } - else -> { - children.getOrPut(name.first().body) { MetaDescriptorBuilder() }.item(name.cutFirst(), block) - } + else -> children.getOrPut(name.first().body) { + MetaDescriptorBuilder() + }.node(name.cutFirst(), descriptorBuilder) } } public fun node( name: Name, - descriptor: MetaDescriptor, - block: MetaDescriptorBuilder.() -> Unit = {}, - ): MetaDescriptorBuilder = when (name.length) { - 0 -> error("Can't set descriptor to root") - 1 -> { - val item = descriptor.toBuilder().apply { - valueRestriction = ValueRestriction.ABSENT - }.apply(block) - children[name.first().body] = item - item - } - - else -> children.getOrPut(name.first().body) { - MetaDescriptorBuilder() - }.node(name.cutFirst(), descriptor, block) + descriptorBuilder: MetaDescriptor, + ): Unit { + node(name, descriptorBuilder.toBuilder()) } public var allowedValues: List @@ -85,10 +76,21 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() { allowedValues = values.map { Value.of(it) } } + public fun from(descriptor: MetaDescriptor) { + description = descriptor.description + children.putAll(descriptor.nodes.mapValues { it.value.toBuilder() }) + multiple = descriptor.multiple + valueRestriction = descriptor.valueRestriction + valueTypes = descriptor.valueTypes + indexKey = descriptor.indexKey + default = descriptor.defaultValue + attributes.update(descriptor.attributes) + } + @PublishedApi internal fun build(): MetaDescriptor = MetaDescriptor( - description = info, - children = children.mapValues { it.value.build() }, + description = description, + nodes = children.mapValues { it.value.build() }, multiple = multiple, valueRestriction = valueRestriction, valueTypes = valueTypes, @@ -98,12 +100,57 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() { ) } -public fun MetaDescriptorBuilder.item(name: String, block: MetaDescriptorBuilder.() -> Unit): MetaDescriptorBuilder = - item(Name.parse(name), block) +//public fun MetaDescriptorBuilder.item(name: String, block: MetaDescriptorBuilder.() -> Unit): MetaDescriptorBuilder = +// item(Name.parse(name), block) public inline fun MetaDescriptor(block: MetaDescriptorBuilder.() -> Unit): MetaDescriptor = MetaDescriptorBuilder().apply(block).build() +/** + * Create and configure child node descriptor + */ +public fun MetaDescriptorBuilder.node( + name: Name, + block: MetaDescriptorBuilder.() -> Unit, +) { + node( + name, + MetaDescriptorBuilder().apply(block) + ) +} + +public fun MetaDescriptorBuilder.node(name: String, descriptor: MetaDescriptor) { + node(Name.parse(name), descriptor) +} + +public fun MetaDescriptorBuilder.node(name: String, block: MetaDescriptorBuilder.() -> Unit) { + node(Name.parse(name), block) +} + +public fun MetaDescriptorBuilder.node( + key: String, + base: Described, + block: MetaDescriptorBuilder.() -> Unit = {}, +) { + node(Name.parse(key), base.descriptor?.toBuilder()?.apply(block) ?: MetaDescriptorBuilder()) +} + +public fun MetaDescriptorBuilder.required() { + valueRestriction = ValueRestriction.REQUIRED +} + +private fun MetaDescriptor.toBuilder(): MetaDescriptorBuilder = MetaDescriptorBuilder().apply { + description = this@toBuilder.description + children = this@toBuilder.nodes.mapValuesTo(LinkedHashMap()) { it.value.toBuilder() } + multiple = this@toBuilder.multiple + valueRestriction = this@toBuilder.valueRestriction + valueTypes = this@toBuilder.valueTypes + indexKey = this@toBuilder.indexKey + default = defaultValue + attributes = this@toBuilder.attributes.toMutableMeta() +} + + /** * Create and configure child value descriptor */ @@ -112,7 +159,7 @@ public fun MetaDescriptorBuilder.value( type: ValueType, vararg additionalTypes: ValueType, block: MetaDescriptorBuilder.() -> Unit = {}, -): MetaDescriptorBuilder = item(name) { +): Unit = node(name) { valueType(type, *additionalTypes) block() } @@ -122,41 +169,14 @@ public fun MetaDescriptorBuilder.value( type: ValueType, vararg additionalTypes: ValueType, block: MetaDescriptorBuilder.() -> Unit = {}, -): MetaDescriptorBuilder = value(Name.parse(name), type, additionalTypes = additionalTypes, block) +): Unit = value(Name.parse(name), type, additionalTypes = additionalTypes, block) -/** - * Create and configure child value descriptor - */ -public fun MetaDescriptorBuilder.node( - name: Name, block: MetaDescriptorBuilder.() -> Unit, -): MetaDescriptorBuilder = item(name) { - valueRestriction = ValueRestriction.ABSENT - block() -} - -public fun MetaDescriptorBuilder.node(name: String, block: MetaDescriptorBuilder.() -> Unit) { - node(Name.parse(name), block) -} - -public fun MetaDescriptorBuilder.node( - key: String, - described: Described, - block: MetaDescriptorBuilder.() -> Unit = {}, -) { - described.descriptor?.let { - node(Name.parse(key), it, block) - } -} - -public fun MetaDescriptorBuilder.required() { - valueRestriction = ValueRestriction.REQUIRED -} public inline fun > MetaDescriptorBuilder.enum( key: Name, default: E?, crossinline modifier: MetaDescriptorBuilder.() -> Unit = {}, -): MetaDescriptorBuilder = value(key, ValueType.STRING) { +): Unit = value(key, ValueType.STRING) { default?.let { this.default = default.asValue() } @@ -164,17 +184,6 @@ public inline fun > MetaDescriptorBuilder.enum( modifier() } -private fun MetaDescriptor.toBuilder(): MetaDescriptorBuilder = MetaDescriptorBuilder().apply { - info = this@toBuilder.description - children = this@toBuilder.children.mapValuesTo(LinkedHashMap()) { it.value.toBuilder() } - multiple = this@toBuilder.multiple - valueRestriction = this@toBuilder.valueRestriction - valueTypes = this@toBuilder.valueTypes - indexKey = this@toBuilder.indexKey - default = defaultValue - attributes = this@toBuilder.attributes.toMutableMeta() -} - /** * Make a deep copy of this descriptor applying given transformation [block] */ diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/schemeDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/schemeDescriptor.kt index 79fb6fdb..c7b73508 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/schemeDescriptor.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/schemeDescriptor.kt @@ -6,10 +6,13 @@ import space.kscience.dataforge.meta.ValueType import kotlin.reflect.KProperty1 import kotlin.reflect.typeOf +/** + * Add a value item to a [MetaDescriptor] inferring some of its properties from the type + */ public inline fun MetaDescriptorBuilder.value( property: KProperty1, noinline block: MetaDescriptorBuilder.() -> Unit = {}, -): MetaDescriptorBuilder = when (typeOf()) { +): Unit = when (typeOf()) { typeOf(), typeOf(), typeOf(), typeOf(), typeOf(), typeOf() -> value(property.name, ValueType.NUMBER) { block() @@ -34,9 +37,12 @@ public inline fun MetaDescriptorBuilder.value( multiple = true block() } - else -> item(property.name, block) + else -> node(property.name, block) } +/** + * Add a schem-based branch to a [MetaDescriptor] + */ public inline fun MetaDescriptorBuilder.scheme( property: KProperty1, spec: SchemeSpec, diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/exoticValues.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/exoticValues.kt index 74952053..17436990 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/exoticValues.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/exoticValues.kt @@ -1,5 +1,9 @@ package space.kscience.dataforge.meta +import space.kscience.dataforge.names.Name +import kotlin.properties.ReadOnlyProperty +import kotlin.properties.ReadWriteProperty + /** * A value built from string which content and type are parsed on-demand @@ -17,6 +21,9 @@ public class LazyParsedValue(public val string: String) : Value { override fun hashCode(): Int = string.hashCode() } +/** + * Read this string as lazily parsed value + */ public fun String.lazyParseValue(): LazyParsedValue = LazyParsedValue(this) /** @@ -43,4 +50,102 @@ public class DoubleArrayValue(override val value: DoubleArray) : Value, Iterable override fun iterator(): Iterator = value.iterator() } + +/** + * A zero-copy wrapping of this [DoubleArray] in a [Value] + */ public fun DoubleArray.asValue(): Value = if (isEmpty()) Null else DoubleArrayValue(this) + +public val Value.doubleArray: DoubleArray + get() = if (this is DoubleArrayValue) { + value + } else { + DoubleArray(list.size) { list[it].double } + } + +public val Meta?.doubleArray: DoubleArray? get() = this?.value?.doubleArray + +public fun MetaProvider.doubleArray( + vararg default: Double, + key: Name? = null, +): ReadOnlyProperty = value( + key, + reader = { it?.doubleArray ?: doubleArrayOf(*default) }, +) + +public fun MutableMetaProvider.doubleArray( + vararg default: Double, + key: Name? = null, +): ReadWriteProperty = value( + key, + writer = { DoubleArrayValue(it) }, + reader = { it?.doubleArray ?: doubleArrayOf(*default) }, +) + +private object DoubleArrayMetaConverter : MetaConverter { + override fun readOrNull(source: Meta): DoubleArray? = source.doubleArray + + override fun convert(obj: DoubleArray): Meta = Meta(obj.asValue()) +} + +public val MetaConverter.Companion.doubleArray: MetaConverter get() = DoubleArrayMetaConverter + +/** + * A [Value] wrapping a [ByteArray] + */ +public class ByteArrayValue(override val value: ByteArray) : Value, Iterable { + override val type: ValueType get() = ValueType.LIST + override val list: List get() = value.map { NumberValue(it) } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Value) return false + + return when (other) { + is ByteArrayValue -> value.contentEquals(other.value) + else -> list == other.list + } + } + + override fun hashCode(): Int = value.contentHashCode() + + override fun toString(): String = list.joinToString(prefix = "[", postfix = "]") + + override fun iterator(): Iterator = value.iterator() +} + +public fun ByteArray.asValue(): Value = ByteArrayValue(this) + +public val Value.byteArray: ByteArray + get() = if (this is ByteArrayValue) { + value + } else { + ByteArray(list.size) { list[it].number.toByte() } + } + +public val Meta?.byteArray: ByteArray? get() = this?.value?.byteArray + +public fun MetaProvider.byteArray( + vararg default: Byte, + key: Name? = null, +): ReadOnlyProperty = value( + key, + reader = { it?.byteArray ?: byteArrayOf(*default) }, +) + +public fun MutableMetaProvider.byteArray( + vararg default: Byte, + key: Name? = null, +): ReadWriteProperty = value( + key, + writer = { ByteArrayValue(it) }, + reader = { it?.byteArray ?: byteArrayOf(*default) }, +) + +private object ByteArrayMetaConverter : MetaConverter { + override fun readOrNull(source: Meta): ByteArray? = source.byteArray + + override fun convert(obj: ByteArray): Meta = Meta(obj.asValue()) +} + +public val MetaConverter.Companion.byteArray: MetaConverter get() = ByteArrayMetaConverter \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaConverter.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaConverter.kt deleted file mode 100644 index 0dfb63d7..00000000 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/transformations/MetaConverter.kt +++ /dev/null @@ -1,163 +0,0 @@ -package space.kscience.dataforge.meta.transformations - -import space.kscience.dataforge.meta.* -import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import kotlin.reflect.KType -import kotlin.reflect.typeOf - -/** - * A converter of generic object to and from [Meta] - */ -public interface MetaConverter { - - /** - * Runtime type of [T] - */ - public val type: KType - - /** - * A descriptor for resulting meta - */ - public val descriptor: MetaDescriptor get() = MetaDescriptor.EMPTY - - /** - * Attempt conversion of [meta] to an object or return null if conversion failed - */ - public fun metaToObjectOrNull(meta: Meta): T? - - public fun metaToObject(meta: Meta): T = - metaToObjectOrNull(meta) ?: error("Meta $meta could not be interpreted by $this") - - public fun objectToMeta(obj: T): Meta - - public companion object { - - public val meta: MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override fun metaToObjectOrNull(meta: Meta): Meta = meta - override fun objectToMeta(obj: Meta): Meta = obj - } - - public val value: MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override fun metaToObjectOrNull(meta: Meta): Value? = meta.value - override fun objectToMeta(obj: Value): Meta = Meta(obj) - } - - public val string: MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override val descriptor: MetaDescriptor = MetaDescriptor { - valueType(ValueType.STRING) - } - - - override fun metaToObjectOrNull(meta: Meta): String? = meta.string - override fun objectToMeta(obj: String): Meta = Meta(obj.asValue()) - } - - public val boolean: MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override val descriptor: MetaDescriptor = MetaDescriptor { - valueType(ValueType.BOOLEAN) - } - - override fun metaToObjectOrNull(meta: Meta): Boolean? = meta.boolean - override fun objectToMeta(obj: Boolean): Meta = Meta(obj.asValue()) - } - - public val number: MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override val descriptor: MetaDescriptor = MetaDescriptor { - valueType(ValueType.NUMBER) - } - - override fun metaToObjectOrNull(meta: Meta): Number? = meta.number - override fun objectToMeta(obj: Number): Meta = Meta(obj.asValue()) - } - - public val double: MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override val descriptor: MetaDescriptor = MetaDescriptor { - valueType(ValueType.NUMBER) - } - - override fun metaToObjectOrNull(meta: Meta): Double? = meta.double - override fun objectToMeta(obj: Double): Meta = Meta(obj.asValue()) - } - - public val float: MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override val descriptor: MetaDescriptor = MetaDescriptor { - valueType(ValueType.NUMBER) - } - - override fun metaToObjectOrNull(meta: Meta): Float? = meta.float - override fun objectToMeta(obj: Float): Meta = Meta(obj.asValue()) - } - - public val int: MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override val descriptor: MetaDescriptor = MetaDescriptor { - valueType(ValueType.NUMBER) - } - - override fun metaToObjectOrNull(meta: Meta): Int? = meta.int - override fun objectToMeta(obj: Int): Meta = Meta(obj.asValue()) - } - - public val long: MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override val descriptor: MetaDescriptor = MetaDescriptor { - valueType(ValueType.NUMBER) - } - - override fun metaToObjectOrNull(meta: Meta): Long? = meta.long - override fun objectToMeta(obj: Long): Meta = Meta(obj.asValue()) - } - - public inline fun > enum(): MetaConverter = object : MetaConverter { - override val type: KType = typeOf() - - override val descriptor: MetaDescriptor = MetaDescriptor { - valueType(ValueType.STRING) - allowedValues(enumValues()) - } - - @Suppress("USELESS_CAST") - override fun metaToObjectOrNull(meta: Meta): E = meta.enum() as? E ?: error("The Item is not a Enum") - - override fun objectToMeta(obj: E): Meta = Meta(obj.asValue()) - } - - public fun valueList( - writer: (T) -> Value = { Value.of(it) }, - reader: (Value) -> T, - ): MetaConverter> = - object : MetaConverter> { - override val type: KType = typeOf>() - - override val descriptor: MetaDescriptor = MetaDescriptor { - valueType(ValueType.LIST) - } - - override fun metaToObjectOrNull(meta: Meta): List? = meta.value?.list?.map(reader) - - override fun objectToMeta(obj: List): Meta = Meta(obj.map(writer).asValue()) - } - - } -} - -public fun MetaConverter.nullableMetaToObject(item: Meta?): T? = item?.let { metaToObject(it) } -public fun MetaConverter.nullableObjectToMeta(obj: T?): Meta? = obj?.let { objectToMeta(it) } - -public fun MetaConverter.valueToObject(value: Value): T? = metaToObject(Meta(value)) diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt index e6b622ff..73464305 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt @@ -31,12 +31,5 @@ public inline fun > Value.enum(): E = if (this is EnumValue< public val Value.stringList: List get() = list.map { it.string } -public val Value.doubleArray: DoubleArray - get() = if (this is DoubleArrayValue) { - value - } else { - DoubleArray(list.size) { list[it].double } - } - public fun Value.toMeta(): Meta = Meta(this) \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/DfId.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/DfType.kt similarity index 80% rename from dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/DfId.kt rename to dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/DfType.kt index 5d485e23..936e793a 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/DfId.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/DfType.kt @@ -5,4 +5,4 @@ package space.kscience.dataforge.misc */ @MustBeDocumented @Target(AnnotationTarget.CLASS) -public annotation class DfId(val id: String) +public annotation class DfType(val id: String) diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/annotations.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/annotations.kt index 3c9d6ac3..29568403 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/annotations.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/annotations.kt @@ -18,4 +18,11 @@ public annotation class DFExperimental */ @RequiresOptIn(level = RequiresOptIn.Level.WARNING) @Retention(AnnotationRetention.BINARY) -public annotation class DFInternal \ No newline at end of file +public annotation class DFInternal + +/** + * Annotation marks methods that explicitly use KType without checking that it corresponds to the type parameter + */ +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) +@Retention(AnnotationRetention.BINARY) +public annotation class UnsafeKType \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/cast.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/cast.kt deleted file mode 100644 index e714d596..00000000 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/cast.kt +++ /dev/null @@ -1,3 +0,0 @@ -package space.kscience.dataforge.misc - -public expect inline fun Any?.unsafeCast(): T \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt index 160ea3a1..7867330c 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt @@ -16,12 +16,10 @@ public class Name(public val tokens: List) { override fun toString(): String = tokens.joinToString(separator = NAME_SEPARATOR) { it.toString() } - override fun equals(other: Any?): Boolean { - return when (other) { - is Name -> this.tokens == other.tokens - is NameToken -> this.length == 1 && this.tokens.first() == other - else -> false - } + override fun equals(other: Any?): Boolean = when (other) { + is Name -> this.tokens == other.tokens + is NameToken -> this.length == 1 && this.tokens.first() == other + else -> false } private val cachedHashCode = if (tokens.size == 1) { @@ -60,7 +58,7 @@ public class Name(public val tokens: List) { */ public fun parse(string: String): Name { if (string.isBlank()) return EMPTY - val tokens = sequence { + val tokens = buildList { var bodyBuilder = StringBuilder() var queryBuilder = StringBuilder() var bracketCount = 0 @@ -93,7 +91,7 @@ public class Name(public val tokens: List) { else -> when (it) { '.' -> { val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString() - yield(NameToken(bodyBuilder.toString(), query)) + add(NameToken(bodyBuilder.toString(), query)) bodyBuilder = StringBuilder() queryBuilder = StringBuilder() } @@ -108,13 +106,20 @@ public class Name(public val tokens: List) { } } val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString() - yield(NameToken(bodyBuilder.toString(), query)) + add(NameToken(bodyBuilder.toString(), query)) } - return Name(tokens.toList()) + return Name(tokens) } } } +/** + * Transform this [Name] to a string without escaping special characters in tokens. + * + * Parsing it back will produce a valid, but different name + */ +public fun Name.toStringUnescaped(): String = tokens.joinToString(separator = Name.NAME_SEPARATOR) { it.toStringUnescaped() } + public operator fun Name.get(i: Int): NameToken = tokens[i] /** @@ -216,9 +221,13 @@ public fun Name.endsWith(token: NameToken): Boolean = lastOrNull() == token public fun Name.startsWith(name: Name): Boolean = this.length >= name.length && (this == name || tokens.subList(0, name.length) == name.tokens) +public fun Name.startsWith(name: String): Boolean = startsWith(name.parseAsName()) + public fun Name.endsWith(name: Name): Boolean = this.length >= name.length && (this == name || tokens.subList(length - name.length, length) == name.tokens) +public fun Name.endsWith(name: String): Boolean = endsWith(name.parseAsName()) + /** * if [this] starts with given [head] name, returns the reminder of the name (could be empty). Otherwise, returns null */ diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameIndexComparator.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameIndexComparator.kt new file mode 100644 index 00000000..742f8ebb --- /dev/null +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameIndexComparator.kt @@ -0,0 +1,30 @@ +package space.kscience.dataforge.names + +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.getIndexed + + +/** + * A comparator for indices in a [Name]. If both indices are integers, compare them as integers. + * Null always stays "before" non-null index. + */ +public object NameIndexComparator : Comparator { + override fun compare(a: String?, b: String?): Int { + if (a == b) return 0 + if (a == null) return 1 + if (b == null) return -1 + val aInt = a.toIntOrNull() + val bInt = b.toIntOrNull() + return if (aInt != null && bInt != null) { + aInt.compareTo(bInt) + } else { + a.compareTo(b) + } + } + +} + +public fun Meta.getIndexedList(name: Name): List = getIndexed(name).entries.sortedWith( + //sort by index + compareBy(space.kscience.dataforge.names.NameIndexComparator) { it.key } +).map{it.value} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameToken.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameToken.kt index 0dc83c57..3994ef27 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameToken.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameToken.kt @@ -67,10 +67,29 @@ public class NameToken(public val body: String, public val index: String? = null * Parse name token from a string */ public fun parse(string: String): NameToken { - val body = string.substringBefore('[') - val index = string.substringAfter('[', "") - if (index.isNotEmpty() && index.endsWith(']')) error("NameToken with index must end with ']'") - return NameToken(body, index.removeSuffix("]")) + var indexStart = -1 + var indexEnd = -1 + string.forEachIndexed { index, c -> + when (c) { + '[' -> when { + indexStart >= 0 -> error("Second opening bracket not allowed in NameToken: $string") + else -> indexStart = index + } + + ']' -> when { + indexStart < 0 -> error("Closing index bracket could not be used before opening bracket in NameToken: $string") + indexEnd >= 0 -> error("Second closing bracket not allowed in NameToken: $string") + else -> indexEnd = index + } + + else -> if(indexEnd>=0) error("Symbols not allowed after index in NameToken: $string") + } + } + if(indexStart>=0 && indexEnd<0) error("Opening bracket without closing bracket not allowed in NameToken: $string") + return NameToken( + if(indexStart>=0) string.substring(0, indexStart) else string, + if(indexStart>=0) string.substring(indexStart + 1, indexEnd) else null + ) } } } diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/ConvertersTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/ConvertersTest.kt new file mode 100644 index 00000000..fda978e4 --- /dev/null +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/ConvertersTest.kt @@ -0,0 +1,17 @@ +package space.kscience.dataforge.meta + +import kotlin.test.Test +import kotlin.test.assertEquals + +class ConvertersTest { + + @Test + fun stringListConversion() { + val list = listOf("A", "B", "C") + val meta = MetaConverter.stringList.convert(list) + val json = meta.toJson() + val reconstructedMeta = json.toMeta() + val reconstructed = MetaConverter.stringList.read(reconstructedMeta) + assertEquals(list,reconstructed) + } +} \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/JsonMetaTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/JsonMetaTest.kt index fba9b596..2ada3ade 100644 --- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/JsonMetaTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/JsonMetaTest.kt @@ -2,7 +2,7 @@ package space.kscience.dataforge.meta import kotlinx.serialization.json.* import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.meta.descriptors.item +import space.kscience.dataforge.meta.descriptors.node import kotlin.test.Test import kotlin.test.assertEquals @@ -32,7 +32,7 @@ class JsonMetaTest { } val descriptor = MetaDescriptor { - item("nodeArray") { + node("nodeArray") { indexKey = "index" } } diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaDelegateTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaDelegateTest.kt index 7a2dbc22..4b99cc3b 100644 --- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaDelegateTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaDelegateTest.kt @@ -20,7 +20,7 @@ class MetaDelegateTest { var myValue by string() var safeValue by double(2.2) var enumValue by enum(TestEnum.YES) - var inner by spec(InnerScheme) + var inner by scheme(InnerScheme) companion object : SchemeSpec(::TestScheme) } diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaRefTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaRefTest.kt new file mode 100644 index 00000000..a93841d9 --- /dev/null +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaRefTest.kt @@ -0,0 +1,35 @@ +package space.kscience.dataforge.meta + +import kotlinx.serialization.Serializable +import space.kscience.dataforge.misc.DFExperimental +import kotlin.test.Test +import kotlin.test.assertEquals + +@DFExperimental +internal class MetaRefTest { + + @Serializable + data class XY(val x: Double, val y: Double) + + object TestMetaSpec : MetaSpec() { + val integer by int { description = "Integer value" } + val string by string { description = "String value" } + val custom by item(MetaConverter.serializable()) { description = "custom value" } + } + + @Test + fun specWriteRead() = with(TestMetaSpec){ + val meta = MutableMeta() + + meta[integer] = 22 + meta[string] = "33" + val xy = XY(33.0, -33.0) + meta[custom] = xy + + val sealed = meta.seal() + + assertEquals(22, sealed[integer]) + assertEquals("33", sealed[string]) + assertEquals(xy, sealed[custom]) + } +} \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaTest.kt index 85db7bd6..78b5cdb4 100644 --- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaTest.kt @@ -61,4 +61,33 @@ class MetaTest { assertEquals(null, indexed["8"]) assertEquals(12, indexed["12"].int) } + + @Test + fun reset() { + val oldMeta = MutableMeta { + "a" put { + "value" put "aValue" + } + "b" put { + "value" put "bValue" + } + "c" put { + "value" put "cValue" + } + } + val newMeta = Meta { + "a" put { + "value" put "aValue" + } + "b" put { + "value" put "bValue" + } + "d" put { + "value" put "dValue" + } + } + oldMeta.reset(newMeta) + println(oldMeta) + assertEquals(setOf("a", "b", "d"), oldMeta.items.keys.map { it.toString() }.toSet()) + } } \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/ObservableMetaTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/ObservableMetaTest.kt new file mode 100644 index 00000000..4681ec12 --- /dev/null +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/ObservableMetaTest.kt @@ -0,0 +1,49 @@ +package space.kscience.dataforge.meta + +import space.kscience.dataforge.names.startsWith +import kotlin.test.Ignore +import kotlin.test.Test +import kotlin.test.assertEquals + +class ObservableMetaTest { + + @Test + fun asObservable() { + val meta = MutableMeta { + "data" put { + "x" put ListValue(1, 2, 3) + "y" put ListValue(5, 6, 7) + "type" put "scatter" + } + }.asObservable() + + assertEquals("scatter", meta["data.type"].string) + } + + @Test + @Ignore + fun detachNode() { + val meta = MutableMeta { + "data" put { + "x" put ListValue(1, 2, 3) + "y" put ListValue(5, 6, 7) + "type" put "scatter" + } + }.asObservable() + + var collector: Value? = null + + meta.onChange(null) { name -> + if (name.startsWith("data")) { + collector = get("data.z")?.value + } + } + + val data = meta["data"]!! + + meta.remove("data") + + data["z"] = ListValue(2, 5, 7) + assertEquals(null, collector) + } +} \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SchemeTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SchemeTest.kt deleted file mode 100644 index bb2736ce..00000000 --- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SchemeTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package space.kscience.dataforge.meta - -import space.kscience.dataforge.misc.DFExperimental -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull - -@DFExperimental -class SchemeTest { - @Test - fun testSchemeWrappingBeforeEdit() { - val config = MutableMeta() - val scheme = TestScheme.write(config) - scheme.a = 29 - assertEquals(29, config["a"].int) - } - - @Test - fun testSchemeWrappingAfterEdit() { - val scheme = TestScheme.empty() - scheme.a = 29 - val config = MutableMeta() - scheme.retarget(config) - assertEquals(29, scheme.a) - } - - @Test - fun testSchemeSubscription() { - val scheme = TestScheme.empty() - var flag: Int? = null - scheme.useProperty(TestScheme::a) { a -> - flag = a - } - scheme.a = 2 - assertEquals(2, flag) - } - - @Test - fun testListSubscription(){ - val scheme = TestScheme.empty() - var value: Value? = null - scheme.v = ListValue(0.0,0.0,0.0) - scheme.useProperty(TestScheme::v){ - value = it - } - scheme.v = ListValue(1.0, 2.0, 3.0) - assertNotNull(value) - } -} \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt index 8d4d3537..dc9b9d64 100644 --- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt @@ -1,7 +1,17 @@ package space.kscience.dataforge.meta +import space.kscience.dataforge.misc.DFExperimental import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotNull + + +internal class SubScheme : Scheme() { + + var subValue by string() + + companion object : SchemeSpec(::SubScheme) +} internal class TestScheme : Scheme() { var list by numberList(1, 2, 3) @@ -11,9 +21,23 @@ internal class TestScheme : Scheme() { var v by value() + var sub by scheme(SubScheme) + companion object : SchemeSpec(::TestScheme) } +private class SchemeWithInit: Scheme(){ + init { + set("initial", "initialValue") + } + + var initial by string() + + companion object: SchemeSpec(::SchemeWithInit) +} + + + class SpecificationTest { // @Test @@ -71,4 +95,64 @@ class SpecificationTest { assertEquals(22, config["child.a"].int) assertEquals("test", config["child.b"].string) } + + @Test + fun testSchemeWrappingBeforeEdit() { + val config = MutableMeta() + val scheme = TestScheme.write(config) + scheme.a = 29 + assertEquals(29, config["a"].int) + } + + @OptIn(DFExperimental::class) + @Test + fun testSchemeWrappingAfterEdit() { + val scheme = TestScheme.empty() + scheme.a = 29 + val config = MutableMeta() + scheme.retarget(config) + assertEquals(29, scheme.a) + } + + @Test + fun testSchemeSubscription() { + val scheme = TestScheme.empty() + var flag: Int? = null + scheme.useProperty(TestScheme::a) { a -> + flag = a + } + scheme.a = 2 + assertEquals(2, flag) + } + + @Test + fun testListSubscription(){ + val scheme = TestScheme.empty() + var value: Value? = null + scheme.v = ListValue(0.0,0.0,0.0) + scheme.useProperty(TestScheme::v){ + value = it + } + scheme.v = ListValue(1.0, 2.0, 3.0) + assertNotNull(value) + } + + @Test + fun testSubScheme(){ + val scheme = TestScheme.empty() + + scheme.sub.subValue = "aaa" + + assertEquals("aaa",scheme.sub.subValue) + } + + + @Test + fun testSchemeWithInit(){ + val scheme = SchemeWithInit() + assertEquals("initialValue", scheme.initial) + scheme.initial = "none" + assertEquals("none", scheme.initial) + } + } \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/descriptors/DescriptorTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/descriptors/DescriptorTest.kt index e8c321fc..1a08ce34 100644 --- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/descriptors/DescriptorTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/descriptors/DescriptorTest.kt @@ -11,14 +11,14 @@ class DescriptorTest { val descriptor = MetaDescriptor { node("aNode") { - info = "A root demo node" + description = "A root demo node" value("b", ValueType.NUMBER) { - info = "b number value" + description = "b number value" } node("otherNode") { value("otherValue", ValueType.BOOLEAN) { default(false) - info = "default value" + description = "default value" } } } diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/names/NameTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/names/NameTest.kt index a5bdf3fc..25725333 100644 --- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/names/NameTest.kt +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/names/NameTest.kt @@ -1,9 +1,6 @@ package space.kscience.dataforge.names -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue +import kotlin.test.* class NameTest { @Test @@ -50,4 +47,31 @@ class NameTest { val name = Name.parse("a.b.c") assertEquals("a.b".parseAsName(), name.cutLast()) } + + @Test + fun tokenParseTest(){ + val token1 = NameToken.parse("token[index]") + assertEquals("token", token1.body) + assertEquals("index", token1.index) + + val token2 = NameToken.parse("token-body") + assertEquals("token-body", token2.body) + assertEquals(null, token2.index) + +// val token3 = NameToken.parse("[token-index]") +// assertEquals("", token3.body) +// assertEquals("token-index", token3.index) + + assertFails{ + NameToken.parse("[token-index]") + } + + assertFails { + NameToken.parse("token[22") + } + + assertFails { + NameToken.parse("token[22]ddd") + } + } } \ No newline at end of file diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/values/DoubleArrayValue.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/values/DoubleArrayValue.kt new file mode 100644 index 00000000..30bc2adc --- /dev/null +++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/values/DoubleArrayValue.kt @@ -0,0 +1,24 @@ +package space.kscience.dataforge.values + +import space.kscience.dataforge.meta.DoubleArrayValue +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.doubleArray +import space.kscience.dataforge.meta.get +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class DoubleArrayValue { + @Test + fun doubleArrayWriteRead(){ + val meta = Meta{ + "doubleArray" put doubleArrayOf(1.0,2.0,3.0) + } + + assertTrue { + meta["doubleArray"]?.value is DoubleArrayValue + } + + assertEquals(2.0, meta["doubleArray"].doubleArray?.get(1)) + } +} \ No newline at end of file diff --git a/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/meta/DynamicMeta.kt b/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/meta/DynamicMeta.kt index b38d5891..57d324c7 100644 --- a/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/meta/DynamicMeta.kt +++ b/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/meta/DynamicMeta.kt @@ -31,6 +31,9 @@ public fun Meta.toDynamic(): dynamic { } public class DynamicMeta(internal val obj: dynamic) : TypedMeta { + + override val self: DynamicMeta get() = this + private fun keys(): Array = js("Object").keys(obj) as Array private fun isArray(obj: dynamic): Boolean = diff --git a/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/misc/castJs.kt b/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/misc/castJs.kt deleted file mode 100644 index b404ebb4..00000000 --- a/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/misc/castJs.kt +++ /dev/null @@ -1,5 +0,0 @@ -package space.kscience.dataforge.misc -import kotlin.js.unsafeCast as unsafeCastJs - -@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") -public actual inline fun Any?.unsafeCast(): T = this.unsafeCastJs() \ No newline at end of file diff --git a/dataforge-meta/src/jvmMain/kotlin/space/kscience/dataforge/misc/castJvm.kt b/dataforge-meta/src/jvmMain/kotlin/space/kscience/dataforge/misc/castJvm.kt deleted file mode 100644 index 27d399fe..00000000 --- a/dataforge-meta/src/jvmMain/kotlin/space/kscience/dataforge/misc/castJvm.kt +++ /dev/null @@ -1,4 +0,0 @@ -package space.kscience.dataforge.misc - -@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") -public actual inline fun Any?.unsafeCast(): T = this as T \ No newline at end of file diff --git a/dataforge-meta/src/nativeMain/kotlin/space/kscience/dataforge/misc/castNative.kt b/dataforge-meta/src/nativeMain/kotlin/space/kscience/dataforge/misc/castNative.kt deleted file mode 100644 index 27d399fe..00000000 --- a/dataforge-meta/src/nativeMain/kotlin/space/kscience/dataforge/misc/castNative.kt +++ /dev/null @@ -1,4 +0,0 @@ -package space.kscience.dataforge.misc - -@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") -public actual inline fun Any?.unsafeCast(): T = this as T \ No newline at end of file diff --git a/dataforge-scripting/README.md b/dataforge-scripting/README.md index af79cc8f..1f650bea 100644 --- a/dataforge-scripting/README.md +++ b/dataforge-scripting/README.md @@ -6,18 +6,16 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:dataforge-scripting:0.7.0`. +The Maven coordinates of this project are `space.kscience:dataforge-scripting:0.9.0-dev-1`. **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - //uncomment to access development builds - //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:dataforge-scripting:0.7.0") + implementation("space.kscience:dataforge-scripting:0.9.0-dev-1") } ``` diff --git a/dataforge-scripting/build.gradle.kts b/dataforge-scripting/build.gradle.kts index be81fe70..d9d87742 100644 --- a/dataforge-scripting/build.gradle.kts +++ b/dataforge-scripting/build.gradle.kts @@ -4,15 +4,15 @@ plugins { kscience{ jvm() - dependencies { + commonMain { api(projects.dataforgeWorkspace) implementation(kotlin("scripting-common")) } - dependencies(jvmMain){ + jvmMain{ implementation(kotlin("scripting-jvm-host")) implementation(kotlin("scripting-jvm")) } - dependencies(jvmTest){ + jvmTest{ implementation(spclibs.logback.classic) } } diff --git a/dataforge-workspace/README.md b/dataforge-workspace/README.md index c096699f..87b38c6e 100644 --- a/dataforge-workspace/README.md +++ b/dataforge-workspace/README.md @@ -6,18 +6,16 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:dataforge-workspace:0.7.0`. +The Maven coordinates of this project are `space.kscience:dataforge-workspace:0.9.0-dev-1`. **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") - //uncomment to access development builds - //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:dataforge-workspace:0.7.0") + implementation("space.kscience:dataforge-workspace:0.9.0-dev-1") } ``` diff --git a/dataforge-workspace/build.gradle.kts b/dataforge-workspace/build.gradle.kts index ec117865..5fa555eb 100644 --- a/dataforge-workspace/build.gradle.kts +++ b/dataforge-workspace/build.gradle.kts @@ -2,29 +2,27 @@ plugins { id("space.kscience.gradle.mpp") } -kscience{ +kscience { jvm() js() native() + wasm() useCoroutines() - useSerialization{ + useSerialization { protobuf() } - commonMain{ - dependencies { - api(projects.dataforgeContext) - api(projects.dataforgeData) - api(projects.dataforgeIo) - } + commonMain { + api(projects.dataforgeContext) + api(projects.dataforgeData) + api(projects.dataforgeIo) + } - jvmTest{ - dependencies { - implementation(spclibs.logback.classic) - implementation(projects.dataforgeIo.dataforgeIoYaml) - } + jvmTest { + implementation(spclibs.logback.classic) + implementation(projects.dataforgeIo.dataforgeIoYaml) } } -readme{ +readme { maturity = space.kscience.gradle.Maturity.EXPERIMENTAL } \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/EnvelopeTask.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/EnvelopeTask.kt deleted file mode 100644 index a1588a54..00000000 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/EnvelopeTask.kt +++ /dev/null @@ -1,46 +0,0 @@ -package space.kscience.dataforge.workspace - -import space.kscience.dataforge.data.DataTree.Companion.META_ITEM_NAME_TOKEN -import space.kscience.dataforge.io.Envelope -import space.kscience.dataforge.io.IOReader -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.asName -import kotlin.reflect.KType - -public abstract class EnvelopeTask( - override val descriptor: MetaDescriptor?, - private val reader: IOReader, -) : Task { - - public abstract suspend fun produceEnvelopes( - workspace: Workspace, - taskName: Name, - taskMeta: Meta, - ): Map - - override suspend fun execute(workspace: Workspace, taskName: Name, taskMeta: Meta): TaskResult = - Result(workspace, taskName, taskMeta, reader, produceEnvelopes(workspace, taskName, taskMeta)) - - private class Result( - override val workspace: Workspace, - override val taskName: Name, - override val taskMeta: Meta, - val reader: IOReader, - envelopes: Map, - ) : TaskResult { - - private val dataMap = envelopes.mapValues { - workspace.wrapData(it.value.toData(reader), it.key, taskName, taskMeta) - } - override val meta: Meta get() = dataMap[META_ITEM_NAME_TOKEN.asName()]?.meta ?: Meta.EMPTY - - override val dataType: KType get() = reader.type - - override fun iterator(): Iterator> = dataMap.values.iterator() - - override fun get(name: Name): TaskData? = dataMap[name] - } -} - diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Task.kt index a1ef7be2..a1a754a4 100644 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Task.kt +++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Task.kt @@ -1,15 +1,16 @@ package space.kscience.dataforge.workspace import kotlinx.coroutines.withContext -import space.kscience.dataforge.data.DataSetBuilder -import space.kscience.dataforge.data.DataTree +import space.kscience.dataforge.data.DataSink import space.kscience.dataforge.data.GoalExecutionRestriction +import space.kscience.dataforge.data.MutableDataTree import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MetaReader import space.kscience.dataforge.meta.MetaRepr -import space.kscience.dataforge.meta.Specification import space.kscience.dataforge.meta.descriptors.Described import space.kscience.dataforge.meta.descriptors.MetaDescriptor -import space.kscience.dataforge.misc.DfId +import space.kscience.dataforge.misc.DfType +import space.kscience.dataforge.misc.UnsafeKType import space.kscience.dataforge.names.Name import space.kscience.dataforge.workspace.Task.Companion.TYPE import kotlin.reflect.KType @@ -19,8 +20,8 @@ import kotlin.reflect.typeOf * A configurable task that could be executed on a workspace. The [TaskResult] represents a lazy result of the task. * In general no computations should be made until the result is called. */ -@DfId(TYPE) -public interface Task : Described { +@DfType(TYPE) +public interface Task : Described { /** * A task identification string used to compare tasks and check task body for change @@ -43,10 +44,10 @@ public interface Task : Described { } /** - * A [Task] with [Specification] for wrapping and unwrapping task configuration + * A [Task] with [MetaReader] for wrapping and unwrapping task configuration */ -public interface TaskWithSpec : Task { - public val spec: Specification +public interface TaskWithSpec : Task { + public val spec: MetaReader override val descriptor: MetaDescriptor? get() = spec.descriptor public suspend fun execute(workspace: Workspace, taskName: Name, configuration: C): TaskResult @@ -55,18 +56,18 @@ public interface TaskWithSpec : Task { execute(workspace, taskName, spec.read(taskMeta)) } -public suspend fun TaskWithSpec.execute( - workspace: Workspace, - taskName: Name, - block: C.() -> Unit = {}, -): TaskResult = execute(workspace, taskName, spec(block)) +//public suspend fun TaskWithSpec.execute( +// workspace: Workspace, +// taskName: Name, +// block: C.() -> Unit = {}, +//): TaskResult = execute(workspace, taskName, spec(block)) -public class TaskResultBuilder( +public class TaskResultBuilder( public val workspace: Workspace, public val taskName: Name, public val taskMeta: Meta, - private val dataDrop: DataSetBuilder, -) : DataSetBuilder by dataDrop + private val dataSink: DataSink, +) : DataSink by dataSink /** * Create a [Task] that composes a result using [builder]. Only data from the workspace could be used. @@ -76,7 +77,6 @@ public class TaskResultBuilder( * @param descriptor of meta accepted by this task * @param builder for resulting data set */ -@Suppress("FunctionName") public fun Task( resultType: KType, descriptor: MetaDescriptor? = null, @@ -89,16 +89,20 @@ public fun Task( workspace: Workspace, taskName: Name, taskMeta: Meta, - ): TaskResult = withContext(GoalExecutionRestriction() + workspace.goalLogger) { + ): TaskResult { //TODO use safe builder and check for external data on add and detects cycles - val dataset = DataTree(resultType) { - TaskResultBuilder(workspace, taskName, taskMeta, this).apply { builder() } + @OptIn(UnsafeKType::class) + val dataset = MutableDataTree(resultType).apply { + TaskResultBuilder(workspace, taskName, taskMeta, this).apply { + withContext(GoalExecutionRestriction() + workspace.goalLogger) { + builder() + } + } } - workspace.wrapResult(dataset, taskName, taskMeta) + return workspace.wrapResult(dataset, taskName, taskMeta) } } -@Suppress("FunctionName") public inline fun Task( descriptor: MetaDescriptor? = null, noinline builder: suspend TaskResultBuilder.() -> Unit, @@ -113,13 +117,15 @@ public inline fun Task( * @param specification a specification for task configuration * @param builder for resulting data set */ + + @Suppress("FunctionName") public fun Task( resultType: KType, - specification: Specification, + specification: MetaReader, builder: suspend TaskResultBuilder.(C) -> Unit, ): TaskWithSpec = object : TaskWithSpec { - override val spec: Specification = specification + override val spec: MetaReader = specification override suspend fun execute( workspace: Workspace, @@ -128,15 +134,15 @@ public fun Task( ): TaskResult = withContext(GoalExecutionRestriction() + workspace.goalLogger) { //TODO use safe builder and check for external data on add and detects cycles val taskMeta = configuration.toMeta() - val dataset = DataTree(resultType) { + @OptIn(UnsafeKType::class) + val dataset = MutableDataTree(resultType).apply { TaskResultBuilder(workspace, taskName, taskMeta, this).apply { builder(configuration) } } workspace.wrapResult(dataset, taskName, taskMeta) } } -@Suppress("FunctionName") public inline fun Task( - specification: Specification, + specification: MetaReader, noinline builder: suspend TaskResultBuilder.(C) -> Unit, ): Task = Task(typeOf(), specification, builder) \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskData.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskData.kt deleted file mode 100644 index 080ffec3..00000000 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskData.kt +++ /dev/null @@ -1,50 +0,0 @@ -package space.kscience.dataforge.workspace - -import space.kscience.dataforge.data.Data -import space.kscience.dataforge.data.NamedData -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.names.Name - -/** - * A [Workspace]-locked [NamedData], that serves as a computation model. - */ -public interface TaskData : 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 taskName: Name - - /** - * Stage configuration used to produce this data. - */ - public val taskMeta: Meta - - /** - * Dependencies that allow to compute transitive dependencies as well. - */ -// override val dependencies: Collection> -} - -private class TaskDataImpl( - override val workspace: Workspace, - override val data: Data, - override val name: Name, - override val taskName: 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") -// } -} - -/** - * Adopt data into this workspace - */ -public fun Workspace.wrapData(data: Data, name: Name, taskName: Name, taskMeta: Meta): TaskData = - TaskDataImpl(this, data, name, taskName, taskMeta) - diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskResult.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskResult.kt index d8db6417..d4d4291a 100644 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskResult.kt +++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskResult.kt @@ -1,54 +1,41 @@ package space.kscience.dataforge.workspace -import space.kscience.dataforge.data.DataSet -import space.kscience.dataforge.data.forEach +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import space.kscience.dataforge.data.DataTree +import space.kscience.dataforge.data.asSequence +import space.kscience.dataforge.data.launch import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name /** * A result of a [Task] + * @param workspace the [Workspace] that produced the result + * @param taskName the name of the task that produced the result + * @param taskMeta The configuration of the task that produced the result */ -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 iterator(): Iterator> - - override fun get(name: Name): TaskData? -} - -private class TaskResultImpl( - override val workspace: Workspace, - override val taskName: Name, - override val taskMeta: Meta, - val dataSet: DataSet, -) : TaskResult, DataSet by dataSet { - - override fun iterator(): Iterator> = iterator { - dataSet.forEach { - yield(workspace.wrapData(it, it.name, taskName, taskMeta)) - } - } - - override fun get(name: Name): TaskData? = dataSet[name]?.let { - workspace.wrapData(it, name, taskName, taskMeta) - } -} +public data class TaskResult( + public val content: DataTree, + public val workspace: Workspace, + public val taskName: Name, + public val taskMeta: Meta, +) : DataTree by content /** * Wrap data into [TaskResult] */ -public fun Workspace.wrapResult(dataSet: DataSet, taskName: Name, taskMeta: Meta): TaskResult = - TaskResultImpl(this, taskName, taskMeta, dataSet) \ No newline at end of file +public fun Workspace.wrapResult(data: DataTree, taskName: Name, taskMeta: Meta): TaskResult = + TaskResult(data, this, taskName, taskMeta) + +/** + * Start computation for all data elements of this node. + * The resulting [Job] is completed only when all of them are completed. + */ +public fun TaskResult<*>.launch(scope: CoroutineScope): Job { + val jobs = asSequence().map { + it.data.launch(scope) + }.toList() + return scope.launch { jobs.joinAll() } +} \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Workspace.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Workspace.kt index ee00f539..7247240b 100644 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Workspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Workspace.kt @@ -1,29 +1,35 @@ package space.kscience.dataforge.workspace +import kotlinx.coroutines.CoroutineScope import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.data.Data -import space.kscience.dataforge.data.DataSet +import space.kscience.dataforge.data.DataTree import space.kscience.dataforge.data.asSequence +import space.kscience.dataforge.data.get import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.misc.DfId +import space.kscience.dataforge.misc.DfType import space.kscience.dataforge.names.Name import space.kscience.dataforge.provider.Provider +import kotlin.coroutines.CoroutineContext -public interface DataSelector{ - public suspend fun select(workspace: Workspace, meta: Meta): DataSet +public fun interface DataSelector { + public suspend fun select(workspace: Workspace, meta: Meta): DataTree } /** * An environment for pull-mode computation */ -@DfId(Workspace.TYPE) -public interface Workspace : ContextAware, Provider { +@DfType(Workspace.TYPE) +public interface Workspace : ContextAware, Provider, CoroutineScope { + + override val coroutineContext: CoroutineContext get() = context.coroutineContext + /** * The whole data node for current workspace */ - public val data: TaskResult<*> + public val data: DataTree<*> /** * All targets associated with the workspace @@ -37,7 +43,7 @@ public interface Workspace : ContextAware, Provider { override fun content(target: String): Map { return when (target) { - "target", Meta.TYPE -> targets.mapKeys { Name.parse(it.key)} + "target", Meta.TYPE -> targets.mapKeys { Name.parse(it.key) } Task.TYPE -> tasks Data.TYPE -> data.asSequence().associateBy { it.name } else -> emptyMap() @@ -49,7 +55,7 @@ public interface Workspace : ContextAware, Provider { return task.execute(this, taskName, taskMeta) } - public suspend fun produceData(taskName: Name, taskMeta: Meta, name: Name): TaskData<*>? = + public suspend fun produceData(taskName: Name, taskMeta: Meta, name: Name): Data<*>? = produce(taskName, taskMeta)[name] public companion object { diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceBuilder.kt index 1538460f..013c0171 100644 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceBuilder.kt +++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceBuilder.kt @@ -1,30 +1,30 @@ package space.kscience.dataforge.workspace -import kotlinx.coroutines.CoroutineScope +import space.kscience.dataforge.actions.Action import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.ContextBuilder import space.kscience.dataforge.context.Global -import space.kscience.dataforge.data.* -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MetaRepr -import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.meta.Specification +import space.kscience.dataforge.data.DataSink +import space.kscience.dataforge.data.DataTree +import space.kscience.dataforge.data.MutableDataTree +import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.descriptors.MetaDescriptor import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder import space.kscience.dataforge.misc.DFBuilder -import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.misc.UnsafeKType import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import kotlin.collections.set import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.typeOf -public data class TaskReference(public val taskName: Name, public val task: Task) : DataSelector { +public data class TaskReference(public val taskName: Name, public val task: Task) : DataSelector { @Suppress("UNCHECKED_CAST") - override suspend fun select(workspace: Workspace, meta: Meta): DataSet { + override suspend fun select(workspace: Workspace, meta: Meta): DataTree { if (workspace.tasks[taskName] == task) { - return workspace.produce(taskName, meta) as TaskResult + return workspace.produce(taskName, meta) as DataTree } else { error("Task $taskName does not belong to the workspace") } @@ -45,6 +45,9 @@ public inline fun TaskContainer.registerTask( noinline builder: suspend TaskResultBuilder.() -> Unit, ): Unit = registerTask(Name.parse(name), Task(MetaDescriptor(descriptorBuilder), builder)) +/** + * Create and register a new task + */ public inline fun TaskContainer.buildTask( name: String, descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, @@ -67,8 +70,11 @@ public inline fun TaskContainer.task( ReadOnlyProperty { _, _ -> TaskReference(taskName, task) } } +/** + * Create a task based on [MetaReader] + */ public inline fun TaskContainer.task( - specification: Specification, + specification: MetaReader, noinline builder: suspend TaskResultBuilder.(C) -> Unit, ): PropertyDelegateProvider>> = PropertyDelegateProvider { _, property -> val taskName = Name.parse(property.name) @@ -77,15 +83,34 @@ public inline fun TaskContainer.task( ReadOnlyProperty { _, _ -> TaskReference(taskName, task) } } +/** + * A delegate to create a custom task + */ public inline fun TaskContainer.task( noinline descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, noinline builder: suspend TaskResultBuilder.() -> Unit, ): PropertyDelegateProvider>> = task(MetaDescriptor(descriptorBuilder), builder) -public class WorkspaceBuilder(private val parentContext: Context = Global) : TaskContainer { +/** + * A delegate for creating a task based on [action] + */ +public inline fun TaskContainer.action( + selector: DataSelector, + action: Action, + noinline metaTransform: MutableMeta.() -> Unit = {}, + noinline descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {}, +): PropertyDelegateProvider>> = + task(MetaDescriptor(descriptorBuilder)) { + result(action.execute(from(selector), taskMeta.copy(metaTransform), workspace)) + } + +public class WorkspaceBuilder( + private val parentContext: Context = Global, +) : TaskContainer { private var context: Context? = null - private var data: DataSet<*>? = null + @OptIn(UnsafeKType::class) + private val data = MutableDataTree(typeOf()) private val targets: HashMap = HashMap() private val tasks = HashMap>() private var cache: WorkspaceCache? = null @@ -100,13 +125,8 @@ public class WorkspaceBuilder(private val parentContext: Context = Global) : Tas /** * Define intrinsic data for the workspace */ - public fun data(builder: DataSetBuilder.() -> Unit) { - data = DataTree(builder) - } - - @DFExperimental - public fun data(scope: CoroutineScope, builder: DataSourceBuilder.() -> Unit) { - data = DataSource(scope, builder) + public fun data(builder: DataSink.() -> Unit) { + data.apply(builder) } /** @@ -130,9 +150,9 @@ public class WorkspaceBuilder(private val parentContext: Context = Global) : Tas public fun build(): Workspace { val postProcess: suspend (TaskResult<*>) -> TaskResult<*> = { result -> - cache?.evaluate(result) ?: result + cache?.cache(result) ?: result } - return WorkspaceImpl(context ?: parentContext, data ?: DataSet.EMPTY, targets, tasks, postProcess) + return WorkspaceImpl(context ?: parentContext, data, targets, tasks, postProcess) } } diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceCache.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceCache.kt index 62df6744..42cb7b4f 100644 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceCache.kt +++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceCache.kt @@ -1,5 +1,5 @@ package space.kscience.dataforge.workspace public interface WorkspaceCache { - public suspend fun evaluate(result: TaskResult): TaskResult + public suspend fun cache(result: TaskResult): TaskResult } diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceImpl.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceImpl.kt index dae9667a..94839d62 100644 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceImpl.kt +++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceImpl.kt @@ -2,21 +2,19 @@ package space.kscience.dataforge.workspace import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.gather -import space.kscience.dataforge.data.DataSet +import space.kscience.dataforge.data.DataTree import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name internal class WorkspaceImpl internal constructor( override val context: Context, - data: DataSet<*>, + override val data: DataTree<*>, override val targets: Map, tasks: Map>, private val postProcess: suspend (TaskResult<*>) -> TaskResult<*>, ) : Workspace { - override val data: TaskResult<*> = wrapResult(data, Name.EMPTY, Meta.EMPTY) - override val tasks: Map> by lazy { context.gather>(Task.TYPE) + tasks } override suspend fun produce(taskName: Name, taskMeta: Meta): TaskResult<*> { diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/envelopeData.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/envelopeData.kt index 39bb0726..a74f8f05 100644 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/envelopeData.kt +++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/envelopeData.kt @@ -3,14 +3,15 @@ package space.kscience.dataforge.workspace import space.kscience.dataforge.data.Data import space.kscience.dataforge.data.await import space.kscience.dataforge.io.* -import space.kscience.dataforge.misc.DFInternal +import space.kscience.dataforge.misc.UnsafeKType +import kotlin.reflect.typeOf /** * Convert an [Envelope] to a data via given format. The actual parsing is done lazily. */ -@OptIn(DFInternal::class) -public fun Envelope.toData(format: IOReader): Data = Data(format.type, meta) { +@OptIn(UnsafeKType::class) +public inline fun Envelope.toData(format: IOReader): Data = Data(typeOf(), meta) { data?.readWith(format) ?: error("Can't convert envelope without data to Data") } diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/taskBuilders.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/taskBuilders.kt index bf3d5921..15565995 100644 --- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/taskBuilders.kt +++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/taskBuilders.kt @@ -1,9 +1,11 @@ package space.kscience.dataforge.workspace +import space.kscience.dataforge.actions.Action import space.kscience.dataforge.context.PluginFactory -import space.kscience.dataforge.data.DataSet +import space.kscience.dataforge.data.DataTree import space.kscience.dataforge.data.forEach -import space.kscience.dataforge.data.map +import space.kscience.dataforge.data.putAll +import space.kscience.dataforge.data.transform import space.kscience.dataforge.meta.* import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name @@ -23,22 +25,22 @@ public val TaskResultBuilder<*>.defaultDependencyMeta: Meta * @param selector a workspace data selector. Could be either task selector or initial data selector. * @param dependencyMeta meta used for selector. The same meta is used for caching. By default, uses [defaultDependencyMeta]. */ -public suspend fun TaskResultBuilder<*>.from( +public suspend fun TaskResultBuilder<*>.from( selector: DataSelector, dependencyMeta: Meta = defaultDependencyMeta, -): DataSet = selector.select(workspace, dependencyMeta) +): DataTree = selector.select(workspace, dependencyMeta) -public suspend inline fun TaskResultBuilder<*>.from( +public suspend inline fun TaskResultBuilder<*>.from( plugin: P, dependencyMeta: Meta = defaultDependencyMeta, selectorBuilder: P.() -> TaskReference, -): DataSet { +): TaskResult { require(workspace.context.plugins.contains(plugin)) { "Plugin $plugin is not loaded into $workspace" } val taskReference: TaskReference = plugin.selectorBuilder() val res = workspace.produce(plugin.name + taskReference.taskName, dependencyMeta) //TODO add explicit check after https://youtrack.jetbrains.com/issue/KT-32956 @Suppress("UNCHECKED_CAST") - return res as TaskResult + return res as TaskResult } /** @@ -48,11 +50,11 @@ public suspend inline fun TaskResultBuild * @param dependencyMeta meta used for selector. The same meta is used for caching. By default, uses [defaultDependencyMeta]. * @param selectorBuilder a builder of task from the plugin. */ -public suspend inline fun TaskResultBuilder<*>.from( +public suspend inline fun TaskResultBuilder<*>.from( pluginFactory: PluginFactory

, dependencyMeta: Meta = defaultDependencyMeta, selectorBuilder: P.() -> TaskReference, -): DataSet { +): TaskResult { val plugin = workspace.context.plugins[pluginFactory] ?: error("Plugin ${pluginFactory.tag} not loaded into workspace context") val taskReference: TaskReference = plugin.selectorBuilder() @@ -63,12 +65,10 @@ public suspend inline fun TaskRes } public val TaskResultBuilder<*>.allData: DataSelector<*> - get() = object : DataSelector { - override suspend fun select(workspace: Workspace, meta: Meta): DataSet = workspace.data - } + get() = DataSelector { workspace, _ -> workspace.data } /** - * Perform a lazy mapping task using given [selector] and [action]. The meta of resulting + * Perform a lazy mapping task using given [selector] and one-to-one [action]. * TODO move selector to receiver with multi-receivers * * @param selector a workspace data selector. Could be either task selector or initial data selector. @@ -77,7 +77,7 @@ public val TaskResultBuilder<*>.allData: DataSelector<*> * @param action process individual data asynchronously. */ @DFExperimental -public suspend inline fun TaskResultBuilder.pipeFrom( +public suspend inline fun TaskResultBuilder.transformEach( selector: DataSelector, dependencyMeta: Meta = defaultDependencyMeta, dataMetaTransform: MutableMeta.(name: Name) -> Unit = {}, @@ -89,12 +89,31 @@ public suspend inline fun TaskResultBuilder.pipeFr dataMetaTransform(data.name) } - val res = data.map(workspace.context.coroutineContext, meta) { + val res = data.transform(meta, workspace.context.coroutineContext) { action(it, data.name, meta) } - data(data.name, res) + put(data.name, res) } } +/** + * Set given [dataSet] as a task result. + */ +public fun TaskResultBuilder.result(dataSet: DataTree) { + this.putAll(dataSet) +} + +/** + * Use provided [action] to fill the result + */ +@DFExperimental +public suspend inline fun TaskResultBuilder.actionFrom( + selector: DataSelector, + action: Action, + dependencyMeta: Meta = defaultDependencyMeta, +) { + putAll(action.execute(from(selector, dependencyMeta), dependencyMeta, workspace)) +} + diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/CachingAction.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/CachingAction.kt new file mode 100644 index 00000000..5f88ab74 --- /dev/null +++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/CachingAction.kt @@ -0,0 +1,19 @@ +package space.kscience.dataforge.workspace + +import space.kscience.dataforge.actions.AbstractAction +import space.kscience.dataforge.data.* +import space.kscience.dataforge.meta.Meta +import kotlin.reflect.KType + +internal class CachingAction(type: KType, private val caching: (NamedData) -> NamedData) : + AbstractAction(type) { + override fun DataSink.generate(source: DataTree, meta: Meta) { + source.forEach { + put(caching(it)) + } + } + + override suspend fun DataSink.update(source: DataTree, meta: Meta, updatedData: DataUpdate) { + put(updatedData.name, updatedData.data?.named(updatedData.name)?.let(caching)) + } +} \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCache.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCache.kt index 279e61a2..4d2578e5 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCache.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCache.kt @@ -6,15 +6,17 @@ import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.serializer +import space.kscience.dataforge.actions.Action +import space.kscience.dataforge.actions.invoke import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger import space.kscience.dataforge.context.request import space.kscience.dataforge.data.Data import space.kscience.dataforge.data.await +import space.kscience.dataforge.data.named import space.kscience.dataforge.io.* import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.misc.DFInternal -import space.kscience.dataforge.names.Name +import space.kscience.dataforge.misc.UnsafeKType import space.kscience.dataforge.names.withIndex import java.nio.file.Path import kotlin.io.path.deleteIfExists @@ -22,7 +24,7 @@ import kotlin.io.path.div import kotlin.io.path.exists import kotlin.reflect.KType -public class JsonIOFormat(override val type: KType) : IOFormat { +public class JsonIOFormat(private val type: KType) : IOFormat { @Suppress("UNCHECKED_CAST") private val serializer: KSerializer = serializer(type) as KSerializer @@ -35,7 +37,7 @@ public class JsonIOFormat(override val type: KType) : IOFormat { } @OptIn(ExperimentalSerializationApi::class) -public class ProtobufIOFormat(override val type: KType) : IOFormat { +public class ProtobufIOFormat(private val type: KType) : IOFormat { @Suppress("UNCHECKED_CAST") private val serializer: KSerializer = serializer(type) as KSerializer @@ -52,16 +54,17 @@ public class FileWorkspaceCache(public val cacheDirectory: Path) : WorkspaceCach // private fun TaskData<*>.checkType(taskType: KType): TaskData = this as TaskData - @OptIn(DFExperimental::class, DFInternal::class) - override suspend fun evaluate(result: TaskResult): TaskResult { + + @OptIn(DFExperimental::class, UnsafeKType::class) + override suspend fun cache(result: TaskResult): TaskResult { val io = result.workspace.context.request(IOPlugin) val format: IOFormat = io.resolveIOFormat(result.dataType, result.taskMeta) ?: ProtobufIOFormat(result.dataType) ?: error("Can't resolve IOFormat for ${result.dataType}") - fun evaluateDatum(data: TaskData): TaskData { + val cachingAction: Action = CachingAction(result.dataType) { data -> val path = cacheDirectory / result.taskName.withIndex(result.taskMeta.hashCode().toString(16)).toString() / data.name.toString() @@ -80,7 +83,7 @@ public class FileWorkspaceCache(public val cacheDirectory: Path) : WorkspaceCach } } - //waiting for data in current scope because Envelope is synchronous + //waiting for data in the current scope because Envelope is synchronous return@Data data.await().also { result -> val envelope = Envelope { meta = data.meta @@ -92,15 +95,12 @@ public class FileWorkspaceCache(public val cacheDirectory: Path) : WorkspaceCach } } - return data.workspace.wrapData(datum, data.name, data.taskName, data.taskMeta) + datum.named(data.name) } - return object : TaskResult by result { - override fun iterator(): Iterator> = - result.iterator().asSequence().map { evaluateDatum(it) }.iterator() + val cachedTree = cachingAction(result) - override fun get(name: Name): TaskData? = result[name]?.let { evaluateDatum(it) } - } + return result.workspace.wrapResult(cachedTree, result.taskName, result.taskMeta) } } diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/InMemoryWorkspaceCache.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/InMemoryWorkspaceCache.kt index bb8ea9a4..8ba39ec1 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/InMemoryWorkspaceCache.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/InMemoryWorkspaceCache.kt @@ -1,39 +1,41 @@ package space.kscience.dataforge.workspace +import space.kscience.dataforge.actions.Action +import space.kscience.dataforge.actions.invoke +import space.kscience.dataforge.data.Data +import space.kscience.dataforge.data.named import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import kotlin.reflect.KType import kotlin.reflect.full.isSubtypeOf -private typealias TaskResultId = Pair +private data class TaskResultId(val name: Name, val meta: Meta) public class InMemoryWorkspaceCache : WorkspaceCache { - // never do that at home! - private val cache = HashMap>>() + private val cache = HashMap>>() @Suppress("UNCHECKED_CAST") - private fun TaskData<*>.checkType(taskType: KType): TaskData = - if (type.isSubtypeOf(taskType)) this as TaskData + private fun Data<*>.checkType(taskType: KType): Data = + if (type.isSubtypeOf(taskType)) this as Data else error("Cached data type mismatch: expected $taskType but got $type") - override suspend fun evaluate(result: TaskResult): TaskResult { - for (d: TaskData in result) { - cache.getOrPut(result.taskName to result.taskMeta) { HashMap() }.getOrPut(d.name) { d } - } - - return object : TaskResult by result { - override fun iterator(): Iterator> = (cache[result.taskName to result.taskMeta] - ?.values?.map { it.checkType(result.dataType) } - ?: emptyList()).iterator() - - override fun get(name: Name): TaskData? { - val cached: TaskData<*> = cache[result.taskName to result.taskMeta]?.get(name) ?: return null - //TODO check types - return cached.checkType(result.dataType) + @OptIn(DFExperimental::class) + override suspend fun cache(result: TaskResult): TaskResult { + val cachingAction: Action = CachingAction(result.dataType) { data -> + val cachedData = cache.getOrPut(TaskResultId(result.taskName, result.taskMeta)){ + HashMap() + }.getOrPut(data.name){ + data.data } + cachedData.checkType(result.dataType).named(data.name) } + + val cachedTree = cachingAction(result) + + return result.workspace.wrapResult(cachedTree, result.taskName, result.taskMeta) } } diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/fileData.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/fileData.kt deleted file mode 100644 index d9f678b3..00000000 --- a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/fileData.kt +++ /dev/null @@ -1,299 +0,0 @@ -package space.kscience.dataforge.workspace - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import space.kscience.dataforge.context.error -import space.kscience.dataforge.context.logger -import space.kscience.dataforge.context.warn -import space.kscience.dataforge.data.* -import space.kscience.dataforge.io.* -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.copy -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.string -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.misc.DFInternal -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.NameToken -import space.kscience.dataforge.names.asName -import space.kscience.dataforge.names.plus -import space.kscience.dataforge.workspace.FileData.Companion.DEFAULT_IGNORE_EXTENSIONS -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardWatchEventKinds -import java.nio.file.WatchEvent -import java.nio.file.attribute.BasicFileAttributes -import java.nio.file.spi.FileSystemProvider -import java.time.Instant -import kotlin.io.path.extension -import kotlin.io.path.name -import kotlin.io.path.nameWithoutExtension -import kotlin.io.path.readAttributes -import kotlin.reflect.KType -import kotlin.reflect.typeOf - - -//public typealias FileFormatResolver = (Path, Meta) -> IOFormat - -public typealias FileFormatResolver = (path: Path, meta: Meta) -> IOReader? - -/** - * A data based on a filesystem [Path] - */ -public class FileData internal constructor(private val data: Data, public val path: Path) : Data by data { - - // public val path: String? get() = meta[META_FILE_PATH_KEY].string -// public val extension: String? get() = meta[META_FILE_EXTENSION_KEY].string -// - public val createdTime: Instant? get() = meta[FILE_CREATE_TIME_KEY].string?.let { Instant.parse(it) } - public val updatedTime: Instant? get() = meta[FILE_UPDATE_TIME_KEY].string?.let { Instant.parse(it) } - - public companion object { - public val FILE_KEY: Name = "file".asName() - public val FILE_PATH_KEY: Name = FILE_KEY + "path" - public val FILE_EXTENSION_KEY: Name = FILE_KEY + "extension" - public val FILE_CREATE_TIME_KEY: Name = FILE_KEY + "created" - public val FILE_UPDATE_TIME_KEY: Name = FILE_KEY + "updated" - public const val DF_FILE_EXTENSION: String = "df" - public val DEFAULT_IGNORE_EXTENSIONS: Set = setOf(DF_FILE_EXTENSION) - } -} - - -/** - * Read data with supported envelope format and binary format. If envelope format is null, then read binary directly from file. - * The operation is blocking since it must read meta header. The reading of envelope body is lazy - */ -@OptIn(DFInternal::class) -@DFExperimental -public fun IOPlugin.readDataFile( - path: Path, - formatResolver: FileFormatResolver, -): FileData? { - val envelope = readEnvelopeFile(path, true) - val format = formatResolver(path, envelope.meta) ?: return null - val updatedMeta = envelope.meta.copy { - FileData.FILE_PATH_KEY put path.toString() - FileData.FILE_EXTENSION_KEY put path.extension - - val attributes = path.readAttributes() - FileData.FILE_UPDATE_TIME_KEY put attributes.lastModifiedTime().toInstant().toString() - FileData.FILE_CREATE_TIME_KEY put attributes.creationTime().toInstant().toString() - } - return FileData( - Data(format.type, updatedMeta) { - (envelope.data ?: Binary.EMPTY).readWith(format) - }, - path - ) -} - - -context(IOPlugin) @DFExperimental -private fun DataSetBuilder.directory( - path: Path, - ignoreExtensions: Set, - formatResolver: FileFormatResolver, -) { - Files.list(path).forEach { childPath -> - val fileName = childPath.fileName.toString() - if (fileName.startsWith(IOPlugin.META_FILE_NAME)) { - meta(readMetaFile(childPath)) - } else if (!fileName.startsWith("@")) { - file(childPath, ignoreExtensions, formatResolver) - } - } -} - -/** - * Read the directory as a data node. If [path] is a zip archive, read it as directory - */ -@DFExperimental -@DFInternal -public fun IOPlugin.readDataDirectory( - type: KType, - path: Path, - ignoreExtensions: Set = DEFAULT_IGNORE_EXTENSIONS, - formatResolver: FileFormatResolver, -): 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 fsProvider = FileSystemProvider.installedProviders().find { it.scheme == "jar" } - ?: error("Zip file system provider not found") - val fs = fsProvider.newFileSystem(path, mapOf("create" to "true")) - - return readDataDirectory(type, fs.rootDirectories.first(), ignoreExtensions, formatResolver) - } - if (!Files.isDirectory(path)) error("Provided path $path is not a directory") - return DataTree(type) { - meta { - FileData.FILE_PATH_KEY put path.toString() - } - directory(path, ignoreExtensions, formatResolver) - } -} - -@OptIn(DFInternal::class) -@DFExperimental -public inline fun IOPlugin.readDataDirectory( - path: Path, - ignoreExtensions: Set = DEFAULT_IGNORE_EXTENSIONS, - noinline formatResolver: FileFormatResolver, -): DataTree = readDataDirectory(typeOf(), path, ignoreExtensions, formatResolver) - -/** - * Read raw binary data tree from the directory. All files are read as-is (save for meta files). - */ -@DFExperimental -public fun IOPlugin.readRawDirectory( - path: Path, - ignoreExtensions: Set = emptySet(), -): DataTree = readDataDirectory(path, ignoreExtensions) { _, _ -> IOReader.binary } - - -private fun Path.toName() = Name(map { NameToken.parse(it.nameWithoutExtension) }) - -@DFInternal -@DFExperimental -public fun IOPlugin.monitorDataDirectory( - type: KType, - path: Path, - ignoreExtensions: Set = DEFAULT_IGNORE_EXTENSIONS, - formatResolver: FileFormatResolver, -): DataSource { - if (path.fileName.toString().endsWith(".zip")) error("Monitoring not supported for ZipFS") - if (!Files.isDirectory(path)) error("Provided path $path is not a directory") - return DataSource(type, context) { - directory(path, ignoreExtensions, formatResolver) - launch(Dispatchers.IO) { - val watchService = path.fileSystem.newWatchService() - - path.register( - watchService, - StandardWatchEventKinds.ENTRY_DELETE, - StandardWatchEventKinds.ENTRY_MODIFY, - StandardWatchEventKinds.ENTRY_CREATE - ) - - do { - val key = watchService.take() - if (key != null) { - for (event: WatchEvent<*> in key.pollEvents()) { - val eventPath = event.context() as Path - if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) { - remove(eventPath.toName()) - } else { - val fileName = eventPath.fileName.toString() - if (fileName.startsWith(IOPlugin.META_FILE_NAME)) { - meta(readMetaFile(eventPath)) - } else if (!fileName.startsWith("@")) { - file(eventPath, ignoreExtensions, formatResolver) - } - } - } - key.reset() - } - } while (isActive && key != null) - } - } -} - - -/** - * Start monitoring given directory ([path]) as a [DataSource]. - */ -@OptIn(DFInternal::class) -@DFExperimental -public inline fun IOPlugin.monitorDataDirectory( - path: Path, - ignoreExtensions: Set = DEFAULT_IGNORE_EXTENSIONS, - noinline formatResolver: FileFormatResolver, -): DataSource = monitorDataDirectory(typeOf(), path, ignoreExtensions, formatResolver) - -/** - * Read and monitor raw binary data tree from the directory. All files are read as-is (save for meta files). - */ -@DFExperimental -public fun IOPlugin.monitorRawDirectory( - path: Path, - ignoreExtensions: Set = DEFAULT_IGNORE_EXTENSIONS, -): DataSource = monitorDataDirectory(path, ignoreExtensions) { _, _ -> IOReader.binary } - -/** - * Write data tree to existing directory or create a new one using default [java.nio.file.FileSystem] provider - */ -@DFExperimental -public suspend fun IOPlugin.writeDataDirectory( - path: Path, - tree: DataTree, - format: IOWriter, - envelopeFormat: EnvelopeFormat? = null, -) { - withContext(Dispatchers.IO) { - if (!Files.exists(path)) { - Files.createDirectories(path) - } else if (!Files.isDirectory(path)) { - error("Can't write a node into file") - } - tree.items.forEach { (token, item) -> - val childPath = path.resolve(token.toString()) - when (item) { - is DataTreeItem.Node -> { - writeDataDirectory(childPath, item.tree, format, envelopeFormat) - } - - is DataTreeItem.Leaf -> { - val envelope = item.data.toEnvelope(format) - if (envelopeFormat != null) { - writeEnvelopeFile(childPath, envelope, envelopeFormat) - } else { - writeEnvelopeDirectory(childPath, envelope) - } - } - } - } - val treeMeta = tree.meta - writeMetaFile(path, treeMeta) - } -} - -/** - * Add file/directory-based data tree item - * - * @param ignoreExtensions a list of file extensions for which extension should be cut from the resulting item name - */ -context(IOPlugin) -@OptIn(DFInternal::class) -@DFExperimental -public fun DataSetBuilder.file( - path: Path, - ignoreExtensions: Set = DEFAULT_IGNORE_EXTENSIONS, - formatResolver: FileFormatResolver, -) { - - fun defaultPath() = if (path.extension in ignoreExtensions) path.nameWithoutExtension else path.name - - try { - //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("@") }) { - val data = readDataFile(path, formatResolver) - if (data == null) { - logger.warn { "File format is not resolved for $path. Skipping." } - return - } - val name: String = data.meta[Envelope.ENVELOPE_NAME_KEY].string ?: defaultPath() - data(name.asName(), data) - } else { - //otherwise, read as directory - val data: DataTree = readDataDirectory(dataType, path, ignoreExtensions, formatResolver) - val name = data.meta[Envelope.ENVELOPE_NAME_KEY].string ?: defaultPath() - node(name.asName(), data) - } - } catch (ex: Exception) { - logger.error { "Failed to read file or directory at $path: ${ex.message}" } - } -} - diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/readFileData.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/readFileData.kt new file mode 100644 index 00000000..37dafab9 --- /dev/null +++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/readFileData.kt @@ -0,0 +1,188 @@ +package space.kscience.dataforge.workspace + +import kotlinx.coroutines.* +import space.kscience.dataforge.data.Data +import space.kscience.dataforge.data.DataSink +import space.kscience.dataforge.data.StaticData +import space.kscience.dataforge.io.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.copy +import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.NameToken +import space.kscience.dataforge.names.asName +import space.kscience.dataforge.names.plus +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes +import java.nio.file.spi.FileSystemProvider +import kotlin.io.path.* +import kotlin.reflect.typeOf + + +public object FileData { + public val FILE_KEY: Name = "file".asName() + public val FILE_PATH_KEY: Name = FILE_KEY + "path" + public val FILE_EXTENSION_KEY: Name = FILE_KEY + "extension" + public val FILE_CREATE_TIME_KEY: Name = FILE_KEY + "created" + public val FILE_UPDATE_TIME_KEY: Name = FILE_KEY + "updated" + public const val DF_FILE_EXTENSION: String = "df" + public val DEFAULT_IGNORE_EXTENSIONS: Set = setOf(DF_FILE_EXTENSION) + +} + + +/** + * Read data with supported envelope format and binary format. If the envelope format is null, then read binary directly from file. + * The operation is blocking since it must read the meta header. The reading of envelope body is lazy + */ +public fun IOPlugin.readFileData( + path: Path, +): Data { + val envelope = readEnvelopeFile(path, true) + val updatedMeta = envelope.meta.copy { + FileData.FILE_PATH_KEY put path.toString() + FileData.FILE_EXTENSION_KEY put path.extension + + val attributes = path.readAttributes() + FileData.FILE_UPDATE_TIME_KEY put attributes.lastModifiedTime().toInstant().toString() + FileData.FILE_CREATE_TIME_KEY put attributes.creationTime().toInstant().toString() + } + return StaticData( + typeOf(), + envelope.data ?: Binary.EMPTY, + updatedMeta + ) +} + +public fun DataSink.file(io: IOPlugin, name: Name, path: Path) { + if (!path.isRegularFile()) error("Only regular files could be handled by this function") + put(name, io.readFileData(path)) +} + +public fun DataSink.directory( + io: IOPlugin, + name: Name, + path: Path, +) { + if (!path.isDirectory()) error("Only directories could be handled by this function") + //process root data + + var dataBinary: Binary? = null + var meta: Meta? = null + Files.list(path).forEach { childPath -> + val fileName = childPath.fileName.toString() + if (fileName == IOPlugin.DATA_FILE_NAME) { + dataBinary = childPath.asBinary() + } else if (fileName.startsWith(IOPlugin.META_FILE_NAME)) { + meta = io.readMetaFileOrNull(childPath) + } else if (!fileName.startsWith("@")) { + val token = if (childPath.isRegularFile() && childPath.extension in FileData.DEFAULT_IGNORE_EXTENSIONS) { + NameToken(childPath.nameWithoutExtension) + } else { + NameToken(childPath.name) + } + + files(io, name + token, childPath) + } + } + + //set data if it is relevant + if (dataBinary != null || meta != null) { + put( + name, + StaticData( + typeOf(), + dataBinary ?: Binary.EMPTY, + meta ?: Meta.EMPTY + ) + ) + } +} + +public fun DataSink.files( + io: IOPlugin, + name: Name, + path: Path, +) { + if (path.isRegularFile() && path.extension == "zip") { + //Using explicit Zip file system to avoid bizarre compatibility bugs + val fsProvider = FileSystemProvider.installedProviders().find { it.scheme == "jar" } + ?: error("Zip file system provider not found") + val fs = fsProvider.newFileSystem(path, emptyMap()) + + files(io, name, fs.rootDirectories.first()) + } + if (path.isRegularFile()) { + file(io, name, path) + } else { + directory(io, name, path) + } +} + + +private fun Path.toName() = Name(map { NameToken.parse(it.nameWithoutExtension) }) + +public fun DataSink.monitorFiles( + io: IOPlugin, + name: Name, + path: Path, + scope: CoroutineScope = io.context, +): Job { + files(io, name, path) + return scope.launch(Dispatchers.IO) { + val watchService = path.fileSystem.newWatchService() + + path.register( + watchService, + StandardWatchEventKinds.ENTRY_DELETE, + StandardWatchEventKinds.ENTRY_MODIFY, + StandardWatchEventKinds.ENTRY_CREATE + ) + + do { + val key = watchService.take() + if (key != null) { + for (event: WatchEvent<*> in key.pollEvents()) { + val eventPath = event.context() as Path + if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) { + put(eventPath.toName(), null) + } else { + val fileName = eventPath.fileName.toString() + if (!fileName.startsWith("@")) { + files(io, name, eventPath) + } + } + } + key.reset() + } + } while (isActive && key != null) + } + +} + +/** + * @param resources The names of the resources to read. + * @param classLoader The class loader to use for loading the resources. By default, it uses the current thread's context class loader. + */ +@DFExperimental +public fun DataSink.resources( + io: IOPlugin, + resource: String, + vararg otherResources: String, + classLoader: ClassLoader = Thread.currentThread().contextClassLoader, +) { + //create a file system if necessary + val uri = Thread.currentThread().contextClassLoader.getResource("common")!!.toURI() + try { + uri.toPath() + } catch (e: FileSystemNotFoundException) { + FileSystems.newFileSystem(uri, mapOf("create" to "true")) + } + + listOf(resource,*otherResources).forEach { r -> + val path = classLoader.getResource(r)?.toURI()?.toPath() ?: error( + "Resource with name $r is not resolved" + ) + files(io, r.asName(), path) + } +} diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/workspaceJvm.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/workspaceJvm.kt index ea6ffb85..61caf7e0 100644 --- a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/workspaceJvm.kt +++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/workspaceJvm.kt @@ -1,6 +1,6 @@ package space.kscience.dataforge.workspace -import space.kscience.dataforge.data.DataSet +import space.kscience.dataforge.data.DataTree import space.kscience.dataforge.data.filterByType import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.misc.DFExperimental @@ -16,14 +16,13 @@ import space.kscience.dataforge.names.matches */ @OptIn(DFExperimental::class) public inline fun TaskResultBuilder<*>.dataByType(namePattern: Name? = null): DataSelector = - object : DataSelector { - override suspend fun select(workspace: Workspace, meta: Meta): DataSet = - workspace.data.filterByType { name, _ -> - namePattern == null || name.matches(namePattern) - } + DataSelector { workspace, _ -> + workspace.data.filterByType { name, _, _ -> + namePattern == null || name.matches(namePattern) + } } public suspend inline fun TaskResultBuilder<*>.fromTask( task: Name, taskMeta: Meta = Meta.EMPTY, -): DataSet = workspace.produce(task, taskMeta).filterByType() \ No newline at end of file +): DataTree = workspace.produce(task, taskMeta).filterByType() \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/writeFileData.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/writeFileData.kt new file mode 100644 index 00000000..379a79dd --- /dev/null +++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/writeFileData.kt @@ -0,0 +1,72 @@ +package space.kscience.dataforge.workspace + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import space.kscience.dataforge.data.* +import space.kscience.dataforge.io.* +import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.names.Name +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.spi.FileSystemProvider +import kotlin.io.path.Path +import kotlin.io.path.createDirectories +import kotlin.io.path.exists +import kotlin.io.path.extension + + +/** + * Write the data tree to existing directory or create a new one using default [java.nio.file.FileSystem] provider + * + * @param nameToPath a [Name] to [Path] converter used to create + */ +@DFExperimental +public suspend fun IOPlugin.writeDataDirectory( + path: Path, + dataSet: DataTree, + format: IOWriter, + envelopeFormat: EnvelopeFormat? = null, +): Unit = withContext(Dispatchers.IO) { + if (!Files.exists(path)) { + Files.createDirectories(path) + } else if (!Files.isDirectory(path)) { + error("Can't write a node into file") + } + dataSet.forEach { (name, data) -> + val childPath = path.resolve(name.tokens.joinToString("/") { token -> token.toStringUnescaped() }) + childPath.parent.createDirectories() + val envelope = data.toEnvelope(format) + if (envelopeFormat != null) { + writeEnvelopeFile(childPath, envelope, envelopeFormat) + } else { + writeEnvelopeDirectory(childPath, envelope) + } + } + dataSet.meta?.let { writeMetaFile(path, it) } + +} + +/** + * Write this [DataTree] as a zip archive + */ +@DFExperimental +public suspend fun IOPlugin.writeZip( + path: Path, + dataSet: DataTree, + format: IOWriter, + envelopeFormat: EnvelopeFormat? = null, +): Unit = withContext(Dispatchers.IO) { + if (path.exists()) error("Can't override existing zip data file $path") + val actualFile = if (path.extension == "zip") { + path + } else { + path.resolveSibling(path.fileName.toString() + ".zip") + } + val fsProvider = FileSystemProvider.installedProviders().find { it.scheme == "jar" } + ?: error("Zip file system provider not found") + //val fs = FileSystems.newFileSystem(actualFile, mapOf("create" to true)) + val fs = fsProvider.newFileSystem(actualFile, mapOf("create" to true)) + fs.use { + writeDataDirectory(fs.rootDirectories.first(), dataSet, format, envelopeFormat) + } +} \ No newline at end of file diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/zipData.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/zipData.kt deleted file mode 100644 index 466552c2..00000000 --- a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/zipData.kt +++ /dev/null @@ -1,73 +0,0 @@ -package space.kscience.dataforge.workspace - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import space.kscience.dataforge.data.DataTree -import space.kscience.dataforge.data.DataTreeItem -import space.kscience.dataforge.io.* -import space.kscience.dataforge.misc.DFExperimental -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardOpenOption -import java.util.zip.ZipEntry -import java.util.zip.ZipOutputStream - - -private suspend fun ZipOutputStream.writeNode( - name: String, - treeItem: DataTreeItem, - dataFormat: IOFormat, - envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat, -): Unit = withContext(Dispatchers.IO) { - when (treeItem) { - is DataTreeItem.Leaf -> { - //TODO add directory-based envelope writer - val envelope = treeItem.data.toEnvelope(dataFormat) - val entry = ZipEntry(name) - putNextEntry(entry) - - //TODO remove additional copy - val bytes = ByteArray { - writeWith(envelopeFormat, envelope) - } - write(bytes) - - } - - is DataTreeItem.Node -> { - val entry = ZipEntry("$name/") - putNextEntry(entry) - closeEntry() - treeItem.tree.items.forEach { (token, item) -> - val childName = "$name/$token" - writeNode(childName, item, dataFormat, envelopeFormat) - } - } - } -} - -/** - * Write this [DataTree] as a zip archive - */ -@DFExperimental -public suspend fun DataTree.writeZip( - path: Path, - format: IOFormat, - envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat, -): Unit = withContext(Dispatchers.IO) { - val actualFile = if (path.toString().endsWith(".zip")) { - path - } else { - path.resolveSibling(path.fileName.toString() + ".zip") - } - val fos = Files.newOutputStream( - actualFile, - StandardOpenOption.WRITE, - StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING - ) - val zos = ZipOutputStream(fos) - zos.use { - it.writeNode("", DataTreeItem.Node(this@writeZip), format, envelopeFormat) - } -} \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/CachingWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/CachingWorkspaceTest.kt index 4e1923bc..7a6a8202 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/CachingWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/CachingWorkspaceTest.kt @@ -1,18 +1,16 @@ package space.kscience.dataforge.workspace -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test -import space.kscience.dataforge.data.startAll -import space.kscience.dataforge.data.static +import space.kscience.dataforge.data.putValue import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.boolean import space.kscience.dataforge.meta.get import space.kscience.dataforge.misc.DFExperimental import kotlin.test.assertEquals -@OptIn(ExperimentalCoroutinesApi::class, DFExperimental::class) +@OptIn(DFExperimental::class) internal class CachingWorkspaceTest { @Test @@ -24,24 +22,23 @@ internal class CachingWorkspaceTest { data { //statically initialize data repeat(5) { - static("myData[$it]", it) + putValue("myData[$it]", it) } } inMemoryCache() val doFirst by task { - pipeFrom(allData) { _, name, _ -> + transformEach(allData) { _, name, _ -> firstCounter++ println("Done first on $name with flag=${taskMeta["flag"].boolean}") } } - @Suppress("UNUSED_VARIABLE") val doSecond by task { - pipeFrom( + transformEach( doFirst, - dependencyMeta = if(taskMeta["flag"].boolean == true) taskMeta else Meta.EMPTY + dependencyMeta = if (taskMeta["flag"].boolean == true) taskMeta else Meta.EMPTY ) { _, name, _ -> secondCounter++ println("Done second on $name with flag=${taskMeta["flag"].boolean ?: false}") @@ -53,13 +50,15 @@ internal class CachingWorkspaceTest { val secondA = workspace.produce("doSecond") val secondB = workspace.produce("doSecond", Meta { "flag" put true }) val secondC = workspace.produce("doSecond") + //use coroutineScope to wait for the result coroutineScope { - first.startAll(this) - secondA.startAll(this) - secondB.startAll(this) + first.launch(this) + secondA.launch(this) + secondB.launch(this) //repeat to check caching - secondC.startAll(this) + secondC.launch(this) } + assertEquals(10, firstCounter) assertEquals(10, secondCounter) } diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/DataPropagationTest.kt index 18086902..cd38f809 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/DataPropagationTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/DataPropagationTest.kt @@ -20,13 +20,13 @@ class DataPropagationTestPlugin : WorkspacePlugin() { val result: Data = selectedData.foldToData(0) { result, data -> result + data.value } - data("result", result) + put("result", result) } val singleData by task { workspace.data.filterByType()["myData[12]"]?.let { - data("result", it) + put("result", it) } } @@ -47,7 +47,7 @@ class DataPropagationTest { } data { repeat(100) { - static("myData[$it]", it) + putValue("myData[$it]", it) } } } @@ -55,12 +55,12 @@ class DataPropagationTest { @Test fun testAllData() = runTest { val node = testWorkspace.produce("Test.allData") - assertEquals(4950, node.asSequence().single().await()) + assertEquals(4950, node.content.asSequence().single().await()) } @Test fun testSingleData() = runTest { val node = testWorkspace.produce("Test.singleData") - assertEquals(12, node.asSequence().single().await()) + assertEquals(12, node.content.asSequence().single().await()) } } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileDataTest.kt index 451c76f4..5466da76 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileDataTest.kt @@ -1,6 +1,5 @@ package space.kscience.dataforge.workspace -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import kotlinx.io.Sink import kotlinx.io.Source @@ -9,38 +8,34 @@ import kotlinx.io.writeString import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Global import space.kscience.dataforge.data.* -import space.kscience.dataforge.io.Envelope -import space.kscience.dataforge.io.IOFormat -import space.kscience.dataforge.io.io -import space.kscience.dataforge.io.readEnvelopeFile +import space.kscience.dataforge.io.* import space.kscience.dataforge.io.yaml.YamlPlugin import space.kscience.dataforge.meta.get import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.names.Name import java.nio.file.Files +import kotlin.io.path.deleteExisting import kotlin.io.path.fileSize import kotlin.io.path.toPath -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") { + putAll("dir") { + putValue("a", "Some string") { "content" put "Some string" } } - static("b", "root data") - meta { - "content" put "This is root meta node" - } + putValue("b", "root data") +// meta { +// "content" put "This is root meta node" +// } } object StringIOFormat : IOFormat { - override val type: KType get() = typeOf() override fun writeTo(sink: Sink, obj: String) { sink.writeString(obj) @@ -51,29 +46,33 @@ class FileDataTest { @Test @DFExperimental - fun testDataWriteRead() = with(Global.io) { + fun testDataWriteRead() = runTest { + val io = Global.io val dir = Files.createTempDirectory("df_data_node") - runBlocking { - writeDataDirectory(dir, dataNode, StringIOFormat) - println(dir.toUri().toString()) - val reconstructed = readDataDirectory(dir) { _, _ -> StringIOFormat } - assertEquals(dataNode["dir.a"]?.meta?.get("content"), reconstructed["dir.a"]?.meta?.get("content")) - assertEquals(dataNode["b"]?.await(), reconstructed["b"]?.await()) + io.writeDataDirectory(dir, dataNode, StringIOFormat) + println(dir.toUri().toString()) + val data = DataTree { + files(io, Name.EMPTY, dir) } + val reconstructed = data.transform { (_, value) -> value.toByteArray().decodeToString() } + assertEquals(dataNode["dir.a"]?.meta?.get("content"), reconstructed["dir.a"]?.meta?.get("content")) + assertEquals(dataNode["b"]?.await(), reconstructed["b"]?.await()) } @Test @DFExperimental fun testZipWriteRead() = runTest { - with(Global.io) { - val zip = Files.createTempFile("df_data_node", ".zip") - dataNode.writeZip(zip, StringIOFormat) - println(zip.toUri().toString()) - val reconstructed = readDataDirectory(zip) { _, _ -> StringIOFormat } - assertEquals(dataNode["dir.a"]?.meta?.get("content"), reconstructed["dir.a"]?.meta?.get("content")) - assertEquals(dataNode["b"]?.await(), reconstructed["b"]?.await()) - } + val io = Global.io + val zip = Files.createTempFile("df_data_node", ".zip") + zip.deleteExisting() + io.writeZip(zip, dataNode, StringIOFormat) + println(zip.toUri().toString()) + val reconstructed = DataTree { files(io, Name.EMPTY, zip) } + .transform { (_, value) -> value.toByteArray().decodeToString() } + assertEquals(dataNode["dir.a"]?.meta?.get("content"), reconstructed["dir.a"]?.meta?.get("content")) + assertEquals(dataNode["b"]?.await(), reconstructed["b"]?.await()) + } @OptIn(DFExperimental::class) diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCacheTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCacheTest.kt index 00ca67cb..0cf4f401 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCacheTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCacheTest.kt @@ -3,12 +3,11 @@ package space.kscience.dataforge.workspace import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test -import space.kscience.dataforge.data.startAll -import space.kscience.dataforge.data.static +import space.kscience.dataforge.data.putValue import space.kscience.dataforge.misc.DFExperimental import java.nio.file.Files -@OptIn(ExperimentalCoroutinesApi::class,DFExperimental::class) +@OptIn(ExperimentalCoroutinesApi::class, DFExperimental::class) class FileWorkspaceCacheTest { @Test @@ -17,18 +16,17 @@ class FileWorkspaceCacheTest { data { //statically initialize data repeat(5) { - static("myData[$it]", it) + putValue("myData[$it]", it) } } fileCache(Files.createTempDirectory("dataforge-temporary-cache")) - @Suppress("UNUSED_VARIABLE") val echo by task { - pipeFrom(dataByType()) { arg, _, _ -> arg } + transformEach(dataByType()) { arg, _, _ -> arg } } } - workspace.produce("echo").startAll(this) + workspace.produce("echo").launch(this) } } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/SimpleWorkspaceTest.kt index 7bfe0927..39837c15 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/SimpleWorkspaceTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/SimpleWorkspaceTest.kt @@ -6,7 +6,6 @@ package space.kscience.dataforge.workspace import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Timeout import space.kscience.dataforge.context.* import space.kscience.dataforge.data.* import space.kscience.dataforge.meta.* @@ -16,6 +15,7 @@ import space.kscience.dataforge.names.plus import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.milliseconds /** @@ -27,8 +27,8 @@ public fun

P.toFactory(): PluginFactory

= object : PluginFactory override val tag: PluginTag = this@toFactory.tag } -public fun Workspace.produceBlocking(task: String, block: MutableMeta.() -> Unit = {}): DataSet = runBlocking { - produce(task, block) +public fun Workspace.produceBlocking(task: String, block: MutableMeta.() -> Unit = {}): DataTree<*> = runBlocking { + produce(task, block).content } @OptIn(DFExperimental::class) @@ -37,7 +37,7 @@ internal object TestPlugin : WorkspacePlugin() { val test by task { // type is inferred - pipeFrom(dataByType()) { arg, _, _ -> + transformEach(dataByType()) { arg, _, _ -> logger.info { "Test: $arg" } arg } @@ -62,19 +62,19 @@ internal class SimpleWorkspaceTest { data { //statically initialize data repeat(100) { - static("myData[$it]", it) + putValue("myData[$it]", it) } } val filterOne by task { val name by taskMeta.string { error("Name field not defined") } - from(testPluginFactory) { test }.getByType(name)?.let { source -> - data(source.name, source.map { it }) + from(testPluginFactory) { test }[name]?.let { source: Data -> + put(name, source) } } val square by task { - pipeFrom(dataByType()) { arg, name, meta -> + transformEach(dataByType()) { arg, name, meta -> if (meta["testFlag"].boolean == true) { println("Side effect") } @@ -84,7 +84,7 @@ internal class SimpleWorkspaceTest { } val linear by task { - pipeFrom(dataByType()) { arg, name, _ -> + transformEach(dataByType()) { arg, name, _ -> workspace.logger.info { "Starting linear on $name" } arg * 2 + 1 } @@ -97,7 +97,7 @@ internal class SimpleWorkspaceTest { val newData: Data = data.combine(linearData[data.name]!!) { l, r -> l + r } - data(data.name, newData) + put(data.name, newData) } } @@ -106,23 +106,23 @@ internal class SimpleWorkspaceTest { val res = from(square).foldToData(0) { l, r -> l + r.value } - data("sum", res) + put("sum", res) } val averageByGroup by task { - val evenSum = workspace.data.filterByType { name, _ -> + val evenSum = workspace.data.filterByType { name, _, _ -> name.toString().toInt() % 2 == 0 }.foldToData(0) { l, r -> l + r.value } - data("even", evenSum) - val oddSum = workspace.data.filterByType { name, _ -> + put("even", evenSum) + val oddSum = workspace.data.filterByType { name, _, _ -> name.toString().toInt() % 2 == 1 }.foldToData(0) { l, r -> l + r.value } - data("odd", oddSum) + put("odd", oddSum) } val delta by task { @@ -132,7 +132,7 @@ internal class SimpleWorkspaceTest { val res = even.combine(odd) { l, r -> l - r } - data("res", res) + put("res", res) } val customPipe by task { @@ -140,7 +140,7 @@ internal class SimpleWorkspaceTest { val meta = data.meta.toMutableMeta().apply { "newValue" put 22 } - data(data.name + "new", data.map { (data.meta["value"].int ?: 0) + it }) + put(data.name + "new", data.transform { (data.meta["value"].int ?: 0) + it }) } } @@ -148,18 +148,16 @@ internal class SimpleWorkspaceTest { } @Test - @Timeout(1) - fun testWorkspace() = runTest { + fun testWorkspace() = runTest(timeout = 100.milliseconds) { val node = workspace.produce("sum") val res = node.asSequence().single() assertEquals(328350, res.await()) } @Test - @Timeout(1) - fun testMetaPropagation() = runTest { + fun testMetaPropagation() = runTest(timeout = 100.milliseconds) { val node = workspace.produce("sum") { "testFlag" put true } - val res = node.asSequence().single().await() + val res = node["sum"]!!.await() } @Test @@ -170,20 +168,25 @@ internal class SimpleWorkspaceTest { } @Test - fun testFullSquare() { - runBlocking { - val node = workspace.produce("fullSquare") - println(node.toMeta()) + fun testFullSquare() = runTest { + val result = workspace.produce("fullSquare") + result.forEach { + println( + """ + Name: ${it.name} + Meta: ${it.meta} + Data: ${it.data.await()} + """.trimIndent() + ) } } @Test - fun testFilter() { - runBlocking { - val node = workspace.produce("filterOne") { - "name" put "myData[12]" - } - assertEquals(12, node.single().await()) + fun testFilter() = runTest { + val node = workspace.produce("filterOne") { + "name" put "myData[12]" } + assertEquals(12, node.asSequence().first().await()) } + } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 31ef2f9a..ffc318d9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,9 @@ +kotlin.code.style=official + org.gradle.parallel=true org.gradle.jvmargs=-Xmx4096m -kotlin.code.style=official kotlin.mpp.stability.nowarn=true -kotlin.incremental.js.ir=true kotlin.native.ignoreDisabledTargets=true -toolsVersion=0.15.1-kotlin-1.9.21 -#kotlin.experimental.tryK2=true \ No newline at end of file +toolsVersion=0.15.4-kotlin-2.0.0 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e411586a..17655d0e 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-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index ca872038..35eae74e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -43,6 +43,7 @@ include( ":dataforge-meta", ":dataforge-io", ":dataforge-io:dataforge-io-yaml", + ":dataforge-io:dataforge-io-proto", ":dataforge-context", ":dataforge-data", ":dataforge-workspace",