Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Alexander Nozik 2019-03-18 20:33:36 +03:00
commit 47e39ae0ac
14 changed files with 154 additions and 39 deletions

4
.gitignore vendored
View File

@ -1,9 +1,9 @@
.idea/ .idea/
*.iws *.iws
*/out/** out/
.gradle .gradle
*/build/** build/
!gradle-wrapper.jar !gradle-wrapper.jar

View File

@ -40,7 +40,7 @@ allprojects {
} }
group = "hep.dataforge" group = "hep.dataforge"
version = "0.1.1-dev-5" version = "0.1.2-dev-1"
// apply bintray configuration // apply bintray configuration
apply(from = "${rootProject.rootDir}/gradle/bintray.gradle") apply(from = "${rootProject.rootDir}/gradle/bintray.gradle")

View File

@ -80,8 +80,8 @@ fun Meta.getAll(name: Name): Map<String, MetaItem<out Meta>> {
val (body, query) = name.last()!! val (body, query) = name.last()!!
val regex = query.toRegex() val regex = query.toRegex()
return (this[name.cutLast()] as? NodeItem<*>)?.node?.items return (this[name.cutLast()] as? NodeItem<*>)?.node?.items
?.filter { it.key.body == body && (query.isEmpty() || regex.matches(it.key.query)) } ?.filter { it.key.body == body && (query.isEmpty() || regex.matches(it.key.index)) }
?.mapKeys { it.key.query } ?.mapKeys { it.key.index }
?: emptyMap() ?: emptyMap()
} }

View File

@ -154,7 +154,7 @@ fun <M : MutableMeta<M>> M.setIndexed(
val tokens = name.tokens.toMutableList() val tokens = name.tokens.toMutableList()
val last = tokens.last() val last = tokens.last()
items.forEachIndexed { index, meta -> items.forEachIndexed { index, meta ->
val indexedToken = NameToken(last.body, last.query + queryFactory(index)) val indexedToken = NameToken(last.body, last.index + queryFactory(index))
tokens[tokens.lastIndex] = indexedToken tokens[tokens.lastIndex] = indexedToken
set(Name(tokens), meta) set(Name(tokens), meta)
} }

View File

@ -4,7 +4,7 @@ package hep.dataforge.names
/** /**
* The general interface for working with names. * The general interface for working with names.
* The name is a dot separated list of strings like `token1.token2.token3`. * The name is a dot separated list of strings like `token1.token2.token3`.
* Each token could contain additional query in square brackets. * Each token could contain additional index in square brackets.
*/ */
inline class Name constructor(val tokens: List<NameToken>) { inline class Name constructor(val tokens: List<NameToken>) {
@ -43,21 +43,21 @@ inline class Name constructor(val tokens: List<NameToken>) {
/** /**
* A single name token. Body is not allowed to be empty. * A single name token. Body is not allowed to be empty.
* Following symbols are prohibited in name tokens: `{}.:\`. * Following symbols are prohibited in name tokens: `{}.:\`.
* A name token could have appendix in square brackets called *query* * A name token could have appendix in square brackets called *index*
*/ */
data class NameToken(val body: String, val query: String = "") { data class NameToken(val body: String, val index: String = "") {
init { init {
if (body.isEmpty()) error("Syntax error: Name token body is empty") if (body.isEmpty()) error("Syntax error: Name token body is empty")
} }
override fun toString(): String = if (hasQuery()) { override fun toString(): String = if (hasIndex()) {
"$body[$query]" "$body[$index]"
} else { } else {
body body
} }
fun hasQuery() = query.isNotEmpty() fun hasIndex() = index.isNotEmpty()
} }
fun String.toName(): Name { fun String.toName(): Name {
@ -84,7 +84,7 @@ fun String.toName(): Name {
'[' -> bracketCount++ '[' -> bracketCount++
']' -> error("Syntax error: closing bracket ] not have not matching open bracket") ']' -> error("Syntax error: closing bracket ] not have not matching open bracket")
else -> { else -> {
if (queryBuilder.isNotEmpty()) error("Syntax error: only name end and name separator are allowed after query") if (queryBuilder.isNotEmpty()) error("Syntax error: only name end and name separator are allowed after index")
bodyBuilder.append(it) bodyBuilder.append(it)
} }
} }

View File

@ -0,0 +1,28 @@
plugins {
kotlin("multiplatform")
}
val htmlVersion by rootProject.extra("0.6.12")
kotlin {
jvm()
js()
sourceSets {
val commonMain by getting {
dependencies {
api(project(":dataforge-output"))
api("org.jetbrains.kotlinx:kotlinx-html-common:$htmlVersion")
}
}
val jsMain by getting {
dependencies {
api("org.jetbrains.kotlinx:kotlinx-html-js:$htmlVersion")
}
}
val jvmMain by getting{
dependencies {
api("org.jetbrains.kotlinx:kotlinx-html-jvm:$htmlVersion")
}
}
}
}

View File

@ -0,0 +1,78 @@
package hep.dataforge.output.html
import hep.dataforge.context.Context
import hep.dataforge.meta.Meta
import hep.dataforge.output.Output
import hep.dataforge.output.TextRenderer
import hep.dataforge.output.TextRenderer.Companion.TEXT_RENDERER_TYPE
import hep.dataforge.output.html.HtmlBuilder.Companion.HTML_CONVERTER_TYPE
import hep.dataforge.provider.Type
import hep.dataforge.provider.provideAll
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.html.TagConsumer
import kotlinx.html.p
import kotlin.reflect.KClass
class HtmlOutput<T : Any>(override val context: Context, private val consumer: TagConsumer<*>) : Output<T> {
private val cache = HashMap<KClass<*>, HtmlBuilder<*>>()
/**
* Find the first [TextRenderer] matching the given object type.
*/
override fun render(obj: T, meta: Meta) {
val builder: HtmlBuilder<*> = if (obj is CharSequence) {
DefaultHtmlBuilder
} else {
val value = cache[obj::class]
if (value == null) {
val answer = context.provideAll(HTML_CONVERTER_TYPE).filterIsInstance<HtmlBuilder<*>>()
.filter { it.type.isInstance(obj) }.firstOrNull()
if (answer != null) {
cache[obj::class] = answer
answer
} else {
DefaultHtmlBuilder
}
} else {
value
}
}
context.launch(Dispatchers.Output) {
(builder as HtmlBuilder<T>).run { consumer.render(obj) }
}
}
}
/**
* A text or binary renderer based on [kotlinx.io.core.Output]
*/
@Type(HTML_CONVERTER_TYPE)
interface HtmlBuilder<T : Any> {
/**
* The priority of this renderer compared to other renderers
*/
val priority: Int
/**
* The type of the content served by this renderer
*/
val type: KClass<T>
suspend fun TagConsumer<*>.render(obj: T)
companion object {
const val HTML_CONVERTER_TYPE = "dataforge.htmlBuilder"
}
}
object DefaultHtmlBuilder : HtmlBuilder<Any> {
override val priority: Int = Int.MAX_VALUE
override val type: KClass<Any> = Any::class
override suspend fun TagConsumer<*>.render(obj: Any) {
p { +obj.toString() }
}
}

View File

@ -1,4 +1,4 @@
package hep.dataforge.io package hep.dataforge.output
import hep.dataforge.context.ContextAware import hep.dataforge.context.ContextAware
import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.EmptyMeta

View File

@ -1,6 +1,7 @@
package hep.dataforge.io package hep.dataforge.output
import hep.dataforge.context.AbstractPlugin import hep.dataforge.context.AbstractPlugin
import hep.dataforge.context.Context
import hep.dataforge.context.Plugin import hep.dataforge.context.Plugin
import hep.dataforge.context.PluginTag import hep.dataforge.context.PluginTag
import hep.dataforge.context.PluginTag.Companion.DATAFORGE_GROUP import hep.dataforge.context.PluginTag.Companion.DATAFORGE_GROUP
@ -9,40 +10,45 @@ import hep.dataforge.meta.Meta
import hep.dataforge.names.EmptyName import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name import hep.dataforge.names.Name
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
* A manager for outputs * A manager for outputs
*/ */
interface OutputManager : Plugin { interface OutputManager : Plugin {
/** /**
* Provide an output for given name and stage. * Get an output specialized for given type, name and stage.
*
* @param stage represents the node or directory for the output. Empty means root node. * @param stage represents the node or directory for the output. Empty means root node.
* @param name represents the name inside the node. * @param name represents the name inside the node.
* @param meta configuration for [Output] (not for rendered object) * @param meta configuration for [Output] (not for rendered object)
*
*/ */
operator fun get(name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output<Any> operator fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output<T>
/**
* Get an output specialized for giver ntype
*/
fun <T : Any> typed(type: KClass<T>, name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output<T>
} }
/**
* Get an output manager for a context
*/
val Context.output: OutputManager get() = plugins.get() ?: ConsoleOutputManager
/** /**
* Get an output with given [name], [stage] and reified content type * Get an output with given [name], [stage] and reified content type
*/ */
inline fun <reified T : Any> OutputManager.typed( inline operator fun <reified T : Any> OutputManager.get(
name: Name, name: Name,
stage: Name = EmptyName, stage: Name = EmptyName,
meta: Meta = EmptyMeta meta: Meta = EmptyMeta
): Output<T> { ): Output<T> {
return typed(T::class, name, stage, meta) return get(T::class, name, stage, meta)
} }
/**
* Directly render an object using the most suitable renderer
*/
fun OutputManager.render(obj: Any, name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta) =
get(obj::class, name,stage).render(obj,meta)
/** /**
* System console output. * System console output.
* The [ConsoleOutput] is used when no other [OutputManager] is provided. * The [ConsoleOutput] is used when no other [OutputManager] is provided.
@ -52,12 +58,10 @@ expect val ConsoleOutput: Output<Any>
object ConsoleOutputManager : AbstractPlugin(), OutputManager { object ConsoleOutputManager : AbstractPlugin(), OutputManager {
override val tag: PluginTag = PluginTag("output.console", group = DATAFORGE_GROUP) override val tag: PluginTag = PluginTag("output.console", group = DATAFORGE_GROUP)
override fun get(name: Name, stage: Name, meta: Meta): Output<Any> = ConsoleOutput override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Output<T> = ConsoleOutput
override fun <T : Any> typed(type: KClass<T>, name: Name, stage: Name, meta: Meta): Output<T> = ConsoleOutput
} }
/** /**
* A dispatcher for output tasks. * A dispatcher for output tasks.
*/ */
expect val OutputDispatcher : CoroutineDispatcher expect val Dispatchers.Output: CoroutineDispatcher

View File

@ -1,10 +1,11 @@
package hep.dataforge.io package hep.dataforge.output
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.io.TextRenderer.Companion.TEXT_RENDERER_TYPE import hep.dataforge.output.TextRenderer.Companion.TEXT_RENDERER_TYPE
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.provider.provideAll import hep.dataforge.provider.provideAll
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -32,12 +33,15 @@ class TextOutput(override val context: Context, private val output: kotlinx.io.c
value value
} }
} }
context.launch(OutputDispatcher) { context.launch(Dispatchers.Output) {
renderer.run { output.render(obj) } renderer.run { output.render(obj) }
} }
} }
} }
/**
* A text or binary renderer based on [kotlinx.io.core.Output]
*/
@Type(TEXT_RENDERER_TYPE) @Type(TEXT_RENDERER_TYPE)
interface TextRenderer { interface TextRenderer {
/** /**

View File

@ -1,4 +1,4 @@
package hep.dataforge.io package hep.dataforge.output
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.Global import hep.dataforge.context.Global
@ -19,4 +19,4 @@ actual val ConsoleOutput: Output<Any> = object : Output<Any> {
} }
actual val OutputDispatcher: CoroutineDispatcher = Dispatchers.Default actual val Dispatchers.Output: CoroutineDispatcher get() = Dispatchers.Default

View File

@ -1,4 +1,4 @@
package hep.dataforge.io package hep.dataforge.output
import hep.dataforge.context.Global import hep.dataforge.context.Global
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -10,4 +10,4 @@ import kotlinx.io.streams.asOutput
*/ */
actual val ConsoleOutput: Output<Any> = TextOutput(Global, System.out.asOutput()) actual val ConsoleOutput: Output<Any> = TextOutput(Global, System.out.asOutput())
actual val OutputDispatcher = Dispatchers.IO actual val Dispatchers.Output get() = Dispatchers.IO

View File

@ -24,7 +24,8 @@ include(
":dataforge-meta-io", ":dataforge-meta-io",
":dataforge-context", ":dataforge-context",
":dataforge-data", ":dataforge-data",
":dataforge-io", ":dataforge-output",
":dataforge-output:dataforge-output-html",
":dataforge-workspace", ":dataforge-workspace",
":dataforge-scripting" ":dataforge-scripting"
) )