Grand refactoring of the scripting DSL + many minor usability refactorings around it
This commit is contained in:
+3
-10
@@ -13,14 +13,14 @@
|
||||
package kotlin.script.experimental.annotations
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.experimental.api.ScriptingProperties
|
||||
import kotlin.script.experimental.api.ScriptDefinition
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class KotlinScript(
|
||||
val name: String = "Kotlin script",
|
||||
val extension: String = "kts",
|
||||
val properties: KClass<out ScriptingProperties> = ScriptingProperties::class // object or class filled in 0-ary constructor
|
||||
val definition: KClass<out ScriptDefinition> = ScriptDefinition.Default::class // object or class filled in 0-ary constructor
|
||||
)
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@@ -29,15 +29,8 @@ annotation class KotlinScriptFileExtension(
|
||||
val extension: String
|
||||
)
|
||||
|
||||
@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
|
||||
val definition: KClass<out ScriptDefinition> // object or class filled in 0-ary constructor
|
||||
)
|
||||
|
||||
@@ -33,10 +33,11 @@ sealed class ResultWithDiagnostics<out R> {
|
||||
|
||||
// call chaining
|
||||
|
||||
suspend fun <R1, R2> ResultWithDiagnostics<R1>.onSuccess(body: suspend (R1) -> ResultWithDiagnostics<R2>): ResultWithDiagnostics<R2> = when (this) {
|
||||
is ResultWithDiagnostics.Success -> this.reports + body(this.value)
|
||||
is ResultWithDiagnostics.Failure -> this
|
||||
}
|
||||
suspend fun <R1, R2> ResultWithDiagnostics<R1>.onSuccess(body: suspend (R1) -> ResultWithDiagnostics<R2>): ResultWithDiagnostics<R2> =
|
||||
when (this) {
|
||||
is ResultWithDiagnostics.Success -> this.reports + body(this.value)
|
||||
is ResultWithDiagnostics.Failure -> this
|
||||
}
|
||||
|
||||
suspend fun <R> ResultWithDiagnostics<R>.onFailure(body: suspend (ResultWithDiagnostics<R>) -> Unit): ResultWithDiagnostics<R> {
|
||||
if (this is ResultWithDiagnostics.Failure) {
|
||||
@@ -52,7 +53,7 @@ operator fun <R> List<ScriptDiagnostic>.plus(res: ResultWithDiagnostics<R>): Res
|
||||
|
||||
// results creation
|
||||
|
||||
fun <R : Any> R.asSuccess(reports: List<ScriptDiagnostic> = listOf()): ResultWithDiagnostics.Success<R> =
|
||||
fun <R> R.asSuccess(reports: List<ScriptDiagnostic> = listOf()): ResultWithDiagnostics.Success<R> =
|
||||
ResultWithDiagnostics.Success(this, reports)
|
||||
|
||||
fun Throwable.asDiagnostics(customMessage: String? = null, location: ScriptSource.Location? = null): ScriptDiagnostic =
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* 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.KProperty
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.script.experimental.util.ChainedPropertyBag
|
||||
import kotlin.script.experimental.util.TypedKey
|
||||
|
||||
interface PropertiesGroup
|
||||
|
||||
|
||||
open class PropertiesBuilder(val props: ScriptingProperties)
|
||||
|
||||
class PropertiesBuilderDelegate<T: PropertiesBuilder>(val kclass: KClass<T>) {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): KClass<T> = kclass
|
||||
}
|
||||
|
||||
inline fun <reified T : PropertiesBuilder> propertiesBuilder() = PropertiesBuilderDelegate(T::class)
|
||||
|
||||
fun buildScriptingProperties(body: ScriptingProperties.() -> Unit): ChainedPropertyBag =
|
||||
ScriptingProperties(body).build()
|
||||
|
||||
open class ScriptingProperties(body: ScriptingProperties.() -> Unit = {}) {
|
||||
|
||||
var parentPropertiesBag: ChainedPropertyBag? = null
|
||||
var parentPropertiesBuilder: ScriptingProperties? = null
|
||||
val data = HashMap<TypedKey<*>, Any?>()
|
||||
|
||||
init {
|
||||
body()
|
||||
setupOnce() // TODO: does it work?
|
||||
}
|
||||
|
||||
private var isSetUp = false
|
||||
fun setupOnce() {
|
||||
if (!isSetUp) setup()
|
||||
}
|
||||
|
||||
open fun setup() {}
|
||||
|
||||
fun build(): ChainedPropertyBag =
|
||||
ChainedPropertyBag.createOptimized(parentPropertiesBag ?: parentPropertiesBuilder?.build(), data)
|
||||
|
||||
// --------------------------
|
||||
// DSL:
|
||||
|
||||
// generic invoke for properties groups, allowing to use syntax:
|
||||
// PropertiesGroup {
|
||||
// ...
|
||||
// }
|
||||
|
||||
inline operator fun <T : PropertiesGroup> T.invoke(body: T.() -> Unit) = body()
|
||||
|
||||
// generic invoke for properties builder - for extending dsl with complex builders
|
||||
|
||||
inline operator fun <reified T : PropertiesBuilder> KClass<T>.invoke(body: T.() -> Unit) {
|
||||
constructors.first().call(this@ScriptingProperties).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) {
|
||||
props.setupOnce()
|
||||
data.putAll(props.data)
|
||||
}
|
||||
|
||||
fun include(props: ChainedPropertyBag) {
|
||||
data.putAll(props.data)
|
||||
}
|
||||
|
||||
// builders for known property types, allowing to use syntax
|
||||
// propertyKey(value...)
|
||||
// or
|
||||
// propertyKey<Type>()
|
||||
|
||||
// generic:
|
||||
|
||||
inline operator fun <reified T> TypedKey<T>.invoke(v: T) {
|
||||
data[this] = v
|
||||
}
|
||||
|
||||
// for KotlinType:
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// for list of KotlinTypes
|
||||
|
||||
@JvmName("invoke_kotlintype_list_from_generic")
|
||||
inline operator fun <reified K> TypedKey<in List<KotlinType>>.invoke() {
|
||||
data.addToListProperty(this, KotlinType(K::class))
|
||||
}
|
||||
|
||||
operator fun TypedKey<List<KotlinType>>.invoke(vararg classes: KClass<*>) {
|
||||
data.addToListProperty(this, classes.map { KotlinType(it) })
|
||||
}
|
||||
|
||||
operator fun TypedKey<List<KotlinType>>.invoke(vararg types: KType) {
|
||||
data.addToListProperty(this, types.map { KotlinType(it) })
|
||||
}
|
||||
|
||||
operator fun TypedKey<List<KotlinType>>.invoke(vararg fqnames: String) {
|
||||
data.addToListProperty(this, fqnames.map { KotlinType(it) })
|
||||
}
|
||||
|
||||
// generic for list
|
||||
|
||||
inline operator fun <reified E> TypedKey<List<E>>.invoke(vararg vs: E) {
|
||||
data.addToListProperty(this, vs.toList())
|
||||
}
|
||||
|
||||
// for map of generic keys to KotlinTypes:
|
||||
|
||||
@JvmName("invoke_kotlintype_map_from_kclass")
|
||||
inline operator fun <reified K> TypedKey<Map<K, KotlinType>>.invoke(vararg classes: Pair<K, KClass<*>>) {
|
||||
data.addToMapProperty(this, classes.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.addToMapProperty(this, types.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.addToMapProperty(this, fqnames.map { (k, v) -> k to KotlinType(v) })
|
||||
}
|
||||
|
||||
// generic for maps:
|
||||
|
||||
inline operator fun <reified K, reified V> TypedKey<Map<K, V>>.invoke(vararg vs: Pair<K, V>) {
|
||||
data.addToMapProperty(this, vs.asIterable())
|
||||
}
|
||||
|
||||
// for strings and list of strings that could be converted from other types
|
||||
|
||||
@JvmName("invoke_string_fqn_from_generic")
|
||||
inline operator fun <reified K> TypedKey<String>.invoke() {
|
||||
data[this] = K::class.qualifiedName!!
|
||||
}
|
||||
|
||||
@JvmName("invoke_string_fqn_from_reflected_class")
|
||||
operator fun TypedKey<String>.invoke(kclass: KClass<*>) {
|
||||
data[this] = kclass.qualifiedName!!
|
||||
}
|
||||
|
||||
@JvmName("invoke_string_list_fqn_from_generic")
|
||||
inline operator fun <reified K> TypedKey<in List<String>>.invoke() {
|
||||
data.addToListProperty(this, K::class.qualifiedName!!)
|
||||
}
|
||||
|
||||
@JvmName("invoke_string_list_fqn_from_reflected_class")
|
||||
operator fun TypedKey<in List<String>>.invoke(vararg kclasses: KClass<*>) {
|
||||
data.addToListProperty(this, kclasses.map { it.qualifiedName!! })
|
||||
}
|
||||
}
|
||||
|
||||
fun <V> HashMap<TypedKey<*>, Any?>.addToListProperty(key: TypedKey<in List<V>>, values: Iterable<V>) {
|
||||
val newValues = get(key)?.let { (it as List<V>) + values } ?: values.toList()
|
||||
put(key, newValues)
|
||||
}
|
||||
|
||||
fun <V> HashMap<TypedKey<*>, Any?>.addToListProperty(key: TypedKey<in List<V>>, vararg values: V) {
|
||||
val newValues = get(key)?.let { (it as List<V>) + values } ?: values.toList()
|
||||
put(key, newValues)
|
||||
}
|
||||
|
||||
fun <K, V> HashMap<TypedKey<*>, Any?>.addToMapProperty(key: TypedKey<in Map<K, V>>, values: Map<K, V>) {
|
||||
val newValues = get(key)?.let { (it as Map<K, V>) + values } ?: values
|
||||
put(key, newValues)
|
||||
}
|
||||
|
||||
fun <K, V> HashMap<TypedKey<*>, Any?>.addToMapProperty(key: TypedKey<in Map<K, V>>, values: Iterable<Pair<K, V>>) {
|
||||
val newValues = get(key)?.let { (it as Map<K, V>) + values } ?: values
|
||||
put(key, newValues)
|
||||
}
|
||||
@@ -17,7 +17,8 @@ interface ScriptCompiler {
|
||||
|
||||
interface CompiledScript<out ScriptBase : Any> {
|
||||
|
||||
val configuration: ScriptCompileConfiguration
|
||||
val definition: ScriptDefinition
|
||||
val additionalConfiguration: ScriptCompileConfiguration?
|
||||
|
||||
suspend fun instantiate(scriptEvaluationEnvironment: ScriptEvaluationEnvironment): ResultWithDiagnostics<ScriptBase>
|
||||
suspend fun instantiate(scriptEvaluationEnvironment: ScriptEvaluationEnvironment?): ResultWithDiagnostics<ScriptBase>
|
||||
}
|
||||
|
||||
+37
-13
@@ -7,30 +7,54 @@
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import kotlin.script.experimental.util.ChainedPropertyBag
|
||||
import kotlin.script.experimental.util.typedKey
|
||||
import kotlin.script.experimental.util.PropertiesCollection
|
||||
|
||||
typealias ScriptCompileConfiguration = ChainedPropertyBag
|
||||
interface ScriptCompileConfiguration : PropertiesCollection {
|
||||
|
||||
companion object : ScriptCompileConfiguration {
|
||||
|
||||
object ScriptCompileConfigurationProperties : PropertiesGroup {
|
||||
class Builder internal constructor() : PropertiesCollection.Builder(), ScriptCompileConfiguration {
|
||||
override val properties = data
|
||||
}
|
||||
|
||||
val sourceFragments by typedKey<List<ScriptSourceNamedFragment>>()
|
||||
fun create(body: Builder.() -> Unit): ScriptCompileConfiguration = Builder().apply(body)
|
||||
}
|
||||
}
|
||||
|
||||
val ScriptingProperties.compilationConfiguration get() = ScriptCompileConfigurationProperties
|
||||
val ScriptCompileConfiguration.sourceFragments by PropertiesCollection.key<List<ScriptSourceNamedFragment>>()
|
||||
|
||||
typealias ProcessedScriptData = ChainedPropertyBag
|
||||
val ScriptCompileConfiguration.scriptBodyTarget by PropertiesCollection.keyCopy(ScriptDefinition.scriptBodyTarget)
|
||||
|
||||
object ProcessedScriptDataProperties : PropertiesGroup {
|
||||
val foundAnnotations by typedKey<List<Annotation>>()
|
||||
val ScriptCompileConfiguration.scriptImplicitReceivers by PropertiesCollection.keyCopy(ScriptDefinition.scriptImplicitReceivers)
|
||||
|
||||
val foundFragments by typedKey<List<ScriptSourceNamedFragment>>()
|
||||
val ScriptCompileConfiguration.contextVariables by PropertiesCollection.keyCopy(ScriptDefinition.contextVariables)
|
||||
|
||||
val ScriptCompileConfiguration.defaultImports by PropertiesCollection.keyCopy(ScriptDefinition.defaultImports)
|
||||
|
||||
val ScriptCompileConfiguration.restrictions by PropertiesCollection.keyCopy(ScriptDefinition.restrictions)
|
||||
|
||||
val ScriptCompileConfiguration.importedScripts by PropertiesCollection.keyCopy(ScriptDefinition.importedScripts)
|
||||
|
||||
val ScriptCompileConfiguration.dependencies by PropertiesCollection.keyCopy(ScriptDefinition.dependencies)
|
||||
|
||||
val ScriptCompileConfiguration.compilerOptions by PropertiesCollection.keyCopy(ScriptDefinition.compilerOptions)
|
||||
|
||||
|
||||
interface ProcessedScriptData : PropertiesCollection {
|
||||
|
||||
companion object : ProcessedScriptData
|
||||
}
|
||||
|
||||
val ProcessedScriptData.foundAnnotations by PropertiesCollection.key<List<Annotation>>()
|
||||
|
||||
val ProcessedScriptData.foundFragments by PropertiesCollection.key<List<ScriptSourceNamedFragment>>()
|
||||
|
||||
|
||||
interface RefineScriptCompilationConfigurationHandler {
|
||||
suspend operator fun invoke(
|
||||
scriptSource: ScriptSource,
|
||||
configuration: ScriptCompileConfiguration,
|
||||
processedScriptData: ProcessedScriptData = ProcessedScriptData()
|
||||
): ResultWithDiagnostics<ScriptCompileConfiguration>
|
||||
scriptDefinition: ScriptDefinition,
|
||||
configuration: ScriptCompileConfiguration?,
|
||||
processedScriptData: ProcessedScriptData? = null
|
||||
): ResultWithDiagnostics<ScriptCompileConfiguration?>
|
||||
}
|
||||
|
||||
@@ -8,73 +8,73 @@
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.experimental.util.ChainedPropertyBag
|
||||
import kotlin.script.experimental.util.typedKey
|
||||
import kotlin.script.experimental.util.PropertiesCollection
|
||||
|
||||
typealias ScriptDefinition = ChainedPropertyBag
|
||||
interface ScriptDefinition : PropertiesCollection {
|
||||
|
||||
object ScriptDefinitionProperties : PropertiesGroup {
|
||||
companion object : ScriptDefinition {
|
||||
|
||||
val name by typedKey<String>() // Name of the script type, by default "Kotlin script"
|
||||
class Builder internal constructor() : PropertiesCollection.Builder(), ScriptDefinition {
|
||||
override val properties = data
|
||||
}
|
||||
|
||||
val fileExtension by typedKey<String>() // default: "kts"
|
||||
|
||||
val baseClass by typedKey<KotlinType>() // script base class
|
||||
|
||||
val scriptBodyTarget by typedKey<ScriptBodyTarget>()
|
||||
|
||||
val scriptImplicitReceivers by typedKey<List<KotlinType>>() // in the order from outer to inner scope
|
||||
|
||||
val contextVariables by typedKey<Map<String, KotlinType>>() // external variables
|
||||
|
||||
val defaultImports by typedKey<List<String>>()
|
||||
|
||||
val restrictions by typedKey<List<ResolvingRestrictionRule>>()
|
||||
|
||||
val importedScripts by typedKey<List<ScriptSource>>()
|
||||
|
||||
val dependencies by typedKey<List<ScriptDependency>>()
|
||||
|
||||
val generatedClassAnnotations by typedKey<List<Annotation>>()
|
||||
|
||||
val generatedMethodAnnotations by typedKey<List<Annotation>>()
|
||||
|
||||
val compilerOptions by typedKey<List<String>>() // Q: CommonCompilerOptions instead?
|
||||
|
||||
val refineConfigurationHandler by typedKey<RefineScriptCompilationConfigurationHandler>() // dynamic configurator
|
||||
|
||||
val refineConfigurationBeforeParsing by typedKey<Boolean>() // default: false
|
||||
|
||||
val refineConfigurationOnAnnotations by typedKey<List<KotlinType>>()
|
||||
|
||||
val refineConfigurationOnSections by typedKey<List<String>>()
|
||||
|
||||
// DSL:
|
||||
|
||||
val refineConfiguration by propertiesBuilder<RefineConfigurationBuilder>()
|
||||
}
|
||||
|
||||
// DSL --------------------
|
||||
|
||||
val ScriptingProperties.scriptDefinition get() = ScriptDefinitionProperties
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class RefineConfigurationBuilder(props: ScriptingProperties) : PropertiesBuilder(props) {
|
||||
|
||||
inline operator fun invoke(body: RefineConfigurationBuilder.() -> Unit) {
|
||||
body()
|
||||
fun create(body: Builder.() -> Unit): ScriptDefinition = Builder().apply(body)
|
||||
}
|
||||
|
||||
object Default : ScriptDefinition
|
||||
}
|
||||
|
||||
val ScriptDefinition.name by PropertiesCollection.key<String>("Kotlin script") // Name of the script type
|
||||
|
||||
val ScriptDefinition.fileExtension by PropertiesCollection.key<String>("kts") // file extension
|
||||
|
||||
val ScriptDefinition.baseClass by PropertiesCollection.key<KotlinType>() // script base class
|
||||
|
||||
val ScriptDefinition.scriptBodyTarget by PropertiesCollection.key<ScriptBodyTarget>(ScriptBodyTarget.Constructor)
|
||||
|
||||
val ScriptDefinition.scriptImplicitReceivers by PropertiesCollection.key<List<KotlinType>>() // in the order from outer to inner scope
|
||||
|
||||
val ScriptDefinition.contextVariables by PropertiesCollection.key<Map<String, KotlinType>>() // external variables
|
||||
|
||||
val ScriptDefinition.defaultImports by PropertiesCollection.key<List<String>>()
|
||||
|
||||
val ScriptDefinition.restrictions by PropertiesCollection.key<List<ResolvingRestrictionRule>>()
|
||||
|
||||
val ScriptDefinition.importedScripts by PropertiesCollection.key<List<ScriptSource>>()
|
||||
|
||||
val ScriptDefinition.dependencies by PropertiesCollection.key<List<ScriptDependency>>()
|
||||
|
||||
val ScriptDefinition.generatedClassAnnotations by PropertiesCollection.key<List<Annotation>>()
|
||||
|
||||
val ScriptDefinition.generatedMethodAnnotations by PropertiesCollection.key<List<Annotation>>()
|
||||
|
||||
val ScriptDefinition.compilerOptions by PropertiesCollection.key<List<String>>() // Q: CommonCompilerOptions instead?
|
||||
|
||||
val ScriptDefinition.refineConfigurationHandler by PropertiesCollection.key<RefineScriptCompilationConfigurationHandler>() // dynamic configurator
|
||||
|
||||
val ScriptDefinition.refineConfigurationBeforeParsing by PropertiesCollection.key<Boolean>() // default: false
|
||||
|
||||
val ScriptDefinition.refineConfigurationOnAnnotations by PropertiesCollection.key<List<KotlinType>>()
|
||||
|
||||
val ScriptDefinition.refineConfigurationOnSections by PropertiesCollection.key<List<String>>()
|
||||
|
||||
// DSL:
|
||||
|
||||
val ScriptDefinition.refineConfiguration get() = RefineConfigurationBuilder()
|
||||
|
||||
|
||||
class RefineConfigurationBuilder : PropertiesCollection.Builder() {
|
||||
|
||||
fun handler(fn: RefineScriptCompilationConfigurationHandler) {
|
||||
props.data[ScriptDefinitionProperties.refineConfigurationHandler] = fn
|
||||
set(ScriptDefinition.refineConfigurationHandler, fn)
|
||||
}
|
||||
|
||||
fun triggerBeforeParsing(value: Boolean = true) {
|
||||
props.data[ScriptDefinitionProperties.refineConfigurationBeforeParsing] = value
|
||||
set(ScriptDefinition.refineConfigurationBeforeParsing, value)
|
||||
}
|
||||
|
||||
fun triggerOnAnnotations(annotations: Iterable<KotlinType>) {
|
||||
props.data.addToListProperty(ScriptDefinitionProperties.refineConfigurationOnAnnotations, annotations)
|
||||
ScriptDefinition.refineConfigurationOnAnnotations.append(annotations)
|
||||
}
|
||||
|
||||
fun triggerOnAnnotations(vararg annotations: KotlinType) {
|
||||
@@ -90,11 +90,11 @@ class RefineConfigurationBuilder(props: ScriptingProperties) : PropertiesBuilder
|
||||
}
|
||||
|
||||
fun triggerOnSections(sections: Iterable<String>) {
|
||||
props.data.addToListProperty(ScriptDefinitionProperties.refineConfigurationOnSections, sections)
|
||||
ScriptDefinition.refineConfigurationOnSections.append(sections)
|
||||
}
|
||||
|
||||
fun triggerOnSections(vararg sections: String) {
|
||||
props.data.addToListProperty(ScriptDefinitionProperties.refineConfigurationOnSections, *sections)
|
||||
ScriptDefinition.refineConfigurationOnSections.append(*sections)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,20 +7,27 @@
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import kotlin.script.experimental.util.ChainedPropertyBag
|
||||
import kotlin.script.experimental.util.typedKey
|
||||
import kotlin.script.experimental.util.PropertiesCollection
|
||||
|
||||
object ScriptEvaluationEnvironmentParams {
|
||||
val implicitReceivers by typedKey<List<Any>>()
|
||||
interface ScriptEvaluationEnvironment : PropertiesCollection {
|
||||
|
||||
val contextVariables by typedKey<Map<String, Any?>>() // external variables
|
||||
companion object : ScriptEvaluationEnvironment {
|
||||
|
||||
val constructorArgs by typedKey<List<Any?>>()
|
||||
class Builder internal constructor() : PropertiesCollection.Builder(), ScriptEvaluationEnvironment {
|
||||
override val properties = data
|
||||
}
|
||||
|
||||
val runArgs by typedKey<List<Any?>>()
|
||||
fun create(body: Builder.() -> Unit): ScriptEvaluationEnvironment = Builder().apply(body)
|
||||
}
|
||||
}
|
||||
|
||||
typealias ScriptEvaluationEnvironment = ChainedPropertyBag
|
||||
val ScriptEvaluationEnvironment.implicitReceivers by PropertiesCollection.key<List<Any>>()
|
||||
|
||||
val ScriptEvaluationEnvironment.contextVariables by PropertiesCollection.key<Map<String, Any?>>() // external variables
|
||||
|
||||
val ScriptEvaluationEnvironment.constructorArgs by PropertiesCollection.key<List<Any?>>()
|
||||
|
||||
val ScriptEvaluationEnvironment.runArgs by PropertiesCollection.key<List<Any?>>()
|
||||
|
||||
sealed class ResultValue {
|
||||
class Value(val name: String, val value: Any?, val type: String) : ResultValue() {
|
||||
@@ -30,12 +37,12 @@ sealed class ResultValue {
|
||||
object Unit : ResultValue()
|
||||
}
|
||||
|
||||
data class EvaluationResult(val returnValue: ResultValue, val environment: ScriptEvaluationEnvironment)
|
||||
data class EvaluationResult(val returnValue: ResultValue, val environment: ScriptEvaluationEnvironment?)
|
||||
|
||||
interface ScriptEvaluator {
|
||||
|
||||
suspend operator fun invoke(
|
||||
compiledScript: CompiledScript<*>,
|
||||
scriptEvaluationEnvironment: ScriptEvaluationEnvironment
|
||||
scriptEvaluationEnvironment: ScriptEvaluationEnvironment?
|
||||
): ResultWithDiagnostics<EvaluationResult>
|
||||
}
|
||||
|
||||
+20
-15
@@ -6,33 +6,38 @@
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.experimental.util.ChainedPropertyBag
|
||||
import kotlin.script.experimental.util.typedKey
|
||||
import kotlin.script.experimental.util.PropertiesCollection
|
||||
import kotlin.script.experimental.util.getOrNull
|
||||
|
||||
typealias ScriptingEnvironment = ChainedPropertyBag
|
||||
interface ScriptingEnvironment : PropertiesCollection {
|
||||
|
||||
object ScriptingEnvironmentProperties : PropertiesGroup {
|
||||
companion object : ScriptingEnvironment {
|
||||
|
||||
// should contain all dependencies needed for baseClass and compilationConfigurator
|
||||
val configurationDependencies by typedKey<List<ScriptDependency>>()
|
||||
class Builder internal constructor() : PropertiesCollection.Builder(), ScriptingEnvironment {
|
||||
override val properties = data
|
||||
}
|
||||
|
||||
// do not use configurationDependencies as script dependencies, so only the dependencies defined by compilationConfigurator will be used
|
||||
// (NOTE: in this case they should include the dependencies for the base class anyway, since this class is needed for script
|
||||
// compilation and instantiation, but compilationConfigurator could be excluded)
|
||||
val isolatedDependencies by typedKey(false)
|
||||
|
||||
// a "class loader" for KotlinTypes
|
||||
val getScriptingClass by typedKey<GetScriptingClass>()
|
||||
fun create(body: Builder.() -> Unit): ScriptingEnvironment = Builder().apply(body)
|
||||
}
|
||||
}
|
||||
|
||||
val ScriptingProperties.scriptingEnvironment get() = ScriptDefinitionProperties
|
||||
// should contain all dependencies needed for baseClass and compilationConfigurator
|
||||
val ScriptingEnvironment.configurationDependencies by PropertiesCollection.key<List<ScriptDependency>>()
|
||||
|
||||
// do not use configurationDependencies as script dependencies, so only the dependencies defined by compilationConfigurator will be used
|
||||
// (NOTE: in this case they should include the dependencies for the base class anyway, since this class is needed for script
|
||||
// compilation and instantiation, but compilationConfigurator could be excluded)
|
||||
val ScriptingEnvironment.isolatedDependencies by PropertiesCollection.key<Boolean>(false)
|
||||
|
||||
// a "class loader" for KotlinTypes
|
||||
val ScriptingEnvironment.getScriptingClass by PropertiesCollection.key<GetScriptingClass>()
|
||||
|
||||
interface GetScriptingClass {
|
||||
operator fun invoke(classType: KotlinType, contextClass: KClass<*>, environment: ScriptingEnvironment): KClass<*>
|
||||
}
|
||||
|
||||
fun ScriptingEnvironment.getScriptingClass(type: KotlinType, contextClass: KClass<*>): KClass<*> {
|
||||
val getClass = getOrNull(ScriptingEnvironmentProperties.getScriptingClass)
|
||||
val getClass = getOrNull(ScriptingEnvironment.getScriptingClass)
|
||||
?: throw IllegalArgumentException("Expecting 'getScriptingClass' property in the scripting environment: unable to load scripting class $type")
|
||||
return getClass(type, contextClass, this)
|
||||
}
|
||||
|
||||
+16
-31
@@ -11,9 +11,8 @@ 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
|
||||
import kotlin.script.experimental.util.getOrNull
|
||||
|
||||
private const val ERROR_MSG_PREFIX = "Unable to construct script definition: "
|
||||
|
||||
@@ -26,7 +25,7 @@ fun createScriptDefinitionFromAnnotatedBaseClass(
|
||||
contextClass: KClass<*> = ScriptDefinition::class
|
||||
): ScriptDefinition {
|
||||
|
||||
val getScriptingClass = environment.getOrNull(ScriptingEnvironmentProperties.getScriptingClass)
|
||||
val getScriptingClass = environment.getOrNull(ScriptingEnvironment.getScriptingClass)
|
||||
?: throw IllegalArgumentException("${ERROR_MSG_PREFIX}Expecting 'getScriptingClass' parameter in the scripting environment")
|
||||
|
||||
val baseClass: KClass<*> =
|
||||
@@ -39,38 +38,24 @@ fun createScriptDefinitionFromAnnotatedBaseClass(
|
||||
val mainAnnotation = baseClass.findAnnotation<KotlinScript>()
|
||||
?: throw IllegalArgumentException("${ERROR_MSG_PREFIX}Expecting KotlinScript annotation on the $baseClass")
|
||||
|
||||
val propertiesData = hashMapOf<TypedKey<*>, Any?>(ScriptDefinitionProperties.baseClass to baseClassType)
|
||||
|
||||
propertiesData[ScriptDefinitionProperties.fileExtension] =
|
||||
baseClass.findAnnotation<KotlinScriptFileExtension>()?.let { it.extension }
|
||||
?: mainAnnotation.extension
|
||||
propertiesData += ScriptDefinitionProperties.name to mainAnnotation.name
|
||||
baseClass.annotations.filterIsInstance(KotlinScriptPropertiesFromList::class.java).forEach { ann ->
|
||||
val params = try {
|
||||
ann.definitionProperties.objectInstance ?: ann.definitionProperties.createInstance()
|
||||
} catch (e: Throwable) {
|
||||
throw IllegalArgumentException(ILLEGAL_CONFIG_ANN_ARG, e)
|
||||
}
|
||||
params.forEach { param ->
|
||||
if (param !is Pair<*, *> || param.first !is TypedKey<*>)
|
||||
throw IllegalArgumentException("$ILLEGAL_CONFIG_ANN_ARG: invalid parameter $param")
|
||||
(param as Pair<TypedKey<*>, Any?>).let { (k, v) ->
|
||||
propertiesData[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun scriptingPropsInstance(kclass: KClass<out ScriptingProperties>): ScriptingProperties = try {
|
||||
kclass.objectInstance ?: kclass.createInstance().also { it.setupOnce() }
|
||||
fun scriptingPropsInstance(kclass: KClass<out ScriptDefinition>): ScriptDefinition = try {
|
||||
kclass.objectInstance ?: kclass.createInstance()
|
||||
} catch (e: Throwable) {
|
||||
throw IllegalArgumentException(ILLEGAL_CONFIG_ANN_ARG, e)
|
||||
}
|
||||
|
||||
baseClass.annotations.filterIsInstance(KotlinScriptProperties::class.java).forEach { ann ->
|
||||
propertiesData.putAll(scriptingPropsInstance(ann.definitionProperties).data)
|
||||
return object : ScriptDefinition {
|
||||
override val properties = properties {
|
||||
baseClass(baseClassType)
|
||||
fileExtension(baseClass.findAnnotation<KotlinScriptFileExtension>()?.extension ?: mainAnnotation.extension)
|
||||
name(mainAnnotation.name)
|
||||
|
||||
include(scriptingPropsInstance(mainAnnotation.definition))
|
||||
|
||||
baseClass.annotations.filterIsInstance(KotlinScriptProperties::class.java).forEach { ann ->
|
||||
include(scriptingPropsInstance(ann.definition))
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: chaining is lost here and above, fix it
|
||||
propertiesData.putAll(scriptingPropsInstance(mainAnnotation.properties).data)
|
||||
return ScriptingEnvironment.createOptimized(null, propertiesData)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -31,8 +31,8 @@ abstract class BasicScriptingHost(
|
||||
|
||||
open fun eval(
|
||||
script: ScriptSource,
|
||||
compileConfiguration: ScriptCompileConfiguration,
|
||||
environment: ScriptEvaluationEnvironment
|
||||
compileConfiguration: ScriptCompileConfiguration?,
|
||||
environment: ScriptEvaluationEnvironment?
|
||||
): ResultWithDiagnostics<EvaluationResult> =
|
||||
runInCoroutineContext {
|
||||
compiler(script, scriptDefinition, compileConfiguration).onSuccess {
|
||||
|
||||
@@ -7,7 +7,11 @@ package kotlin.script.experimental.host
|
||||
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.api.ScriptCompileConfiguration
|
||||
import kotlin.script.experimental.api.ScriptSource
|
||||
import kotlin.script.experimental.api.ScriptSourceNamedFragment
|
||||
import kotlin.script.experimental.api.sourceFragments
|
||||
import kotlin.script.experimental.util.getOrNull
|
||||
|
||||
fun ScriptSource.getScriptText(): String = when {
|
||||
text != null -> text!!
|
||||
@@ -16,9 +20,9 @@ fun ScriptSource.getScriptText(): String = when {
|
||||
else -> throw RuntimeException("unable to get text from null script")
|
||||
}
|
||||
|
||||
fun getMergedScriptText(script: ScriptSource, configuration: ScriptCompileConfiguration): String {
|
||||
fun getMergedScriptText(script: ScriptSource, configuration: ScriptCompileConfiguration?): String {
|
||||
val originalScriptText = script.getScriptText()
|
||||
val sourceFragments = configuration.getOrNull(ScriptCompileConfigurationProperties.sourceFragments)
|
||||
val sourceFragments = configuration?.getOrNull(ScriptCompileConfiguration.sourceFragments)
|
||||
return if (sourceFragments == null || sourceFragments.isEmpty()) {
|
||||
originalScriptText
|
||||
} else {
|
||||
|
||||
@@ -7,15 +7,20 @@ package kotlin.script.experimental.repl
|
||||
|
||||
import kotlin.script.experimental.api.EvaluationResult
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.util.ChainedPropertyBag
|
||||
import kotlin.script.experimental.util.PropertiesCollection
|
||||
|
||||
interface ReplEvaluationEnvironment : PropertiesCollection {
|
||||
|
||||
object ReplEvaluationEnvironmentParams {
|
||||
companion object : ReplEvaluationEnvironment {
|
||||
|
||||
class Builder internal constructor() : PropertiesCollection.Builder(), ReplEvaluationEnvironment {
|
||||
override val properties = data
|
||||
}
|
||||
|
||||
fun create(body: Builder.() -> Unit): ReplEvaluationEnvironment = Builder().apply(body)
|
||||
}
|
||||
}
|
||||
|
||||
typealias ReplEvaluationEnvironment = ChainedPropertyBag
|
||||
|
||||
interface ReplSnippetEvaluator {
|
||||
|
||||
suspend operator fun invoke(
|
||||
|
||||
@@ -11,8 +11,7 @@ import kotlin.script.experimental.api.EvaluationResult
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.ScriptCompileConfiguration
|
||||
import kotlin.script.experimental.api.ScriptDefinition
|
||||
import kotlin.script.experimental.util.ChainedPropertyBag
|
||||
import kotlin.script.experimental.util.typedKey
|
||||
import kotlin.script.experimental.util.PropertiesCollection
|
||||
|
||||
interface ReplCommandProcessor {
|
||||
suspend operator fun invoke(
|
||||
@@ -23,12 +22,19 @@ interface ReplCommandProcessor {
|
||||
|
||||
data class ReplCommand(val commandName: String, val processor: ReplCommandProcessor)
|
||||
|
||||
object ReplHostEnvironmentParams {
|
||||
interface ReplHostEnvironment : PropertiesCollection {
|
||||
|
||||
val replCommands by typedKey<List<ReplCommand>>()
|
||||
companion object : ReplHostEnvironment {
|
||||
|
||||
class Builder internal constructor() : PropertiesCollection.Builder(), ReplHostEnvironment {
|
||||
override val properties = data
|
||||
}
|
||||
|
||||
fun create(body: Builder.() -> Unit): ReplHostEnvironment = Builder().apply(body)
|
||||
}
|
||||
}
|
||||
|
||||
typealias ReplHostEnvironment = ChainedPropertyBag
|
||||
val ReplHostEnvironment.replCommands by PropertiesCollection.key<List<ReplCommand>>()
|
||||
|
||||
abstract class ReplHost(
|
||||
val environment: ReplHostEnvironment,
|
||||
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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.util
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.script.experimental.api.KotlinType
|
||||
|
||||
interface PropertiesCollection {
|
||||
|
||||
val properties: Map<Key<*>, Any> get() = emptyMap()
|
||||
|
||||
data class Key<T>(val name: String, val defaultValue: T? = null)
|
||||
|
||||
class PropertyKeyDelegate<T>(private val defaultValue: T? = null) {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Key<T> =
|
||||
Key(property.name, defaultValue)
|
||||
}
|
||||
|
||||
class PropertyKeyCopyDelegate<T>(val source: Key<T>) {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Key<T> = source
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <T> key(defaultValue: T? = null) = PropertyKeyDelegate(defaultValue)
|
||||
fun <T> keyCopy(source: Key<T>) = PropertyKeyCopyDelegate(source)
|
||||
}
|
||||
|
||||
fun properties(body: Builder.() -> Unit): Map<Key<*>, Any> = Builder().apply(body).data
|
||||
|
||||
// properties builder base class (DSL for building properties collection)
|
||||
|
||||
open class Builder {
|
||||
|
||||
val data: MutableMap<PropertiesCollection.Key<*>, Any> = LinkedHashMap()
|
||||
|
||||
// generic builder for all properties
|
||||
operator fun <T : Any> PropertiesCollection.Key<T>.invoke(v: T) {
|
||||
set(this, v)
|
||||
}
|
||||
|
||||
// generic for lists
|
||||
|
||||
operator fun <T> PropertiesCollection.Key<in List<T>>.invoke(vararg vals: T) {
|
||||
append(vals.asIterable())
|
||||
}
|
||||
|
||||
// generic for maps:
|
||||
|
||||
operator fun <K, V> PropertiesCollection.Key<Map<K, V>>.invoke(vararg vs: Pair<K, V>) {
|
||||
append(vs.asIterable())
|
||||
}
|
||||
|
||||
// for strings and list of strings that could be converted from other types
|
||||
|
||||
@JvmName("invoke_string_fqn_from_generic")
|
||||
inline operator fun <reified K> PropertiesCollection.Key<String>.invoke() {
|
||||
set(this, K::class.qualifiedName!!)
|
||||
}
|
||||
|
||||
@JvmName("invoke_string_fqn_from_reflected_class")
|
||||
operator fun PropertiesCollection.Key<String>.invoke(kclass: KClass<*>) {
|
||||
set(this, kclass.qualifiedName!!)
|
||||
}
|
||||
|
||||
@JvmName("invoke_string_list_fqn_from_generic")
|
||||
inline operator fun <reified K> PropertiesCollection.Key<in List<String>>.invoke() {
|
||||
append(K::class.qualifiedName!!)
|
||||
}
|
||||
|
||||
@JvmName("invoke_string_list_fqn_from_reflected_class")
|
||||
operator fun PropertiesCollection.Key<in List<String>>.invoke(vararg kclasses: KClass<*>) {
|
||||
append(kclasses.map { it.qualifiedName!! })
|
||||
}
|
||||
|
||||
// for KotlinType:
|
||||
|
||||
inline operator fun <reified K> PropertiesCollection.Key<KotlinType>.invoke() {
|
||||
set(this, KotlinType(K::class))
|
||||
}
|
||||
|
||||
operator fun PropertiesCollection.Key<KotlinType>.invoke(kclass: KClass<*>) {
|
||||
data[this] = KotlinType(kclass)
|
||||
}
|
||||
|
||||
operator fun PropertiesCollection.Key<KotlinType>.invoke(ktype: KType) {
|
||||
data[this] = KotlinType(ktype)
|
||||
}
|
||||
|
||||
operator fun PropertiesCollection.Key<KotlinType>.invoke(fqname: String) {
|
||||
data[this] = KotlinType(fqname)
|
||||
}
|
||||
|
||||
// for list of KotlinTypes
|
||||
|
||||
@JvmName("invoke_kotlintype_list_from_generic")
|
||||
inline operator fun <reified K> PropertiesCollection.Key<in List<KotlinType>>.invoke() {
|
||||
append(KotlinType(K::class))
|
||||
}
|
||||
|
||||
operator fun PropertiesCollection.Key<List<KotlinType>>.invoke(vararg classes: KClass<*>) {
|
||||
append(classes.map { KotlinType(it) })
|
||||
}
|
||||
|
||||
operator fun PropertiesCollection.Key<List<KotlinType>>.invoke(vararg types: KType) {
|
||||
append(types.map { KotlinType(it) })
|
||||
}
|
||||
|
||||
operator fun PropertiesCollection.Key<List<KotlinType>>.invoke(vararg fqnames: String) {
|
||||
append(fqnames.map { KotlinType(it) })
|
||||
}
|
||||
|
||||
// for map of generic keys to KotlinTypes:
|
||||
|
||||
@JvmName("invoke_kotlintype_map_from_kclass")
|
||||
operator fun <K> PropertiesCollection.Key<Map<K, KotlinType>>.invoke(vararg classes: Pair<K, KClass<*>>) {
|
||||
append(classes.map { (k, v) -> k to KotlinType(v) })
|
||||
}
|
||||
|
||||
@JvmName("invoke_kotlintype_map_from_ktype")
|
||||
operator fun <K> PropertiesCollection.Key<Map<K, KotlinType>>.invoke(vararg types: Pair<K, KType>) {
|
||||
append(types.map { (k, v) -> k to KotlinType(v) })
|
||||
}
|
||||
|
||||
@JvmName("invoke_kotlintype_map_from_fqname")
|
||||
operator fun <K> PropertiesCollection.Key<Map<K, KotlinType>>.invoke(vararg fqnames: Pair<K, String>) {
|
||||
append(fqnames.map { (k, v) -> k to KotlinType(v) })
|
||||
}
|
||||
|
||||
// direct manipulation - public - for usage in inline dsl methods and for extending dsl
|
||||
|
||||
operator fun <T : Any> set(key: PropertiesCollection.Key<in T>, value: T) {
|
||||
data[key] = value
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
operator fun <T : Any> get(key: PropertiesCollection.Key<in T>): T? = data[key]?.let { it as T }
|
||||
|
||||
// appenders to list and map properties
|
||||
|
||||
@JvmName("appendToList")
|
||||
fun <V> PropertiesCollection.Key<in List<V>>.append(values: Iterable<V>) {
|
||||
val newValues = get(this)?.let { it + values } ?: values.toList()
|
||||
data[this] = newValues
|
||||
}
|
||||
|
||||
fun <V> PropertiesCollection.Key<in List<V>>.append(vararg values: V) {
|
||||
val newValues = get(this)?.let { it + values } ?: values.toList()
|
||||
data[this] = newValues
|
||||
}
|
||||
|
||||
fun <K, V> PropertiesCollection.Key<in Map<K, V>>.append(values: Map<K, V>) {
|
||||
val newValues = get(this)?.let { it + values } ?: values
|
||||
data[this] = newValues
|
||||
}
|
||||
|
||||
@JvmName("appendToMap")
|
||||
fun <K, V> PropertiesCollection.Key<in Map<K, V>>.append(values: Iterable<Pair<K, V>>) {
|
||||
val newValues = get(this)?.let { it + values } ?: values
|
||||
data[this] = newValues
|
||||
}
|
||||
|
||||
// include other properties
|
||||
fun include(other: PropertiesCollection?) {
|
||||
other?.properties?.let { data.putAll(it) }
|
||||
}
|
||||
|
||||
// include another builder
|
||||
operator fun <T : Builder> T.invoke(body: T.() -> Unit) {
|
||||
this.body()
|
||||
this@Builder.data.putAll(this.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> PropertiesCollection.getOrNull(key: PropertiesCollection.Key<T>): T? =
|
||||
properties[key]?.let { it as T } ?: key.defaultValue
|
||||
|
||||
fun <T> PropertiesCollection.getOrError(key: PropertiesCollection.Key<T>): T =
|
||||
getOrNull(key) ?: throw IllegalArgumentException("Unknown key $key")
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> getFirstFromChainOrNull(key: PropertiesCollection.Key<T>, vararg propertyCollections: PropertiesCollection?): T? {
|
||||
for (collection in propertyCollections) {
|
||||
val value = collection?.properties?.get(key)
|
||||
if (value != null) return value as T
|
||||
}
|
||||
return key.defaultValue
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> getMergedFromChainOrNull(key: PropertiesCollection.Key<List<T>>, vararg propertyCollections: PropertiesCollection?): List<T>? {
|
||||
var found = false
|
||||
val res = ArrayList<T>()
|
||||
for (collection in propertyCollections) {
|
||||
val value = collection?.properties?.get(key)
|
||||
if (value != null) {
|
||||
found = true
|
||||
res.addAll(value as List<T>)
|
||||
}
|
||||
}
|
||||
return if (found) res else key.defaultValue
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package kotlin.script.experimental.util
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
data class TypedKey<T>(val name: String, val defaultValue: T? = null)
|
||||
|
||||
class TypedKeyDelegate<T>(val defaultValue: T? = null) {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): TypedKey<T> = TypedKey(property.name, defaultValue)
|
||||
}
|
||||
|
||||
fun <T> typedKey(defaultValue: T? = null) = TypedKeyDelegate(defaultValue)
|
||||
|
||||
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) })
|
||||
|
||||
constructor(pairs: Iterable<Pair<TypedKey<*>, Any?>>) : this(null, pairs)
|
||||
constructor(parent: ChainedPropertyBag, vararg pairs: Pair<TypedKey<*>, Any?>) : this(parent, pairs.asIterable())
|
||||
constructor(vararg pairs: Pair<TypedKey<*>, Any?>) : this(null, pairs.asIterable())
|
||||
|
||||
fun cloneWithNewParent(newParent: ChainedPropertyBag?): ChainedPropertyBag = when {
|
||||
this == newParent -> this
|
||||
newParent == null -> this
|
||||
parent == null -> createOptimized(newParent, data)
|
||||
else -> createOptimized(parent.cloneWithNewParent(newParent), data)
|
||||
}
|
||||
|
||||
inline operator fun <reified T> get(key: TypedKey<T>): T = getRaw(key) as T
|
||||
|
||||
fun <T> getRaw(key: TypedKey<T>): Any? = getOrNullRaw(key) ?: throw IllegalArgumentException("Unknown key $key")
|
||||
|
||||
inline fun <reified T> getOrNull(key: TypedKey<T>): T? = getOrNullRaw(key)?.let { it as T }
|
||||
|
||||
fun <T> getOrNullRaw(key: TypedKey<T>): Any? = data[key] ?: parent?.getOrNullRaw(key) ?: key.defaultValue
|
||||
|
||||
companion object {
|
||||
fun createOptimized(parent: ChainedPropertyBag?, data: Map<TypedKey<*>, Any?>): ChainedPropertyBag = when {
|
||||
parent != null && data.isEmpty() -> parent
|
||||
parent != null && parent.data.isEmpty() -> createOptimized(parent.parent, data)
|
||||
else -> ChainedPropertyBag(parent, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun chainPropertyBags(propertyBags: Iterable<ChainedPropertyBag?>): ChainedPropertyBag =
|
||||
propertyBags.fold(ChainedPropertyBag()) { res, next -> if (next == null) res else res.cloneWithNewParent(next) }
|
||||
|
||||
fun chainPropertyBags(vararg propertyBags: ChainedPropertyBag?): ChainedPropertyBag = chainPropertyBags(propertyBags.asIterable())
|
||||
Reference in New Issue
Block a user