From b229de3eb7f711120b14735c41c3e54ee7aa899b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 13 Sep 2020 19:35:58 +0300 Subject: [PATCH] Context content resolution refactor. Explicit API mode. --- CHANGELOG.md | 5 + dataforge-context/build.gradle.kts | 6 +- .../kotlin/hep/dataforge/context/Context.kt | 79 +++++++------- .../hep/dataforge/context/ContextBuilder.kt | 13 ++- .../kotlin/hep/dataforge/context/Global.kt | 3 +- .../kotlin/hep/dataforge/context/Plugin.kt | 13 +-- .../hep/dataforge/context/PluginManager.kt | 28 +++-- .../kotlin/hep/dataforge/context/resolve.kt | 96 +++++++++++++++++ .../kotlin/hep/dataforge/provider/Path.kt | 2 +- .../kotlin/hep/dataforge/provider/Provider.kt | 19 ++-- .../hep/dataforge/context/ContextTest.kt | 4 +- .../kotlin/hep/dataforge/provider/Types.kt | 44 -------- .../kotlin/hep/dataforge/provider/dfType.kt | 36 +++++++ .../kotlin/hep/dataforge/data/Data.kt | 35 +++--- .../kotlin/hep/dataforge/data/DataFilter.kt | 12 +-- .../kotlin/hep/dataforge/data/DataNode.kt | 102 +++++++++--------- .../kotlin/hep/dataforge/data/Goal.kt | 34 +++--- .../kotlin/hep/dataforge/data/GroupRule.kt | 10 +- .../kotlin/hep/dataforge/data/MapAction.kt | 17 ++- .../kotlin/hep/dataforge/data/ReduceAction.kt | 26 ++--- .../kotlin/hep/dataforge/data/SplitAction.kt | 16 +-- .../kotlin/hep/dataforge/data/dataCast.kt | 16 +-- .../dataforge/data/TypeFilteredDataNode.kt | 2 +- .../kotlin/hep/dataforge/data/dataJVM.kt | 4 +- .../kotlin/hep/dataforge/io/IOPlugin.kt | 10 +- .../kotlin/hep/dataforge/meta/Laminate.kt | 5 +- .../kotlin/hep/dataforge/values/Value.kt | 2 +- .../hep/dataforge/workspace/Workspace.kt | 2 +- .../dataforge/workspace/WorkspacePlugin.kt | 2 +- settings.gradle.kts | 4 +- 30 files changed, 372 insertions(+), 275 deletions(-) create mode 100644 dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt delete mode 100644 dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt create mode 100644 dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index b08d3d08..35d67eee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,20 @@ ### Added ### Changed +- Context content resolution refactor +- Kotlin 1.4.10 (build tools 0.6.0) - Empty query in Name is null instead of "" - Provider provides an empty map instead of error by default +- Hidden delegates hierarchy in favor of stdlib properties ### Deprecated +- Context activation API ### Removed - Functional server prototype ### Fixed - Global context CoroutineScope resolution +- Library mode compliance ### Security diff --git a/dataforge-context/build.gradle.kts b/dataforge-context/build.gradle.kts index 3ef9f80f..c8e3e588 100644 --- a/dataforge-context/build.gradle.kts +++ b/dataforge-context/build.gradle.kts @@ -10,16 +10,12 @@ kscience { useCoroutines() } -repositories { - maven("https://maven.pkg.github.com/altavir/kotlin-logging") -} - kotlin { sourceSets { val commonMain by getting { dependencies { api(project(":dataforge-meta")) - api("io.github.microutils:kotlin-logging:1.9.0-dev-npm") + api("io.github.microutils:kotlin-logging:1.9.0-dev-npm-2") } } val jvmMain by getting { diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt index f9f07aef..640c7b5a 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Context.kt @@ -1,18 +1,18 @@ package hep.dataforge.context -import hep.dataforge.meta.* +import hep.dataforge.meta.Laminate +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaRepr +import hep.dataforge.meta.sequence import hep.dataforge.names.Name import hep.dataforge.names.plus import hep.dataforge.provider.Provider -import hep.dataforge.provider.top -import hep.dataforge.values.Value import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import mu.KLogger import mu.KotlinLogging import kotlin.coroutines.CoroutineContext -import kotlin.jvm.JvmName /** * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top. @@ -23,23 +23,20 @@ import kotlin.jvm.JvmName * different plugins with the same interface in different contexts in the hierarchy. The usual behaviour is to use nearest one, but it could * be overridden by plugin implementation. * - * Since plugins could contain mutable state, context has two states: active and inactive. No changes are allowed to active context. - * @author Alexander Nozik */ public open class Context( final override val name: Name, - public val parent: Context? = Global, + public val parent: Context?, + meta: Meta, ) : Named, MetaRepr, Provider, CoroutineScope { - private val config = Config() - /** * Context properties. Working as substitute for environment variables */ - private val properties: Meta = if (parent == null) { - config + private val properties: Laminate = if (parent == null) { + Laminate(meta) } else { - Laminate(config, parent.properties) + Laminate(meta, parent.properties) } /** @@ -52,26 +49,19 @@ public open class Context( */ public val plugins: PluginManager by lazy { PluginManager(this) } + @Deprecated("To be removed in favor of immutable plugins") private val activators = HashSet() /** * Defines if context is used in any kind of active computations. Active context properties and plugins could not be changed */ + @Deprecated("To be removed in favor of immutable plugins") public val isActive: Boolean = activators.isNotEmpty() - override val defaultTarget: String get() = Plugin.PLUGIN_TARGET - - override fun provideTop(target: String): Map { - return when (target) { - Value.TYPE -> properties.sequence().toMap() - Plugin.PLUGIN_TARGET -> plugins.sequence(true).associateBy { it.name } - else -> emptyMap() - } - } - /** * Mark context as active and used by [activator] */ + @Deprecated("To be removed in favor of immutable plugins") public fun activate(activator: Any) { activators.add(activator) } @@ -79,19 +69,32 @@ public open class Context( /** * Mark context unused by [activator] */ + @Deprecated("To be removed in favor of immutable plugins") public fun deactivate(activator: Any) { activators.remove(activator) } - /** - * Change the properties of the context. If active, throw an exception - */ - public fun configure(action: Config.() -> Unit) { - if (isActive) error("Can't configure active context") - config.action() + override val defaultTarget: String get() = Plugin.TARGET + + public fun content(target: String, inherit: Boolean): Map { + return if (inherit) { + when (target) { + PROPERTY_TARGET -> properties.sequence().toMap() + Plugin.TARGET -> plugins.list(true).associateBy { it.name } + else -> emptyMap() + } + } else { + when (target) { + PROPERTY_TARGET -> properties.layers.firstOrNull()?.sequence()?.toMap() ?: emptyMap() + Plugin.TARGET -> plugins.list(false).associateBy { it.name } + else -> emptyMap() + } + } } - open override val coroutineContext: CoroutineContext by lazy { + override fun content(target: String): Map = content(target,true) + + override val coroutineContext: CoroutineContext by lazy { (parent ?: Global).coroutineContext.let { parenContext -> parenContext + SupervisorJob(parenContext[Job]) } @@ -101,6 +104,7 @@ public open class Context( * Detach all plugins and terminate context */ public open fun close() { + @Suppress("DEPRECATION") if (isActive) error("Can't close active context") //detach all plugins plugins.forEach { it.detach() } @@ -108,22 +112,15 @@ public open class Context( override fun toMeta(): Meta = Meta { "parent" to parent?.name - "properties" put properties.seal() + "properties" put properties.layers.firstOrNull() "plugins" put plugins.map { it.toMeta() } } + + public companion object { + public const val PROPERTY_TARGET: String = "context.property" + } } -/** - * A map of all objects provided by plugins with given target and type - */ -@JvmName("typedContent") -public inline fun Context.resolve(target: String): Map = plugins.flatMap { plugin -> - plugin.top(target).entries.map { (plugin.name + it.key) to it.value } -}.associate { it } - - -public fun Context.resolve(target: String): Map = resolve(target) - /** * The interface for something that encapsulated in context * diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt index ecb6726e..75bd6c5d 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/ContextBuilder.kt @@ -1,8 +1,6 @@ package hep.dataforge.context -import hep.dataforge.meta.DFBuilder -import hep.dataforge.meta.Meta -import hep.dataforge.meta.MetaBuilder +import hep.dataforge.meta.* import hep.dataforge.names.toName /** @@ -21,10 +19,11 @@ public class ContextBuilder(private val parent: Context = Global, public var nam plugins.add(plugin) } - public fun plugin(tag: PluginTag, action: MetaBuilder.() -> Unit = {}) { - val factory = parent.resolve>(PluginFactory.TYPE).values + @OptIn(DFExperimental::class) + public fun plugin(tag: PluginTag, metaBuilder: MetaBuilder.() -> Unit = {}) { + val factory = parent.gatherInSequence>(PluginFactory.TYPE).values .find { it.tag.matches(tag) } ?: error("Can't resolve plugin factory for $tag") - val plugin = factory.invoke(Meta(action), parent) + val plugin = factory.invoke(Meta(metaBuilder), parent) plugins.add(plugin) } @@ -37,7 +36,7 @@ public class ContextBuilder(private val parent: Context = Global, public var nam } public fun build(): Context { - return Context(name.toName(), parent).apply { + return Context(name.toName(), parent, meta.seal()).apply { this@ContextBuilder.plugins.forEach { plugins.load(it) } diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Global.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Global.kt index d7a52c46..4666fb78 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Global.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Global.kt @@ -1,5 +1,6 @@ package hep.dataforge.context +import hep.dataforge.meta.Meta import hep.dataforge.names.asName import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.SupervisorJob @@ -8,7 +9,7 @@ import kotlin.coroutines.CoroutineContext /** * A global root context. Closing [Global] terminates the framework. */ -public object Global : Context("GLOBAL".asName(), null) { +public object Global : Context("GLOBAL".asName(), null, Meta.EMPTY) { override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + SupervisorJob() diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt index b8bc6601..ff0e3a0c 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/Plugin.kt @@ -1,27 +1,23 @@ package hep.dataforge.context +import hep.dataforge.context.Plugin.Companion.TARGET import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaRepr import hep.dataforge.names.Name import hep.dataforge.names.toName import hep.dataforge.provider.Provider +import hep.dataforge.provider.Type /** * The interface to define a Context plugin. A plugin stores all runtime features of a context. * The plugin is by default configurable and a Provider (both features could be ignored). * The plugin must in most cases have an empty constructor in order to be able to load it from library. * - * * The plugin lifecycle is the following: * - * * create - configure - attach - detach - destroy - * - * - * Configuration of attached plugin is possible for a context which is not in a runtime mode, but it is not recommended. - * - * @author Alexander Nozik */ +@Type(TARGET) public interface Plugin : Named, ContextAware, Provider, MetaRepr { /** @@ -64,8 +60,7 @@ public interface Plugin : Named, ContextAware, Provider, MetaRepr { } public companion object { - - public const val PLUGIN_TARGET = "plugin" + public const val TARGET: String = "plugin" } } \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt index dadb333e..cdae4f53 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/PluginManager.kt @@ -2,14 +2,16 @@ package hep.dataforge.context import hep.dataforge.meta.Meta import hep.dataforge.meta.MetaBuilder +import hep.dataforge.provider.Type import kotlin.reflect.KClass +@Type(PluginFactory.TYPE) public interface PluginFactory : Factory { public val tag: PluginTag public val type: KClass - public companion object{ + public companion object { public const val TYPE: String = "pluginFactory" } } @@ -23,6 +25,8 @@ public interface PluginFactory : Factory { */ public class PluginManager(override val context: Context) : ContextAware, Iterable { + //TODO refactor to read-only container + /** * A set of loaded plugins */ @@ -33,21 +37,24 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab */ private val parent: PluginManager? = context.parent?.plugins - - public fun sequence(recursive: Boolean): Sequence { - return if (recursive && parent != null) { - plugins.asSequence() + parent.sequence(true) + /** + * List plugins stored in this [PluginManager]. If [inherit] is true, include parent plugins as well + */ + public fun list(inherit: Boolean): Collection { + return if (inherit && parent != null) { + plugins + parent.list(true) } else { - plugins.asSequence() + plugins } } /** * Get existing plugin or return null if not present. Only first matching plugin is returned. - * @param recursive search for parent [PluginManager] plugins + * @param inherit search for parent [PluginManager] plugins * @param predicate condition for the plugin */ - public fun find(recursive: Boolean = true, predicate: (Plugin) -> Boolean): Plugin? = sequence(recursive).find(predicate) + public fun find(inherit: Boolean = true, predicate: (Plugin) -> Boolean): Plugin? = + list(inherit).find(predicate) /** @@ -56,7 +63,8 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab * @param tag * @return */ - public operator fun get(tag: PluginTag, recursive: Boolean = true): Plugin? = find(recursive) { tag.matches(it.tag) } + public operator fun get(tag: PluginTag, inherit: Boolean = true): Plugin? = + find(inherit) { tag.matches(it.tag) } /** @@ -142,7 +150,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab public fun fetch( factory: PluginFactory, recursive: Boolean = true, - metaBuilder: MetaBuilder.() -> Unit + metaBuilder: MetaBuilder.() -> Unit, ): T = fetch(factory, recursive, Meta(metaBuilder)) override fun iterator(): Iterator = plugins.iterator() diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt new file mode 100644 index 00000000..811220af --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/context/resolve.kt @@ -0,0 +1,96 @@ +package hep.dataforge.context + +import hep.dataforge.meta.DFExperimental +import hep.dataforge.names.Name +import hep.dataforge.names.plus +import hep.dataforge.provider.Provider +import hep.dataforge.provider.top +import kotlin.reflect.KClass +import kotlin.reflect.cast + +/** + * Resolve a specific element in top level elements of the provider and attempt to cast it to the given type + */ +private fun Provider.provide(target: String, name: Name, type: KClass): T? { + return content(target)[name]?.let { type.cast(it) } +} + +/** + * Resolve a top level object with given [target] and [name] in a [Context] own scope or its plugins. + */ +public fun Context.resolve(target: String, name: Name, type: KClass): T? { + //Try searching for plugin an context property + provide(target, name, type)?.let { return it } + val pluginContent = plugins.mapNotNull { it.provide(target, name, type) } + return if (pluginContent.isEmpty()) { + parent?.resolve(target, name, type) + } else { + pluginContent.single() // throws error in case of name/type conflicts + } +} + +/** + * Resolve a top level object with given [target] and [name] in a [Context] own scope or its plugins. + */ +public inline fun Context.resolve(target: String, name: Name): T? = + resolve(target, name, T::class) + +/** + * Gather a map of all top-level objects with given [target] from context plugins. + * Content from plugins is prefixed by plugin name so name conflicts are impossible + * This operation could be slow in case of large number of plugins + */ +public fun Context.gather( + target: String, + type: KClass, + inherit: Boolean = true, +): Map = buildMap { + putAll(top(target, type)) + plugins.forEach { plugin -> + plugin.top(target, type).forEach { (name, value) -> + if (containsKey(name)) error("Name conflict during gather. An item with name $name could not be gathered from $plugin because key is already present.") + put(plugin.name + name, value) + } + } + if (inherit) { + parent?.gather(target, type, inherit)?.forEach { + //put all values from parent if they are not conflicting + if (!containsKey(it.key)) { + put(it.key, it.value) + } + } + } +} + +public inline fun Context.gather(target: String, inherit: Boolean = true): Map = + gather(target, T::class, inherit) + +/** + * Gather all content from context itself and its plugins in a form of sequence of name-value pairs. Ignores name conflicts. + * + * Adds parent context sequence as well if [inherit] is true + */ +@DFExperimental +public fun Context.gatherInSequence( + target: String, + type: KClass, + inherit: Boolean = true, +): Sequence> = sequence { + yieldAll(top(target, type).entries) + plugins.forEach { plugin -> + yieldAll(plugin.top(target, type).mapKeys { plugin.name + it.key }.entries) + } + if (inherit) { + parent?.gather(target, type, inherit)?.let { + yieldAll(it.entries) + } + } +} + +@DFExperimental +public inline fun Context.gatherInSequence( + target: String, + inherit: Boolean = true, +): Sequence> = gatherInSequence(target, T::class, inherit) + +public val Sequence>.values: Sequence get() = map { it.value } \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt index c69e939c..6cd0d7fa 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Path.kt @@ -42,7 +42,7 @@ public inline class Path(public val tokens: List) : Iterable = tokens.iterator() public companion object { - public const val PATH_SEGMENT_SEPARATOR = "/" + public const val PATH_SEGMENT_SEPARATOR: String = "/" public fun parse(path: String): Path { val head = path.substringBefore(PATH_SEGMENT_SEPARATOR) diff --git a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt index 665912b8..3a153d86 100644 --- a/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt +++ b/dataforge-context/src/commonMain/kotlin/hep/dataforge/provider/Provider.kt @@ -16,6 +16,8 @@ package hep.dataforge.provider import hep.dataforge.names.Name +import kotlin.reflect.KClass +import kotlin.reflect.safeCast /** * A marker utility interface for providers. @@ -37,14 +39,14 @@ public interface Provider { /** * A map of direct children for specific target */ - public fun provideTop(target: String): Map = emptyMap() + public fun content(target: String): Map = emptyMap() } public fun Provider.provide(path: Path, targetOverride: String? = null): Any? { if (path.length == 0) throw IllegalArgumentException("Can't provide by empty path") val first = path.first() val target = targetOverride ?: first.target ?: defaultTarget - val res = provideTop(target)[first.name] ?: return null + val res = content(target)[first.name] ?: return null return when (path.length) { 1 -> res else -> { @@ -66,17 +68,22 @@ public inline fun Provider.provide(path: String, targetOverrid //inline fun Provider.provide(target: String, name: Name): T? { // return provide(PathToken(name, target).toPath()) as? T //} -// + //inline fun Provider.provide(target: String, name: String): T? = // provide(target, name.toName()) /** * Typed top level content */ -public inline fun Provider.top(target: String): Map { - return provideTop(target).mapValues { - it.value as? T ?: error("The type of element $it is ${it::class} but ${T::class} is expected") +public fun Provider.top(target: String, type: KClass): Map { + return content(target).mapValues { + type.safeCast(it.value) ?: error("The type of element $it is ${it::class} but $type is expected") } } +/** + * Typed top level content + */ +public inline fun Provider.top(target: String): Map = top(target, T::class) + diff --git a/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt b/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt index ba7e17de..ef3d1254 100644 --- a/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt +++ b/dataforge-context/src/commonTest/kotlin/hep/dataforge/context/ContextTest.kt @@ -11,7 +11,7 @@ class ContextTest { class DummyPlugin : AbstractPlugin() { override val tag get() = PluginTag("test") - override fun provideTop(target: String): Map { + override fun content(target: String): Map { return when(target){ "test" -> listOf("a", "b", "c.d").associate { it.toName() to it.toName() } else -> emptyMap() @@ -22,7 +22,7 @@ class ContextTest { @Test fun testPluginManager() { Global.plugins.load(DummyPlugin()) - val members = Global.resolve("test") + val members = Global.gather("test") assertEquals(3, members.count()) members.forEach { assertEquals(it.key, it.value.appendLeft("test")) diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt deleted file mode 100644 index 232e2655..00000000 --- a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/Types.kt +++ /dev/null @@ -1,44 +0,0 @@ -package hep.dataforge.provider - -import hep.dataforge.context.Context -import hep.dataforge.context.resolve -import hep.dataforge.names.Name -import kotlin.reflect.KClass -import kotlin.reflect.full.findAnnotation - -/** - * - */ -object Types { - operator fun get(cl: KClass<*>): String { - return cl.findAnnotation()?.id ?: cl.simpleName ?: "" - } - - operator fun get(obj: Any): String { - return get(obj::class) - } -} - -/** - * Provide an object with given name inferring target from its type using [Type] annotation - */ -inline fun Provider.provideByType(name: String): T? { - val target = Types[T::class] - return provide(target, name) -} -// -//inline fun Provider.provideByType(name: Name): T? { -// val target = Types[T::class] -// return provide(target, name) -//} - -inline fun Provider.top(): Map { - val target = Types[T::class] - return top(target) -} - -/** - * A sequences of all objects provided by plugins with given target and type - */ -inline fun Context.content(): Map = resolve(Types[T::class]) - diff --git a/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt new file mode 100644 index 00000000..9c08d6a8 --- /dev/null +++ b/dataforge-context/src/jvmMain/kotlin/hep/dataforge/provider/dfType.kt @@ -0,0 +1,36 @@ +package hep.dataforge.provider + +import hep.dataforge.context.Context +import hep.dataforge.context.gather +import hep.dataforge.meta.DFExperimental +import hep.dataforge.names.Name +import kotlin.reflect.KClass +import kotlin.reflect.full.findAnnotation + + +@DFExperimental +public val KClass<*>.dfType: String + get() = findAnnotation()?.id ?: simpleName ?: "" + +/** + * Provide an object with given name inferring target from its type using [Type] annotation + */ +@DFExperimental +public inline fun Provider.provideByType(name: String): T? { + val target = T::class.dfType + return provide(target, name) +} + +@DFExperimental +public inline fun Provider.top(): Map { + val target = T::class.dfType + return top(target) +} + +/** + * All objects provided by plugins with given target and type + */ +@DFExperimental +public inline fun Context.gather(inherit: Boolean = true): Map = + gather(T::class.dfType, inherit) + diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt index c7acf91c..c573aad8 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Data.kt @@ -15,11 +15,12 @@ public interface Data : Goal, MetaRepr{ /** * Type marker for the data. The type is known before the calculation takes place so it could be checked. */ - val type: KClass + public val type: KClass + /** * Meta for the data */ - val meta: Meta + public val meta: Meta override fun toMeta(): Meta = Meta { "type" put (type.simpleName?:"undefined") @@ -28,10 +29,10 @@ public interface Data : Goal, MetaRepr{ } } - companion object { - const val TYPE = "data" + public companion object { + public const val TYPE: String = "data" - operator fun invoke( + public operator fun invoke( type: KClass, meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, @@ -39,14 +40,14 @@ public interface Data : Goal, MetaRepr{ block: suspend CoroutineScope.() -> T ): Data = DynamicData(type, meta, context, dependencies, block) - inline operator fun invoke( + public inline operator fun invoke( meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, dependencies: Collection> = emptyList(), noinline block: suspend CoroutineScope.() -> T ): Data = invoke(T::class, meta, context, dependencies, block) - operator fun invoke( + public operator fun invoke( name: String, type: KClass, meta: Meta = Meta.EMPTY, @@ -55,7 +56,7 @@ public interface Data : Goal, MetaRepr{ block: suspend CoroutineScope.() -> T ): Data = NamedData(name, invoke(type, meta, context, dependencies, block)) - inline operator fun invoke( + public inline operator fun invoke( name: String, meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, @@ -64,13 +65,13 @@ public interface Data : Goal, MetaRepr{ ): Data = invoke(name, T::class, meta, context, dependencies, block) - fun static(value: T, meta: Meta = Meta.EMPTY): Data = + public fun static(value: T, meta: Meta = Meta.EMPTY): Data = StaticData(value, meta) } } -class DynamicData( +public class DynamicData( override val type: KClass, override val meta: Meta = Meta.EMPTY, context: CoroutineContext = EmptyCoroutineContext, @@ -78,16 +79,16 @@ class DynamicData( block: suspend CoroutineScope.() -> T ) : Data, DynamicGoal(context, dependencies, block) -class StaticData( +public class StaticData( value: T, override val meta: Meta = Meta.EMPTY ) : Data, StaticGoal(value) { override val type: KClass get() = value::class } -class NamedData(val name: String, data: Data) : Data by data +public class NamedData(public val name: String, data: Data) : Data by data -fun Data.map( +public fun Data.map( outputType: KClass, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, @@ -100,7 +101,7 @@ fun Data.map( /** * Create a data pipe */ -inline fun Data.map( +public inline fun Data.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta = this.meta, noinline block: suspend CoroutineScope.(T) -> R @@ -111,7 +112,7 @@ inline fun Data.map( /** * Create a joined data. */ -inline fun Collection>.reduce( +public inline fun Collection>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta, noinline block: suspend CoroutineScope.(Collection) -> R @@ -124,7 +125,7 @@ inline fun Collection>.reduce( block(map { run { it.await() } }) } -fun Map>.reduce( +public fun Map>.reduce( outputType: KClass, coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta, @@ -145,7 +146,7 @@ fun Map>.reduce( * @param T type of the input goal * @param R type of the result goal */ -inline fun Map>.reduce( +public inline fun Map>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, meta: Meta, noinline block: suspend CoroutineScope.(Map) -> R diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt index b24f87a2..79f790ab 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataFilter.kt @@ -4,25 +4,25 @@ import hep.dataforge.meta.* import hep.dataforge.names.toName -class DataFilter : Scheme() { +public class DataFilter : Scheme() { /** * A source node for the filter */ - var from by string() + public var from: String? by string() /** * A target placement for the filtered node */ - var to by string() + public var to: String? by string() /** * A regular expression pattern for the filter */ - var pattern by string(".*") + public var pattern: String by string(".*") // val prefix by string() // val suffix by string() - fun isEmpty(): Boolean = config.isEmpty() + public fun isEmpty(): Boolean = config.isEmpty() - companion object : SchemeSpec(::DataFilter) + public companion object : SchemeSpec(::DataFilter) } /** diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt index 1eac4745..23fea9ef 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/DataNode.kt @@ -11,12 +11,12 @@ import kotlin.collections.component2 import kotlin.collections.set import kotlin.reflect.KClass -sealed class DataItem : MetaRepr { - abstract val type: KClass +public sealed class DataItem : MetaRepr { + public abstract val type: KClass - abstract val meta: Meta + public abstract val meta: Meta - class Node(val node: DataNode) : DataItem() { + public class Node(public val node: DataNode) : DataItem() { override val type: KClass get() = node.type override fun toMeta(): Meta = node.toMeta() @@ -24,7 +24,7 @@ sealed class DataItem : MetaRepr { override val meta: Meta get() = node.meta } - class Leaf(val data: Data) : DataItem() { + public class Leaf(public val data: Data) : DataItem() { override val type: KClass get() = data.type override fun toMeta(): Meta = data.toMeta() @@ -36,16 +36,16 @@ sealed class DataItem : MetaRepr { /** * A tree-like data structure grouped into the node. All data inside the node must inherit its type */ -interface DataNode : MetaRepr { +public interface DataNode : MetaRepr { /** * The minimal common ancestor to all data in the node */ - val type: KClass + public val type: KClass - val items: Map> + public val items: Map> - val meta: Meta + public val meta: Meta override fun toMeta(): Meta = Meta { "type" put (type.simpleName ?: "undefined") @@ -60,7 +60,7 @@ interface DataNode : MetaRepr { * Start computation for all goals in data node and return a job for the whole node */ @Suppress("DeferredResultUnused") - fun CoroutineScope.startAll(): Job = launch { + public fun CoroutineScope.startAll(): Job = launch { items.values.forEach { when (it) { is DataItem.Node<*> -> it.node.run { startAll() } @@ -69,36 +69,36 @@ interface DataNode : MetaRepr { } } - companion object { - const val TYPE = "dataNode" + public companion object { + public const val TYPE: String = "dataNode" - operator fun invoke(type: KClass, block: DataTreeBuilder.() -> Unit) = + public operator fun invoke(type: KClass, block: DataTreeBuilder.() -> Unit): DataTree = DataTreeBuilder(type).apply(block).build() - inline operator fun invoke(noinline block: DataTreeBuilder.() -> Unit) = + public inline operator fun invoke(noinline block: DataTreeBuilder.() -> Unit): DataTree = DataTreeBuilder(T::class).apply(block).build() - fun builder(type: KClass) = DataTreeBuilder(type) + public fun builder(type: KClass): DataTreeBuilder = DataTreeBuilder(type) } } -suspend fun DataNode.join(): Unit = coroutineScope { startAll().join() } +public suspend fun DataNode.join(): Unit = coroutineScope { startAll().join() } -val DataItem?.node: DataNode? get() = (this as? DataItem.Node)?.node -val DataItem?.data: Data? get() = (this as? DataItem.Leaf)?.data +public val DataItem?.node: DataNode? get() = (this as? DataItem.Node)?.node +public val DataItem?.data: Data? get() = (this as? DataItem.Leaf)?.data -operator fun DataNode.get(name: Name): DataItem? = when (name.length) { +public operator fun DataNode.get(name: Name): DataItem? = when (name.length) { 0 -> error("Empty name") 1 -> items[name.firstOrNull()] else -> get(name.firstOrNull()!!.asName()).node?.get(name.cutFirst()) } -operator fun DataNode.get(name: String): DataItem? = get(name.toName()) +public operator fun DataNode.get(name: String): DataItem? = get(name.toName()) /** * Sequence of all children including nodes */ -fun DataNode.asSequence(): Sequence>> = sequence { +public fun DataNode.asSequence(): Sequence>> = sequence { items.forEach { (head, item) -> yield(head.asName() to item) if (item is DataItem.Node) { @@ -112,7 +112,7 @@ fun DataNode.asSequence(): Sequence>> = sequ /** * Sequence of data entries */ -fun DataNode.dataSequence(): Sequence>> = sequence { +public fun DataNode.dataSequence(): Sequence>> = sequence { items.forEach { (head, item) -> when (item) { is DataItem.Leaf -> yield(head.asName() to item.data) @@ -125,9 +125,9 @@ fun DataNode.dataSequence(): Sequence>> = sequen } } -operator fun DataNode.iterator(): Iterator>> = asSequence().iterator() +public operator fun DataNode.iterator(): Iterator>> = asSequence().iterator() -class DataTree internal constructor( +public class DataTree internal constructor( override val type: KClass, override val items: Map>, override val meta: Meta @@ -142,17 +142,17 @@ private sealed class DataTreeBuilderItem { * A builder for a DataTree. */ @DFBuilder -class DataTreeBuilder(val type: KClass) { +public class DataTreeBuilder(public val type: KClass) { private val map = HashMap>() private var meta = MetaBuilder() - operator fun set(token: NameToken, node: DataTreeBuilder) { + public operator fun set(token: NameToken, node: DataTreeBuilder) { if (map.containsKey(token)) error("Tree entry with name $token is not empty") map[token] = DataTreeBuilderItem.Node(node) } - operator fun set(token: NameToken, data: Data) { + public operator fun set(token: NameToken, data: Data) { if (map.containsKey(token)) error("Tree entry with name $token is not empty") map[token] = DataTreeBuilderItem.Leaf(data) } @@ -173,7 +173,7 @@ class DataTreeBuilder(val type: KClass) { } } - operator fun set(name: Name, data: Data) { + public operator fun set(name: Name, data: Data) { when (name.length) { 0 -> error("Can't add data with empty name") 1 -> set(name.firstOrNull()!!, data) @@ -181,7 +181,7 @@ class DataTreeBuilder(val type: KClass) { } } - operator fun set(name: Name, node: DataTreeBuilder) { + public operator fun set(name: Name, node: DataTreeBuilder) { when (name.length) { 0 -> error("Can't add data with empty name") 1 -> set(name.firstOrNull()!!, node) @@ -189,9 +189,9 @@ class DataTreeBuilder(val type: KClass) { } } - operator fun set(name: Name, node: DataNode) = set(name, node.builder()) + public operator fun set(name: Name, node: DataNode): Unit = set(name, node.builder()) - operator fun set(name: Name, item: DataItem) = when (item) { + public operator fun set(name: Name, item: DataItem): Unit = when (item) { is DataItem.Node -> set(name, item.node.builder()) is DataItem.Leaf -> set(name, item.data) } @@ -199,25 +199,25 @@ class DataTreeBuilder(val type: KClass) { /** * Append data to node */ - infix fun String.put(data: Data) = set(toName(), data) + public infix fun String.put(data: Data): Unit = set(toName(), data) /** * Append node */ - infix fun String.put(node: DataNode) = set(toName(), node) + public infix fun String.put(node: DataNode): Unit = set(toName(), node) - infix fun String.put(item: DataItem) = set(toName(), item) + public infix fun String.put(item: DataItem): Unit = set(toName(), item) /** * Build and append node */ - infix fun String.put(block: DataTreeBuilder.() -> Unit) = set(toName(), DataTreeBuilder(type).apply(block)) + public infix fun String.put(block: DataTreeBuilder.() -> Unit): Unit = set(toName(), DataTreeBuilder(type).apply(block)) /** * Update data with given node data and meta with node meta. */ - fun update(node: DataNode) { + public fun update(node: DataNode) { node.dataSequence().forEach { //TODO check if the place is occupied this[it.first] = it.second @@ -225,13 +225,13 @@ class DataTreeBuilder(val type: KClass) { meta.update(node.meta) } - fun meta(block: MetaBuilder.() -> Unit) = meta.apply(block) + public fun meta(block: MetaBuilder.() -> Unit): MetaBuilder = meta.apply(block) - fun meta(meta: Meta) { + public fun meta(meta: Meta) { this.meta = meta.builder() } - fun build(): DataTree { + public fun build(): DataTree { val resMap = map.mapValues { (_, value) -> when (value) { is DataTreeBuilderItem.Leaf -> DataItem.Leaf(value.value) @@ -242,50 +242,50 @@ class DataTreeBuilder(val type: KClass) { } } -fun DataTreeBuilder.datum(name: Name, data: Data) { +public fun DataTreeBuilder.datum(name: Name, data: Data) { this[name] = data } -fun DataTreeBuilder.datum(name: String, data: Data) { +public fun DataTreeBuilder.datum(name: String, data: Data) { this[name.toName()] = data } -fun DataTreeBuilder.static(name: Name, data: T, meta: Meta = Meta.EMPTY) { +public fun DataTreeBuilder.static(name: Name, data: T, meta: Meta = Meta.EMPTY) { this[name] = Data.static(data, meta) } -fun DataTreeBuilder.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { +public fun DataTreeBuilder.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { this[name] = Data.static(data, Meta(block)) } -fun DataTreeBuilder.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { +public fun DataTreeBuilder.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { this[name.toName()] = Data.static(data, Meta(block)) } -fun DataTreeBuilder.node(name: Name, node: DataNode) { +public fun DataTreeBuilder.node(name: Name, node: DataNode) { this[name] = node } -fun DataTreeBuilder.node(name: String, node: DataNode) { +public fun DataTreeBuilder.node(name: String, node: DataNode) { this[name.toName()] = node } -inline fun DataTreeBuilder.node(name: Name, noinline block: DataTreeBuilder.() -> Unit) { +public inline fun DataTreeBuilder.node(name: Name, noinline block: DataTreeBuilder.() -> Unit) { this[name] = DataNode(T::class, block) } -inline fun DataTreeBuilder.node(name: String, noinline block: DataTreeBuilder.() -> Unit) { +public inline fun DataTreeBuilder.node(name: String, noinline block: DataTreeBuilder.() -> Unit) { this[name.toName()] = DataNode(T::class, block) } /** * Generate a mutable builder from this node. Node content is not changed */ -fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder(type).apply { +public fun DataNode.builder(): DataTreeBuilder = DataTreeBuilder(type).apply { dataSequence().forEach { (name, data) -> this[name] = data } } -fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataNode.invoke(type) { +public fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNode = DataNode.invoke(type) { dataSequence().forEach { (name, data) -> if (predicate(name, data)) { this[name] = data @@ -293,4 +293,4 @@ fun DataNode.filter(predicate: (Name, Data) -> Boolean): DataNod } } -fun DataNode.first(): Data? = dataSequence().first().second \ No newline at end of file +public fun DataNode.first(): Data? = dataSequence().first().second \ No newline at end of file diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt index 8c0eeec7..e5050d22 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/Goal.kt @@ -1,37 +1,36 @@ package hep.dataforge.data +import hep.dataforge.meta.DFExperimental import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -interface Goal { - val dependencies: Collection> +public interface Goal { + public val dependencies: Collection> /** * Returns current running coroutine if the goal is started */ - val result: Deferred? + public val result: Deferred? /** * Get ongoing computation or start a new one. * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. */ - fun CoroutineScope.startAsync(): Deferred + public fun CoroutineScope.startAsync(): Deferred /** * Reset the computation */ - fun reset() + public fun reset() - companion object { - - } + public companion object } -suspend fun Goal.await(): T = coroutineScope { startAsync().await() } +public suspend fun Goal.await(): T = coroutineScope { startAsync().await() } -val Goal<*>.isComplete get() = result?.isCompleted ?: false +public val Goal<*>.isComplete: Boolean get() = result?.isCompleted ?: false -open class StaticGoal(val value: T) : Goal { +public open class StaticGoal(public val value: T) : Goal { override val dependencies: Collection> get() = emptyList() override val result: Deferred = CompletableDeferred(value) @@ -42,10 +41,10 @@ open class StaticGoal(val value: T) : Goal { } } -open class DynamicGoal( - val coroutineContext: CoroutineContext = EmptyCoroutineContext, +public open class DynamicGoal( + private val coroutineContext: CoroutineContext = EmptyCoroutineContext, override val dependencies: Collection> = emptyList(), - val block: suspend CoroutineScope.() -> T + public val block: suspend CoroutineScope.() -> T ) : Goal { final override var result: Deferred? = null @@ -55,6 +54,7 @@ open class DynamicGoal( * Get ongoing computation or start a new one. * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. */ + @DFExperimental override fun CoroutineScope.startAsync(): Deferred { val startedDependencies = this@DynamicGoal.dependencies.map { goal -> goal.run { startAsync() } @@ -82,7 +82,7 @@ open class DynamicGoal( /** * Create a one-to-one goal based on existing goal */ -fun Goal.map( +public fun Goal.map( coroutineContext: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.(T) -> R ): Goal = DynamicGoal(coroutineContext, listOf(this)) { @@ -92,7 +92,7 @@ fun Goal.map( /** * Create a joining goal. */ -fun Collection>.reduce( +public fun Collection>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.(Collection) -> R ): Goal = DynamicGoal(coroutineContext, this) { @@ -105,7 +105,7 @@ fun Collection>.reduce( * @param T type of the input goal * @param R type of the result goal */ -fun Map>.reduce( +public fun Map>.reduce( coroutineContext: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.(Map) -> R ): Goal = DynamicGoal(coroutineContext, this.values) { diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt index 5cfc55e8..0a5de778 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/GroupRule.kt @@ -19,10 +19,10 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.get import hep.dataforge.meta.string -interface GroupRule { - operator fun invoke(node: DataNode): Map> +public interface GroupRule { + public operator fun invoke(node: DataNode): Map> - companion object{ + public companion object{ /** * Create grouping rule that creates groups for different values of value * field with name [key] @@ -31,7 +31,7 @@ interface GroupRule { * @param defaultTagValue * @return */ - fun byValue(key: String, defaultTagValue: String): GroupRule = object : + public fun byValue(key: String, defaultTagValue: String): GroupRule = object : GroupRule { override fun invoke(node: DataNode): Map> { val map = HashMap>() @@ -52,7 +52,7 @@ interface GroupRule { // def = "default", // info = "Default value which should be used for content in which the grouping value is not presented" // ) - fun byMeta(config: Meta): GroupRule { + public fun byMeta(config: Meta): GroupRule { //TODO expand grouping options return config["byValue"]?.string?.let { byValue( diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt index 61598349..e5504530 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/MapAction.kt @@ -7,32 +7,31 @@ import kotlin.reflect.KClass /** * Action environment includes data name, data meta and action configuration meta */ -data class ActionEnv( +public data class ActionEnv( val name: Name, val meta: Meta, val actionMeta: Meta ) - /** * Action environment */ @DFBuilder -class MapActionBuilder(var name: Name, var meta: MetaBuilder, val actionMeta: Meta) { - lateinit var result: suspend ActionEnv.(T) -> R +public class MapActionBuilder(public var name: Name, public var meta: MetaBuilder, public val actionMeta: Meta) { + public lateinit var result: suspend ActionEnv.(T) -> R /** * Calculate the result of goal */ - fun result(f: suspend ActionEnv.(T) -> R) { + public fun result(f: suspend ActionEnv.(T) -> R) { result = f; } } -class MapAction( - val inputType: KClass, - val outputType: KClass, +public class MapAction( + public val inputType: KClass, + public val outputType: KClass, private val block: MapActionBuilder.() -> Unit ) : Action { @@ -67,7 +66,7 @@ class MapAction( } } -inline fun DataNode.map( +public inline fun DataNode.map( meta: Meta, noinline action: MapActionBuilder.() -> Unit ): DataNode = MapAction(T::class, R::class, action).invoke(this, meta) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt index 0efb5506..a0227af9 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/ReduceAction.kt @@ -7,25 +7,25 @@ import hep.dataforge.names.toName import kotlin.reflect.KClass -class JoinGroup(var name: String, internal val node: DataNode) { +public class JoinGroup(public var name: String, internal val node: DataNode) { - var meta: MetaBuilder = MetaBuilder() + public var meta: MetaBuilder = MetaBuilder() - lateinit var result: suspend ActionEnv.(Map) -> R + public lateinit var result: suspend ActionEnv.(Map) -> R - fun result(f: suspend ActionEnv.(Map) -> R) { + public fun result(f: suspend ActionEnv.(Map) -> R) { this.result = f; } } -class ReduceGroupBuilder(val actionMeta: Meta) { +public class ReduceGroupBuilder(public val actionMeta: Meta) { private val groupRules: MutableList<(DataNode) -> List>> = ArrayList(); /** * introduce grouping by value name */ - fun byValue(tag: String, defaultTag: String = "@default", action: JoinGroup.() -> Unit) { + public fun byValue(tag: String, defaultTag: String = "@default", action: JoinGroup.() -> Unit) { groupRules += { node -> GroupRule.byValue(tag, defaultTag).invoke(node).map { JoinGroup(it.key, it.value).apply(action) @@ -36,7 +36,7 @@ class ReduceGroupBuilder(val actionMeta: Meta) { /** * Add a single fixed group to grouping rules */ - fun group(groupName: String, filter: DataFilter, action: JoinGroup.() -> Unit) { + public fun group(groupName: String, filter: DataFilter, action: JoinGroup.() -> Unit) { groupRules += { node -> listOf( JoinGroup(groupName, node.filter(filter)).apply(action) @@ -44,7 +44,7 @@ class ReduceGroupBuilder(val actionMeta: Meta) { } } - fun group(groupName: String, filter: (Name, Data) -> Boolean, action: JoinGroup.() -> Unit) { + public fun group(groupName: String, filter: (Name, Data) -> Boolean, action: JoinGroup.() -> Unit) { groupRules += { node -> listOf( JoinGroup(groupName, node.filter(filter)).apply(action) @@ -55,7 +55,7 @@ class ReduceGroupBuilder(val actionMeta: Meta) { /** * Apply transformation to the whole node */ - fun result(resultName: String, f: suspend ActionEnv.(Map) -> R) { + public fun result(resultName: String, f: suspend ActionEnv.(Map) -> R) { groupRules += { node -> listOf(JoinGroup(resultName, node).apply { result(f) }) } @@ -71,9 +71,9 @@ class ReduceGroupBuilder(val actionMeta: Meta) { /** * The same rules as for KPipe */ -class ReduceAction( - val inputType: KClass, - val outputType: KClass, +public class ReduceAction( + public val inputType: KClass, + public val outputType: KClass, private val action: ReduceGroupBuilder.() -> Unit ) : Action { @@ -104,4 +104,4 @@ class ReduceAction( } } -operator fun Map.get(name: String) = get(name.toName()) +public operator fun Map.get(name: String): T? = get(name.toName()) diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt index dc29394c..731a9403 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/SplitAction.kt @@ -10,16 +10,16 @@ import kotlin.collections.set import kotlin.reflect.KClass -class FragmentRule(val name: Name, var meta: MetaBuilder) { - lateinit var result: suspend (T) -> R +public class FragmentRule(public val name: Name, public var meta: MetaBuilder) { + public lateinit var result: suspend (T) -> R - fun result(f: suspend (T) -> R) { + public fun result(f: suspend (T) -> R) { result = f; } } -class SplitBuilder(val name: Name, val meta: Meta) { +public class SplitBuilder(public val name: Name, public val meta: Meta) { internal val fragments: MutableMap.() -> Unit> = HashMap() /** @@ -27,14 +27,14 @@ class SplitBuilder(val name: Name, val meta: Meta) { * @param name the name of a fragment * @param rule the rule to transform fragment name and meta using */ - fun fragment(name: String, rule: FragmentRule.() -> Unit) { + public fun fragment(name: String, rule: FragmentRule.() -> Unit) { fragments[name.toName()] = rule } } -class SplitAction( - val inputType: KClass, - val outputType: KClass, +public class SplitAction( + public val inputType: KClass, + public val outputType: KClass, private val action: SplitBuilder.() -> Unit ) : Action { diff --git a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt index db8b7ab5..26563bc8 100644 --- a/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt +++ b/dataforge-data/src/commonMain/kotlin/hep/dataforge/data/dataCast.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlin.reflect.KClass -fun Data.upcast(type: KClass): Data { +public fun Data.upcast(type: KClass): Data { return object : Data by this { override val type: KClass = type } @@ -15,7 +15,7 @@ fun Data.upcast(type: KClass): Data { /** * Safe upcast a [Data] to a supertype */ -inline fun Data.upcast(): Data = upcast(R::class) +public inline fun Data.upcast(): Data = upcast(R::class) /** * Check if node could be safely cast to given class @@ -27,7 +27,7 @@ internal expect fun DataNode<*>.canCast(type: KClass): Boolean */ internal expect fun Data<*>.canCast(type: KClass): Boolean -fun DataItem<*>.canCast(type: KClass): Boolean = when (this) { +public fun DataItem<*>.canCast(type: KClass): Boolean = when (this) { is DataItem.Node -> node.canCast(type) is DataItem.Leaf -> data.canCast(type) } @@ -36,7 +36,7 @@ fun DataItem<*>.canCast(type: KClass): Boolean = when (this) { * Unsafe cast of data node */ @Suppress("UNCHECKED_CAST") -fun Data<*>.cast(type: KClass): Data { +public fun Data<*>.cast(type: KClass): Data { return object : Data { override val meta: Meta get() = this@cast.meta override val dependencies: Collection> get() = this@cast.dependencies @@ -47,10 +47,10 @@ fun Data<*>.cast(type: KClass): Data { } } -inline fun Data<*>.cast(): Data = cast(R::class) +public inline fun Data<*>.cast(): Data = cast(R::class) @Suppress("UNCHECKED_CAST") -fun DataNode<*>.cast(type: KClass): DataNode { +public fun DataNode<*>.cast(type: KClass): DataNode { return object : DataNode { override val meta: Meta get() = this@cast.meta override val type: KClass = type @@ -58,12 +58,12 @@ fun DataNode<*>.cast(type: KClass): DataNode { } } -inline fun DataNode<*>.cast(): DataNode = cast(R::class) +public inline fun DataNode<*>.cast(): DataNode = cast(R::class) /** * Check that node is compatible with given type meaning that each element could be cast to the type */ -fun DataNode<*>.ensureType(type: KClass) { +public fun DataNode<*>.ensureType(type: KClass) { if (!canCast(type)) { error("$type expected, but $type received") } diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/TypeFilteredDataNode.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/TypeFilteredDataNode.kt index 3590679c..b67becff 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/TypeFilteredDataNode.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/TypeFilteredDataNode.kt @@ -8,7 +8,7 @@ import kotlin.reflect.KClass /** * A zero-copy data node wrapper that returns only children with appropriate type. */ -class TypeFilteredDataNode(val origin: DataNode<*>, override val type: KClass) : DataNode { +public class TypeFilteredDataNode(public val origin: DataNode<*>, override val type: KClass) : DataNode { override val meta: Meta get() = origin.meta override val items: Map> by lazy { origin.items.mapNotNull { (key, item) -> diff --git a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt index ca9b0024..6f758dae 100644 --- a/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt +++ b/dataforge-data/src/jvmMain/kotlin/hep/dataforge/data/dataJVM.kt @@ -39,10 +39,10 @@ public fun DataNode<*>.filterIsInstance(type: KClass): DataNode /** * Filter all elements of given data item that could be cast to given type. If no elements are available, return null. */ -fun DataItem<*>?.filterIsInstance(type: KClass): DataItem? = when (this) { +public fun DataItem<*>?.filterIsInstance(type: KClass): DataItem? = when (this) { null -> null is DataItem.Node -> DataItem.Node(this.node.filterIsInstance(type)) is DataItem.Leaf -> this.data.filterIsInstance(type)?.let { DataItem.Leaf(it) } } -inline fun DataItem<*>?.filterIsInstance(): DataItem? = this@filterIsInstance.filterIsInstance(R::class) \ No newline at end of file +public inline fun DataItem<*>?.filterIsInstance(): DataItem? = this@filterIsInstance.filterIsInstance(R::class) \ No newline at end of file diff --git a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt index f1684ce8..f0c47609 100644 --- a/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt +++ b/dataforge-io/src/commonMain/kotlin/hep/dataforge/io/IOPlugin.kt @@ -15,7 +15,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { override val tag: PluginTag get() = Companion.tag public val ioFormatFactories: Collection> by lazy { - context.resolve>(IO_FORMAT_TYPE).values + context.gather>(IO_FORMAT_TYPE).values } public fun resolveIOFormat(item: MetaItem<*>, type: KClass): IOFormat? { @@ -30,7 +30,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { public val metaFormatFactories: Collection by lazy { - context.resolve(META_FORMAT_TYPE).values + context.gather(META_FORMAT_TYPE).values } public fun resolveMetaFormat(key: Short, meta: Meta = Meta.EMPTY): MetaFormat? = @@ -40,7 +40,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { metaFormatFactories.find { it.shortName == name }?.invoke(meta) public val envelopeFormatFactories: Collection by lazy { - context.resolve(ENVELOPE_FORMAT_TYPE).values + context.gather(ENVELOPE_FORMAT_TYPE).values } private fun resolveEnvelopeFormat(name: Name, meta: Meta = Meta.EMPTY): EnvelopeFormat? = @@ -52,11 +52,11 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { return resolveEnvelopeFormat(name.toName(), meta) } - override fun provideTop(target: String): Map { + override fun content(target: String): Map { return when (target) { META_FORMAT_TYPE -> defaultMetaFormats.toMap() ENVELOPE_FORMAT_TYPE -> defaultEnvelopeFormats.toMap() - else -> super.provideTop(target) + else -> super.content(target) } } diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt index 96f42b46..5545358c 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/meta/Laminate.kt @@ -17,8 +17,6 @@ public class Laminate(layers: List) : MetaBase() { } } - public constructor(vararg layers: Meta?) : this(layers.filterNotNull()) - override val items: Map> by lazy { layers.map { it.items.keys }.flatten().associateWith { key -> layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule) @@ -80,6 +78,9 @@ public class Laminate(layers: List) : MetaBase() { } } +@Suppress("FunctionName") +public fun Laminate(vararg layers: Meta?): Laminate = Laminate(layers.filterNotNull()) + /** * Performance optimized version of get method */ diff --git a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt index 9a95c86d..ca78f0ae 100644 --- a/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt +++ b/dataforge-meta/src/commonMain/kotlin/hep/dataforge/values/Value.kt @@ -51,7 +51,7 @@ public interface Value { override fun hashCode(): Int public companion object { - public const val TYPE: String = "value" + public const val TARGET: String = "value" /** * Convert object to value diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt index 063ac25d..b45a662c 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/Workspace.kt @@ -31,7 +31,7 @@ public interface Workspace : ContextAware, Provider { */ public val tasks: Map> - override fun provideTop(target: String): Map { + override fun content(target: String): Map { return when (target) { "target", Meta.TYPE -> targets.mapKeys { it.key.toName() } Task.TYPE -> tasks diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt index 02452ce6..9079139d 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/WorkspacePlugin.kt @@ -13,7 +13,7 @@ public abstract class WorkspacePlugin : AbstractPlugin() { private val _tasks = HashSet>() public val tasks: Collection> get() = _tasks - override fun provideTop(target: String): Map { + override fun content(target: String): Map { return when (target) { Task.TYPE -> tasks.toMap() else -> emptyMap() diff --git a/settings.gradle.kts b/settings.gradle.kts index e3c8a08e..a76ddb02 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,8 +8,8 @@ pluginManagement { maven("https://dl.bintray.com/mipt-npm/dev") } - val toolsVersion = "0.6.0-dev-4" - val kotlinVersion = "1.4.0" + val toolsVersion = "0.6.0" + val kotlinVersion = "1.4.10" plugins { id("ru.mipt.npm.mpp") version toolsVersion