
com.strumenta.kolasu.semantics.symbol.provider.declarative.DeclarativeSymbolProvider.kt Maven / Gradle / Ivy
package com.strumenta.kolasu.semantics.symbol.provider.declarative
import com.strumenta.kolasu.ids.NodeIdProvider
import com.strumenta.kolasu.model.Node
import com.strumenta.kolasu.model.ReferenceByName
import com.strumenta.kolasu.semantics.symbol.description.BooleanValueDescription
import com.strumenta.kolasu.semantics.symbol.description.ContainmentValueDescription
import com.strumenta.kolasu.semantics.symbol.description.IntegerValueDescription
import com.strumenta.kolasu.semantics.symbol.description.ListValueDescription
import com.strumenta.kolasu.semantics.symbol.description.NullValueDescription
import com.strumenta.kolasu.semantics.symbol.description.ReferenceValueDescription
import com.strumenta.kolasu.semantics.symbol.description.StringValueDescription
import com.strumenta.kolasu.semantics.symbol.description.SymbolDescription
import com.strumenta.kolasu.semantics.symbol.description.ValueDescription
import com.strumenta.kolasu.semantics.symbol.provider.SymbolProvider
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.full.allSuperclasses
import kotlin.reflect.full.isSuperclassOf
import kotlin.reflect.full.safeCast
inline fun symbolFor(
noinline specification: DeclarativeSymbolProvideRuleApi.(
DeclarativeSymbolProviderRuleContext
) -> Unit
): DeclarativeSymbolProviderRule {
return DeclarativeSymbolProviderRule(NodeTy::class, specification)
}
open class DeclarativeSymbolProvider(
private val nodeIdProvider: NodeIdProvider,
vararg rules: DeclarativeSymbolProviderRule
) : SymbolProvider {
private val rules: List> = rules.sorted()
override fun symbolFor(node: Node): SymbolDescription? {
return this.rules
.firstOrNull { it.isCompatibleWith(node::class) }
?.invoke(nodeIdProvider, this, node)
}
}
class DeclarativeSymbolProviderRule(
private val nodeType: KClass,
private val specification: DeclarativeSymbolProvideRuleApi.(
DeclarativeSymbolProviderRuleContext
) -> Unit
) : DeclarativeSymbolProvideRuleApi,
(NodeIdProvider, SymbolProvider, Node) -> SymbolDescription,
Comparable> {
private var name: (Node) -> String? = { null }
private val properties: MutableMap ValueDescription> = mutableMapOf()
override fun name(name: String) {
this.name = { name }
}
override fun include(property: KProperty1) {
if (property.name == "name") {
this.name = { node ->
@Suppress("UNCHECKED_CAST")
String::class.safeCast(property.get(node as NodeTy))
}
}
this.properties[property.name] = { nodeIdProvider, node ->
@Suppress("UNCHECKED_CAST")
this.toValueDescription(nodeIdProvider, property.get(node as NodeTy))
}
}
override fun invoke(
nodeIdProvider: NodeIdProvider,
symbolProvider: SymbolProvider,
node: Node
): SymbolDescription {
@Suppress("UNCHECKED_CAST")
return (node as NodeTy).let {
this.specification(DeclarativeSymbolProviderRuleContext(node, symbolProvider))
SymbolDescription(
getName(node),
nodeIdProvider.id(node),
getTypes(node),
getProperties(nodeIdProvider, node)
)
}
}
fun isCompatibleWith(nodeType: KClass<*>): Boolean {
return this.nodeType.isSuperclassOf(nodeType)
}
override fun compareTo(other: DeclarativeSymbolProviderRule): Int {
return when {
this.nodeType.isSuperclassOf(other.nodeType) -> 1
other.nodeType.isSuperclassOf(this.nodeType) -> -1
else -> (this.nodeType.qualifiedName ?: "") compareTo (other.nodeType.qualifiedName ?: "")
}
}
private fun getName(node: NodeTy): String {
return this.name(node)
?: throw RuntimeException("Symbol description name property not set for node: ${node::class.qualifiedName}")
}
private fun getTypes(node: NodeTy): List {
return listOfNotNull(
node::class.qualifiedName
) + node::class.allSuperclasses.mapNotNull(KClass<*>::qualifiedName)
}
private fun getProperties(
nodeIdProvider: NodeIdProvider,
node: NodeTy
): Map {
return this.properties.mapValues { (_, valueDescriptionProvider) ->
valueDescriptionProvider(nodeIdProvider, node)
}
}
private fun toValueDescription(
nodeIdProvider: NodeIdProvider,
source: Any?
): ValueDescription {
return when (source) {
is Boolean -> BooleanValueDescription(source)
is Int -> IntegerValueDescription(source)
is String -> StringValueDescription(source)
is Node -> toContainmentValueDescription(nodeIdProvider, source)
is ReferenceByName<*> -> toReferenceValueDescription(nodeIdProvider, source)
is List<*> -> toListValueDescription(nodeIdProvider, source)
null -> NullValueDescription
else -> throw RuntimeException("Unsupported value description for ${source::class.qualifiedName}")
}
}
private fun toReferenceValueDescription(
nodeIdProvider: NodeIdProvider,
source: ReferenceByName<*>
): ReferenceValueDescription {
return ReferenceValueDescription(
source.referred?.let { it as? Node }?.let { nodeIdProvider.id(it) }
)
}
private fun toContainmentValueDescription(
nodeIdProvider: NodeIdProvider,
source: Node
): ContainmentValueDescription {
return ContainmentValueDescription(nodeIdProvider.id(source))
}
private fun toListValueDescription(
nodeIdProvider: NodeIdProvider,
source: List<*>
): ListValueDescription {
return ListValueDescription(source.map { this.toValueDescription(nodeIdProvider, it) }.toList())
}
}
interface DeclarativeSymbolProvideRuleApi {
fun name(name: String)
fun include(property: KProperty1)
}
data class DeclarativeSymbolProviderRuleContext(val node: NodeTy, val symbolProvider: SymbolProvider)
© 2015 - 2025 Weber Informatics LLC | Privacy Policy