Context content resolution refactor.
Explicit API mode.
This commit is contained in:
parent
d170f5d60c
commit
b229de3eb7
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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<Any>()
|
||||
|
||||
/**
|
||||
* 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<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]
|
||||
*/
|
||||
@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<Name, Any> {
|
||||
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<Name, Any> = 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,21 +112,14 @@ 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 <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
|
||||
|
@ -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<*>>(PluginFactory.TYPE).values
|
||||
@OptIn(DFExperimental::class)
|
||||
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")
|
||||
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)
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
|
||||
}
|
@ -2,9 +2,11 @@ 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<T : Plugin> : Factory<T> {
|
||||
public val tag: PluginTag
|
||||
public val type: KClass<out T>
|
||||
@ -23,6 +25,8 @@ public interface PluginFactory<T : Plugin> : Factory<T> {
|
||||
*/
|
||||
public class PluginManager(override val context: Context) : ContextAware, Iterable<Plugin> {
|
||||
|
||||
//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<Plugin> {
|
||||
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<Plugin> {
|
||||
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 <T : Plugin> fetch(
|
||||
factory: PluginFactory<T>,
|
||||
recursive: Boolean = true,
|
||||
metaBuilder: MetaBuilder.() -> Unit
|
||||
metaBuilder: MetaBuilder.() -> Unit,
|
||||
): T = fetch(factory, recursive, Meta(metaBuilder))
|
||||
|
||||
override fun iterator(): Iterator<Plugin> = plugins.iterator()
|
||||
|
@ -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 }
|
@ -42,7 +42,7 @@ public inline class Path(public val tokens: List<PathToken>) : Iterable<PathToke
|
||||
override fun iterator(): Iterator<PathToken> = 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)
|
||||
|
@ -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<Name, Any> = emptyMap()
|
||||
public fun content(target: String): Map<Name, Any> = 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 <reified T : Any> Provider.provide(path: String, targetOverrid
|
||||
//inline fun <reified T : Any> Provider.provide(target: String, name: Name): T? {
|
||||
// return provide(PathToken(name, target).toPath()) as? T
|
||||
//}
|
||||
//
|
||||
|
||||
//inline fun <reified T : Any> Provider.provide(target: String, name: String): T? =
|
||||
// provide(target, name.toName())
|
||||
|
||||
/**
|
||||
* Typed top level content
|
||||
*/
|
||||
public inline fun <reified T : Any> Provider.top(target: String): Map<Name, T> {
|
||||
return provideTop(target).mapValues {
|
||||
it.value as? T ?: error("The type of element $it is ${it::class} but ${T::class} is expected")
|
||||
public fun <T : Any> Provider.top(target: String, type: KClass<out T>): Map<Name, T> {
|
||||
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 <reified T : Any> Provider.top(target: String): Map<Name, T> = top(target, T::class)
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ class ContextTest {
|
||||
class DummyPlugin : AbstractPlugin() {
|
||||
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){
|
||||
"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<Name>("test")
|
||||
val members = Global.gather<Name>("test")
|
||||
assertEquals(3, members.count())
|
||||
members.forEach {
|
||||
assertEquals(it.key, it.value.appendLeft("test"))
|
||||
|
@ -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])
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
*/
|
||||
val type: KClass<out T>
|
||||
public val type: KClass<out T>
|
||||
|
||||
/**
|
||||
* 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<out T : Any> : Goal<T>, MetaRepr{
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE = "data"
|
||||
public companion object {
|
||||
public const val TYPE: String = "data"
|
||||
|
||||
operator fun <T : Any> invoke(
|
||||
public operator fun <T : Any> invoke(
|
||||
type: KClass<out T>,
|
||||
meta: Meta = Meta.EMPTY,
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
@ -39,14 +40,14 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr{
|
||||
block: suspend CoroutineScope.() -> T
|
||||
): 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,
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
dependencies: Collection<Data<*>> = emptyList(),
|
||||
noinline block: suspend CoroutineScope.() -> T
|
||||
): Data<T> = invoke(T::class, meta, context, dependencies, block)
|
||||
|
||||
operator fun <T : Any> invoke(
|
||||
public operator fun <T : Any> invoke(
|
||||
name: String,
|
||||
type: KClass<out T>,
|
||||
meta: Meta = Meta.EMPTY,
|
||||
@ -55,7 +56,7 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr{
|
||||
block: suspend CoroutineScope.() -> T
|
||||
): 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,
|
||||
meta: Meta = Meta.EMPTY,
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
@ -64,13 +65,13 @@ public interface Data<out T : Any> : Goal<T>, MetaRepr{
|
||||
): Data<T> =
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DynamicData<T : Any>(
|
||||
public class DynamicData<T : Any>(
|
||||
override val type: KClass<out T>,
|
||||
override val meta: Meta = Meta.EMPTY,
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
@ -78,16 +79,16 @@ class DynamicData<T : Any>(
|
||||
block: suspend CoroutineScope.() -> T
|
||||
) : Data<T>, DynamicGoal<T>(context, dependencies, block)
|
||||
|
||||
class StaticData<T : Any>(
|
||||
public class StaticData<T : Any>(
|
||||
value: T,
|
||||
override val meta: Meta = Meta.EMPTY
|
||||
) : Data<T>, StaticGoal<T>(value) {
|
||||
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>,
|
||||
coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||
meta: Meta = this.meta,
|
||||
@ -100,7 +101,7 @@ fun <T : Any, R : Any> Data<T>.map(
|
||||
/**
|
||||
* 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,
|
||||
meta: Meta = this.meta,
|
||||
noinline block: suspend CoroutineScope.(T) -> R
|
||||
@ -111,7 +112,7 @@ inline fun <T : Any, reified R : Any> Data<T>.map(
|
||||
/**
|
||||
* 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,
|
||||
meta: Meta,
|
||||
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() } })
|
||||
}
|
||||
|
||||
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>,
|
||||
coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||
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 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,
|
||||
meta: Meta,
|
||||
noinline block: suspend CoroutineScope.(Map<K, T>) -> R
|
||||
|
@ -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>(::DataFilter)
|
||||
public companion object : SchemeSpec<DataFilter>(::DataFilter)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,12 +11,12 @@ import kotlin.collections.component2
|
||||
import kotlin.collections.set
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
sealed class DataItem<out T : Any> : MetaRepr {
|
||||
abstract val type: KClass<out T>
|
||||
public sealed class DataItem<out T : Any> : MetaRepr {
|
||||
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 fun toMeta(): Meta = node.toMeta()
|
||||
@ -24,7 +24,7 @@ sealed class DataItem<out T : Any> : MetaRepr {
|
||||
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 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
|
||||
*/
|
||||
interface DataNode<out T : Any> : MetaRepr {
|
||||
public interface DataNode<out T : Any> : MetaRepr {
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
"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
|
||||
*/
|
||||
@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<out T : Any> : MetaRepr {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE = "dataNode"
|
||||
public companion object {
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
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
|
||||
val <T : Any> DataItem<T>?.data: Data<T>? get() = (this as? DataItem.Leaf<T>)?.data
|
||||
public val <T : Any> DataItem<T>?.node: DataNode<T>? get() = (this as? DataItem.Node<T>)?.node
|
||||
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")
|
||||
1 -> items[name.firstOrNull()]
|
||||
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
|
||||
*/
|
||||
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) ->
|
||||
yield(head.asName() to item)
|
||||
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
|
||||
*/
|
||||
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) ->
|
||||
when (item) {
|
||||
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 items: Map<NameToken, DataItem<T>>,
|
||||
override val meta: Meta
|
||||
@ -142,17 +142,17 @@ private sealed class DataTreeBuilderItem<out T : Any> {
|
||||
* A builder for a DataTree.
|
||||
*/
|
||||
@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 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")
|
||||
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")
|
||||
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) {
|
||||
0 -> error("Can't add data with empty name")
|
||||
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) {
|
||||
0 -> error("Can't add data with empty name")
|
||||
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.Leaf<T> -> set(name, item.data)
|
||||
}
|
||||
@ -199,25 +199,25 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
fun update(node: DataNode<T>) {
|
||||
public fun update(node: DataNode<T>) {
|
||||
node.dataSequence().forEach {
|
||||
//TODO check if the place is occupied
|
||||
this[it.first] = it.second
|
||||
@ -225,13 +225,13 @@ class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
|
||||
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<T> {
|
||||
public fun build(): DataTree<T> {
|
||||
val resMap = map.mapValues { (_, value) ->
|
||||
when (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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
|
||||
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) ->
|
||||
if (predicate(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
|
@ -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<out T> {
|
||||
val dependencies: Collection<Goal<*>>
|
||||
public interface Goal<out T> {
|
||||
public val dependencies: Collection<Goal<*>>
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
*/
|
||||
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 result: Deferred<T> = CompletableDeferred(value)
|
||||
|
||||
@ -42,10 +41,10 @@ open class StaticGoal<T>(val value: T) : Goal<T> {
|
||||
}
|
||||
}
|
||||
|
||||
open class DynamicGoal<T>(
|
||||
val coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||
public open class DynamicGoal<T>(
|
||||
private val coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||
override val dependencies: Collection<Goal<*>> = emptyList(),
|
||||
val block: suspend CoroutineScope.() -> T
|
||||
public val block: suspend CoroutineScope.() -> T
|
||||
) : Goal<T> {
|
||||
|
||||
final override var result: Deferred<T>? = null
|
||||
@ -55,6 +54,7 @@ open class DynamicGoal<T>(
|
||||
* 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<T> {
|
||||
val startedDependencies = this@DynamicGoal.dependencies.map { goal ->
|
||||
goal.run { startAsync() }
|
||||
@ -82,7 +82,7 @@ open class DynamicGoal<T>(
|
||||
/**
|
||||
* 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,
|
||||
block: suspend CoroutineScope.(T) -> R
|
||||
): Goal<R> = DynamicGoal(coroutineContext, listOf(this)) {
|
||||
@ -92,7 +92,7 @@ fun <T, R> Goal<T>.map(
|
||||
/**
|
||||
* Create a joining goal.
|
||||
*/
|
||||
fun <T, R> Collection<Goal<T>>.reduce(
|
||||
public fun <T, R> Collection<Goal<T>>.reduce(
|
||||
coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||
block: suspend CoroutineScope.(Collection<T>) -> R
|
||||
): Goal<R> = DynamicGoal(coroutineContext, this) {
|
||||
@ -105,7 +105,7 @@ fun <T, R> Collection<Goal<T>>.reduce(
|
||||
* @param T type of the input 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,
|
||||
block: suspend CoroutineScope.(Map<K, T>) -> R
|
||||
): Goal<R> = DynamicGoal(coroutineContext, this.values) {
|
||||
|
@ -19,10 +19,10 @@ import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.string
|
||||
|
||||
interface GroupRule {
|
||||
operator fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>>
|
||||
public interface GroupRule {
|
||||
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
|
||||
* 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 <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>> {
|
||||
val map = HashMap<String, DataTreeBuilder<T>>()
|
||||
@ -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(
|
||||
|
@ -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<T, R>(var name: Name, var meta: MetaBuilder, val actionMeta: Meta) {
|
||||
lateinit var result: suspend ActionEnv.(T) -> R
|
||||
public class MapActionBuilder<T, R>(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<T : Any, out R : Any>(
|
||||
val inputType: KClass<T>,
|
||||
val outputType: KClass<out R>,
|
||||
public class MapAction<T : Any, out R : Any>(
|
||||
public val inputType: KClass<T>,
|
||||
public val outputType: KClass<out R>,
|
||||
private val block: MapActionBuilder<T, R>.() -> Unit
|
||||
) : 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,
|
||||
noinline action: MapActionBuilder<in T, out R>.() -> Unit
|
||||
): DataNode<R> = MapAction(T::class, R::class, action).invoke(this, meta)
|
||||
|
@ -7,25 +7,25 @@ import hep.dataforge.names.toName
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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 ->
|
||||
GroupRule.byValue(tag, defaultTag).invoke(node).map {
|
||||
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
|
||||
*/
|
||||
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 ->
|
||||
listOf(
|
||||
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 ->
|
||||
listOf(
|
||||
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
|
||||
*/
|
||||
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 ->
|
||||
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
|
||||
*/
|
||||
class ReduceAction<T : Any, R : Any>(
|
||||
val inputType: KClass<T>,
|
||||
val outputType: KClass<out R>,
|
||||
public class ReduceAction<T : Any, R : Any>(
|
||||
public val inputType: KClass<T>,
|
||||
public val outputType: KClass<out R>,
|
||||
private val action: ReduceGroupBuilder<T, R>.() -> Unit
|
||||
) : 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())
|
||||
|
@ -10,16 +10,16 @@ import kotlin.collections.set
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
class FragmentRule<T : Any, R : Any>(val name: Name, var meta: MetaBuilder) {
|
||||
lateinit var result: suspend (T) -> R
|
||||
public class FragmentRule<T : Any, R : Any>(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<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()
|
||||
|
||||
/**
|
||||
@ -27,14 +27,14 @@ class SplitBuilder<T : Any, R : Any>(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<T, R>.() -> Unit) {
|
||||
public fun fragment(name: String, rule: FragmentRule<T, R>.() -> Unit) {
|
||||
fragments[name.toName()] = rule
|
||||
}
|
||||
}
|
||||
|
||||
class SplitAction<T : Any, R : Any>(
|
||||
val inputType: KClass<T>,
|
||||
val outputType: KClass<out R>,
|
||||
public class SplitAction<T : Any, R : Any>(
|
||||
public val inputType: KClass<T>,
|
||||
public val outputType: KClass<out R>,
|
||||
private val action: SplitBuilder<T, R>.() -> Unit
|
||||
) : Action<T, R> {
|
||||
|
||||
|
@ -6,7 +6,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
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 {
|
||||
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
|
||||
*/
|
||||
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
|
||||
@ -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
|
||||
|
||||
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.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
|
||||
*/
|
||||
@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> {
|
||||
override val meta: Meta get() = this@cast.meta
|
||||
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")
|
||||
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> {
|
||||
override val meta: Meta get() = this@cast.meta
|
||||
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
|
||||
*/
|
||||
fun <T : Any> DataNode<*>.ensureType(type: KClass<out T>) {
|
||||
public fun <T : Any> DataNode<*>.ensureType(type: KClass<out T>) {
|
||||
if (!canCast(type)) {
|
||||
error("$type expected, but $type received")
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import kotlin.reflect.KClass
|
||||
/**
|
||||
* 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 items: Map<NameToken, DataItem<T>> by lazy {
|
||||
origin.items.mapNotNull { (key, item) ->
|
||||
|
@ -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.
|
||||
*/
|
||||
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
|
||||
is DataItem.Node -> DataItem.Node(this.node.filterIsInstance(type))
|
||||
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)
|
@ -15,7 +15,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
|
||||
override val tag: PluginTag get() = Companion.tag
|
||||
|
||||
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>? {
|
||||
@ -30,7 +30,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
|
||||
|
||||
|
||||
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? =
|
||||
@ -40,7 +40,7 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
|
||||
metaFormatFactories.find { it.shortName == name }?.invoke(meta)
|
||||
|
||||
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? =
|
||||
@ -52,11 +52,11 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(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) {
|
||||
META_FORMAT_TYPE -> defaultMetaFormats.toMap()
|
||||
ENVELOPE_FORMAT_TYPE -> defaultEnvelopeFormats.toMap()
|
||||
else -> super.provideTop(target)
|
||||
else -> super.content(target)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
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<Meta>) : MetaBase() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
public fun Laminate(vararg layers: Meta?): Laminate = Laminate(layers.filterNotNull())
|
||||
|
||||
/**
|
||||
* Performance optimized version of get method
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -31,7 +31,7 @@ public interface Workspace : ContextAware, Provider {
|
||||
*/
|
||||
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) {
|
||||
"target", Meta.TYPE -> targets.mapKeys { it.key.toName() }
|
||||
Task.TYPE -> tasks
|
||||
|
@ -13,7 +13,7 @@ public abstract class WorkspacePlugin : AbstractPlugin() {
|
||||
private val _tasks = HashSet<Task<*>>()
|
||||
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) {
|
||||
Task.TYPE -> tasks.toMap()
|
||||
else -> emptyMap()
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user