diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e195e9..980aad47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,16 @@ ### Added - LogManager plugin - dataforge-context API dependency on SLF4j +- Context `withEnv` and `fetch` methods to manipulate plugins without changing plugins after creation. ### Changed - Kotlin-logging moved from common to JVM and JS. Replaced by console for native. - Package changed to `space.kscience` +- Scheme made observable +- Global context is a variable (the singleton is hidden and will be deprecated in future) ### Deprecated +- Direct use of PluginManager ### Removed - Common dependency on Kotlin-logging diff --git a/dataforge-context/api/dataforge-context.api b/dataforge-context/api/dataforge-context.api index 5674e74b..1b1a849f 100644 --- a/dataforge-context/api/dataforge-context.api +++ b/dataforge-context/api/dataforge-context.api @@ -38,6 +38,7 @@ public final class space/kscience/dataforge/context/ClassLoaderPluginKt { public class space/kscience/dataforge/context/Context : kotlinx/coroutines/CoroutineScope, space/kscience/dataforge/meta/MetaRepr, space/kscience/dataforge/misc/Named, space/kscience/dataforge/provider/Provider { public static final field Companion Lspace/kscience/dataforge/context/Context$Companion; public static final field PROPERTY_TARGET Ljava/lang/String; + public final fun buildContext (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context; public fun close ()V public fun content (Ljava/lang/String;)Ljava/util/Map; public final fun content (Ljava/lang/String;Z)Ljava/util/Map; @@ -47,6 +48,7 @@ public class space/kscience/dataforge/context/Context : kotlinx/coroutines/Corou public final fun getName ()Lspace/kscience/dataforge/names/Name; public final fun getParent ()Lspace/kscience/dataforge/context/Context; public final fun getPlugins ()Lspace/kscience/dataforge/context/PluginManager; + public final fun getProperties ()Lspace/kscience/dataforge/meta/Laminate; public fun toMeta ()Lspace/kscience/dataforge/meta/Meta; } @@ -58,24 +60,35 @@ public abstract interface class space/kscience/dataforge/context/ContextAware { } public final class space/kscience/dataforge/context/ContextBuilder { - public fun ()V - public fun (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;)V - public synthetic fun (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun build ()Lspace/kscience/dataforge/context/Context; - public final fun getName ()Ljava/lang/String; + public final fun getName ()Lspace/kscience/dataforge/names/Name; + public final fun name (Ljava/lang/String;)V 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/PluginFactory;Lkotlin/jvm/functions/Function1;)V + public final fun plugin (Lspace/kscience/dataforge/context/PluginFactory;Lspace/kscience/dataforge/meta/Meta;)V public final fun plugin (Lspace/kscience/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;)V public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Lspace/kscience/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Lspace/kscience/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public final fun properties (Lkotlin/jvm/functions/Function1;)V - public final fun setName (Ljava/lang/String;)V + public final fun setName (Lspace/kscience/dataforge/names/Name;)V } -public final class space/kscience/dataforge/context/ContextKt { - public static final fun Context (Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context; - public static synthetic fun Context$default (Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/context/Context; +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 + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; + public fun log (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V +} + +public final class space/kscience/dataforge/context/DefaultLogManager$Companion : space/kscience/dataforge/context/PluginFactory { + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; + public fun getType ()Lkotlin/reflect/KClass; + public synthetic fun invoke (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/context/Context;)Ljava/lang/Object; + public fun invoke (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/context/Context;)Lspace/kscience/dataforge/context/DefaultLogManager; } public abstract interface class space/kscience/dataforge/context/Factory { @@ -88,12 +101,13 @@ public final class space/kscience/dataforge/context/Factory$DefaultImpls { public final class space/kscience/dataforge/context/Global : space/kscience/dataforge/context/Context { public static final field INSTANCE Lspace/kscience/dataforge/context/Global; - public fun close ()V - public final fun context (Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context; - public static synthetic fun context$default (Lspace/kscience/dataforge/context/Global;Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/context/Context; - public final fun getContext (Ljava/lang/String;)Lspace/kscience/dataforge/context/Context; public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; - public final fun getLogger ()Lspace/kscience/dataforge/context/LogManager; + public final fun getDefaultLogger ()Lspace/kscience/dataforge/context/LogManager; +} + +public final class space/kscience/dataforge/context/GlobalKt { + public static final fun Context (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context; + public static synthetic fun Context$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/context/Context; } public abstract interface class space/kscience/dataforge/context/LogManager : space/kscience/dataforge/context/Logable, space/kscience/dataforge/context/Plugin { @@ -128,7 +142,7 @@ public final class space/kscience/dataforge/context/LogManagerKt { public static final fun error (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)V public static synthetic fun error$default (Lspace/kscience/dataforge/context/Logable;Ljava/lang/Throwable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static synthetic fun error$default (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V - public static final fun getLogger (Lspace/kscience/dataforge/context/Context;)Lspace/kscience/dataforge/context/LogManager; + public static final fun getLogger (Lspace/kscience/dataforge/context/Context;)Lspace/kscience/dataforge/context/Logable; public static final fun getLogger (Lspace/kscience/dataforge/context/ContextAware;)Lspace/kscience/dataforge/context/Logable; public static final fun info (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)V public static synthetic fun info$default (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V 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 081d44d8..56a10925 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 @@ -11,6 +11,7 @@ import space.kscience.dataforge.misc.Named import space.kscience.dataforge.names.Name import space.kscience.dataforge.provider.Provider import kotlin.coroutines.CoroutineContext +import kotlin.jvm.Synchronized /** * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top. @@ -31,12 +32,13 @@ public open class Context internal constructor( /** * Context properties. Working as substitute for environment variables */ - private val properties: Laminate = if (parent == null) { + public val properties: Laminate = if (parent == null) { Laminate(meta) } else { Laminate(meta, parent.properties) } + /** * A [PluginManager] for current context */ @@ -68,10 +70,24 @@ public open class Context internal constructor( } } + private val childrenContexts = HashMap() + /** - * Detach all plugins and terminate context + * Build and register a child context + */ + @Synchronized + public fun buildContext(block: ContextBuilder.() -> Unit): Context{ + val newContext = ContextBuilder(this).apply(block).build() + childrenContexts[newContext.name] = newContext + return newContext + } + + /** + * Detach all plugins, and close child contexts */ public open fun close() { + //recursively closed child context + childrenContexts.forEach { it.value.close() } //detach all plugins plugins.forEach { it.detach() } } @@ -87,9 +103,6 @@ public open class Context internal constructor( } } -public fun Context(name: String, parent: Context = Global, block: ContextBuilder.() -> Unit = {}): Context = - Global.context(name, parent, block) - /** * The interface for something that encapsulated in context * 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 f367b5d4..5f9b86b8 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 @@ -3,8 +3,11 @@ package space.kscience.dataforge.context import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaBuilder 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.names.Name +import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.toName import kotlin.collections.component1 import kotlin.collections.component2 @@ -14,14 +17,22 @@ import kotlin.collections.set * A convenience builder for context */ @DFBuilder -public class ContextBuilder(private val parent: Context = Global, public var name: String = "@anonymous") { - private val factories = HashMap, Meta>() - private var meta = MetaBuilder() +public class ContextBuilder internal constructor( + private val parent: Context, + public var name: Name? = null, + meta: Meta = Meta.EMPTY, +) { + internal val factories = HashMap, Meta>() + internal var meta = meta.toMutableMeta() public fun properties(action: MetaBuilder.() -> Unit) { meta.action() } + public fun name(string: String){ + this.name = string.toName() + } + @OptIn(DFExperimental::class) private fun findPluginFactory(tag: PluginTag): PluginFactory<*> = parent.gatherInSequence>(PluginFactory.TYPE).values @@ -32,6 +43,10 @@ public class ContextBuilder(private val parent: Context = Global, public var nam factories[factory] = Meta(metaBuilder) } + public fun plugin(factory: PluginFactory<*>, meta: Meta){ + factories[factory] = meta + } + public fun plugin(factory: PluginFactory<*>, metaBuilder: MetaBuilder.() -> Unit = {}) { factories[factory] = Meta(metaBuilder) } @@ -41,10 +56,29 @@ public class ContextBuilder(private val parent: Context = Global, public var nam } public fun build(): Context { - return Context(name.toName(), parent, meta.seal()).apply { + val contextName = name ?: "@auto[${hashCode().toUInt().toString(16)}]".toName() + return Context(contextName, parent, meta.seal()).apply { factories.forEach { (factory, meta) -> plugins.load(factory, meta) } } } +} + +/** + * Check if current context contains all plugins required by the builder and return it it does or forks to a new context + * if it does not. + */ +public fun Context.withEnv(block: ContextBuilder.() -> Unit): Context { + + fun Context.contains(factory: PluginFactory<*>, meta: Meta): Boolean { + val loaded = plugins[factory.tag] ?: return false + return loaded.meta == meta + } + + val builder = ContextBuilder(this, name + "env", properties).apply(block) + val requiresFork = builder.factories.any { (factory, meta) -> + !contains(factory, meta) + } || ((properties as Meta) == builder.meta) + return if (requiresFork) builder.build() else this } \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Global.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Global.kt index 645b0805..45ff3c60 100644 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Global.kt +++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Global.kt @@ -1,7 +1,7 @@ package space.kscience.dataforge.context import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.Job import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.asName import kotlin.coroutines.CoroutineContext @@ -13,43 +13,10 @@ internal expect val globalLoggerFactory: PluginFactory * A global root context. Closing [Global] terminates the framework. */ @ThreadLocal -public object Global : Context("GLOBAL".asName(), null, Meta.EMPTY) { +private object GlobalContext : Context("GLOBAL".asName(), null, Meta.EMPTY) { + override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + Job() +} - override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + SupervisorJob() +public val Global: Context get() = GlobalContext - /** - * The default logging manager - */ - public val logger: LogManager by lazy { globalLoggerFactory.invoke(context = this).apply { attach(this@Global) } } - - /** - * Closing all contexts - * - * @throws Exception - */ - override fun close() { - logger.info { "Shutting down GLOBAL" } - for (ctx in contextRegistry.values) { - ctx.close() - } - super.close() - } - - private val contextRegistry = HashMap() - - /** - * Get previously built context - * - * @param name - * @return - */ - public fun getContext(name: String): Context? { - return contextRegistry[name] - } - - public fun context(name: String, parent: Context = this, block: ContextBuilder.() -> Unit = {}): Context = - ContextBuilder(parent, name).apply(block).build().also { - contextRegistry[name] = it - } - -} \ No newline at end of file +public fun Context(block: ContextBuilder.() -> Unit = {}): Context = Global.buildContext(block) \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/LogManager.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/LogManager.kt index 2deda918..10f9c7af 100644 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/LogManager.kt +++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/LogManager.kt @@ -1,15 +1,16 @@ package space.kscience.dataforge.context +import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.misc.Named import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.plus +import kotlin.reflect.KClass -public interface Logable { +public fun interface Logable { public fun log(name: Name, tag: String, body: () -> String) } public interface LogManager : Plugin, Logable { - public companion object { public const val TRACE: String = "TRACE" public const val INFO: String = "INFO" @@ -41,11 +42,30 @@ public fun Logable.error(throwable: Throwable?, name: Name = Name.EMPTY, body: ( } } + +public class DefaultLogManager : AbstractPlugin(), LogManager { + + override fun log(name: Name, tag: String, body: () -> String) { + val message: String = body.safe + println("[${context.name}] $name: $message") + } + + override val tag: PluginTag get() = Companion.tag + + public companion object : PluginFactory { + override fun invoke(meta: Meta, context: Context): DefaultLogManager = DefaultLogManager() + + override val tag: PluginTag = PluginTag(group = PluginTag.DATAFORGE_GROUP, name = "log.default") + override val type: KClass = DefaultLogManager::class + } +} + /** * Context log manager inherited from parent */ -public val Context.logger: LogManager - get() = plugins.find(inherit = true) { it is LogManager } as? LogManager ?: Global.logger +public val Context.logger: Logable + get() = plugins.find(inherit = true) { it is LogManager } as? LogManager + ?: globalLoggerFactory(context = Global).apply { attach(Global) } /** * The named proxy logger for a context member 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 5929515e..d0f03172 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 @@ -63,5 +63,4 @@ public interface Plugin : Named, ContextAware, Provider, MetaRepr { public companion object { public const val TARGET: String = "plugin" } - } \ No newline at end of file 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 3e045bb5..540b200d 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 @@ -13,8 +13,6 @@ import kotlin.reflect.KClass */ public class PluginManager(override val context: Context) : ContextAware, Iterable { - //TODO refactor to read-only container - /** * A set of loaded plugins */ @@ -85,6 +83,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab * @param plugin * @return */ + @Deprecated("Use immutable contexts instead") public 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}") @@ -93,7 +92,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab fetch(factory, meta, true) } - Global.logger.info { "Loading plugin ${plugin.name} into ${context.name}" } + logger.info { "Loading plugin ${plugin.name} into ${context.name}" } plugin.attach(context) plugins.add(plugin) return plugin @@ -103,15 +102,18 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab /** * Load a plugin using its factory */ + @Deprecated("Use immutable contexts instead") public fun load(factory: PluginFactory, meta: Meta = Meta.EMPTY): T = load(factory(meta, context)) + @Deprecated("Use immutable contexts instead") public fun load(factory: PluginFactory, metaBuilder: MetaBuilder.() -> Unit): T = load(factory, Meta(metaBuilder)) /** * 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}" } @@ -123,6 +125,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab /** * Get an existing plugin with given meta or load new one using provided factory */ + @Deprecated("Use immutable contexts instead") public fun fetch(factory: PluginFactory, meta: Meta = Meta.EMPTY, recursive: Boolean = true): T { val loaded = get(factory.type, factory.tag, recursive) return when { @@ -132,6 +135,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab } } + @Deprecated("Use immutable contexts instead") public fun fetch( factory: PluginFactory, recursive: Boolean = true, @@ -140,4 +144,18 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab override fun iterator(): Iterator = plugins.iterator() +} + +/** + * Fetch a plugin with given meta from the context. If the plugin (with given meta) is already registered, it is returned. + * Otherwise, new child context with the plugin is created. In the later case the context could be retrieved from the plugin. + */ +public inline fun Context.fetch(factory: PluginFactory, meta: Meta = Meta.EMPTY): T { + val existing = plugins[factory] + return if (existing != null && existing.meta == meta) existing + else { + buildContext { + plugin(factory, meta) + }.plugins[factory]!! + } } \ No newline at end of file diff --git a/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/context/ContextTest.kt b/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/context/ContextTest.kt index 4bc937aa..262d49f1 100644 --- a/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/context/ContextTest.kt +++ b/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/context/ContextTest.kt @@ -22,7 +22,9 @@ class ContextTest { @Test fun testPluginManager() { - val context = Global.context("test") + val context = Global.buildContext{ + name("test") + } context.plugins.load(DummyPlugin()) //Global.plugins.load(DummyPlugin()) val members = context.gather("test") diff --git a/dataforge-context/src/nativeMain/kotlin/space/kscience/dataforge/context/loggingNative.kt b/dataforge-context/src/nativeMain/kotlin/space/kscience/dataforge/context/loggingNative.kt index f890b238..62512132 100644 --- a/dataforge-context/src/nativeMain/kotlin/space/kscience/dataforge/context/loggingNative.kt +++ b/dataforge-context/src/nativeMain/kotlin/space/kscience/dataforge/context/loggingNative.kt @@ -1,25 +1,4 @@ package space.kscience.dataforge.context -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.names.Name -import kotlin.reflect.KClass - -public class NativeLogManager : AbstractPlugin(), LogManager { - - override fun log(name: Name, tag: String, body: () -> String) { - val message: String = body.safe - println("[${context.name}] $name: $message") - } - - override val tag: PluginTag get() = Companion.tag - - public companion object : PluginFactory { - override fun invoke(meta: Meta, context: Context): NativeLogManager = NativeLogManager() - - override val tag: PluginTag = PluginTag(group = PluginTag.DATAFORGE_GROUP, name = "log.native") - override val type: KClass = NativeLogManager::class - } -} - -internal actual val globalLoggerFactory: PluginFactory = NativeLogManager +internal actual val globalLoggerFactory: PluginFactory = DefaultLogManager 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 4a723967..91442603 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 @@ -9,6 +9,7 @@ import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.toName +import kotlin.native.concurrent.ThreadLocal import kotlin.reflect.KClass public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { @@ -62,7 +63,8 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { public companion object : PluginFactory { public val defaultMetaFormats: List = listOf(JsonMetaFormat, BinaryMetaFormat) - public val defaultEnvelopeFormats: List = listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat) + public val defaultEnvelopeFormats: List = + listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat) override val tag: PluginTag = PluginTag("io", group = PluginTag.DATAFORGE_GROUP) @@ -71,4 +73,10 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { } } -public val Context.io: IOPlugin get() = plugins.fetch(IOPlugin) \ No newline at end of file +@ThreadLocal +internal val ioContext = Global.withEnv { + name("IO") + plugin(IOPlugin) +} + +public val Context.io: IOPlugin get() = (if (this == Global) ioContext else this).fetch(IOPlugin) \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/TaggedEnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/TaggedEnvelopeFormat.kt index ad0d1aa4..dfc5f233 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/TaggedEnvelopeFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/TaggedEnvelopeFormat.kt @@ -161,7 +161,7 @@ public class TaggedEnvelopeFormat( } } - private val default by lazy { invoke() } + private val default by lazy { invoke(context = ioContext) } override fun readPartial(input: Input): PartialEnvelope = default.run { readPartial(input) } diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/TaglessEnvelopeFormat.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/TaglessEnvelopeFormat.kt index 30dce96b..e1a55607 100644 --- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/TaglessEnvelopeFormat.kt +++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/TaglessEnvelopeFormat.kt @@ -197,7 +197,7 @@ public class TaglessEnvelopeFormat( return TaglessEnvelopeFormat(context.io, meta) } - private val default by lazy { invoke() } + private val default by lazy { invoke(context = ioContext) } override fun readPartial(input: Input): PartialEnvelope = default.run { readPartial(input) } diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api index f5e908d2..abec55f4 100644 --- a/dataforge-meta/api/dataforge-meta.api +++ b/dataforge-meta/api/dataforge-meta.api @@ -193,6 +193,7 @@ public abstract interface class space/kscience/dataforge/meta/Meta : space/kscie public final class space/kscience/dataforge/meta/Meta$Companion { public static final field TYPE Ljava/lang/String; public static final field VALUE_KEY Ljava/lang/String; + public final fun equals (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;)Z public final fun getEMPTY ()Lspace/kscience/dataforge/meta/Meta; } 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 46ff1d9b..12a03a62 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 @@ -54,6 +54,8 @@ public interface Meta : MetaRepr, ItemProvider { */ public const val VALUE_KEY: String = "@value" + public fun equals(meta1: Meta, meta2: Meta): Boolean = meta1.items == meta2.items + public val EMPTY: Meta = object : MetaBase() { override val items: Map = emptyMap() } diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/TypedMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/TypedMeta.kt index 9d064e11..cd1c3c47 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/TypedMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/TypedMeta.kt @@ -32,7 +32,7 @@ public operator fun > M.get(key: NameToken): TypedMetaItem? public abstract class MetaBase : Meta { override fun equals(other: Any?): Boolean = if (other is Meta) { - this.items == other.items + Meta.equals(this, other) } else { false } diff --git a/dataforge-scripting/src/jvmTest/kotlin/space/kscience/dataforge/scripting/BuildersKtTest.kt b/dataforge-scripting/src/jvmTest/kotlin/space/kscience/dataforge/scripting/BuildersKtTest.kt index 8059b070..c0f557e4 100644 --- a/dataforge-scripting/src/jvmTest/kotlin/space/kscience/dataforge/scripting/BuildersKtTest.kt +++ b/dataforge-scripting/src/jvmTest/kotlin/space/kscience/dataforge/scripting/BuildersKtTest.kt @@ -4,7 +4,6 @@ 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.target import kotlin.test.Test import kotlin.test.assertEquals @@ -12,13 +11,13 @@ import kotlin.test.assertEquals class BuildersKtTest { @Test - fun checkBuilder(){ + fun checkBuilder() { val workspace = WorkspaceBuilder(Global).apply { println("I am working") - context("test") + context { name("test") } - target("testTarget"){ + target("testTarget") { "a" put 12 } } @@ -29,7 +28,7 @@ class BuildersKtTest { val script = """ println("I am working") - context("test") + context{ name("test") } target("testTarget"){ "a" put 12 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 5d475cd8..1a6c240f 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 @@ -50,8 +50,8 @@ public class WorkspaceBuilder(private val parentContext: Context = Global) : Tas /** * Define a context for the workspace */ - public fun context(name: String = "workspace", block: ContextBuilder.() -> Unit = {}) { - this.context = ContextBuilder(parentContext, name).apply(block).build() + public fun context(block: ContextBuilder.() -> Unit = {}) { + this.context = parentContext.buildContext(block) } /**