diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a704f2..03bc4627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] ### Added +- Experimental `listOfSpec` delegate. ### Changed - **API breaking** Descriptor no has a member property `defaultValue` instead of `defaultItem()` extension. It cahces default value state on the first call. It is done because computing default on each call is too expensive. 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 c302cb6b..c56d4b28 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 @@ -67,7 +67,7 @@ public class ContextBuilder internal constructor( return Context(contextName, parent, meta.seal()).apply { factories.forEach { (factory, meta) -> @Suppress("DEPRECATION") - plugins.load(factory, meta) + plugins.fetch(factory, meta) } } } @@ -85,7 +85,7 @@ public fun Context.withEnv(block: ContextBuilder.() -> Unit): Context { } val builder = ContextBuilder(this, name + "env", properties).apply(block) - val requiresFork = builder.factories.any { (factory, meta) -> + val requiresFork = builder.factories.values.any { (factory, meta) -> !contains(factory, meta) } || ((properties as Meta) == builder.meta) return if (requiresFork) builder.build() else this diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/PluginManager.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/PluginManager.kt index 0fd1e9e1..26e32f3c 100644 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/PluginManager.kt +++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/PluginManager.kt @@ -83,7 +83,6 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab * @param plugin * @return */ - @Deprecated("Use immutable contexts instead") private fun load(plugin: T): T { if (get(plugin::class, plugin.tag, recursive = false) != null) { error("Plugin with tag ${plugin.tag} already exists in ${context.name}") @@ -99,12 +98,6 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab } } - /** - * Load a plugin using its factory - */ - @Deprecated("Use immutable contexts instead") - internal fun load(factory: PluginFactory, meta: Meta = Meta.EMPTY): T = - load(factory(meta, context)) /** * Remove a plugin from [PluginManager] diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ItemProvider.kt index 1201e9da..24a61ba0 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ItemProvider.kt @@ -68,7 +68,7 @@ public fun ItemProvider.getIndexed(name: Name): Map { } } -public fun ItemProvider.getIndexed(name: String): Map = this@getIndexed.getIndexed(name.toName()) +public fun ItemProvider.getIndexed(name: String): Map = getIndexed(name.toName()) /** * Return a provider referencing a child node diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaItem.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaItem.kt index bf43119c..4b8dc25d 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaItem.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaItem.kt @@ -1,6 +1,8 @@ package space.kscience.dataforge.meta import kotlinx.serialization.Serializable +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.isEmpty import space.kscience.dataforge.values.* /** @@ -9,7 +11,7 @@ import space.kscience.dataforge.values.* * * a [MetaItemNode] (node) */ @Serializable(MetaItemSerializer::class) -public sealed class TypedMetaItem() { +public sealed class TypedMetaItem : ItemProvider { abstract override fun equals(other: Any?): Boolean @@ -32,6 +34,8 @@ public typealias MetaItem = TypedMetaItem<*> @Serializable(MetaItemSerializer::class) public class MetaItemValue(public val value: Value) : TypedMetaItem() { + override fun getItem(name: Name): MetaItem? = if (name.isEmpty()) this else null + override fun toString(): String = value.toString() override fun equals(other: Any?): Boolean { @@ -45,6 +49,8 @@ public class MetaItemValue(public val value: Value) : TypedMetaItem() { @Serializable(MetaItemSerializer::class) public class MetaItemNode(public val node: M) : TypedMetaItem() { + override fun getItem(name: Name): MetaItem? = if (name.isEmpty()) this else node.getItem(name) + //Fixing serializer for node could cause class cast problems, but it should not since Meta descendants are not serializable override fun toString(): String = node.toString() diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableItemProvider.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableItemProvider.kt index 46a47ad5..cb901c40 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableItemProvider.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableItemProvider.kt @@ -96,6 +96,8 @@ public fun MutableItemProvider.getChild(childName: Name): MutableItemProvider { public fun MutableItemProvider.getChild(childName: String): MutableItemProvider = getChild(childName.toName()) + + /** * Update existing mutable node with another node. The rules are following: * * value replaces anything @@ -126,4 +128,4 @@ public fun MutableItemProvider.withDefault(default: ItemProvider?): MutableItemP } override fun getItem(name: Name): MetaItem? = this@withDefault.getItem(name) ?: default.getItem(name) - } + } \ No newline at end of file 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 index 865b2f31..4590785d 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Specification.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Specification.kt @@ -1,5 +1,6 @@ package space.kscience.dataforge.meta +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import kotlin.properties.ReadWriteProperty @@ -30,7 +31,7 @@ public interface ReadOnlySpecification { * By convention [Scheme] companion should inherit this class * */ -public interface Specification: ReadOnlySpecification { +public interface Specification : ReadOnlySpecification { /** * Wrap [MutableItemProvider], using it as inner storage (changes to [Specification] are reflected on [MutableItemProvider] */ @@ -57,6 +58,9 @@ public fun > Configurable.update( public fun TypedMetaItem>.withSpec(spec: Specification): T? = node?.let { spec.write(it) } +/** + * A delegate that uses a [Specification] to wrap a child of this provider + */ public fun MutableItemProvider.spec( spec: Specification, key: Name? = null, @@ -71,3 +75,32 @@ public fun MutableItemProvider.spec( set(name, value.toMeta().asMetaItem()) } } + +/** + * 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 MutableItemProvider.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).map { + when (val value = it.value) { + is MetaItemNode<*> -> when (value.node) { + is MutableItemProvider -> spec.write(value.node) + else -> spec.read(value.node) + } + is MetaItemValue -> spec.read(value) + } + } + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: List) { + val name = key ?: property.name.asName() + setIndexed(name, value.map { it.toMeta() }) + } +}