Skip to content

Commit fad1070

Browse files
committed
feat(model-api-gen): generation of meta properties
1 parent f9dc362 commit fad1070

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/GeneratorInput.kt

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ internal class ProcessedLanguageSet(dataList: List<LanguageData>) : IProcessedLa
3636
private lateinit var uid2language: Map<String, ProcessedLanguage>
3737
private lateinit var fqName2concept: Map<String, ProcessedConcept>
3838
private lateinit var uid2concept: Map<String, ProcessedConcept>
39+
private lateinit var conceptFqName2RootMetaProperties: MutableMap<String, MutableSet<String>>
3940

4041
init {
4142
load(dataList)
@@ -62,6 +63,18 @@ internal class ProcessedLanguageSet(dataList: List<LanguageData>) : IProcessedLa
6263
initIndexes()
6364
resolveConceptReferences()
6465
fixRoleConflicts()
66+
propagateMetaProperties()
67+
}
68+
69+
private fun propagateMetaProperties() {
70+
conceptFqName2RootMetaProperties = mutableMapOf()
71+
val concepts = languages.flatMap { it.getConcepts() }
72+
concepts.forEach { concept ->
73+
val keys = concept.metaProperties.keys
74+
// TODO the computation of rootConcept will visit some concepts multiple times across loop iterations; could potentially be optimized
75+
val rootConcept = concept.getNonInterfaceRootConcept()
76+
conceptFqName2RootMetaProperties.getOrPut(rootConcept.fqName()) { mutableSetOf() }.addAll(keys)
77+
}
6578
}
6679

6780
private fun initIndexes() {
@@ -137,6 +150,10 @@ internal class ProcessedLanguageSet(dataList: List<LanguageData>) : IProcessedLa
137150
fun getLanguages(): List<ProcessedLanguage> {
138151
return languages
139152
}
153+
154+
fun getMetaPropertyRoots(fqConceptName: String): Set<String> {
155+
return conceptFqName2RootMetaProperties[fqConceptName] ?: emptySet()
156+
}
140157
}
141158

142159
internal class ProcessedLanguage(var name: String, var uid: String?) {
@@ -162,7 +179,14 @@ internal class ProcessedLanguage(var name: String, var uid: String?) {
162179
fun load(dataList: List<ConceptData>) {
163180
for (data in dataList) {
164181
addConcept(
165-
ProcessedConcept(data.name, data.uid, data.abstract, data.extends.map { ProcessedConceptReference(it) }.toMutableList(), data.deprecationMessage).also { concept ->
182+
ProcessedConcept(
183+
data.name,
184+
data.uid,
185+
data.abstract,
186+
data.extends.map { ProcessedConceptReference(it) }.toMutableList(),
187+
data.deprecationMessage,
188+
data.metaProperties,
189+
).also { concept ->
166190
concept.loadRoles(data)
167191
},
168192
)
@@ -220,6 +244,7 @@ internal class ProcessedConcept(
220244
var abstract: Boolean,
221245
val extends: MutableList<ProcessedConceptReference>,
222246
override var deprecationMessage: String?,
247+
val metaProperties: MutableMap<String, String>,
223248
) : IProcessedDeprecatable {
224249
lateinit var language: ProcessedLanguage
225250
private val roles: MutableList<ProcessedRole> = ArrayList()
@@ -253,6 +278,17 @@ internal class ProcessedConcept(
253278
fun getAllSuperConcepts(): Sequence<ProcessedConcept> = getAllSuperConcepts_().distinct()
254279
fun getAllSuperConceptsAndSelf(): Sequence<ProcessedConcept> = getAllSuperConceptsAndSelf_().distinct()
255280
fun getDuplicateSuperConcepts() = getAllSuperConcepts_().groupBy { it }.filter { it.value.size > 1 }.map { it.key }
281+
282+
fun getNonInterfaceSuperConcept() = getDirectSuperConcepts().firstOrNull()
283+
fun getNonInterfaceRootConcept(): ProcessedConcept {
284+
var current: ProcessedConcept? = this
285+
var result = this
286+
while (current != null) {
287+
result = current
288+
current = current.getNonInterfaceSuperConcept()
289+
}
290+
return result
291+
}
256292
}
257293

258294
internal sealed class ProcessedRole(

model-api-gen/src/main/kotlin/org/modelix/metamodel/generator/MetaModelGenerator.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,15 @@ class MetaModelGenerator(val outputDir: Path, val nameConfig: NameConfig = NameC
647647
for (extended in concept.getDirectSuperConcepts()) {
648648
addSuperinterface(extended.conceptWrapperInterfaceClass().parameterizedBy(nodeT))
649649
}
650+
651+
for (metaPropertyName in concept.language.languageSet.getMetaPropertyRoots(concept.fqName())) {
652+
addProperty(
653+
PropertySpec.builder(metaPropertyName, String::class.asTypeName().copy(nullable = true))
654+
.getter(FunSpec.getterBuilder().addCode("return null").build())
655+
.build(),
656+
)
657+
}
658+
650659
for (feature in concept.getOwnRoles()) {
651660
when (feature) {
652661
is ProcessedProperty -> addProperty(
@@ -694,6 +703,14 @@ class MetaModelGenerator(val outputDir: Path, val nameConfig: NameConfig = NameC
694703
.addStatement("return %T", concept.conceptObjectType())
695704
.build(),
696705
)
706+
concept.metaProperties.forEach { (key, value) ->
707+
addProperty(
708+
PropertySpec.builder(key, String::class.asTypeName())
709+
.addModifiers(KModifier.OVERRIDE)
710+
.initializer("%S", value)
711+
.build(),
712+
)
713+
}
697714
}.build(),
698715
)
699716
}.build()

0 commit comments

Comments
 (0)