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] ## [Unreleased]
### Added ### Added
- Experimental `listOfSpec` delegate.
### Changed ### 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. - **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 { return Context(contextName, parent, meta.seal()).apply {
factories.forEach { (factory, meta) -> factories.forEach { (factory, meta) ->
@Suppress("DEPRECATION") @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 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) !contains(factory, meta)
} || ((properties as Meta) == builder.meta) } || ((properties as Meta) == builder.meta)
return if (requiresFork) builder.build() else this return if (requiresFork) builder.build() else this

View File

@ -83,7 +83,6 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
* @param plugin * @param plugin
* @return * @return
*/ */
@Deprecated("Use immutable contexts instead")
private fun <T : Plugin> load(plugin: T): T { private fun <T : Plugin> load(plugin: T): T {
if (get(plugin::class, plugin.tag, recursive = false) != null) { if (get(plugin::class, plugin.tag, recursive = false) != null) {
error("Plugin with tag ${plugin.tag} already exists in ${context.name}") 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] * 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 * Return a provider referencing a child node

View File

@ -1,6 +1,8 @@
package space.kscience.dataforge.meta package space.kscience.dataforge.meta
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.values.* import space.kscience.dataforge.values.*
/** /**
@ -9,7 +11,7 @@ import space.kscience.dataforge.values.*
* * a [MetaItemNode] (node) * * a [MetaItemNode] (node)
*/ */
@Serializable(MetaItemSerializer::class) @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 abstract override fun equals(other: Any?): Boolean
@ -32,6 +34,8 @@ public typealias MetaItem = TypedMetaItem<*>
@Serializable(MetaItemSerializer::class) @Serializable(MetaItemSerializer::class)
public class MetaItemValue(public val value: Value) : TypedMetaItem<Nothing>() { 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 toString(): String = value.toString()
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@ -45,6 +49,8 @@ public class MetaItemValue(public val value: Value) : TypedMetaItem<Nothing>() {
@Serializable(MetaItemSerializer::class) @Serializable(MetaItemSerializer::class)
public class MetaItemNode<M : Meta>(public val node: M) : TypedMetaItem<M>() { 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 //Fixing serializer for node could cause class cast problems, but it should not since Meta descendants are not serializable
override fun toString(): String = node.toString() override fun 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()) public fun MutableItemProvider.getChild(childName: String): MutableItemProvider = getChild(childName.toName())
/** /**
* Update existing mutable node with another node. The rules are following: * Update existing mutable node with another node. The rules are following:
* * value replaces anything * * 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) override fun getItem(name: Name): MetaItem? = this@withDefault.getItem(name) ?: default.getItem(name)
} }

View File

@ -1,5 +1,6 @@
package space.kscience.dataforge.meta package space.kscience.dataforge.meta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
@ -30,7 +31,7 @@ public interface ReadOnlySpecification<out T : ItemProvider> {
* By convention [Scheme] companion should inherit this class * By convention [Scheme] companion should inherit this class
* *
*/ */
public interface Specification<out T : MutableItemProvider>: ReadOnlySpecification<T> { public interface Specification<out T : MutableItemProvider> : ReadOnlySpecification<T> {
/** /**
* Wrap [MutableItemProvider], using it as inner storage (changes to [Specification] are reflected on [MutableItemProvider] * Wrap [MutableItemProvider], using it as inner storage (changes to [Specification] are reflected on [MutableItemProvider]
*/ */
@ -57,6 +58,9 @@ public fun <C : MutableItemProvider, S : Specification<C>> Configurable.update(
public fun <T : MutableItemProvider> TypedMetaItem<MutableMeta<*>>.withSpec(spec: Specification<T>): T? = public fun <T : MutableItemProvider> TypedMetaItem<MutableMeta<*>>.withSpec(spec: Specification<T>): T? =
node?.let { spec.write(it) } node?.let { spec.write(it) }
/**
* A delegate that uses a [Specification] to wrap a child of this provider
*/
public fun <T : Scheme> MutableItemProvider.spec( public fun <T : Scheme> MutableItemProvider.spec(
spec: Specification<T>, spec: Specification<T>,
key: Name? = null, key: Name? = null,
@ -71,3 +75,32 @@ public fun <T : Scheme> MutableItemProvider.spec(
set(name, value.toMeta().asMetaItem()) 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() })
}
}