Immutable plugin manager
This commit is contained in:
parent
474597777c
commit
73b3bbe7fc
@ -5,14 +5,17 @@
|
||||
- Experimental `listOfSpec` delegate.
|
||||
|
||||
### Changed
|
||||
- **API breaking** Config is deprecated, use `ObservableMeta` instead.
|
||||
- **API breaking** Descriptor no has a member property `defaultValue` instead of `defaultItem()` extension. It caches default value state on the first call. It is done because computing default on each call is too expensive.
|
||||
- Kotlin 1.5.10
|
||||
- Build tools 0.10.0
|
||||
- Relaxed type restriction on `MetaConverter`. Now nullables are available.
|
||||
|
||||
### Deprecated
|
||||
- Direct use of `Config`
|
||||
|
||||
### Removed
|
||||
- Public PluginManager mutability
|
||||
|
||||
### Fixed
|
||||
- Proper json array index treatment
|
||||
|
@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "space.kscience"
|
||||
version = "0.4.4-dev-1"
|
||||
version = "0.5.0-dev-1"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
@ -26,6 +26,7 @@ import kotlin.jvm.Synchronized
|
||||
public open class Context internal constructor(
|
||||
final override val name: Name,
|
||||
public val parent: Context?,
|
||||
plugins: Set<Plugin>,
|
||||
meta: Meta,
|
||||
) : Named, MetaRepr, Provider, CoroutineScope {
|
||||
|
||||
@ -42,7 +43,7 @@ public open class Context internal constructor(
|
||||
/**
|
||||
* A [PluginManager] for current context
|
||||
*/
|
||||
public val plugins: PluginManager by lazy { PluginManager(this) }
|
||||
public val plugins: PluginManager by lazy { PluginManager(this, plugins) }
|
||||
|
||||
override val defaultTarget: String get() = Plugin.TARGET
|
||||
|
||||
|
@ -64,12 +64,32 @@ public class ContextBuilder internal constructor(
|
||||
|
||||
public fun build(): Context {
|
||||
val contextName = name ?: "@auto[${hashCode().toUInt().toString(16)}]".toName()
|
||||
return Context(contextName, parent, meta.seal()).apply {
|
||||
factories.forEach { (factory, meta) ->
|
||||
@Suppress("DEPRECATION")
|
||||
plugins.fetch(factory, meta)
|
||||
val plugins = HashMap<PluginTag, Plugin>()
|
||||
|
||||
fun addPlugin(factory: PluginFactory<*>, meta: Meta) {
|
||||
val existing = plugins[factory.tag]
|
||||
// Add if does not exist
|
||||
if (existing == null) {
|
||||
//TODO bypass if parent already has plugin with given meta?
|
||||
val plugin = factory(meta, parent)
|
||||
|
||||
for ((depFactory, deoMeta) in plugin.dependsOn()) {
|
||||
addPlugin(depFactory, deoMeta)
|
||||
}
|
||||
|
||||
parent.logger.info { "Loading plugin ${plugin.name} into $contextName" }
|
||||
plugins[plugin.tag] = plugin
|
||||
} else if (existing.meta != meta) {
|
||||
error("Plugin with tag ${factory.tag} and meta $meta already exists in $contextName")
|
||||
}
|
||||
//bypass if exists with the same meta
|
||||
}
|
||||
|
||||
factories.forEach { (factory, meta) ->
|
||||
addPlugin(factory, meta)
|
||||
}
|
||||
|
||||
return Context(contextName, parent, plugins.values.toSet(), meta.seal())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ internal expect val globalLoggerFactory: PluginFactory<out LogManager>
|
||||
* A global root context. Closing [Global] terminates the framework.
|
||||
*/
|
||||
@ThreadLocal
|
||||
private object GlobalContext : Context("GLOBAL".asName(), null, Meta.EMPTY) {
|
||||
private object GlobalContext : Context("GLOBAL".asName(), null, emptySet(), Meta.EMPTY) {
|
||||
override val coroutineContext: CoroutineContext = Job() + CoroutineName("GlobalContext")
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package space.kscience.dataforge.context
|
||||
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaBuilder
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
@ -11,12 +10,10 @@ import kotlin.reflect.KClass
|
||||
* @property context A context for this plugin manager
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public class PluginManager(override val context: Context) : ContextAware, Iterable<Plugin> {
|
||||
|
||||
/**
|
||||
* A set of loaded plugins
|
||||
*/
|
||||
private val plugins: HashSet<Plugin> = HashSet()
|
||||
public class PluginManager internal constructor(
|
||||
override val context: Context,
|
||||
private val plugins: Set<Plugin>
|
||||
) : ContextAware, Iterable<Plugin> {
|
||||
|
||||
init {
|
||||
plugins.forEach { it.attach(context) }
|
||||
@ -76,64 +73,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
|
||||
public inline operator fun <reified T : Plugin> get(factory: PluginFactory<T>, recursive: Boolean = true): T? =
|
||||
get(factory.type, factory.tag, recursive)
|
||||
|
||||
/**
|
||||
* Load given plugin into this manager and return loaded instance.
|
||||
* Throw error if plugin of the same type and tag already exists in manager.
|
||||
*
|
||||
* @param plugin
|
||||
* @return
|
||||
*/
|
||||
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}")
|
||||
} else {
|
||||
for ((factory, meta) in plugin.dependsOn()) {
|
||||
@Suppress("DEPRECATION")
|
||||
fetch(factory, meta, true)
|
||||
}
|
||||
|
||||
logger.info { "Loading plugin ${plugin.name} into ${context.name}" }
|
||||
plugin.attach(context)
|
||||
plugins.add(plugin)
|
||||
return plugin
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove a plugin from [PluginManager]
|
||||
*/
|
||||
@Deprecated("Use immutable contexts instead")
|
||||
public fun remove(plugin: Plugin) {
|
||||
if (plugins.contains(plugin)) {
|
||||
Global.logger.info { "Removing plugin ${plugin.name} from ${context.name}" }
|
||||
plugin.detach()
|
||||
plugins.remove(plugin)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an existing plugin with given meta or load new one using provided factory
|
||||
*/
|
||||
@Deprecated("Use immutable contexts instead")
|
||||
public fun <T : Plugin> fetch(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY, recursive: Boolean = true): T {
|
||||
val loaded = get(factory.type, factory.tag, recursive)
|
||||
return when {
|
||||
loaded == null -> load(factory(meta, context))
|
||||
loaded.meta == meta -> loaded // if meta is the same, return existing plugin
|
||||
else -> throw RuntimeException("Can't load plugin with tag ${factory.tag}. Plugin with this tag and different configuration already exists in context.")
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use immutable contexts instead")
|
||||
public fun <T : Plugin> fetch(
|
||||
factory: PluginFactory<T>,
|
||||
recursive: Boolean = true,
|
||||
metaBuilder: MetaBuilder.() -> Unit,
|
||||
): T = fetch(factory, Meta(metaBuilder), recursive)
|
||||
|
||||
override fun iterator(): Iterator<Plugin> = plugins.iterator()
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,110 +0,0 @@
|
||||
package space.kscience.dataforge.meta
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
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 kotlin.collections.set
|
||||
import kotlin.jvm.Synchronized
|
||||
|
||||
//TODO add validator to configuration
|
||||
|
||||
/**
|
||||
* Mutable meta representing object state
|
||||
*/
|
||||
@Serializable(Config.Companion::class)
|
||||
public class Config : AbstractMutableMeta<Config>(), ItemPropertyProvider {
|
||||
|
||||
private val listeners = HashSet<ItemListener>()
|
||||
|
||||
@Synchronized
|
||||
private fun itemChanged(name: Name, oldItem: MetaItem?, newItem: MetaItem?) {
|
||||
listeners.forEach { it.action(name, oldItem, newItem) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Add change listener to this meta. Owner is declared to be able to remove listeners later. Listener without owner could not be removed
|
||||
*/
|
||||
@Synchronized
|
||||
override fun onChange(owner: Any?, action: (Name, MetaItem?, MetaItem?) -> Unit) {
|
||||
listeners.add(ItemListener(owner, action))
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all listeners belonging to given owner
|
||||
*/
|
||||
@Synchronized
|
||||
override fun removeListener(owner: Any?) {
|
||||
listeners.removeAll { it.owner === owner }
|
||||
}
|
||||
|
||||
override fun replaceItem(key: NameToken, oldItem: TypedMetaItem<Config>?, newItem: TypedMetaItem<Config>?) {
|
||||
if (newItem == null) {
|
||||
children.remove(key)
|
||||
if (oldItem != null && oldItem is MetaItemNode<Config>) {
|
||||
oldItem.node.removeListener(this)
|
||||
}
|
||||
} else {
|
||||
children[key] = newItem
|
||||
if (newItem is MetaItemNode) {
|
||||
newItem.node.onChange(this) { name, oldChild, newChild ->
|
||||
itemChanged(key + name, oldChild, newChild)
|
||||
}
|
||||
}
|
||||
}
|
||||
itemChanged(key.asName(), oldItem, newItem)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach configuration node instead of creating one
|
||||
*/
|
||||
override fun wrapNode(meta: Meta): Config = meta.asConfig()
|
||||
|
||||
override fun empty(): Config = Config()
|
||||
|
||||
public companion object ConfigSerializer : KSerializer<Config> {
|
||||
|
||||
public fun empty(): Config = Config()
|
||||
override val descriptor: SerialDescriptor get() = MetaSerializer.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): Config {
|
||||
return MetaSerializer.deserialize(decoder).asConfig()
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Config) {
|
||||
MetaSerializer.serialize(encoder, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public operator fun Config.get(token: NameToken): TypedMetaItem<Config>? = items[token]
|
||||
|
||||
/**
|
||||
* Create a mutable copy of this [Meta]. The copy is created event if initial [Meta] is a [Config].
|
||||
* Listeners are not preserved
|
||||
*/
|
||||
public fun Meta.toConfig(): Config = Config().also { builder ->
|
||||
this.items.mapValues { entry ->
|
||||
val item = entry.value
|
||||
builder[entry.key.asName()] = when (item) {
|
||||
is MetaItemValue -> item.value
|
||||
is MetaItemNode -> MetaItemNode(item.node.asConfig())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of this config, optionally applying the given [block].
|
||||
* The listeners of the original Config are not retained.
|
||||
*/
|
||||
public inline fun Config.copy(block: Config.() -> Unit = {}): Config = toConfig().apply(block)
|
||||
|
||||
/**
|
||||
* Return this [Meta] as [Config] if it is [Config] and create a new copy otherwise
|
||||
*/
|
||||
public fun Meta.asConfig(): Config = this as? Config ?: toConfig()
|
@ -5,21 +5,21 @@ import space.kscience.dataforge.names.Name
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
|
||||
/**
|
||||
* A container that holds a [Config].
|
||||
* A container that holds a [ObservableMeta].
|
||||
*/
|
||||
public interface Configurable {
|
||||
/**
|
||||
* Backing config
|
||||
*/
|
||||
public val config: Config
|
||||
public val config: ObservableMeta
|
||||
}
|
||||
|
||||
|
||||
public fun <T : Configurable> T.configure(meta: Meta): T = this.apply { config.update(meta) }
|
||||
|
||||
@DFBuilder
|
||||
public inline fun <T : Configurable> T.configure(action: Config.() -> Unit): T = apply { config.apply(action) }
|
||||
public inline fun <T : Configurable> T.configure(action: ObservableMeta.() -> Unit): T = apply { config.apply(action) }
|
||||
|
||||
/* Node delegates */
|
||||
|
||||
public fun Configurable.config(key: Name? = null): ReadWriteProperty<Any?, Config?> = config.node(key)
|
||||
public fun Configurable.config(key: Name? = null): ReadWriteProperty<Any?, ObservableMeta?> = config.node(key)
|
||||
|
@ -1,9 +1,14 @@
|
||||
package space.kscience.dataforge.meta
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
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 space.kscience.dataforge.names.*
|
||||
import kotlin.js.JsName
|
||||
import kotlin.jvm.Synchronized
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
|
||||
@ -12,13 +17,105 @@ internal data class ItemListener(
|
||||
val action: (name: Name, oldItem: MetaItem?, newItem: MetaItem?) -> Unit,
|
||||
)
|
||||
|
||||
|
||||
public interface ObservableItemProvider : ItemProvider {
|
||||
/**
|
||||
* An item provider that could be observed and mutated
|
||||
*/
|
||||
public interface ObservableItemProvider : ItemProvider, MutableItemProvider {
|
||||
/**
|
||||
* Add change listener to this meta. Owner is declared to be able to remove listeners later. Listener without owner could not be removed
|
||||
*/
|
||||
public fun onChange(owner: Any?, action: (name: Name, oldItem: MetaItem?, newItem: MetaItem?) -> Unit)
|
||||
|
||||
/**
|
||||
* Remove all listeners belonging to given owner
|
||||
*/
|
||||
public fun removeListener(owner: Any?)
|
||||
}
|
||||
|
||||
public interface ItemPropertyProvider: ObservableItemProvider, MutableItemProvider
|
||||
private open class ObservableItemProviderWrapper(
|
||||
open val itemProvider: MutableItemProvider,
|
||||
open val parent: Pair<ObservableItemProviderWrapper, Name>? = null
|
||||
) : ObservableItemProvider {
|
||||
|
||||
override fun getItem(name: Name): MetaItem? = itemProvider.getItem(name)
|
||||
|
||||
private val listeners = HashSet<ItemListener>()
|
||||
|
||||
@Synchronized
|
||||
private fun itemChanged(name: Name, oldItem: MetaItem?, newItem: MetaItem?) {
|
||||
listeners.forEach { it.action(name, oldItem, newItem) }
|
||||
}
|
||||
|
||||
override fun setItem(name: Name, item: MetaItem?) {
|
||||
val oldItem = getItem(name)
|
||||
itemProvider.setItem(name, item)
|
||||
itemChanged(name, oldItem, item)
|
||||
|
||||
//Recursively send update to parent listeners
|
||||
parent?.let { (parentNode, token) ->
|
||||
parentNode.itemChanged(token + name, oldItem, item)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onChange(owner: Any?, action: (Name, MetaItem?, MetaItem?) -> Unit) {
|
||||
listeners.add(ItemListener(owner, action))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun removeListener(owner: Any?) {
|
||||
listeners.removeAll { it.owner === owner }
|
||||
}
|
||||
}
|
||||
|
||||
public fun MutableItemProvider.asObservable(): ObservableItemProvider =
|
||||
(this as? ObservableItemProvider) ?: ObservableItemProviderWrapper(this)
|
||||
|
||||
/**
|
||||
* A Meta instance that could be both mutated and observed.
|
||||
*/
|
||||
@Serializable(ObservableMetaSerializer::class)
|
||||
public interface ObservableMeta : ObservableItemProvider, MutableMeta<ObservableMeta>
|
||||
|
||||
/**
|
||||
* A wrapper class that creates observable meta node from regular meta node
|
||||
*/
|
||||
private class ObservableMetaWrapper<M : MutableMeta<M>>(
|
||||
override val itemProvider: M,
|
||||
override val parent: Pair<ObservableMetaWrapper<M>, Name>? = null
|
||||
) : ObservableItemProviderWrapper(itemProvider, parent), ObservableMeta {
|
||||
override fun equals(other: Any?): Boolean = (itemProvider == other)
|
||||
|
||||
override fun hashCode(): Int = itemProvider.hashCode()
|
||||
|
||||
override fun toString(): String = itemProvider.toString()
|
||||
|
||||
private fun wrapItem(name: Name, item: TypedMetaItem<M>): TypedMetaItem<ObservableMeta> {
|
||||
return when (item) {
|
||||
is MetaItemValue -> item
|
||||
is MetaItemNode<M> -> ObservableMetaWrapper(item.node, this to name).asMetaItem()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItem(name: Name): TypedMetaItem<ObservableMeta>? = itemProvider[name]?.let {
|
||||
wrapItem(name, it)
|
||||
}
|
||||
|
||||
override val items: Map<NameToken, TypedMetaItem<ObservableMeta>>
|
||||
get() = itemProvider.items.mapValues { (token, childItem: TypedMetaItem<M>) ->
|
||||
wrapItem(token.asName(), childItem)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this meta is observable return itself. Otherwise return an observable wrapper. The changes of initial meta are
|
||||
* reflected on wrapper but are **NOT** observed.
|
||||
*/
|
||||
public fun <M : MutableMeta<M>> M.asObservable(): ObservableMeta =
|
||||
(this as? ObservableMeta) ?: ObservableMetaWrapper(this)
|
||||
|
||||
@JsName("buildObservableMeta")
|
||||
public fun ObservableMeta(): ObservableMeta = MetaBuilder().asObservable()
|
||||
|
||||
/**
|
||||
* Use the value of the property in a [callBack].
|
||||
@ -40,3 +137,25 @@ public fun <O : ObservableItemProvider, T> O.useProperty(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object ObservableMetaSerializer : KSerializer<ObservableMeta> {
|
||||
public fun empty(): ObservableMeta = ObservableMeta()
|
||||
override val descriptor: SerialDescriptor get() = MetaSerializer.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): ObservableMeta {
|
||||
return MetaSerializer.deserialize(decoder).toMutableMeta().asObservable()
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: ObservableMeta) {
|
||||
MetaSerializer.serialize(encoder, value)
|
||||
}
|
||||
}
|
||||
|
||||
public operator fun ObservableMeta.get(token: NameToken): TypedMetaItem<ObservableMeta>? = items[token]
|
||||
|
||||
/**
|
||||
* Create a copy of this config, optionally applying the given [block].
|
||||
* The listeners of the original Config are not retained.
|
||||
*/
|
||||
public inline fun ObservableMeta.copy(block: ObservableMeta.() -> Unit = {}): ObservableMeta =
|
||||
asObservable().apply(block)
|
@ -13,16 +13,11 @@ import kotlin.jvm.Synchronized
|
||||
* A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [Specification].
|
||||
* Default item provider and [NodeDescriptor] are optional
|
||||
*/
|
||||
public open class Scheme() : Described, MetaRepr, ItemPropertyProvider {
|
||||
|
||||
private var items: MutableItemProvider = Config()
|
||||
|
||||
private val listeners = HashSet<ItemListener>()
|
||||
|
||||
public open class Scheme(
|
||||
private var items: ObservableItemProvider = ObservableMeta(),
|
||||
final override var descriptor: NodeDescriptor? = null,
|
||||
private var default: ItemProvider? = null
|
||||
|
||||
final override var descriptor: NodeDescriptor? = null
|
||||
|
||||
) : Described, MetaRepr, ObservableItemProvider, MutableItemProvider {
|
||||
|
||||
/**
|
||||
* Add a listener to this scheme changes. If the inner provider is observable, then listening will be delegated to it.
|
||||
@ -30,8 +25,7 @@ public open class Scheme() : Described, MetaRepr, ItemPropertyProvider {
|
||||
*/
|
||||
@Synchronized
|
||||
override fun onChange(owner: Any?, action: (Name, MetaItem?, MetaItem?) -> Unit) {
|
||||
(items as? ObservableItemProvider)?.onChange(owner, action)
|
||||
?: run { listeners.add(ItemListener(owner, action)) }
|
||||
items.onChange(owner, action)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,8 +33,7 @@ public open class Scheme() : Described, MetaRepr, ItemPropertyProvider {
|
||||
*/
|
||||
@Synchronized
|
||||
override fun removeListener(owner: Any?) {
|
||||
(items as? ObservableItemProvider)?.removeListener(owner)
|
||||
?: listeners.removeAll { it.owner === owner }
|
||||
items.removeListener(owner)
|
||||
}
|
||||
|
||||
internal fun wrap(
|
||||
@ -51,11 +44,11 @@ public open class Scheme() : Described, MetaRepr, ItemPropertyProvider {
|
||||
//use properties in the init block as default
|
||||
this.default = this.items.withDefault(default)
|
||||
//reset values, defaults are already saved
|
||||
this.items = items
|
||||
this.items = items.asObservable()
|
||||
this.descriptor = descriptor
|
||||
}
|
||||
|
||||
private fun getDefaultItem(name: Name): MetaItem? {
|
||||
protected open fun getDefaultItem(name: Name): MetaItem? {
|
||||
return default?.get(name) ?: descriptor?.get(name)?.defaultValue
|
||||
}
|
||||
|
||||
@ -79,7 +72,6 @@ public open class Scheme() : Described, MetaRepr, ItemPropertyProvider {
|
||||
val oldItem = items[name]
|
||||
if (validateItem(name, item)) {
|
||||
items[name] = item
|
||||
listeners.forEach { it.action(name, oldItem, item) }
|
||||
} else {
|
||||
error("Validation failed for property $name with value $item")
|
||||
}
|
||||
@ -105,7 +97,7 @@ public open class Scheme() : Described, MetaRepr, ItemPropertyProvider {
|
||||
}
|
||||
}
|
||||
|
||||
override fun toMeta(): Laminate = Laminate(items[Name.EMPTY].node, defaultLayer)
|
||||
override fun toMeta(): Laminate = Laminate(items.rootNode, defaultLayer)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,7 +137,7 @@ public open class SchemeSpec<out T : Scheme>(
|
||||
|
||||
override fun empty(): T = builder()
|
||||
|
||||
override fun read(items: ItemProvider): T = wrap(Config(), items, descriptor)
|
||||
override fun read(items: ItemProvider): T = wrap(ObservableMeta(), items, descriptor)
|
||||
|
||||
override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): T =
|
||||
wrap(target, defaultProvider, descriptor)
|
||||
|
@ -52,7 +52,7 @@ public sealed interface ItemDescriptor: MetaRepr {
|
||||
* The builder for [ItemDescriptor]
|
||||
*/
|
||||
@DFBuilder
|
||||
public sealed class ItemDescriptorBuilder(final override val config: Config) : Configurable, ItemDescriptor {
|
||||
public sealed class ItemDescriptorBuilder(final override val config: ObservableMeta) : Configurable, ItemDescriptor {
|
||||
|
||||
/**
|
||||
* True if same name siblings with this name are allowed
|
||||
@ -75,7 +75,7 @@ public sealed class ItemDescriptorBuilder(final override val config: Config) : C
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
override var attributes: Config? by config.node()
|
||||
override var attributes: ObservableMeta? by config.node()
|
||||
|
||||
/**
|
||||
* An index field by which this node is identified in case of same name siblings construct
|
||||
@ -94,8 +94,8 @@ public sealed class ItemDescriptorBuilder(final override val config: Config) : C
|
||||
/**
|
||||
* Configure attributes of the descriptor, creating an attributes node if needed.
|
||||
*/
|
||||
public inline fun ItemDescriptorBuilder.attributes(block: Config.() -> Unit) {
|
||||
(attributes ?: Config().also { this.attributes = it }).apply(block)
|
||||
public inline fun ItemDescriptorBuilder.attributes(block: ObservableMeta.() -> Unit) {
|
||||
(attributes ?: ObservableMeta().also { this.attributes = it }).apply(block)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,7 +41,7 @@ public sealed interface NodeDescriptor : ItemDescriptor {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public val default: Config?
|
||||
public val default: Meta?
|
||||
|
||||
/**
|
||||
* The map of children item descriptors (both nodes and values)
|
||||
@ -74,7 +74,7 @@ public sealed interface NodeDescriptor : ItemDescriptor {
|
||||
|
||||
|
||||
@DFBuilder
|
||||
public class NodeDescriptorBuilder(config: Config = Config()) : ItemDescriptorBuilder(config), NodeDescriptor {
|
||||
public class NodeDescriptorBuilder(config: ObservableMeta = ObservableMeta()) : ItemDescriptorBuilder(config), NodeDescriptor {
|
||||
init {
|
||||
config[IS_NODE_KEY] = true
|
||||
}
|
||||
@ -91,7 +91,7 @@ public class NodeDescriptorBuilder(config: Config = Config()) : ItemDescriptorBu
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
override var default: Config? by config.node()
|
||||
override var default: ObservableMeta? by config.node()
|
||||
|
||||
/**
|
||||
* The map of children item descriptors (both nodes and values)
|
||||
@ -101,9 +101,9 @@ public class NodeDescriptorBuilder(config: Config = Config()) : ItemDescriptorBu
|
||||
if (name == null) error("Child item index should not be null")
|
||||
val node = item.node ?: error("Node descriptor must be a node")
|
||||
if (node[IS_NODE_KEY].boolean == true) {
|
||||
name to NodeDescriptorBuilder(node as Config)
|
||||
name to NodeDescriptorBuilder(node as ObservableMeta)
|
||||
} else {
|
||||
name to ValueDescriptorBuilder(node as Config)
|
||||
name to ValueDescriptorBuilder(node as ObservableMeta)
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ public class NodeDescriptorBuilder(config: Config = Config()) : ItemDescriptorBu
|
||||
}.associate { (name, item) ->
|
||||
if (name == null) error("Child node index should not be null")
|
||||
val node = item.node ?: error("Node descriptor must be a node")
|
||||
name to NodeDescriptorBuilder(node as Config)
|
||||
name to NodeDescriptorBuilder(node as ObservableMeta)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,7 +129,7 @@ public class NodeDescriptorBuilder(config: Config = Config()) : ItemDescriptorBu
|
||||
}.associate { (name, item) ->
|
||||
if (name == null) error("Child value index should not be null")
|
||||
val node = item.node ?: error("Node descriptor must be a node")
|
||||
name to ValueDescriptorBuilder(node as Config)
|
||||
name to ValueDescriptorBuilder(node as ObservableMeta)
|
||||
}
|
||||
|
||||
private fun buildNode(name: Name): NodeDescriptorBuilder {
|
||||
@ -137,7 +137,7 @@ public class NodeDescriptorBuilder(config: Config = Config()) : ItemDescriptorBu
|
||||
0 -> this
|
||||
1 -> {
|
||||
val token = NameToken(ITEM_KEY.toString(), name.toString())
|
||||
val config: Config = config[token].node ?: Config().also {
|
||||
val config: ObservableMeta = config[token].node ?: ObservableMeta().also {
|
||||
it[IS_NODE_KEY] = true
|
||||
config[token] = it
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import space.kscience.dataforge.misc.DFBuilder
|
||||
import space.kscience.dataforge.values.*
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A descriptor for meta value
|
||||
*
|
||||
@ -14,7 +13,7 @@ import space.kscience.dataforge.values.*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
@DFBuilder
|
||||
public sealed interface ValueDescriptor: ItemDescriptor{
|
||||
public sealed interface ValueDescriptor : ItemDescriptor {
|
||||
|
||||
/**
|
||||
* True if the value is required
|
||||
@ -37,6 +36,7 @@ public sealed interface ValueDescriptor: ItemDescriptor{
|
||||
* @return
|
||||
*/
|
||||
public val type: List<ValueType>?
|
||||
|
||||
/**
|
||||
* Check if given value is allowed for here. The type should be allowed and
|
||||
* if it is value should be within allowed values
|
||||
@ -61,7 +61,9 @@ public sealed interface ValueDescriptor: ItemDescriptor{
|
||||
* A builder fir [ValueDescriptor]
|
||||
*/
|
||||
@DFBuilder
|
||||
public class ValueDescriptorBuilder(config: Config = Config()) : ItemDescriptorBuilder(config), ValueDescriptor {
|
||||
public class ValueDescriptorBuilder(
|
||||
config: ObservableMeta = ObservableMeta()
|
||||
) : ItemDescriptorBuilder(config), ValueDescriptor {
|
||||
|
||||
/**
|
||||
* True if the value is required
|
||||
|
@ -105,7 +105,7 @@ public value class MetaTransformation(private val transformations: Collection<Tr
|
||||
* Generate an observable configuration that contains only elements defined by transformation rules and changes with the source
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun generate(source: Config): ObservableItemProvider = Config().apply {
|
||||
public fun generate(source: ObservableMeta): ObservableItemProvider = ObservableMeta().apply {
|
||||
transformations.forEach { rule ->
|
||||
rule.selectItems(source).forEach { name ->
|
||||
rule.transformItem(name, source[name], this)
|
||||
@ -131,7 +131,7 @@ public value class MetaTransformation(private val transformations: Collection<Tr
|
||||
/**
|
||||
* Listens for changes in the source node and translates them into second node if transformation set contains a corresponding rule.
|
||||
*/
|
||||
public fun <M : MutableMeta<M>> bind(source: Config, target: M) {
|
||||
public fun <M : MutableMeta<M>> bind(source: ObservableMeta, target: M) {
|
||||
source.onChange(target) { name, _, newItem ->
|
||||
transformations.forEach { t ->
|
||||
if (t.matches(name, newItem)) {
|
||||
|
@ -6,7 +6,7 @@ import kotlin.test.assertEquals
|
||||
class ConfigTest {
|
||||
@Test
|
||||
fun testIndexedWrite(){
|
||||
val config = Config()
|
||||
val config = MetaBuilder()
|
||||
config["a[1].b"] = 1
|
||||
assertEquals(null, config["a.b"].int)
|
||||
assertEquals(1, config["a[1].b"].int)
|
||||
|
@ -14,7 +14,7 @@ class MutableMetaTest{
|
||||
"b" put 22
|
||||
"c" put "StringValue"
|
||||
}
|
||||
}.asConfig()
|
||||
}.asObservable()
|
||||
|
||||
meta.remove("aNode.c")
|
||||
assertEquals(meta["aNode.c"], null)
|
||||
|
@ -8,7 +8,7 @@ import kotlin.test.assertEquals
|
||||
class SchemeTest {
|
||||
@Test
|
||||
fun testSchemeWrappingBeforeEdit(){
|
||||
val config = Config()
|
||||
val config = MetaBuilder()
|
||||
val scheme = TestScheme.wrap(config)
|
||||
scheme.a = 29
|
||||
assertEquals(29, config["a"].int)
|
||||
@ -18,7 +18,7 @@ class SchemeTest {
|
||||
fun testSchemeWrappingAfterEdit(){
|
||||
val scheme = TestScheme.empty()
|
||||
scheme.a = 29
|
||||
val config = Config()
|
||||
val config = MetaBuilder()
|
||||
scheme.retarget(config)
|
||||
assertEquals(29, scheme.a)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ internal class TestScheme : Scheme() {
|
||||
override fun empty(): TestScheme = TestScheme()
|
||||
|
||||
override fun read(items: ItemProvider): TestScheme =
|
||||
wrap(Config(), items)
|
||||
wrap(MetaBuilder(), items)
|
||||
|
||||
override fun write(target: MutableItemProvider, defaultProvider: ItemProvider): TestScheme =
|
||||
wrap(target, defaultProvider)
|
||||
@ -58,7 +58,7 @@ class SpecificationTest {
|
||||
|
||||
@Test
|
||||
fun testChildModification() {
|
||||
val config = Config()
|
||||
val config = MetaBuilder()
|
||||
val child = config.getChild("child")
|
||||
val scheme = TestScheme.write(child)
|
||||
scheme.a = 22
|
||||
@ -69,7 +69,7 @@ class SpecificationTest {
|
||||
|
||||
@Test
|
||||
fun testChildUpdate() {
|
||||
val config = Config()
|
||||
val config = MetaBuilder()
|
||||
val child = config.getChild("child")
|
||||
child.update(TestScheme) {
|
||||
a = 22
|
||||
|
@ -51,7 +51,7 @@ public object Builders {
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
public fun buildWorkspace(file: File): Workspace = buildWorkspace(file.toScriptSource())
|
||||
public fun buildWorkspace(scriptFile: File): Workspace = buildWorkspace(scriptFile.toScriptSource())
|
||||
|
||||
public fun buildWorkspace(string: String): Workspace = buildWorkspace(string.toScriptSource())
|
||||
public fun buildWorkspace(scriptString: String): Workspace = buildWorkspace(scriptString.toScriptSource())
|
||||
}
|
@ -3,7 +3,7 @@ package space.kscience.dataforge.scripting
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.dataforge.workspace.WorkspaceBuilder
|
||||
import space.kscience.dataforge.workspace.Workspace
|
||||
import space.kscience.dataforge.workspace.target
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
@ -12,7 +12,7 @@ import kotlin.test.assertEquals
|
||||
class BuildersKtTest {
|
||||
@Test
|
||||
fun checkBuilder() {
|
||||
val workspace = WorkspaceBuilder(Global).apply {
|
||||
Workspace(Global){
|
||||
println("I am working")
|
||||
|
||||
context { name("test") }
|
||||
|
@ -91,6 +91,5 @@ public inline fun WorkspaceBuilder.target(name: String, metaBuilder: MetaBuilder
|
||||
target(name, Meta(metaBuilder))
|
||||
|
||||
@DFBuilder
|
||||
public fun Workspace(parentContext: Context = Global, builder: WorkspaceBuilder.() -> Unit): Workspace {
|
||||
return WorkspaceBuilder(parentContext).apply(builder).build()
|
||||
}
|
||||
public fun Workspace(parentContext: Context = Global, builder: WorkspaceBuilder.() -> Unit): Workspace =
|
||||
WorkspaceBuilder(parentContext).apply(builder).build()
|
Loading…
Reference in New Issue
Block a user