Context content resolution refactor.

Explicit API mode.
This commit is contained in:
Alexander Nozik 2020-09-13 19:35:58 +03:00
parent d170f5d60c
commit b229de3eb7
30 changed files with 372 additions and 275 deletions

View File

@ -4,15 +4,20 @@
### Added ### Added
### Changed ### Changed
- Context content resolution refactor
- Kotlin 1.4.10 (build tools 0.6.0)
- Empty query in Name is null instead of "" - Empty query in Name is null instead of ""
- Provider provides an empty map instead of error by default - Provider provides an empty map instead of error by default
- Hidden delegates hierarchy in favor of stdlib properties
### Deprecated ### Deprecated
- Context activation API
### Removed ### Removed
- Functional server prototype - Functional server prototype
### Fixed ### Fixed
- Global context CoroutineScope resolution - Global context CoroutineScope resolution
- Library mode compliance
### Security ### Security

View File

@ -10,16 +10,12 @@ kscience {
useCoroutines() useCoroutines()
} }
repositories {
maven("https://maven.pkg.github.com/altavir/kotlin-logging")
}
kotlin { kotlin {
sourceSets { sourceSets {
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
api(project(":dataforge-meta")) 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 { val jvmMain by getting {

View File

@ -1,18 +1,18 @@
package hep.dataforge.context 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.Name
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.provider.Provider import hep.dataforge.provider.Provider
import hep.dataforge.provider.top
import hep.dataforge.values.Value
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import mu.KLogger import mu.KLogger
import mu.KotlinLogging import mu.KotlinLogging
import kotlin.coroutines.CoroutineContext 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. * 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 * 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. * 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( public open class Context(
final override val name: Name, final override val name: Name,
public val parent: Context? = Global, public val parent: Context?,
meta: Meta,
) : Named, MetaRepr, Provider, CoroutineScope { ) : Named, MetaRepr, Provider, CoroutineScope {
private val config = Config()
/** /**
* Context properties. Working as substitute for environment variables * Context properties. Working as substitute for environment variables
*/ */
private val properties: Meta = if (parent == null) { private val properties: Laminate = if (parent == null) {
config Laminate(meta)
} else { } 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) } public val plugins: PluginManager by lazy { PluginManager(this) }
@Deprecated("To be removed in favor of immutable plugins")
private val activators = HashSet<Any>() private val activators = HashSet<Any>()
/** /**
* Defines if context is used in any kind of active computations. Active context properties and plugins could not be changed * 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() public val isActive: Boolean = activators.isNotEmpty()
override val defaultTarget: String get() = Plugin.PLUGIN_TARGET
override fun provideTop(target: String): Map<Name, Any> {
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] * Mark context as active and used by [activator]
*/ */
@Deprecated("To be removed in favor of immutable plugins")
public fun activate(activator: Any) { public fun activate(activator: Any) {
activators.add(activator) activators.add(activator)
} }
@ -79,19 +69,32 @@ public open class Context(
/** /**
* Mark context unused by [activator] * Mark context unused by [activator]
*/ */
@Deprecated("To be removed in favor of immutable plugins")
public fun deactivate(activator: Any) { public fun deactivate(activator: Any) {
activators.remove(activator) activators.remove(activator)
} }
/** override val defaultTarget: String get() = Plugin.TARGET
* Change the properties of the context. If active, throw an exception
*/ public fun content(target: String, inherit: Boolean): Map<Name, Any> {
public fun configure(action: Config.() -> Unit) { return if (inherit) {
if (isActive) error("Can't configure active context") when (target) {
config.action() 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<Name, Any> = content(target,true)
override val coroutineContext: CoroutineContext by lazy {
(parent ?: Global).coroutineContext.let { parenContext -> (parent ?: Global).coroutineContext.let { parenContext ->
parenContext + SupervisorJob(parenContext[Job]) parenContext + SupervisorJob(parenContext[Job])
} }
@ -101,6 +104,7 @@ public open class Context(
* Detach all plugins and terminate context * Detach all plugins and terminate context
*/ */
public open fun close() { public open fun close() {
@Suppress("DEPRECATION")
if (isActive) error("Can't close active context") if (isActive) error("Can't close active context")
//detach all plugins //detach all plugins
plugins.forEach { it.detach() } plugins.forEach { it.detach() }
@ -108,22 +112,15 @@ public open class Context(
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
"parent" to parent?.name "parent" to parent?.name
"properties" put properties.seal() "properties" put properties.layers.firstOrNull()
"plugins" put plugins.map { it.toMeta() } "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 <reified T : Any> Context.resolve(target: String): Map<Name, T> = plugins.flatMap { plugin ->
plugin.top<T>(target).entries.map { (plugin.name + it.key) to it.value }
}.associate { it }
public fun Context.resolve(target: String): Map<Name, Any> = resolve<Any>(target)
/** /**
* The interface for something that encapsulated in context * The interface for something that encapsulated in context
* *

View File

@ -1,8 +1,6 @@
package hep.dataforge.context package hep.dataforge.context
import hep.dataforge.meta.DFBuilder import hep.dataforge.meta.*
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.names.toName import hep.dataforge.names.toName
/** /**
@ -21,10 +19,11 @@ public class ContextBuilder(private val parent: Context = Global, public var nam
plugins.add(plugin) plugins.add(plugin)
} }
public fun plugin(tag: PluginTag, action: MetaBuilder.() -> Unit = {}) { @OptIn(DFExperimental::class)
val factory = parent.resolve<PluginFactory<*>>(PluginFactory.TYPE).values public fun plugin(tag: PluginTag, metaBuilder: MetaBuilder.() -> Unit = {}) {
val factory = parent.gatherInSequence<PluginFactory<*>>(PluginFactory.TYPE).values
.find { it.tag.matches(tag) } ?: error("Can't resolve plugin factory for $tag") .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) plugins.add(plugin)
} }
@ -37,7 +36,7 @@ public class ContextBuilder(private val parent: Context = Global, public var nam
} }
public fun build(): Context { public fun build(): Context {
return Context(name.toName(), parent).apply { return Context(name.toName(), parent, meta.seal()).apply {
this@ContextBuilder.plugins.forEach { this@ContextBuilder.plugins.forEach {
plugins.load(it) plugins.load(it)
} }

View File

@ -1,5 +1,6 @@
package hep.dataforge.context package hep.dataforge.context
import hep.dataforge.meta.Meta
import hep.dataforge.names.asName import hep.dataforge.names.asName
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@ -8,7 +9,7 @@ import kotlin.coroutines.CoroutineContext
/** /**
* A global root context. Closing [Global] terminates the framework. * 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() override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + SupervisorJob()

View File

@ -1,27 +1,23 @@
package hep.dataforge.context package hep.dataforge.context
import hep.dataforge.context.Plugin.Companion.TARGET
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaRepr import hep.dataforge.meta.MetaRepr
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.provider.Provider 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 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 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 must in most cases have an empty constructor in order to be able to load it from library.
* *
*
* The plugin lifecycle is the following: * The plugin lifecycle is the following:
* *
*
* create - configure - attach - detach - destroy * 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 { public interface Plugin : Named, ContextAware, Provider, MetaRepr {
/** /**
@ -64,8 +60,7 @@ public interface Plugin : Named, ContextAware, Provider, MetaRepr {
} }
public companion object { public companion object {
public const val TARGET: String = "plugin"
public const val PLUGIN_TARGET = "plugin"
} }
} }

View File

@ -2,14 +2,16 @@ package hep.dataforge.context
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.provider.Type
import kotlin.reflect.KClass import kotlin.reflect.KClass
@Type(PluginFactory.TYPE)
public interface PluginFactory<T : Plugin> : Factory<T> { public interface PluginFactory<T : Plugin> : Factory<T> {
public val tag: PluginTag public val tag: PluginTag
public val type: KClass<out T> public val type: KClass<out T>
public companion object{ public companion object {
public const val TYPE: String = "pluginFactory" public const val TYPE: String = "pluginFactory"
} }
} }
@ -23,6 +25,8 @@ public interface PluginFactory<T : Plugin> : Factory<T> {
*/ */
public class PluginManager(override val context: Context) : ContextAware, Iterable<Plugin> { public class PluginManager(override val context: Context) : ContextAware, Iterable<Plugin> {
//TODO refactor to read-only container
/** /**
* A set of loaded plugins * 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 private val parent: PluginManager? = context.parent?.plugins
/**
public fun sequence(recursive: Boolean): Sequence<Plugin> { * List plugins stored in this [PluginManager]. If [inherit] is true, include parent plugins as well
return if (recursive && parent != null) { */
plugins.asSequence() + parent.sequence(true) public fun list(inherit: Boolean): Collection<Plugin> {
return if (inherit && parent != null) {
plugins + parent.list(true)
} else { } else {
plugins.asSequence() plugins
} }
} }
/** /**
* Get existing plugin or return null if not present. Only first matching plugin is returned. * 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 * @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 * @param tag
* @return * @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 <T : Plugin> fetch( public fun <T : Plugin> fetch(
factory: PluginFactory<T>, factory: PluginFactory<T>,
recursive: Boolean = true, recursive: Boolean = true,
metaBuilder: MetaBuilder.() -> Unit metaBuilder: MetaBuilder.() -> Unit,
): T = fetch(factory, recursive, Meta(metaBuilder)) ): T = fetch(factory, recursive, Meta(metaBuilder))
override fun iterator(): Iterator<Plugin> = plugins.iterator() override fun iterator(): Iterator<Plugin> = plugins.iterator()

View File

@ -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 <T : Any> Provider.provide(target: String, name: Name, type: KClass<out T>): 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 <T : Any> Context.resolve(target: String, name: Name, type: KClass<out T>): 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<T>(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 <reified T : Any> 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 <T : Any> Context.gather(
target: String,
type: KClass<out T>,
inherit: Boolean = true,
): Map<Name, T> = 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 <reified T : Any> Context.gather(target: String, inherit: Boolean = true): Map<Name, T> =
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 <T : Any> Context.gatherInSequence(
target: String,
type: KClass<out T>,
inherit: Boolean = true,
): Sequence<Map.Entry<Name, T>> = 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 <reified T : Any> Context.gatherInSequence(
target: String,
inherit: Boolean = true,
): Sequence<Map.Entry<Name, T>> = gatherInSequence(target, T::class, inherit)
public val <T> Sequence<Map.Entry<Name, T>>.values: Sequence<T> get() = map { it.value }

View File

@ -42,7 +42,7 @@ public inline class Path(public val tokens: List<PathToken>) : Iterable<PathToke
override fun iterator(): Iterator<PathToken> = tokens.iterator() override fun iterator(): Iterator<PathToken> = tokens.iterator()
public companion object { public companion object {
public const val PATH_SEGMENT_SEPARATOR = "/" public const val PATH_SEGMENT_SEPARATOR: String = "/"
public fun parse(path: String): Path { public fun parse(path: String): Path {
val head = path.substringBefore(PATH_SEGMENT_SEPARATOR) val head = path.substringBefore(PATH_SEGMENT_SEPARATOR)

View File

@ -16,6 +16,8 @@
package hep.dataforge.provider package hep.dataforge.provider
import hep.dataforge.names.Name import hep.dataforge.names.Name
import kotlin.reflect.KClass
import kotlin.reflect.safeCast
/** /**
* A marker utility interface for providers. * A marker utility interface for providers.
@ -37,14 +39,14 @@ public interface Provider {
/** /**
* A map of direct children for specific target * A map of direct children for specific target
*/ */
public fun provideTop(target: String): Map<Name, Any> = emptyMap() public fun content(target: String): Map<Name, Any> = emptyMap()
} }
public fun Provider.provide(path: Path, targetOverride: String? = null): Any? { public fun Provider.provide(path: Path, targetOverride: String? = null): Any? {
if (path.length == 0) throw IllegalArgumentException("Can't provide by empty path") if (path.length == 0) throw IllegalArgumentException("Can't provide by empty path")
val first = path.first() val first = path.first()
val target = targetOverride ?: first.target ?: defaultTarget 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) { return when (path.length) {
1 -> res 1 -> res
else -> { else -> {
@ -66,17 +68,22 @@ public inline fun <reified T : Any> Provider.provide(path: String, targetOverrid
//inline fun <reified T : Any> Provider.provide(target: String, name: Name): T? { //inline fun <reified T : Any> Provider.provide(target: String, name: Name): T? {
// return provide(PathToken(name, target).toPath()) as? T // return provide(PathToken(name, target).toPath()) as? T
//} //}
//
//inline fun <reified T : Any> Provider.provide(target: String, name: String): T? = //inline fun <reified T : Any> Provider.provide(target: String, name: String): T? =
// provide(target, name.toName()) // provide(target, name.toName())
/** /**
* Typed top level content * Typed top level content
*/ */
public inline fun <reified T : Any> Provider.top(target: String): Map<Name, T> { public fun <T : Any> Provider.top(target: String, type: KClass<out T>): Map<Name, T> {
return provideTop(target).mapValues { return content(target).mapValues {
it.value as? T ?: error("The type of element $it is ${it::class} but ${T::class} is expected") type.safeCast(it.value) ?: error("The type of element $it is ${it::class} but $type is expected")
} }
} }
/**
* Typed top level content
*/
public inline fun <reified T : Any> Provider.top(target: String): Map<Name, T> = top(target, T::class)

View File

@ -11,7 +11,7 @@ class ContextTest {
class DummyPlugin : AbstractPlugin() { class DummyPlugin : AbstractPlugin() {
override val tag get() = PluginTag("test") override val tag get() = PluginTag("test")
override fun provideTop(target: String): Map<Name, Any> { override fun content(target: String): Map<Name, Any> {
return when(target){ return when(target){
"test" -> listOf("a", "b", "c.d").associate { it.toName() to it.toName() } "test" -> listOf("a", "b", "c.d").associate { it.toName() to it.toName() }
else -> emptyMap() else -> emptyMap()
@ -22,7 +22,7 @@ class ContextTest {
@Test @Test
fun testPluginManager() { fun testPluginManager() {
Global.plugins.load(DummyPlugin()) Global.plugins.load(DummyPlugin())
val members = Global.resolve<Name>("test") val members = Global.gather<Name>("test")
assertEquals(3, members.count()) assertEquals(3, members.count())
members.forEach { members.forEach {
assertEquals(it.key, it.value.appendLeft("test")) assertEquals(it.key, it.value.appendLeft("test"))

View File

@ -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<Type>()?.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 <reified T : Any> Provider.provideByType(name: String): T? {
val target = Types[T::class]
return provide(target, name)
}
//
//inline fun <reified T : Any> Provider.provideByType(name: Name): T? {
// val target = Types[T::class]
// return provide(target, name)
//}
inline fun <reified T : Any> Provider.top(): Map<Name, T> {
val target = Types[T::class]
return top(target)
}
/**
* A sequences of all objects provided by plugins with given target and type
*/
inline fun <reified T : Any> Context.content(): Map<Name, T> = resolve<T>(Types[T::class])

View File

@ -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<Type>()?.id ?: simpleName ?: ""
/**
* Provide an object with given name inferring target from its type using [Type] annotation
*/
@DFExperimental
public inline fun <reified T : Any> Provider.provideByType(name: String): T? {
val target = T::class.dfType
return provide(target, name)
}
@DFExperimental
public inline fun <reified T : Any> Provider.top(): Map<Name, T> {
val target = T::class.dfType
return top(target)
}
/**
* All objects provided by plugins with given target and type
*/
@DFExperimental
public inline fun <reified T : Any> Context.gather(inherit: Boolean = true): Map<Name, T> =
gather<T>(T::class.dfType, inherit)

View File

@ -15,11 +15,12 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr{
/** /**
* Type marker for the data. The type is known before the calculation takes place so it could be checked. * Type marker for the data. The type is known before the calculation takes place so it could be checked.
*/ */
val type: KClass<out T> public val type: KClass<out T>
/** /**
* Meta for the data * Meta for the data
*/ */
val meta: Meta public val meta: Meta
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
"type" put (type.simpleName?:"undefined") "type" put (type.simpleName?:"undefined")
@ -28,10 +29,10 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr{
} }
} }
companion object { public companion object {
const val TYPE = "data" public const val TYPE: String = "data"
operator fun <T : Any> invoke( public operator fun <T : Any> invoke(
type: KClass<out T>, type: KClass<out T>,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
@ -39,14 +40,14 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr{
block: suspend CoroutineScope.() -> T block: suspend CoroutineScope.() -> T
): Data<T> = DynamicData(type, meta, context, dependencies, block) ): Data<T> = DynamicData(type, meta, context, dependencies, block)
inline operator fun <reified T : Any> invoke( public inline operator fun <reified T : Any> invoke(
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
dependencies: Collection<Data<*>> = emptyList(), dependencies: Collection<Data<*>> = emptyList(),
noinline block: suspend CoroutineScope.() -> T noinline block: suspend CoroutineScope.() -> T
): Data<T> = invoke(T::class, meta, context, dependencies, block) ): Data<T> = invoke(T::class, meta, context, dependencies, block)
operator fun <T : Any> invoke( public operator fun <T : Any> invoke(
name: String, name: String,
type: KClass<out T>, type: KClass<out T>,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
@ -55,7 +56,7 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr{
block: suspend CoroutineScope.() -> T block: suspend CoroutineScope.() -> T
): Data<T> = NamedData(name, invoke(type, meta, context, dependencies, block)) ): Data<T> = NamedData(name, invoke(type, meta, context, dependencies, block))
inline operator fun <reified T : Any> invoke( public inline operator fun <reified T : Any> invoke(
name: String, name: String,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
@ -64,13 +65,13 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr{
): Data<T> = ): Data<T> =
invoke(name, T::class, meta, context, dependencies, block) invoke(name, T::class, meta, context, dependencies, block)
fun <T : Any> static(value: T, meta: Meta = Meta.EMPTY): Data<T> = public fun <T : Any> static(value: T, meta: Meta = Meta.EMPTY): Data<T> =
StaticData(value, meta) StaticData(value, meta)
} }
} }
class DynamicData<T : Any>( public class DynamicData<T : Any>(
override val type: KClass<out T>, override val type: KClass<out T>,
override val meta: Meta = Meta.EMPTY, override val meta: Meta = Meta.EMPTY,
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
@ -78,16 +79,16 @@ class DynamicData<T : Any>(
block: suspend CoroutineScope.() -> T block: suspend CoroutineScope.() -> T
) : Data<T>, DynamicGoal<T>(context, dependencies, block) ) : Data<T>, DynamicGoal<T>(context, dependencies, block)
class StaticData<T : Any>( public class StaticData<T : Any>(
value: T, value: T,
override val meta: Meta = Meta.EMPTY override val meta: Meta = Meta.EMPTY
) : Data<T>, StaticGoal<T>(value) { ) : Data<T>, StaticGoal<T>(value) {
override val type: KClass<out T> get() = value::class override val type: KClass<out T> get() = value::class
} }
class NamedData<out T : Any>(val name: String, data: Data<T>) : Data<T> by data public class NamedData<out T : Any>(public val name: String, data: Data<T>) : Data<T> by data
fun <T : Any, R : Any> Data<T>.map( public fun <T : Any, R : Any> Data<T>.map(
outputType: KClass<out R>, outputType: KClass<out R>,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = this.meta, meta: Meta = this.meta,
@ -100,7 +101,7 @@ fun <T : Any, R : Any> Data<T>.map(
/** /**
* Create a data pipe * Create a data pipe
*/ */
inline fun <T : Any, reified R : Any> Data<T>.map( public inline fun <T : Any, reified R : Any> Data<T>.map(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta = this.meta, meta: Meta = this.meta,
noinline block: suspend CoroutineScope.(T) -> R noinline block: suspend CoroutineScope.(T) -> R
@ -111,7 +112,7 @@ inline fun <T : Any, reified R : Any> Data<T>.map(
/** /**
* Create a joined data. * Create a joined data.
*/ */
inline fun <T : Any, reified R : Any> Collection<Data<T>>.reduce( public inline fun <T : Any, reified R : Any> Collection<Data<T>>.reduce(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta, meta: Meta,
noinline block: suspend CoroutineScope.(Collection<T>) -> R noinline block: suspend CoroutineScope.(Collection<T>) -> R
@ -124,7 +125,7 @@ inline fun <T : Any, reified R : Any> Collection<Data<T>>.reduce(
block(map { run { it.await() } }) block(map { run { it.await() } })
} }
fun <K, T : Any, R : Any> Map<K, Data<T>>.reduce( public fun <K, T : Any, R : Any> Map<K, Data<T>>.reduce(
outputType: KClass<out R>, outputType: KClass<out R>,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta, meta: Meta,
@ -145,7 +146,7 @@ fun <K, T : Any, R : Any> Map<K, Data<T>>.reduce(
* @param T type of the input goal * @param T type of the input goal
* @param R type of the result goal * @param R type of the result goal
*/ */
inline fun <K, T : Any, reified R : Any> Map<K, Data<T>>.reduce( public inline fun <K, T : Any, reified R : Any> Map<K, Data<T>>.reduce(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
meta: Meta, meta: Meta,
noinline block: suspend CoroutineScope.(Map<K, T>) -> R noinline block: suspend CoroutineScope.(Map<K, T>) -> R

View File

@ -4,25 +4,25 @@ import hep.dataforge.meta.*
import hep.dataforge.names.toName import hep.dataforge.names.toName
class DataFilter : Scheme() { public class DataFilter : Scheme() {
/** /**
* A source node for the filter * A source node for the filter
*/ */
var from by string() public var from: String? by string()
/** /**
* A target placement for the filtered node * A target placement for the filtered node
*/ */
var to by string() public var to: String? by string()
/** /**
* A regular expression pattern for the filter * A regular expression pattern for the filter
*/ */
var pattern by string(".*") public var pattern: String by string(".*")
// val prefix by string() // val prefix by string()
// val suffix by string() // val suffix by string()
fun isEmpty(): Boolean = config.isEmpty() public fun isEmpty(): Boolean = config.isEmpty()
companion object : SchemeSpec<DataFilter>(::DataFilter) public companion object : SchemeSpec<DataFilter>(::DataFilter)
} }
/** /**

View File

@ -11,12 +11,12 @@ import kotlin.collections.component2
import kotlin.collections.set import kotlin.collections.set
import kotlin.reflect.KClass import kotlin.reflect.KClass
sealed class DataItem<out T : Any> : MetaRepr { public sealed class DataItem<out T : Any> : MetaRepr {
abstract val type: KClass<out T> public abstract val type: KClass<out T>
abstract val meta: Meta public abstract val meta: Meta
class Node<out T : Any>(val node: DataNode<T>) : DataItem<T>() { public class Node<out T : Any>(public val node: DataNode<T>) : DataItem<T>() {
override val type: KClass<out T> get() = node.type override val type: KClass<out T> get() = node.type
override fun toMeta(): Meta = node.toMeta() override fun toMeta(): Meta = node.toMeta()
@ -24,7 +24,7 @@ sealed class DataItem<out T : Any> : MetaRepr {
override val meta: Meta get() = node.meta override val meta: Meta get() = node.meta
} }
class Leaf<out T : Any>(val data: Data<T>) : DataItem<T>() { public class Leaf<out T : Any>(public val data: Data<T>) : DataItem<T>() {
override val type: KClass<out T> get() = data.type override val type: KClass<out T> get() = data.type
override fun toMeta(): Meta = data.toMeta() override fun toMeta(): Meta = data.toMeta()
@ -36,16 +36,16 @@ sealed class DataItem<out T : Any> : MetaRepr {
/** /**
* A tree-like data structure grouped into the node. All data inside the node must inherit its type * A tree-like data structure grouped into the node. All data inside the node must inherit its type
*/ */
interface DataNode<out T : Any> : MetaRepr { public interface DataNode<out T : Any> : MetaRepr {
/** /**
* The minimal common ancestor to all data in the node * The minimal common ancestor to all data in the node
*/ */
val type: KClass<out T> public val type: KClass<out T>
val items: Map<NameToken, DataItem<T>> public val items: Map<NameToken, DataItem<T>>
val meta: Meta public val meta: Meta
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
"type" put (type.simpleName ?: "undefined") "type" put (type.simpleName ?: "undefined")
@ -60,7 +60,7 @@ interface DataNode<out T : Any> : MetaRepr {
* Start computation for all goals in data node and return a job for the whole node * Start computation for all goals in data node and return a job for the whole node
*/ */
@Suppress("DeferredResultUnused") @Suppress("DeferredResultUnused")
fun CoroutineScope.startAll(): Job = launch { public fun CoroutineScope.startAll(): Job = launch {
items.values.forEach { items.values.forEach {
when (it) { when (it) {
is DataItem.Node<*> -> it.node.run { startAll() } is DataItem.Node<*> -> it.node.run { startAll() }
@ -69,36 +69,36 @@ interface DataNode<out T : Any> : MetaRepr {
} }
} }
companion object { public companion object {
const val TYPE = "dataNode" public const val TYPE: String = "dataNode"
operator fun <T : Any> invoke(type: KClass<out T>, block: DataTreeBuilder<T>.() -> Unit) = public operator fun <T : Any> invoke(type: KClass<out T>, block: DataTreeBuilder<T>.() -> Unit): DataTree<T> =
DataTreeBuilder(type).apply(block).build() DataTreeBuilder(type).apply(block).build()
inline operator fun <reified T : Any> invoke(noinline block: DataTreeBuilder<T>.() -> Unit) = public inline operator fun <reified T : Any> invoke(noinline block: DataTreeBuilder<T>.() -> Unit): DataTree<T> =
DataTreeBuilder(T::class).apply(block).build() DataTreeBuilder(T::class).apply(block).build()
fun <T : Any> builder(type: KClass<out T>) = DataTreeBuilder(type) public fun <T : Any> builder(type: KClass<out T>): DataTreeBuilder<T> = DataTreeBuilder(type)
} }
} }
suspend fun <T: Any> DataNode<T>.join(): Unit = coroutineScope { startAll().join() } public suspend fun <T: Any> DataNode<T>.join(): Unit = coroutineScope { startAll().join() }
val <T : Any> DataItem<T>?.node: DataNode<T>? get() = (this as? DataItem.Node<T>)?.node public val <T : Any> DataItem<T>?.node: DataNode<T>? get() = (this as? DataItem.Node<T>)?.node
val <T : Any> DataItem<T>?.data: Data<T>? get() = (this as? DataItem.Leaf<T>)?.data public val <T : Any> DataItem<T>?.data: Data<T>? get() = (this as? DataItem.Leaf<T>)?.data
operator fun <T : Any> DataNode<T>.get(name: Name): DataItem<T>? = when (name.length) { public operator fun <T : Any> DataNode<T>.get(name: Name): DataItem<T>? = when (name.length) {
0 -> error("Empty name") 0 -> error("Empty name")
1 -> items[name.firstOrNull()] 1 -> items[name.firstOrNull()]
else -> get(name.firstOrNull()!!.asName()).node?.get(name.cutFirst()) else -> get(name.firstOrNull()!!.asName()).node?.get(name.cutFirst())
} }
operator fun <T : Any> DataNode<T>.get(name: String): DataItem<T>? = get(name.toName()) public operator fun <T : Any> DataNode<T>.get(name: String): DataItem<T>? = get(name.toName())
/** /**
* Sequence of all children including nodes * Sequence of all children including nodes
*/ */
fun <T : Any> DataNode<T>.asSequence(): Sequence<Pair<Name, DataItem<T>>> = sequence { public fun <T : Any> DataNode<T>.asSequence(): Sequence<Pair<Name, DataItem<T>>> = sequence {
items.forEach { (head, item) -> items.forEach { (head, item) ->
yield(head.asName() to item) yield(head.asName() to item)
if (item is DataItem.Node) { if (item is DataItem.Node) {
@ -112,7 +112,7 @@ fun <T : Any> DataNode<T>.asSequence(): Sequence<Pair<Name, DataItem<T>>> = sequ
/** /**
* Sequence of data entries * Sequence of data entries
*/ */
fun <T : Any> DataNode<T>.dataSequence(): Sequence<Pair<Name, Data<T>>> = sequence { public fun <T : Any> DataNode<T>.dataSequence(): Sequence<Pair<Name, Data<T>>> = sequence {
items.forEach { (head, item) -> items.forEach { (head, item) ->
when (item) { when (item) {
is DataItem.Leaf -> yield(head.asName() to item.data) is DataItem.Leaf -> yield(head.asName() to item.data)
@ -125,9 +125,9 @@ fun <T : Any> DataNode<T>.dataSequence(): Sequence<Pair<Name, Data<T>>> = sequen
} }
} }
operator fun <T : Any> DataNode<T>.iterator(): Iterator<Pair<Name, DataItem<T>>> = asSequence().iterator() public operator fun <T : Any> DataNode<T>.iterator(): Iterator<Pair<Name, DataItem<T>>> = asSequence().iterator()
class DataTree<out T : Any> internal constructor( public class DataTree<out T : Any> internal constructor(
override val type: KClass<out T>, override val type: KClass<out T>,
override val items: Map<NameToken, DataItem<T>>, override val items: Map<NameToken, DataItem<T>>,
override val meta: Meta override val meta: Meta
@ -142,17 +142,17 @@ private sealed class DataTreeBuilderItem<out T : Any> {
* A builder for a DataTree. * A builder for a DataTree.
*/ */
@DFBuilder @DFBuilder
class DataTreeBuilder<T : Any>(val type: KClass<out T>) { public class DataTreeBuilder<T : Any>(public val type: KClass<out T>) {
private val map = HashMap<NameToken, DataTreeBuilderItem<T>>() private val map = HashMap<NameToken, DataTreeBuilderItem<T>>()
private var meta = MetaBuilder() private var meta = MetaBuilder()
operator fun set(token: NameToken, node: DataTreeBuilder<out T>) { public operator fun set(token: NameToken, node: DataTreeBuilder<out T>) {
if (map.containsKey(token)) error("Tree entry with name $token is not empty") if (map.containsKey(token)) error("Tree entry with name $token is not empty")
map[token] = DataTreeBuilderItem.Node(node) map[token] = DataTreeBuilderItem.Node(node)
} }
operator fun set(token: NameToken, data: Data<T>) { public operator fun set(token: NameToken, data: Data<T>) {
if (map.containsKey(token)) error("Tree entry with name $token is not empty") if (map.containsKey(token)) error("Tree entry with name $token is not empty")
map[token] = DataTreeBuilderItem.Leaf(data) map[token] = DataTreeBuilderItem.Leaf(data)
} }
@ -173,7 +173,7 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
} }
} }
operator fun set(name: Name, data: Data<T>) { public operator fun set(name: Name, data: Data<T>) {
when (name.length) { when (name.length) {
0 -> error("Can't add data with empty name") 0 -> error("Can't add data with empty name")
1 -> set(name.firstOrNull()!!, data) 1 -> set(name.firstOrNull()!!, data)
@ -181,7 +181,7 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
} }
} }
operator fun set(name: Name, node: DataTreeBuilder<out T>) { public operator fun set(name: Name, node: DataTreeBuilder<out T>) {
when (name.length) { when (name.length) {
0 -> error("Can't add data with empty name") 0 -> error("Can't add data with empty name")
1 -> set(name.firstOrNull()!!, node) 1 -> set(name.firstOrNull()!!, node)
@ -189,9 +189,9 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
} }
} }
operator fun set(name: Name, node: DataNode<T>) = set(name, node.builder()) public operator fun set(name: Name, node: DataNode<T>): Unit = set(name, node.builder())
operator fun set(name: Name, item: DataItem<T>) = when (item) { public operator fun set(name: Name, item: DataItem<T>): Unit = when (item) {
is DataItem.Node<T> -> set(name, item.node.builder()) is DataItem.Node<T> -> set(name, item.node.builder())
is DataItem.Leaf<T> -> set(name, item.data) is DataItem.Leaf<T> -> set(name, item.data)
} }
@ -199,25 +199,25 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
/** /**
* Append data to node * Append data to node
*/ */
infix fun String.put(data: Data<T>) = set(toName(), data) public infix fun String.put(data: Data<T>): Unit = set(toName(), data)
/** /**
* Append node * Append node
*/ */
infix fun String.put(node: DataNode<T>) = set(toName(), node) public infix fun String.put(node: DataNode<T>): Unit = set(toName(), node)
infix fun String.put(item: DataItem<T>) = set(toName(), item) public infix fun String.put(item: DataItem<T>): Unit = set(toName(), item)
/** /**
* Build and append node * Build and append node
*/ */
infix fun String.put(block: DataTreeBuilder<T>.() -> Unit) = set(toName(), DataTreeBuilder(type).apply(block)) public infix fun String.put(block: DataTreeBuilder<T>.() -> Unit): Unit = set(toName(), DataTreeBuilder(type).apply(block))
/** /**
* Update data with given node data and meta with node meta. * Update data with given node data and meta with node meta.
*/ */
fun update(node: DataNode<T>) { public fun update(node: DataNode<T>) {
node.dataSequence().forEach { node.dataSequence().forEach {
//TODO check if the place is occupied //TODO check if the place is occupied
this[it.first] = it.second this[it.first] = it.second
@ -225,13 +225,13 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
meta.update(node.meta) 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() this.meta = meta.builder()
} }
fun build(): DataTree<T> { public fun build(): DataTree<T> {
val resMap = map.mapValues { (_, value) -> val resMap = map.mapValues { (_, value) ->
when (value) { when (value) {
is DataTreeBuilderItem.Leaf -> DataItem.Leaf(value.value) is DataTreeBuilderItem.Leaf -> DataItem.Leaf(value.value)
@ -242,50 +242,50 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
} }
} }
fun <T : Any> DataTreeBuilder<T>.datum(name: Name, data: Data<T>) { public fun <T : Any> DataTreeBuilder<T>.datum(name: Name, data: Data<T>) {
this[name] = data this[name] = data
} }
fun <T : Any> DataTreeBuilder<T>.datum(name: String, data: Data<T>) { public fun <T : Any> DataTreeBuilder<T>.datum(name: String, data: Data<T>) {
this[name.toName()] = data this[name.toName()] = data
} }
fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, meta: Meta = Meta.EMPTY) { public fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, meta: Meta = Meta.EMPTY) {
this[name] = Data.static(data, meta) this[name] = Data.static(data, meta)
} }
fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) { public fun <T : Any> DataTreeBuilder<T>.static(name: Name, data: T, block: MetaBuilder.() -> Unit = {}) {
this[name] = Data.static(data, Meta(block)) this[name] = Data.static(data, Meta(block))
} }
fun <T : Any> DataTreeBuilder<T>.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) { public fun <T : Any> DataTreeBuilder<T>.static(name: String, data: T, block: MetaBuilder.() -> Unit = {}) {
this[name.toName()] = Data.static(data, Meta(block)) this[name.toName()] = Data.static(data, Meta(block))
} }
fun <T : Any> DataTreeBuilder<T>.node(name: Name, node: DataNode<T>) { public fun <T : Any> DataTreeBuilder<T>.node(name: Name, node: DataNode<T>) {
this[name] = node this[name] = node
} }
fun <T : Any> DataTreeBuilder<T>.node(name: String, node: DataNode<T>) { public fun <T : Any> DataTreeBuilder<T>.node(name: String, node: DataNode<T>) {
this[name.toName()] = node this[name.toName()] = node
} }
inline fun <reified T : Any> DataTreeBuilder<T>.node(name: Name, noinline block: DataTreeBuilder<T>.() -> Unit) { public inline fun <reified T : Any> DataTreeBuilder<T>.node(name: Name, noinline block: DataTreeBuilder<T>.() -> Unit) {
this[name] = DataNode(T::class, block) this[name] = DataNode(T::class, block)
} }
inline fun <reified T : Any> DataTreeBuilder<T>.node(name: String, noinline block: DataTreeBuilder<T>.() -> Unit) { public inline fun <reified T : Any> DataTreeBuilder<T>.node(name: String, noinline block: DataTreeBuilder<T>.() -> Unit) {
this[name.toName()] = DataNode(T::class, block) this[name.toName()] = DataNode(T::class, block)
} }
/** /**
* Generate a mutable builder from this node. Node content is not changed * Generate a mutable builder from this node. Node content is not changed
*/ */
fun <T : Any> DataNode<T>.builder(): DataTreeBuilder<T> = DataTreeBuilder(type).apply { public fun <T : Any> DataNode<T>.builder(): DataTreeBuilder<T> = DataTreeBuilder(type).apply {
dataSequence().forEach { (name, data) -> this[name] = data } dataSequence().forEach { (name, data) -> this[name] = data }
} }
fun <T : Any> DataNode<T>.filter(predicate: (Name, Data<T>) -> Boolean): DataNode<T> = DataNode.invoke(type) { public fun <T : Any> DataNode<T>.filter(predicate: (Name, Data<T>) -> Boolean): DataNode<T> = DataNode.invoke(type) {
dataSequence().forEach { (name, data) -> dataSequence().forEach { (name, data) ->
if (predicate(name, data)) { if (predicate(name, data)) {
this[name] = data this[name] = data
@ -293,4 +293,4 @@ fun <T : Any> DataNode<T>.filter(predicate: (Name, Data<T>) -> Boolean): DataNod
} }
} }
fun <T : Any> DataNode<T>.first(): Data<T>? = dataSequence().first().second public fun <T : Any> DataNode<T>.first(): Data<T>? = dataSequence().first().second

View File

@ -1,37 +1,36 @@
package hep.dataforge.data package hep.dataforge.data
import hep.dataforge.meta.DFExperimental
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
interface Goal<out T> { public interface Goal<out T> {
val dependencies: Collection<Goal<*>> public val dependencies: Collection<Goal<*>>
/** /**
* Returns current running coroutine if the goal is started * Returns current running coroutine if the goal is started
*/ */
val result: Deferred<T>? public val result: Deferred<T>?
/** /**
* Get ongoing computation or start a new one. * Get ongoing computation or start a new one.
* Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations.
*/ */
fun CoroutineScope.startAsync(): Deferred<T> public fun CoroutineScope.startAsync(): Deferred<T>
/** /**
* Reset the computation * Reset the computation
*/ */
fun reset() public fun reset()
companion object { public companion object
}
} }
suspend fun <T> Goal<T>.await(): T = coroutineScope { startAsync().await() } public suspend fun <T> Goal<T>.await(): T = coroutineScope { startAsync().await() }
val Goal<*>.isComplete get() = result?.isCompleted ?: false public val Goal<*>.isComplete: Boolean get() = result?.isCompleted ?: false
open class StaticGoal<T>(val value: T) : Goal<T> { public open class StaticGoal<T>(public val value: T) : Goal<T> {
override val dependencies: Collection<Goal<*>> get() = emptyList() override val dependencies: Collection<Goal<*>> get() = emptyList()
override val result: Deferred<T> = CompletableDeferred(value) override val result: Deferred<T> = CompletableDeferred(value)
@ -42,10 +41,10 @@ open class StaticGoal<T>(val value: T) : Goal<T> {
} }
} }
open class DynamicGoal<T>( public open class DynamicGoal<T>(
val coroutineContext: CoroutineContext = EmptyCoroutineContext, private val coroutineContext: CoroutineContext = EmptyCoroutineContext,
override val dependencies: Collection<Goal<*>> = emptyList(), override val dependencies: Collection<Goal<*>> = emptyList(),
val block: suspend CoroutineScope.() -> T public val block: suspend CoroutineScope.() -> T
) : Goal<T> { ) : Goal<T> {
final override var result: Deferred<T>? = null final override var result: Deferred<T>? = null
@ -55,6 +54,7 @@ open class DynamicGoal<T>(
* Get ongoing computation or start a new one. * Get ongoing computation or start a new one.
* Does not guarantee thread safety. In case of multi-thread access, could create orphan computations. * Does not guarantee thread safety. In case of multi-thread access, could create orphan computations.
*/ */
@DFExperimental
override fun CoroutineScope.startAsync(): Deferred<T> { override fun CoroutineScope.startAsync(): Deferred<T> {
val startedDependencies = this@DynamicGoal.dependencies.map { goal -> val startedDependencies = this@DynamicGoal.dependencies.map { goal ->
goal.run { startAsync() } goal.run { startAsync() }
@ -82,7 +82,7 @@ open class DynamicGoal<T>(
/** /**
* Create a one-to-one goal based on existing goal * Create a one-to-one goal based on existing goal
*/ */
fun <T, R> Goal<T>.map( public fun <T, R> Goal<T>.map(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.(T) -> R block: suspend CoroutineScope.(T) -> R
): Goal<R> = DynamicGoal(coroutineContext, listOf(this)) { ): Goal<R> = DynamicGoal(coroutineContext, listOf(this)) {
@ -92,7 +92,7 @@ fun <T, R> Goal<T>.map(
/** /**
* Create a joining goal. * Create a joining goal.
*/ */
fun <T, R> Collection<Goal<T>>.reduce( public fun <T, R> Collection<Goal<T>>.reduce(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.(Collection<T>) -> R block: suspend CoroutineScope.(Collection<T>) -> R
): Goal<R> = DynamicGoal(coroutineContext, this) { ): Goal<R> = DynamicGoal(coroutineContext, this) {
@ -105,7 +105,7 @@ fun <T, R> Collection<Goal<T>>.reduce(
* @param T type of the input goal * @param T type of the input goal
* @param R type of the result goal * @param R type of the result goal
*/ */
fun <K, T, R> Map<K, Goal<T>>.reduce( public fun <K, T, R> Map<K, Goal<T>>.reduce(
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.(Map<K, T>) -> R block: suspend CoroutineScope.(Map<K, T>) -> R
): Goal<R> = DynamicGoal(coroutineContext, this.values) { ): Goal<R> = DynamicGoal(coroutineContext, this.values) {

View File

@ -19,10 +19,10 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.string import hep.dataforge.meta.string
interface GroupRule { public interface GroupRule {
operator fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>> public operator fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>>
companion object{ public companion object{
/** /**
* Create grouping rule that creates groups for different values of value * Create grouping rule that creates groups for different values of value
* field with name [key] * field with name [key]
@ -31,7 +31,7 @@ interface GroupRule {
* @param defaultTagValue * @param defaultTagValue
* @return * @return
*/ */
fun byValue(key: String, defaultTagValue: String): GroupRule = object : public fun byValue(key: String, defaultTagValue: String): GroupRule = object :
GroupRule { GroupRule {
override fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>> { override fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>> {
val map = HashMap<String, DataTreeBuilder<T>>() val map = HashMap<String, DataTreeBuilder<T>>()
@ -52,7 +52,7 @@ interface GroupRule {
// def = "default", // def = "default",
// info = "Default value which should be used for content in which the grouping value is not presented" // 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 //TODO expand grouping options
return config["byValue"]?.string?.let { return config["byValue"]?.string?.let {
byValue( byValue(

View File

@ -7,32 +7,31 @@ import kotlin.reflect.KClass
/** /**
* Action environment includes data name, data meta and action configuration meta * Action environment includes data name, data meta and action configuration meta
*/ */
data class ActionEnv( public data class ActionEnv(
val name: Name, val name: Name,
val meta: Meta, val meta: Meta,
val actionMeta: Meta val actionMeta: Meta
) )
/** /**
* Action environment * Action environment
*/ */
@DFBuilder @DFBuilder
class MapActionBuilder<T, R>(var name: Name, var meta: MetaBuilder, val actionMeta: Meta) { public class MapActionBuilder<T, R>(public var name: Name, public var meta: MetaBuilder, public val actionMeta: Meta) {
lateinit var result: suspend ActionEnv.(T) -> R public lateinit var result: suspend ActionEnv.(T) -> R
/** /**
* Calculate the result of goal * Calculate the result of goal
*/ */
fun result(f: suspend ActionEnv.(T) -> R) { public fun result(f: suspend ActionEnv.(T) -> R) {
result = f; result = f;
} }
} }
class MapAction<T : Any, out R : Any>( public class MapAction<T : Any, out R : Any>(
val inputType: KClass<T>, public val inputType: KClass<T>,
val outputType: KClass<out R>, public val outputType: KClass<out R>,
private val block: MapActionBuilder<T, R>.() -> Unit private val block: MapActionBuilder<T, R>.() -> Unit
) : Action<T, R> { ) : Action<T, R> {
@ -67,7 +66,7 @@ class MapAction<T : Any, out R : Any>(
} }
} }
inline fun <reified T : Any, reified R : Any> DataNode<T>.map( public inline fun <reified T : Any, reified R : Any> DataNode<T>.map(
meta: Meta, meta: Meta,
noinline action: MapActionBuilder<in T, out R>.() -> Unit noinline action: MapActionBuilder<in T, out R>.() -> Unit
): DataNode<R> = MapAction(T::class, R::class, action).invoke(this, meta) ): DataNode<R> = MapAction(T::class, R::class, action).invoke(this, meta)

View File

@ -7,25 +7,25 @@ import hep.dataforge.names.toName
import kotlin.reflect.KClass import kotlin.reflect.KClass
class JoinGroup<T : Any, R : Any>(var name: String, internal val node: DataNode<T>) { public class JoinGroup<T : Any, R : Any>(public var name: String, internal val node: DataNode<T>) {
var meta: MetaBuilder = MetaBuilder() public var meta: MetaBuilder = MetaBuilder()
lateinit var result: suspend ActionEnv.(Map<Name, T>) -> R public lateinit var result: suspend ActionEnv.(Map<Name, T>) -> R
fun result(f: suspend ActionEnv.(Map<Name, T>) -> R) { public fun result(f: suspend ActionEnv.(Map<Name, T>) -> R) {
this.result = f; this.result = f;
} }
} }
class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) { public class ReduceGroupBuilder<T : Any, R : Any>(public val actionMeta: Meta) {
private val groupRules: MutableList<(DataNode<T>) -> List<JoinGroup<T, R>>> = ArrayList(); private val groupRules: MutableList<(DataNode<T>) -> List<JoinGroup<T, R>>> = ArrayList();
/** /**
* introduce grouping by value name * introduce grouping by value name
*/ */
fun byValue(tag: String, defaultTag: String = "@default", action: JoinGroup<T, R>.() -> Unit) { public fun byValue(tag: String, defaultTag: String = "@default", action: JoinGroup<T, R>.() -> Unit) {
groupRules += { node -> groupRules += { node ->
GroupRule.byValue(tag, defaultTag).invoke(node).map { GroupRule.byValue(tag, defaultTag).invoke(node).map {
JoinGroup<T, R>(it.key, it.value).apply(action) JoinGroup<T, R>(it.key, it.value).apply(action)
@ -36,7 +36,7 @@ class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) {
/** /**
* Add a single fixed group to grouping rules * Add a single fixed group to grouping rules
*/ */
fun group(groupName: String, filter: DataFilter, action: JoinGroup<T, R>.() -> Unit) { public fun group(groupName: String, filter: DataFilter, action: JoinGroup<T, R>.() -> Unit) {
groupRules += { node -> groupRules += { node ->
listOf( listOf(
JoinGroup<T, R>(groupName, node.filter(filter)).apply(action) JoinGroup<T, R>(groupName, node.filter(filter)).apply(action)
@ -44,7 +44,7 @@ class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) {
} }
} }
fun group(groupName: String, filter: (Name, Data<T>) -> Boolean, action: JoinGroup<T, R>.() -> Unit) { public fun group(groupName: String, filter: (Name, Data<T>) -> Boolean, action: JoinGroup<T, R>.() -> Unit) {
groupRules += { node -> groupRules += { node ->
listOf( listOf(
JoinGroup<T, R>(groupName, node.filter(filter)).apply(action) JoinGroup<T, R>(groupName, node.filter(filter)).apply(action)
@ -55,7 +55,7 @@ class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) {
/** /**
* Apply transformation to the whole node * Apply transformation to the whole node
*/ */
fun result(resultName: String, f: suspend ActionEnv.(Map<Name, T>) -> R) { public fun result(resultName: String, f: suspend ActionEnv.(Map<Name, T>) -> R) {
groupRules += { node -> groupRules += { node ->
listOf(JoinGroup<T, R>(resultName, node).apply { result(f) }) listOf(JoinGroup<T, R>(resultName, node).apply { result(f) })
} }
@ -71,9 +71,9 @@ class ReduceGroupBuilder<T : Any, R : Any>(val actionMeta: Meta) {
/** /**
* The same rules as for KPipe * The same rules as for KPipe
*/ */
class ReduceAction<T : Any, R : Any>( public class ReduceAction<T : Any, R : Any>(
val inputType: KClass<T>, public val inputType: KClass<T>,
val outputType: KClass<out R>, public val outputType: KClass<out R>,
private val action: ReduceGroupBuilder<T, R>.() -> Unit private val action: ReduceGroupBuilder<T, R>.() -> Unit
) : Action<T, R> { ) : Action<T, R> {
@ -104,4 +104,4 @@ class ReduceAction<T : Any, R : Any>(
} }
} }
operator fun <T> Map<Name, T>.get(name: String) = get(name.toName()) public operator fun <T> Map<Name, T>.get(name: String): T? = get(name.toName())

View File

@ -10,16 +10,16 @@ import kotlin.collections.set
import kotlin.reflect.KClass import kotlin.reflect.KClass
class FragmentRule<T : Any, R : Any>(val name: Name, var meta: MetaBuilder) { public class FragmentRule<T : Any, R : Any>(public val name: Name, public var meta: MetaBuilder) {
lateinit var result: suspend (T) -> R public lateinit var result: suspend (T) -> R
fun result(f: suspend (T) -> R) { public fun result(f: suspend (T) -> R) {
result = f; result = f;
} }
} }
class SplitBuilder<T : Any, R : Any>(val name: Name, val meta: Meta) { public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val meta: Meta) {
internal val fragments: MutableMap<Name, FragmentRule<T, R>.() -> Unit> = HashMap() internal val fragments: MutableMap<Name, FragmentRule<T, R>.() -> Unit> = HashMap()
/** /**
@ -27,14 +27,14 @@ class SplitBuilder<T : Any, R : Any>(val name: Name, val meta: Meta) {
* @param name the name of a fragment * @param name the name of a fragment
* @param rule the rule to transform fragment name and meta using * @param rule the rule to transform fragment name and meta using
*/ */
fun fragment(name: String, rule: FragmentRule<T, R>.() -> Unit) { public fun fragment(name: String, rule: FragmentRule<T, R>.() -> Unit) {
fragments[name.toName()] = rule fragments[name.toName()] = rule
} }
} }
class SplitAction<T : Any, R : Any>( public class SplitAction<T : Any, R : Any>(
val inputType: KClass<T>, public val inputType: KClass<T>,
val outputType: KClass<out R>, public val outputType: KClass<out R>,
private val action: SplitBuilder<T, R>.() -> Unit private val action: SplitBuilder<T, R>.() -> Unit
) : Action<T, R> { ) : Action<T, R> {

View File

@ -6,7 +6,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlin.reflect.KClass import kotlin.reflect.KClass
fun <R : Any, T : R> Data<T>.upcast(type: KClass<out R>): Data<R> { public fun <R : Any, T : R> Data<T>.upcast(type: KClass<out R>): Data<R> {
return object : Data<R> by this { return object : Data<R> by this {
override val type: KClass<out R> = type override val type: KClass<out R> = type
} }
@ -15,7 +15,7 @@ fun <R : Any, T : R> Data<T>.upcast(type: KClass<out R>): Data<R> {
/** /**
* Safe upcast a [Data] to a supertype * Safe upcast a [Data] to a supertype
*/ */
inline fun <reified R : Any, T : R> Data<T>.upcast(): Data<R> = upcast(R::class) public inline fun <reified R : Any, T : R> Data<T>.upcast(): Data<R> = upcast(R::class)
/** /**
* Check if node could be safely cast to given class * Check if node could be safely cast to given class
@ -27,7 +27,7 @@ internal expect fun <R : Any> DataNode<*>.canCast(type: KClass<out R>): Boolean
*/ */
internal expect fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean internal expect fun <R : Any> Data<*>.canCast(type: KClass<out R>): Boolean
fun <R : Any> DataItem<*>.canCast(type: KClass<out R>): Boolean = when (this) { public fun <R : Any> DataItem<*>.canCast(type: KClass<out R>): Boolean = when (this) {
is DataItem.Node -> node.canCast(type) is DataItem.Node -> node.canCast(type)
is DataItem.Leaf -> data.canCast(type) is DataItem.Leaf -> data.canCast(type)
} }
@ -36,7 +36,7 @@ fun <R : Any> DataItem<*>.canCast(type: KClass<out R>): Boolean = when (this) {
* Unsafe cast of data node * Unsafe cast of data node
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <R : Any> Data<*>.cast(type: KClass<out R>): Data<R> { public fun <R : Any> Data<*>.cast(type: KClass<out R>): Data<R> {
return object : Data<R> { return object : Data<R> {
override val meta: Meta get() = this@cast.meta override val meta: Meta get() = this@cast.meta
override val dependencies: Collection<Goal<*>> get() = this@cast.dependencies override val dependencies: Collection<Goal<*>> get() = this@cast.dependencies
@ -47,10 +47,10 @@ fun <R : Any> Data<*>.cast(type: KClass<out R>): Data<R> {
} }
} }
inline fun <reified R : Any> Data<*>.cast(): Data<R> = cast(R::class) public inline fun <reified R : Any> Data<*>.cast(): Data<R> = cast(R::class)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <R : Any> DataNode<*>.cast(type: KClass<out R>): DataNode<R> { public fun <R : Any> DataNode<*>.cast(type: KClass<out R>): DataNode<R> {
return object : DataNode<R> { return object : DataNode<R> {
override val meta: Meta get() = this@cast.meta override val meta: Meta get() = this@cast.meta
override val type: KClass<out R> = type override val type: KClass<out R> = type
@ -58,12 +58,12 @@ fun <R : Any> DataNode<*>.cast(type: KClass<out R>): DataNode<R> {
} }
} }
inline fun <reified R : Any> DataNode<*>.cast(): DataNode<R> = cast(R::class) public inline fun <reified R : Any> DataNode<*>.cast(): DataNode<R> = cast(R::class)
/** /**
* Check that node is compatible with given type meaning that each element could be cast to the type * Check that node is compatible with given type meaning that each element could be cast to the type
*/ */
fun <T : Any> DataNode<*>.ensureType(type: KClass<out T>) { public fun <T : Any> DataNode<*>.ensureType(type: KClass<out T>) {
if (!canCast(type)) { if (!canCast(type)) {
error("$type expected, but $type received") error("$type expected, but $type received")
} }

View File

@ -8,7 +8,7 @@ import kotlin.reflect.KClass
/** /**
* A zero-copy data node wrapper that returns only children with appropriate type. * A zero-copy data node wrapper that returns only children with appropriate type.
*/ */
class TypeFilteredDataNode<out T : Any>(val origin: DataNode<*>, override val type: KClass<out T>) : DataNode<T> { public class TypeFilteredDataNode<out T : Any>(public val origin: DataNode<*>, override val type: KClass<out T>) : DataNode<T> {
override val meta: Meta get() = origin.meta override val meta: Meta get() = origin.meta
override val items: Map<NameToken, DataItem<T>> by lazy { override val items: Map<NameToken, DataItem<T>> by lazy {
origin.items.mapNotNull { (key, item) -> origin.items.mapNotNull { (key, item) ->

View File

@ -39,10 +39,10 @@ public fun <R : Any> DataNode<*>.filterIsInstance(type: KClass<out R>): DataNode
/** /**
* Filter all elements of given data item that could be cast to given type. If no elements are available, return null. * Filter all elements of given data item that could be cast to given type. If no elements are available, return null.
*/ */
fun <R : Any> DataItem<*>?.filterIsInstance(type: KClass<out R>): DataItem<R>? = when (this) { public fun <R : Any> DataItem<*>?.filterIsInstance(type: KClass<out R>): DataItem<R>? = when (this) {
null -> null null -> null
is DataItem.Node -> DataItem.Node(this.node.filterIsInstance(type)) is DataItem.Node -> DataItem.Node(this.node.filterIsInstance(type))
is DataItem.Leaf -> this.data.filterIsInstance(type)?.let { DataItem.Leaf(it) } is DataItem.Leaf -> this.data.filterIsInstance(type)?.let { DataItem.Leaf(it) }
} }
inline fun <reified R : Any> DataItem<*>?.filterIsInstance(): DataItem<R>? = this@filterIsInstance.filterIsInstance(R::class) public inline fun <reified R : Any> DataItem<*>?.filterIsInstance(): DataItem<R>? = this@filterIsInstance.filterIsInstance(R::class)

View File

@ -15,7 +15,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
public val ioFormatFactories: Collection<IOFormatFactory<*>> by lazy { public val ioFormatFactories: Collection<IOFormatFactory<*>> by lazy {
context.resolve<IOFormatFactory<*>>(IO_FORMAT_TYPE).values context.gather<IOFormatFactory<*>>(IO_FORMAT_TYPE).values
} }
public fun <T : Any> resolveIOFormat(item: MetaItem<*>, type: KClass<out T>): IOFormat<T>? { public fun <T : Any> resolveIOFormat(item: MetaItem<*>, type: KClass<out T>): IOFormat<T>? {
@ -30,7 +30,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
public val metaFormatFactories: Collection<MetaFormatFactory> by lazy { public val metaFormatFactories: Collection<MetaFormatFactory> by lazy {
context.resolve<MetaFormatFactory>(META_FORMAT_TYPE).values context.gather<MetaFormatFactory>(META_FORMAT_TYPE).values
} }
public fun resolveMetaFormat(key: Short, meta: Meta = Meta.EMPTY): MetaFormat? = 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) metaFormatFactories.find { it.shortName == name }?.invoke(meta)
public val envelopeFormatFactories: Collection<EnvelopeFormatFactory> by lazy { public val envelopeFormatFactories: Collection<EnvelopeFormatFactory> by lazy {
context.resolve<EnvelopeFormatFactory>(ENVELOPE_FORMAT_TYPE).values context.gather<EnvelopeFormatFactory>(ENVELOPE_FORMAT_TYPE).values
} }
private fun resolveEnvelopeFormat(name: Name, meta: Meta = Meta.EMPTY): EnvelopeFormat? = 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) return resolveEnvelopeFormat(name.toName(), meta)
} }
override fun provideTop(target: String): Map<Name, Any> { override fun content(target: String): Map<Name, Any> {
return when (target) { return when (target) {
META_FORMAT_TYPE -> defaultMetaFormats.toMap() META_FORMAT_TYPE -> defaultMetaFormats.toMap()
ENVELOPE_FORMAT_TYPE -> defaultEnvelopeFormats.toMap() ENVELOPE_FORMAT_TYPE -> defaultEnvelopeFormats.toMap()
else -> super.provideTop(target) else -> super.content(target)
} }
} }

View File

@ -17,8 +17,6 @@ public class Laminate(layers: List<Meta>) : MetaBase() {
} }
} }
public constructor(vararg layers: Meta?) : this(layers.filterNotNull())
override val items: Map<NameToken, MetaItem<Meta>> by lazy { override val items: Map<NameToken, MetaItem<Meta>> by lazy {
layers.map { it.items.keys }.flatten().associateWith { key -> layers.map { it.items.keys }.flatten().associateWith { key ->
layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule) layers.asSequence().map { it.items[key] }.filterNotNull().let(replaceRule)
@ -80,6 +78,9 @@ public class Laminate(layers: List<Meta>) : MetaBase() {
} }
} }
@Suppress("FunctionName")
public fun Laminate(vararg layers: Meta?): Laminate = Laminate(layers.filterNotNull())
/** /**
* Performance optimized version of get method * Performance optimized version of get method
*/ */

View File

@ -51,7 +51,7 @@ public interface Value {
override fun hashCode(): Int override fun hashCode(): Int
public companion object { public companion object {
public const val TYPE: String = "value" public const val TARGET: String = "value"
/** /**
* Convert object to value * Convert object to value

View File

@ -31,7 +31,7 @@ public interface Workspace : ContextAware, Provider {
*/ */
public val tasks: Map<Name, Task<*>> public val tasks: Map<Name, Task<*>>
override fun provideTop(target: String): Map<Name, Any> { override fun content(target: String): Map<Name, Any> {
return when (target) { return when (target) {
"target", Meta.TYPE -> targets.mapKeys { it.key.toName() } "target", Meta.TYPE -> targets.mapKeys { it.key.toName() }
Task.TYPE -> tasks Task.TYPE -> tasks

View File

@ -13,7 +13,7 @@ public abstract class WorkspacePlugin : AbstractPlugin() {
private val _tasks = HashSet<Task<*>>() private val _tasks = HashSet<Task<*>>()
public val tasks: Collection<Task<*>> get() = _tasks public val tasks: Collection<Task<*>> get() = _tasks
override fun provideTop(target: String): Map<Name, Any> { override fun content(target: String): Map<Name, Any> {
return when (target) { return when (target) {
Task.TYPE -> tasks.toMap() Task.TYPE -> tasks.toMap()
else -> emptyMap() else -> emptyMap()

View File

@ -8,8 +8,8 @@ pluginManagement {
maven("https://dl.bintray.com/mipt-npm/dev") maven("https://dl.bintray.com/mipt-npm/dev")
} }
val toolsVersion = "0.6.0-dev-4" val toolsVersion = "0.6.0"
val kotlinVersion = "1.4.0" val kotlinVersion = "1.4.10"
plugins { plugins {
id("ru.mipt.npm.mpp") version toolsVersion id("ru.mipt.npm.mpp") version toolsVersion