All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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