Experimental listOfSpec delegate.

This commit is contained in:
Alexander Nozik 2021-05-11 12:42:41 +03:00
parent a6f1e54255
commit 82b328f797
7 changed files with 48 additions and 13 deletions

View File

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

View File

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

View File

@ -83,7 +83,6 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
* @param plugin
* @return
*/
@Deprecated("Use immutable contexts instead")
private fun <T : Plugin> 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 <T : Plugin> load(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY): T =
load(factory(meta, context))
/**
* Remove a plugin from [PluginManager]

View File

@ -68,7 +68,7 @@ public fun ItemProvider.getIndexed(name: Name): Map<String?, MetaItem> {
}
}
public fun ItemProvider.getIndexed(name: String): Map<String?, MetaItem> = this@getIndexed.getIndexed(name.toName())
public fun ItemProvider.getIndexed(name: String): Map<String?, MetaItem> = getIndexed(name.toName())
/**
* Return a provider referencing a child node

View File

@ -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<out M : Meta>() {
public sealed class TypedMetaItem<out M : Meta> : 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<Nothing>() {
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<Nothing>() {
@Serializable(MetaItemSerializer::class)
public class MetaItemNode<M : Meta>(public val node: M) : TypedMetaItem<M>() {
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()

View File

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

View File

@ -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
@ -57,6 +58,9 @@ public fun <C : MutableItemProvider, S : Specification<C>> Configurable.update(
public fun <T : MutableItemProvider> TypedMetaItem<MutableMeta<*>>.withSpec(spec: Specification<T>): T? =
node?.let { spec.write(it) }
/**
* A delegate that uses a [Specification] to wrap a child of this provider
*/
public fun <T : Scheme> MutableItemProvider.spec(
spec: Specification<T>,
key: Name? = null,
@ -71,3 +75,32 @@ public fun <T : Scheme> 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 <T : Scheme> MutableItemProvider.listOfSpec(
spec: Specification<T>,
key: Name? = null,
): ReadWriteProperty<Any?, List<T>> = object : ReadWriteProperty<Any?, List<T>> {
override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
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<T>) {
val name = key ?: property.name.asName()
setIndexed(name, value.map { it.toMeta() })
}
}