34
34
import java .io .File ;
35
35
import java .io .IOException ;
36
36
import java .util .*;
37
+ import java .util .function .BiConsumer ;
37
38
import java .util .function .Function ;
38
39
import java .util .regex .Pattern ;
39
40
import java .util .stream .Collectors ;
@@ -407,10 +408,40 @@ public String modelFileFolder() {
407
408
return outputFolder + File .separator + sourceFolder + File .separator + modelPackage ().replace ('.' , File .separatorChar );
408
409
}
409
410
411
+ private enum KotlinModelType {
412
+ INTERFACE ("interface" , "x-kotlin-interface" ), OPEN_CLASS ("open class" , "x-kotlin-open-class" ), DATA_CLASS (
413
+ "data class" , "x-kotlin-data-class" );
414
+
415
+ final String literal ;
416
+ final String flag ;
417
+
418
+ KotlinModelType (String literal , String flag ) {
419
+ this .literal = literal ;
420
+ this .flag = flag ;
421
+ }
422
+ }
423
+
424
+ private enum CombinationType {
425
+ ALL_OF (it -> it .allOf ), ONE_OF (it -> it .oneOf );
426
+ final Function <CodegenModel , Set <String >> modelNamesFunc ;
427
+
428
+ CombinationType (Function <CodegenModel , Set <String >> modelNamesFunc ) {
429
+ this .modelNamesFunc = modelNamesFunc ;
430
+ }
431
+ }
432
+
433
+ private List <CodegenModel > getModels (CodegenModel cm , CombinationType ct ) {
434
+ List <CodegenModel > interfaceModels = cm .getInterfaceModels ();
435
+ Set <String > names = ct .modelNamesFunc .apply (cm );
436
+ if (names == null || interfaceModels == null ) {
437
+ return Collections .emptyList ();
438
+ }
439
+ return interfaceModels .stream ().filter (it -> names .contains (it .classname )).collect (Collectors .toList ());
440
+ }
441
+
410
442
@ Override
411
443
public Map <String , ModelsMap > postProcessAllModels (Map <String , ModelsMap > objs ) {
412
444
objs = super .postProcessAllModels (objs );
413
- objs = super .updateAllModels (objs );
414
445
415
446
if (!additionalModelTypeAnnotations .isEmpty ()) {
416
447
for (String modelName : objs .keySet ()) {
@@ -419,26 +450,118 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
419
450
}
420
451
}
421
452
422
- return objs ;
423
- }
453
+ final BiConsumer <CodegenModel , CodegenModel > markAsExtends = (childModel , superModel ) -> {
454
+ ((Map <CodegenModel , CodegenModel >) childModel .vendorExtensions .computeIfAbsent ("x-implements" ,
455
+ (k ) -> new IdentityHashMap <>())).put (superModel , superModel );
456
+ superModel .vendorExtensions .put ("x-has-implementors" , Boolean .TRUE );
457
+ };
424
458
425
- @ Override
426
- public ModelsMap postProcessModels (ModelsMap objs ) {
427
- objs = super .postProcessModelsEnum (objs );
428
- for (ModelMap mo : objs .getModels ()) {
429
- CodegenModel cm = mo .getModel ();
430
- if (cm .getDiscriminator () != null ) {
431
- cm .vendorExtensions .put ("x-has-data-class-body" , true );
432
- break ;
459
+ for (ModelsMap map : objs .values ()) {
460
+ for (ModelMap mo : map .getModels ()) {
461
+ CodegenModel codegenModel = mo .getModel ();
462
+ for (CodegenModel referencedModel : getModels (codegenModel , CombinationType .ALL_OF )) {
463
+ if (!referencedModel .name .equals (codegenModel .parent )) {
464
+ markAsExtends .accept (codegenModel , referencedModel );
465
+ }
466
+ }
467
+ for (CodegenModel interfaceModel : getModels (codegenModel , CombinationType .ONE_OF )) {
468
+ markAsExtends .accept (interfaceModel , codegenModel );
469
+ }
433
470
}
471
+ }
472
+
473
+ for (ModelsMap map : objs .values ()) {
474
+ for (ModelMap mo : map .getModels ()) {
475
+ CodegenModel cm = mo .getModel ();
476
+
477
+ KotlinModelType kotlinType ;
478
+ if (cm .getDiscriminator () != null || cm .vendorExtensions .get ("x-has-implementors" ) == Boolean .TRUE ) {
479
+ kotlinType = KotlinModelType .INTERFACE ;
480
+ } else if (cm .hasChildren ) {
481
+ kotlinType = KotlinModelType .OPEN_CLASS ;
482
+ } else {
483
+ kotlinType = KotlinModelType .DATA_CLASS ;
484
+ }
485
+ cm .vendorExtensions .put ("x-kotlin-model-type" , kotlinType .literal );
486
+ cm .vendorExtensions .put (kotlinType .flag , true );
487
+ cm .vendorExtensions .put ("x-parent-kotlin-interface" ,
488
+ cm .parentModel != null && (cm .parentModel .getDiscriminator () != null || cm .vendorExtensions .get ("x-has-implementors" ) == Boolean .TRUE ));
434
489
435
- for (CodegenProperty var : cm .vars ) {
436
- if (var .isEnum || isSerializableModel ()) {
490
+ if (kotlinType != KotlinModelType .DATA_CLASS || isSerializableModel () || cm .vars .stream ().anyMatch (var -> var .isEnum )) {
437
491
cm .vendorExtensions .put ("x-has-data-class-body" , true );
438
- break ;
492
+ }
493
+
494
+ final Map <CodegenModel , CodegenModel > xImplements =
495
+ (Map <CodegenModel , CodegenModel >) cm .vendorExtensions .getOrDefault ("x-implements" ,
496
+ Collections .emptyMap ());
497
+ if (!xImplements .isEmpty ()) {
498
+ cm .vendorExtensions .put ("x-kotlin-interfaces-list" ,
499
+ xImplements .keySet ().stream ().filter (im -> im != cm .parentModel ).map (CodegenModel ::getClassname ).sorted ().collect (Collectors .joining (", " )));
500
+ }
501
+ }
502
+ }
503
+
504
+ // we need to add variables from parent to current model if current is data class
505
+ for (ModelsMap map : objs .values ()) {
506
+ for (ModelMap mo : map .getModels ()) {
507
+ final CodegenModel cm = mo .getModel ();
508
+ final CodegenModel parentModel = cm .getParentModel ();
509
+ final boolean selfIsInterface = Objects .equals (cm .getVendorExtensions ().get ("x-kotlin-model-type" ),
510
+ KotlinModelType .INTERFACE .literal );
511
+
512
+ if (!selfIsInterface && parentModel != null ) {
513
+ // TODO: handle other interfaces as well, not only parent? need test case
514
+ Set <String > alreadyDefined =
515
+ cm .getAllVars ().stream ().map (CodegenProperty ::getName ).collect (Collectors .toSet ());
516
+ for (CodegenProperty prop : parentModel .getVars ()) {
517
+ if (!alreadyDefined .contains (prop .name )) {
518
+ cm .allVars .add (prop .clone ());
519
+ alreadyDefined .add (prop .name );
520
+ }
521
+ }
522
+ }
523
+ }
524
+ }
525
+
526
+ // mark all override variables as such
527
+ for (ModelsMap map : objs .values ()) {
528
+ for (ModelMap mo : map .getModels ()) {
529
+ CodegenModel cm = mo .getModel ();
530
+ Set <String > inheritedPropertyNames = new TreeSet <>();
531
+
532
+ Collection <CodegenModel > interfaceModels =
533
+ ((Map <CodegenModel , CodegenModel >) cm .getVendorExtensions ().getOrDefault ("x-implements" ,
534
+ Collections .emptyMap ())).values ();
535
+ for (CodegenModel interfaceModel : interfaceModels ) {
536
+ interfaceModel .vars .stream ().map (it -> it .name ).forEach (inheritedPropertyNames ::add );
537
+ interfaceModel .allVars .stream ().map (it -> it .name ).forEach (inheritedPropertyNames ::add );
538
+ }
539
+
540
+ final CodegenModel parentModel = cm .getParentModel ();
541
+ if (parentModel != null ) {
542
+ parentModel .vars .stream ().map (it -> it .name ).forEach (inheritedPropertyNames ::add );
543
+ parentModel .allVars .stream ().map (it -> it .name ).forEach (inheritedPropertyNames ::add );
544
+ }
545
+
546
+ for (CodegenProperty prop : cm .vars ) {
547
+ if (inheritedPropertyNames .contains (prop .name )) {
548
+ prop .vendorExtensions .put ("x-kotlin-override" , Boolean .TRUE );
549
+ }
550
+ }
551
+ for (CodegenProperty prop : cm .allVars ) {
552
+ if (inheritedPropertyNames .contains (prop .name )) {
553
+ prop .vendorExtensions .put ("x-kotlin-override" , Boolean .TRUE );
554
+ }
439
555
}
440
556
}
441
557
}
558
+
559
+ return objs ;
560
+ }
561
+
562
+ @ Override
563
+ public ModelsMap postProcessModels (ModelsMap objs ) {
564
+ objs = super .postProcessModels (objs );
442
565
return postProcessModelsEnum (objs );
443
566
}
444
567
@@ -890,29 +1013,6 @@ protected boolean needToImport(String type) {
890
1013
!type .contains ("." );
891
1014
}
892
1015
893
- @ Override
894
- public CodegenModel fromModel (String name , Schema schema ) {
895
- CodegenModel m = super .fromModel (name , schema );
896
- m .optionalVars = m .optionalVars .stream ().distinct ().collect (Collectors .toList ());
897
- // Update allVars/requiredVars/optionalVars with isInherited
898
- // Each of these lists contains elements that are similar, but they are all cloned
899
- // via CodegenModel.removeAllDuplicatedProperty and therefore need to be updated
900
- // separately.
901
- // First find only the parent vars via baseName matching
902
- Map <String , CodegenProperty > allVarsMap = m .allVars .stream ()
903
- .collect (Collectors .toMap (CodegenProperty ::getBaseName , Function .identity ()));
904
- allVarsMap .keySet ()
905
- .removeAll (m .vars .stream ().map (CodegenProperty ::getBaseName ).collect (Collectors .toSet ()));
906
- // Update the allVars
907
- allVarsMap .values ().forEach (p -> p .isInherited = true );
908
- // Update any other vars (requiredVars, optionalVars)
909
- Stream .of (m .requiredVars , m .optionalVars )
910
- .flatMap (List ::stream )
911
- .filter (p -> allVarsMap .containsKey (p .baseName ))
912
- .forEach (p -> p .isInherited = true );
913
- return m ;
914
- }
915
-
916
1016
@ Override
917
1017
public String toEnumValue (String value , String datatype ) {
918
1018
if ("kotlin.Int" .equals (datatype ) || "kotlin.Long" .equals (datatype )) {
0 commit comments