1
1
package org.modelix.metamodel.generator
2
2
3
3
import com.squareup.kotlinpoet.*
4
+ import com.squareup.kotlinpoet.MemberName.Companion.member
4
5
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
5
6
import org.modelix.metamodel.*
6
7
import org.modelix.model.api.*
@@ -18,9 +19,14 @@ private val reservedPropertyNames: Set<String> = setOf(
18
19
) + IConcept ::class .members.map { it.name }
19
20
20
21
class MetaModelGenerator (val outputDir : Path ) {
22
+ var alwaysUseNonNullableProperties: Boolean = true
21
23
22
24
private val headerComment = " \n generated by modelix metamodel generator\n "
23
25
26
+ private fun PropertyData.asKotlinType (): TypeName {
27
+ return if (! optional || alwaysUseNonNullableProperties) type.asKotlinType() else type.asKotlinType().copy(nullable = true )
28
+ }
29
+
24
30
private fun FileSpec.write () {
25
31
writeTo(outputDir)
26
32
}
@@ -97,7 +103,7 @@ class MetaModelGenerator(val outputDir: Path) {
97
103
val receiverType = Iterable ::class .asTypeName().parameterizedBy(concept.nodeWrapperInterfaceType())
98
104
when (val data = feature.data) {
99
105
is PropertyData -> {
100
- addProperty(PropertySpec .builder(feature.validName, List ::class .asTypeName().parameterizedBy(data.type. asKotlinType()))
106
+ addProperty(PropertySpec .builder(feature.validName, List ::class .asTypeName().parameterizedBy(data.asKotlinType()))
101
107
.receiver(receiverType)
102
108
.getter(FunSpec .getterBuilder().addStatement(" return map { it.%N }" , feature.validName).build())
103
109
.build())
@@ -176,19 +182,40 @@ class MetaModelGenerator(val outputDir: Path) {
176
182
for (feature in concept.directFeatures()) {
177
183
when (val data = feature.data) {
178
184
is PropertyData -> {
179
- addProperty(PropertySpec .builder(feature.validName, IProperty ::class )
180
- .initializer(""" ${GeneratedConcept <* , * >::newProperty.name} ("${feature.originalName} ", ${data.optional} )""" )
185
+ val serializer = (if (! data.optional || alwaysUseNonNullableProperties) {
186
+ when (data.type) {
187
+ PropertyType .STRING -> MandatoryStringPropertySerializer ::class
188
+ PropertyType .BOOLEAN -> MandatoryBooleanPropertySerializer ::class
189
+ PropertyType .INT -> MandatoryIntPropertySerializer ::class
190
+ }
191
+ } else {
192
+ when (data.type) {
193
+ PropertyType .STRING -> OptionalStringPropertySerializer ::class
194
+ PropertyType .BOOLEAN -> OptionalBooleanPropertySerializer ::class
195
+ PropertyType .INT -> OptionalIntPropertySerializer ::class
196
+ }
197
+ }).asTypeName()
198
+ addProperty(PropertySpec .builder(feature.validName, GeneratedProperty ::class .asClassName().parameterizedBy(data.asKotlinType()))
199
+ .initializer(""" newProperty("${feature.originalName} ", %T, ${data.optional} )""" , serializer)
181
200
.build())
182
201
}
183
202
is ChildLinkData -> {
184
203
val methodName = if (data.multiple) " newChildListLink" else " newSingleChildLink"
185
204
addProperty(PropertySpec .builder(feature.validName, feature.generatedChildLinkType())
186
- .initializer(""" $methodName ("${feature.originalName} ", ${data.optional} , ${data.type.conceptObjectName()} )""" )
205
+ .initializer(
206
+ """ $methodName ("${feature.originalName} ", ${data.optional} , %T, %T::class)""" ,
207
+ data.type.conceptObjectName().parseClassName(),
208
+ data.type.nodeWrapperInterfaceName().parseClassName()
209
+ )
187
210
.build())
188
211
}
189
212
is ReferenceLinkData -> {
190
213
addProperty(PropertySpec .builder(feature.validName, feature.generatedReferenceLinkType())
191
- .initializer(""" newReferenceLink("${feature.originalName} ", ${data.optional} , ${data.type.conceptObjectName()} )""" )
214
+ .initializer(
215
+ """ newReferenceLink("${feature.originalName} ", ${data.optional} , %T, %T::class)""" ,
216
+ data.type.conceptObjectName().parseClassName(),
217
+ data.type.nodeWrapperInterfaceName().parseClassName()
218
+ )
192
219
.build())
193
220
}
194
221
}
@@ -204,22 +231,21 @@ class MetaModelGenerator(val outputDir: Path) {
204
231
}
205
232
for (feature in concept.directFeatures()) {
206
233
when (val data = feature.data) {
207
- is PropertyData -> addProperty(PropertySpec .builder(feature.validName, IProperty ::class ).build())
234
+ is PropertyData -> addProperty(PropertySpec .builder(feature.validName, GeneratedProperty ::class .asClassName().parameterizedBy(data.asKotlinType()) ).build())
208
235
is ChildLinkData -> addProperty(PropertySpec .builder(feature.validName, feature.generatedChildLinkType()).build())
209
236
is ReferenceLinkData -> addProperty(PropertySpec .builder(feature.validName, feature.generatedReferenceLinkType()).build())
210
237
}
211
238
}
212
239
213
240
addType(TypeSpec .companionObjectBuilder().apply {
214
241
superclass(concept.conceptWrapperImplType())
215
- if (! concept.concept.abstract) {
216
- addSuperinterface(INonAbstractConcept ::class .asTypeName().parameterizedBy(concept.nodeWrapperInterfaceType()))
217
- addFunction(FunSpec .builder(INonAbstractConcept <* >::getInstanceClass.name)
218
- .addModifiers(KModifier .OVERRIDE )
219
- .returns(KClass ::class .asTypeName().parameterizedBy(concept.nodeWrapperInterfaceType()))
220
- .addStatement(" return ${concept.nodeWrapperInterfaceType().simpleName} ::class" )
221
- .build())
222
- }
242
+ val t = if (concept.concept.abstract) IConceptOfTypedNode ::class else INonAbstractConcept ::class
243
+ addSuperinterface(t.asTypeName().parameterizedBy(concept.nodeWrapperInterfaceType()))
244
+ addFunction(FunSpec .builder(IConceptOfTypedNode <* >::getInstanceInterface.name)
245
+ .addModifiers(KModifier .OVERRIDE )
246
+ .returns(KClass ::class .asTypeName().parameterizedBy(concept.nodeWrapperInterfaceType()))
247
+ .addStatement(" return ${concept.nodeWrapperInterfaceType().simpleName} ::class" )
248
+ .build())
223
249
}.build())
224
250
}.build()
225
251
}
@@ -238,15 +264,16 @@ class MetaModelGenerator(val outputDir: Path) {
238
264
239
265
primaryConstructor(FunSpec .constructorBuilder().addModifiers(KModifier .PROTECTED ).build())
240
266
241
- addProperty(PropertySpec .builder(ITypedConcept ::_concept .name, IConcept ::class )
267
+ addFunction(FunSpec .builder(ITypedConcept ::untyped.name)
268
+ .returns(IConcept ::class )
242
269
.addModifiers(KModifier .OVERRIDE )
243
- .initializer( concept.conceptObjectName ())
270
+ .addStatement( " return %T " , concept.conceptObjectType ())
244
271
.build())
245
272
246
273
for (feature in concept.directFeaturesAndConflicts()) {
247
274
when (val data = feature.data) {
248
275
is PropertyData -> {
249
- addProperty(PropertySpec .builder(feature.validName, IProperty ::class )
276
+ addProperty(PropertySpec .builder(feature.validName, GeneratedProperty ::class .asClassName().parameterizedBy(data.asKotlinType()) )
250
277
.addModifiers(KModifier .OVERRIDE )
251
278
.initializer(feature.kotlinRef())
252
279
.build())
@@ -298,20 +325,25 @@ class MetaModelGenerator(val outputDir: Path) {
298
325
for (feature in concept.directFeaturesAndConflicts()) {
299
326
when (val data = feature.data) {
300
327
is PropertyData -> {
301
- val accessorClass = when (data.type) {
302
- PropertyType .STRING -> StringPropertyAccessor ::class
303
- PropertyType .BOOLEAN -> BooleanPropertyAccessor ::class
304
- PropertyType .INT -> IntPropertyAccessor ::class
305
- }
306
- addProperty(PropertySpec .builder(feature.validName, data.type.asKotlinType())
328
+ addProperty(PropertySpec .builder(feature.validName, data.asKotlinType())
307
329
.addModifiers(KModifier .OVERRIDE )
308
330
.mutable(true )
309
- .delegate(""" ${accessorClass.qualifiedName} (unwrap(), "${feature.originalName} ")""" )
331
+ .delegate(
332
+ """ %T(unwrap(), %T.%N)""" ,
333
+ TypedPropertyAccessor ::class .asTypeName(),
334
+ feature.concept.conceptObjectType(),
335
+ feature.validName,
336
+ )
310
337
.build())
311
338
addProperty(PropertySpec .builder(" raw_" + feature.validName, String ::class .asTypeName().copy(nullable = true ))
312
339
.addModifiers(KModifier .OVERRIDE )
313
340
.mutable(true )
314
- .delegate(""" ${RawPropertyAccessor ::class .qualifiedName} (unwrap(), "${feature.originalName} ")""" )
341
+ .delegate(
342
+ """ %T(unwrap(), %T.%N.untyped())""" ,
343
+ RawPropertyAccessor ::class .asTypeName(),
344
+ feature.concept.conceptObjectType(),
345
+ feature.validName,
346
+ )
315
347
.build())
316
348
}
317
349
is ChildLinkData -> {
@@ -323,20 +355,32 @@ class MetaModelGenerator(val outputDir: Path) {
323
355
val accessorName = accessorSubclass.qualifiedName
324
356
addProperty(PropertySpec .builder(feature.validName, type)
325
357
.addModifiers(KModifier .OVERRIDE )
326
- .initializer(""" $accessorName (${ITypedNode ::unwrap.name} (), "${feature.originalName} ", ${data.type.conceptObjectName()} , ${data.type.nodeWrapperInterfaceName()} ::class)""" )
358
+ .initializer(
359
+ """ $accessorName (${ITypedNode ::unwrap.name} (), %T.%N, ${data.type.conceptObjectName()} , ${data.type.nodeWrapperInterfaceName()} ::class)""" ,
360
+ feature.concept.conceptObjectType(),
361
+ feature.validName,
362
+ )
327
363
.build())
328
364
}
329
365
is ReferenceLinkData -> {
330
366
val accessorClass = if (data.optional) OptionalReferenceAccessor ::class else MandatoryReferenceAccessor ::class
331
367
addProperty(PropertySpec .builder(feature.validName, data.type.parseConceptRef(concept.language).nodeWrapperInterfaceType().copy(nullable = data.optional))
332
368
.addModifiers(KModifier .OVERRIDE )
333
369
.mutable(true )
334
- .delegate(""" ${accessorClass.qualifiedName} (${ITypedNode ::unwrap.name} (), "${feature.originalName} ", ${data.type.nodeWrapperInterfaceName()} ::class)""" )
370
+ .delegate(
371
+ """ ${accessorClass.qualifiedName} (${ITypedNode ::unwrap.name} (), %T.%N, ${data.type.nodeWrapperInterfaceName()} ::class)""" ,
372
+ feature.concept.conceptObjectType(),
373
+ feature.validName,
374
+ )
335
375
.build())
336
376
addProperty(PropertySpec .builder(" raw_" + feature.validName, INode ::class .asTypeName().copy(nullable = true ))
337
377
.addModifiers(KModifier .OVERRIDE )
338
378
.mutable(true )
339
- .delegate(""" ${RawReferenceAccessor ::class .qualifiedName} (${ITypedNode ::unwrap.name} (), "${feature.originalName} ")""" )
379
+ .delegate(
380
+ """ ${RawReferenceAccessor ::class .qualifiedName} (${ITypedNode ::unwrap.name} (), %T.%N)""" ,
381
+ feature.concept.conceptObjectType(),
382
+ feature.validName,
383
+ )
340
384
.build())
341
385
}
342
386
}
@@ -353,7 +397,7 @@ class MetaModelGenerator(val outputDir: Path) {
353
397
for (feature in concept.directFeatures()) {
354
398
when (val data = feature.data) {
355
399
is PropertyData -> {
356
- addProperty(PropertySpec .builder(feature.validName, data.type. asKotlinType())
400
+ addProperty(PropertySpec .builder(feature.validName, data.asKotlinType())
357
401
.mutable(true )
358
402
.build())
359
403
addProperty(PropertySpec .builder(" raw_" + feature.validName, String ::class .asTypeName().copy(nullable = true ))
@@ -390,6 +434,7 @@ fun PropertyType.asKotlinType(): TypeName {
390
434
PropertyType .INT -> Int ::class .asTypeName()
391
435
}
392
436
}
437
+ fun String.parseClassName () = ClassName (substringBeforeLast(" ." ), substringAfterLast(" ." ))
393
438
fun ConceptRef.conceptWrapperImplType () = ClassName (languageName, conceptName.conceptWrapperImplName())
394
439
fun ConceptRef.conceptWrapperInterfaceType () = ClassName (languageName, conceptName.conceptWrapperInterfaceName())
395
440
fun ConceptRef.nodeWrapperImplType () = ClassName (languageName, conceptName.nodeWrapperImplName())
@@ -425,7 +470,7 @@ fun LanguageSet.ConceptInLanguage.nodeWrapperInterfaceType() = ClassName(languag
425
470
fun LanguageSet.ConceptInLanguage.conceptWrapperImplType () = ClassName (language.name, concept.conceptWrapperImplName())
426
471
fun LanguageSet.ConceptInLanguage.conceptWrapperInterfaceType () = ClassName (language.name, concept.conceptWrapperInterfaceName())
427
472
428
- fun FeatureInConcept.kotlinRef () = concept.conceptObjectType().canonicalName + " . " + CodeBlock .of(" %N " , validName)
473
+ fun FeatureInConcept.kotlinRef () = CodeBlock .of(" %T.%N " , concept.conceptObjectType() , validName)
429
474
fun FeatureInConcept.generatedChildLinkType (): TypeName {
430
475
val childConcept = (data as ChildLinkData ).type.parseConceptRef(concept.language)
431
476
val linkClass = if (data.multiple) GeneratedChildListLink ::class else GeneratedSingleChildLink ::class
0 commit comments