Add generic properties builder dsl, convert maven example to it

This commit is contained in:
Ilya Chernikov
2018-07-17 10:06:14 +02:00
parent d0ed86c11c
commit 7a621405ae
10 changed files with 158 additions and 19 deletions
@@ -13,6 +13,7 @@
package kotlin.script.experimental.annotations
import kotlin.reflect.KClass
import kotlin.script.experimental.api.ScriptingProperties
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@@ -28,6 +29,13 @@ annotation class KotlinScriptFileExtension(
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Deprecated("Use KotlinScriptProperties", replaceWith = ReplaceWith("KotlinScriptProperties"))
annotation class KotlinScriptPropertiesFromList(
val definitionProperties: KClass<out List<*>> // object or class filled in 0-ary constructor
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class KotlinScriptProperties(
val definitionProperties: KClass<out ScriptingProperties> // object or class filled in 0-ary constructor
)
@@ -0,0 +1,115 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.api
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.script.experimental.util.ChainedPropertyBag
import kotlin.script.experimental.util.TypedKey
interface PropertiesGroup
open class ScriptingProperties(body: ScriptingProperties.() -> Unit = {}) {
var parentPropertiesBag: ChainedPropertyBag? = null
var parentPropertiesBuilder: ScriptingProperties? = null
val data = HashMap<TypedKey<*>, Any?>()
init {
body()
}
internal fun makePropertyBag(): ChainedPropertyBag =
ChainedPropertyBag.createOptimized(parentPropertiesBag ?: parentPropertiesBuilder?.makePropertyBag(), data)
// generic invoke for properties groups
inline operator fun <T : PropertiesGroup> T.invoke(body: T.() -> Unit) = body()
// chaining
private fun chain(propsBag: ChainedPropertyBag?, propsBuilder: ScriptingProperties?, replaceParent: Boolean) {
assert(propsBag == null || propsBuilder == null)
if (!replaceParent && parentPropertiesBag != null || parentPropertiesBuilder != null)
throw RuntimeException("Parent already defined for properties being build: ${parentPropertiesBag ?: parentPropertiesBuilder}")
parentPropertiesBag = propsBag
parentPropertiesBuilder = propsBuilder
}
fun chain(props: ScriptingProperties, replaceParent: Boolean = false) {
chain(null, props, replaceParent)
}
fun chain(props: ChainedPropertyBag, replaceParent: Boolean = false) {
chain(props, null, replaceParent)
}
// inclusion
fun include(props: ScriptingProperties) {
data.putAll(props.data)
}
fun include(props: ChainedPropertyBag) {
data.putAll(props.data)
}
// builders for known property types:
inline operator fun <reified T> TypedKey<T>.invoke(v: T) {
data[this] = v
}
inline operator fun <reified K> TypedKey<KotlinType>.invoke() {
data[this] = KotlinType(K::class)
}
operator fun TypedKey<KotlinType>.invoke(kclass: KClass<*>) {
data[this] = KotlinType(kclass)
}
operator fun TypedKey<KotlinType>.invoke(ktype: KType) {
data[this] = KotlinType(ktype)
}
operator fun TypedKey<KotlinType>.invoke(fqname: String) {
data[this] = KotlinType(fqname)
}
operator fun TypedKey<List<KotlinType>>.invoke(vararg classes: KClass<*>) {
data[this] = classes.map { KotlinType(it) }
}
operator fun TypedKey<List<KotlinType>>.invoke(vararg types: KType) {
data[this] = types.map { KotlinType(it) }
}
operator fun TypedKey<List<KotlinType>>.invoke(vararg fqnames: String) {
data[this] = fqnames.map { KotlinType(it) }
}
inline operator fun <reified E> TypedKey<List<E>>.invoke(vararg vs: E) {
data[this] = vs.toList()
}
@JvmName("invoke_kotlintype_map_from_kclass")
inline operator fun <reified K> TypedKey<Map<K, KotlinType>>.invoke(vararg classes: Pair<K, KClass<*>>) {
data[this] = HashMap<K, KotlinType>().also { it.putAll(classes.asSequence().map { (k, v) -> k to KotlinType(v) }) }
}
@JvmName("invoke_kotlintype_map_from_ktype")
inline operator fun <reified K> TypedKey<Map<K, KotlinType>>.invoke(vararg types: Pair<K, KType>) {
data[this] = HashMap<K, KotlinType>().also { it.putAll(types.asSequence().map { (k, v) -> k to KotlinType(v) }) }
}
@JvmName("invoke_kotlintype_map_from_fqname")
inline operator fun <reified K> TypedKey<Map<K, KotlinType>>.invoke(vararg fqnames: Pair<K, String>) {
data[this] = HashMap<K, KotlinType>().also { it.putAll(fqnames.asSequence().map { (k, v) -> k to KotlinType(v) }) }
}
inline operator fun <reified K, reified V> TypedKey<Map<K, V>>.invoke(vararg vs: Pair<K, V>) {
data[this] = hashMapOf(*vs)
}
}
@@ -12,14 +12,14 @@ import kotlin.script.experimental.util.typedKey
typealias ScriptCompileConfiguration = ChainedPropertyBag
object ScriptCompileConfigurationProperties {
object ScriptCompileConfigurationProperties : PropertiesGroup {
val sourceFragments by typedKey<List<ScriptSourceNamedFragment>>()
}
typealias ProcessedScriptData = ChainedPropertyBag
object ProcessedScriptDataProperties {
object ProcessedScriptDataProperties : PropertiesGroup {
val foundAnnotations by typedKey<List<Annotation>>()
val foundFragments by typedKey<List<ScriptSourceNamedFragment>>()
@@ -12,7 +12,7 @@ import kotlin.script.experimental.util.typedKey
typealias ScriptDefinition = ChainedPropertyBag
object ScriptDefinitionProperties {
object ScriptDefinitionProperties : PropertiesGroup {
val name by typedKey<String>() // Name of the script type, by default "Kotlin script"
@@ -11,7 +11,7 @@ import kotlin.script.experimental.util.typedKey
typealias ScriptingEnvironment = ChainedPropertyBag
object ScriptingEnvironmentProperties {
object ScriptingEnvironmentProperties : PropertiesGroup {
// should contain all dependencies needed for baseClass and compilationConfigurator
val configurationDependencies by typedKey<List<ScriptDependency>>()
@@ -10,6 +10,7 @@ import kotlin.reflect.full.createInstance
import kotlin.reflect.full.findAnnotation
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.annotations.KotlinScriptFileExtension
import kotlin.script.experimental.annotations.KotlinScriptProperties
import kotlin.script.experimental.annotations.KotlinScriptPropertiesFromList
import kotlin.script.experimental.api.*
import kotlin.script.experimental.util.TypedKey
@@ -57,6 +58,15 @@ fun createScriptDefinitionFromAnnotatedBaseClass(
}
}
}
baseClass.annotations.filterIsInstance(KotlinScriptProperties::class.java).forEach { ann ->
val params = try {
ann.definitionProperties.objectInstance ?: ann.definitionProperties.createInstance()
} catch (e: Throwable) {
throw IllegalArgumentException(ILLEGAL_CONFIG_ANN_ARG, e)
}
propertiesData.putAll(params.data)
// TODO: chaining is lost here, fix it
}
return ScriptingEnvironment.createOptimized(null, propertiesData)
}
@@ -17,7 +17,7 @@ class TypedKeyDelegate<T>(val defaultValue: T? = null) {
fun <T> typedKey(defaultValue: T? = null) = TypedKeyDelegate(defaultValue)
open class ChainedPropertyBag private constructor(private val parent: ChainedPropertyBag?, private val data: Map<TypedKey<*>, Any?>) {
open class ChainedPropertyBag internal constructor(private val parent: ChainedPropertyBag?, internal val data: Map<TypedKey<*>, Any?>) {
constructor(parent: ChainedPropertyBag? = null, pairs: Iterable<Pair<TypedKey<*>, Any?>>) :
this(parent, HashMap<TypedKey<*>, Any?>().also { it.putAll(pairs) })