Fixed inline invloke on Specification

This commit is contained in:
Alexander Nozik 2020-11-20 14:47:18 +03:00
parent 920366388d
commit 0a77f729ec
12 changed files with 23 additions and 150 deletions

View File

@ -14,9 +14,11 @@
### Deprecated ### Deprecated
- Context activation API - Context activation API
- TextRenderer
### Removed ### Removed
- Functional server prototype - Functional server prototype
- `dataforge-output` module
### Fixed ### Fixed
- Global context CoroutineScope resolution - Global context CoroutineScope resolution

View File

@ -2,7 +2,7 @@ plugins {
id("ru.mipt.npm.project") id("ru.mipt.npm.project")
} }
val dataforgeVersion by extra("0.2.0-dev-7") val dataforgeVersion by extra("0.2.0-dev-8")
val bintrayRepo by extra("dataforge") val bintrayRepo by extra("dataforge")
val githubProject by extra("dataforge-core") val githubProject by extra("dataforge-core")

View File

@ -18,10 +18,10 @@ public interface Specification<T : MutableItemProvider> {
public fun wrap(meta: Meta, defaultProvider: ItemProvider = ItemProvider.EMPTY): T public fun wrap(meta: Meta, defaultProvider: ItemProvider = ItemProvider.EMPTY): T
} }
public operator fun <T : MutableItemProvider> Specification<T>.invoke(action: T.() -> Unit): T = empty().apply(action)
public fun <T : MutableItemProvider> Specification<T>.empty(): T = wrap(Config()) public fun <T : MutableItemProvider> Specification<T>.empty(): T = wrap(Config())
public inline operator fun <T : MutableItemProvider> Specification<T>.invoke(action: T.() -> Unit): T = empty().apply(action)
/** /**
* Update given configuration using given type as a builder * Update given configuration using given type as a builder
*/ */

View File

@ -1,26 +0,0 @@
public final class hep/dataforge/output/html/DefaultHtmlBuilder : hep/dataforge/output/html/HtmlBuilder {
public static final field INSTANCE Lhep/dataforge/output/html/DefaultHtmlBuilder;
public fun getPriority ()I
public fun getType ()Lkotlin/reflect/KClass;
public fun render (Lkotlinx/html/FlowContent;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public abstract interface class hep/dataforge/output/html/HtmlBuilder {
public static final field Companion Lhep/dataforge/output/html/HtmlBuilder$Companion;
public static final field HTML_CONVERTER_TYPE Ljava/lang/String;
public abstract fun getPriority ()I
public abstract fun getType ()Lkotlin/reflect/KClass;
public abstract fun render (Lkotlinx/html/FlowContent;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class hep/dataforge/output/html/HtmlBuilder$Companion {
public static final field HTML_CONVERTER_TYPE Ljava/lang/String;
}
public final class hep/dataforge/output/html/HtmlRenderer : hep/dataforge/output/Renderer {
public fun <init> (Lhep/dataforge/context/Context;Lkotlinx/html/TagConsumer;)V
public fun getContext ()Lhep/dataforge/context/Context;
public fun getLogger ()Lmu/KLogger;
public fun render (Ljava/lang/Object;Lhep/dataforge/meta/Meta;)V
}

View File

@ -1,16 +0,0 @@
plugins {
id("ru.mipt.npm.mpp")
}
val htmlVersion by rootProject.extra("0.7.2")
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
api(project(":dataforge-output"))
api("org.jetbrains.kotlinx:kotlinx-html:$htmlVersion")
}
}
}
}

View File

@ -1,82 +0,0 @@
package hep.dataforge.output.html
import hep.dataforge.context.Context
import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.Meta
import hep.dataforge.output.Output
import hep.dataforge.output.Renderer
import hep.dataforge.output.TextFormat
import hep.dataforge.output.html.HtmlBuilder.Companion.HTML_CONVERTER_TYPE
import hep.dataforge.provider.Type
import hep.dataforge.provider.top
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.html.FlowContent
import kotlinx.html.TagConsumer
import kotlinx.html.p
import kotlin.reflect.KClass
@DFExperimental
public class HtmlRenderer<T : Any>(override val context: Context, private val consumer: TagConsumer<*>) : Renderer<T> {
private val cache = HashMap<KClass<*>, HtmlBuilder<*>>()
/**
* Find the first [TextFormat] 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.top<HtmlBuilder<*>>(HTML_CONVERTER_TYPE).values.firstOrNull { it.type.isInstance(obj) }
if (answer != null) {
cache[obj::class] = answer
answer
} else {
DefaultHtmlBuilder
}
} else {
value
}
}
context.launch(Dispatchers.Output) {
@Suppress("UNCHECKED_CAST")
(builder as HtmlBuilder<T>).run { render(obj) }
}
}
}
/**
* A text or binary renderer based on [Renderer]
*/
@Type(HTML_CONVERTER_TYPE)
public interface HtmlBuilder<T : Any> {
/**
* The priority of this renderer compared to other renderers
*/
public val priority: Int
/**
* The type of the content served by this renderer
*/
public val type: KClass<T>
public suspend fun FlowContent.render(obj: T)
public companion object {
public const val HTML_CONVERTER_TYPE: String = "dataforge.htmlBuilder"
}
}
public object DefaultHtmlBuilder : HtmlBuilder<Any> {
override val priority: Int = Int.MAX_VALUE
override val type: KClass<Any> = Any::class
override suspend fun FlowContent.render(obj: Any) {
p { +obj.toString() }
}
}

View File

@ -19,7 +19,7 @@ public interface OutputManager {
* @param name represents the name inside the node. * @param name represents the name inside the node.
* @param meta configuration for [Renderer] (not for rendered object) * @param meta configuration for [Renderer] (not for rendered object)
*/ */
public operator fun <T : Any> get( public fun <T : Any> getOutputContainer(
type: KClass<out T>, type: KClass<out T>,
name: Name, name: Name,
stage: Name = Name.EMPTY, stage: Name = Name.EMPTY,
@ -35,37 +35,30 @@ public val Context.output: OutputManager get() = plugins.get() ?: ConsoleOutputM
/** /**
* Get an output with given [name], [stage] and reified content type * Get an output with given [name], [stage] and reified content type
*/ */
public inline operator fun <reified T : Any> OutputManager.get( public inline fun <reified T : Any> OutputManager.getOutputContainer(
name: Name, name: Name,
stage: Name = Name.EMPTY, stage: Name = Name.EMPTY,
meta: Meta = Meta.EMPTY meta: Meta = Meta.EMPTY
): Renderer<T> { ): Renderer<T> {
return get(T::class, name, stage, meta) return getOutputContainer(T::class, name, stage, meta)
} }
/** /**
* Directly render an object using the most suitable renderer * Directly render an object using the most suitable renderer
*/ */
public fun OutputManager.render(obj: Any, name: Name, stage: Name = Name.EMPTY, meta: Meta = Meta.EMPTY): Unit = public fun OutputManager.render(obj: Any, name: Name, stage: Name = Name.EMPTY, meta: Meta = Meta.EMPTY): Unit =
get(obj::class, name, stage).render(obj, meta) getOutputContainer(obj::class, name, stage).render(obj, meta)
/** /**
* System console output. * System console output.
* The [CONSOLE_RENDERER] is used when no other [OutputManager] is provided. * The [CONSOLE_RENDERER] is used when no other [OutputManager] is provided.
*/ */
public val CONSOLE_RENDERER: Renderer<Any> = object : Renderer<Any> { public val CONSOLE_RENDERER: Renderer<Any> = Renderer { obj, meta -> println(obj) }
override fun render(obj: Any, meta: Meta) {
println(obj)
}
override val context: Context get() = Global
}
public class ConsoleOutputManager : AbstractPlugin(), OutputManager { public class ConsoleOutputManager : AbstractPlugin(), OutputManager {
override val tag: PluginTag get() = ConsoleOutputManager.tag override val tag: PluginTag get() = ConsoleOutputManager.tag
override fun <T : Any> get(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> = CONSOLE_RENDERER override fun <T : Any> getOutputContainer(type: KClass<out T>, name: Name, stage: Name, meta: Meta): Renderer<T> = CONSOLE_RENDERER
public companion object : PluginFactory<ConsoleOutputManager> { public companion object : PluginFactory<ConsoleOutputManager> {
override val tag: PluginTag = PluginTag("output.console", group = DATAFORGE_GROUP) override val tag: PluginTag = PluginTag("output.console", group = DATAFORGE_GROUP)

View File

@ -10,12 +10,12 @@ import hep.dataforge.meta.Meta
* based on its configuration and provided meta * based on its configuration and provided meta
* *
*/ */
public interface Renderer<in T : Any> : ContextAware { public fun interface Renderer<in T : Any> {
/** /**
* Render specific object with configuration. * Render specific object with configuration.
* *
* By convention actual render is called in asynchronous mode, so this method should never * By convention actual render is called in asynchronous mode, so this method should never
* block execution * block execution
*/ */
public fun render(obj: T, meta: Meta = Meta.EMPTY) public fun render(obj: T, meta: Meta)
} }

View File

@ -8,12 +8,14 @@ import hep.dataforge.provider.top
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KType
/** /**
* A text or binary renderer based on [Output] * A text or binary renderer based on [Output]
*/ */
@Type(TEXT_RENDERER_TYPE) @Type(TEXT_RENDERER_TYPE)
@Deprecated("Bad design")
public interface TextFormat { public interface TextFormat {
/** /**
* The priority of this renderer compared to other renderers * The priority of this renderer compared to other renderers
@ -31,6 +33,7 @@ public interface TextFormat {
} }
} }
@Deprecated("Bad design")
public object DefaultTextFormat : TextFormat { public object DefaultTextFormat : TextFormat {
override val priority: Int = Int.MAX_VALUE override val priority: Int = Int.MAX_VALUE
override val type: KClass<*> = Any::class override val type: KClass<*> = Any::class
@ -43,6 +46,7 @@ public object DefaultTextFormat : TextFormat {
/** /**
* A text-based renderer * A text-based renderer
*/ */
@Deprecated("Bad design")
public class TextRenderer(override val context: Context, private val output: Appendable) : Renderer<Any> { public class TextRenderer(override val context: Context, private val output: Appendable) : Renderer<Any> {
private val cache = HashMap<KClass<*>, TextFormat>() private val cache = HashMap<KClass<*>, TextFormat>()

View File

@ -9,7 +9,6 @@ kotlin {
dependencies { dependencies {
api(project(":dataforge-context")) api(project(":dataforge-context"))
api(project(":dataforge-data")) api(project(":dataforge-data"))
api(project(":dataforge-output"))
api(project(":dataforge-io")) api(project(":dataforge-io"))
} }
} }

View File

@ -36,7 +36,7 @@ private fun newZFS(path: Path): FileSystem {
* @param metaFileFormat the meta format for override * @param metaFileFormat the meta format for override
*/ */
@DFExperimental @DFExperimental
fun <T : Any> IOPlugin.readDataFile( public fun <T : Any> IOPlugin.readDataFile(
path: Path, path: Path,
type: KClass<out T>, type: KClass<out T>,
formatResolver: FileFormatResolver<T> formatResolver: FileFormatResolver<T>
@ -47,7 +47,7 @@ fun <T : Any> IOPlugin.readDataFile(
} }
@DFExperimental @DFExperimental
inline fun <reified T : Any> IOPlugin.readDataFile(path: Path): Data<T> = public inline fun <reified T : Any> IOPlugin.readDataFile(path: Path): Data<T> =
readDataFile(path, T::class) { _, _ -> readDataFile(path, T::class) { _, _ ->
resolveIOFormat<T>() ?: error("Can't resolve IO format for ${T::class}") resolveIOFormat<T>() ?: error("Can't resolve IO format for ${T::class}")
} }
@ -56,7 +56,7 @@ inline fun <reified T : Any> IOPlugin.readDataFile(path: Path): Data<T> =
* Add file/directory-based data tree item * Add file/directory-based data tree item
*/ */
@DFExperimental @DFExperimental
fun <T : Any> DataTreeBuilder<T>.file( public fun <T : Any> DataTreeBuilder<T>.file(
plugin: IOPlugin, plugin: IOPlugin,
path: Path, path: Path,
formatResolver: FileFormatResolver<T> formatResolver: FileFormatResolver<T>
@ -109,7 +109,7 @@ fun <T : Any> IOPlugin.readDataDirectory(
} }
@DFExperimental @DFExperimental
inline fun <reified T : Any> IOPlugin.readDataDirectory(path: Path): DataNode<T> = public inline fun <reified T : Any> IOPlugin.readDataDirectory(path: Path): DataNode<T> =
readDataDirectory(path, T::class) { _, _ -> readDataDirectory(path, T::class) { _, _ ->
resolveIOFormat<T>() ?: error("Can't resolve IO format for ${T::class}") resolveIOFormat<T>() ?: error("Can't resolve IO format for ${T::class}")
} }
@ -118,7 +118,7 @@ inline fun <reified T : Any> IOPlugin.readDataDirectory(path: Path): DataNode<T>
* Write data tree to existing directory or create a new one using default [java.nio.file.FileSystem] provider * Write data tree to existing directory or create a new one using default [java.nio.file.FileSystem] provider
*/ */
@DFExperimental @DFExperimental
suspend fun <T : Any> IOPlugin.writeDataDirectory( public suspend fun <T : Any> IOPlugin.writeDataDirectory(
path: Path, path: Path,
node: DataNode<T>, node: DataNode<T>,
format: IOFormat<T>, format: IOFormat<T>,

View File

@ -30,8 +30,7 @@ include(
":dataforge-io:dataforge-io-yaml", ":dataforge-io:dataforge-io-yaml",
":dataforge-context", ":dataforge-context",
":dataforge-data", ":dataforge-data",
":dataforge-output", // ":dataforge-output",
":dataforge-output:dataforge-output-html",
":dataforge-tables", ":dataforge-tables",
":dataforge-workspace", ":dataforge-workspace",
":dataforge-scripting" ":dataforge-scripting"