From f11120e0a34e6bfb034ed520d80c5e61b8840dbc Mon Sep 17 00:00:00 2001 From: Leonie Dahle Date: Fri, 4 Sep 2026 14:31:13 +0200 Subject: [PATCH 1/5] add CDAssociationModifierCoCo to prevent visibility conflicts in CDAssociationModifierCoCo.java --- .../cocos/CDAssociationCoCos.java | 9 +++-- .../cocos/ebnf/CDAssociationModifierCoCo.java | 40 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java diff --git a/cdlang/src/main/java/de/monticore/cdassociation/cocos/CDAssociationCoCos.java b/cdlang/src/main/java/de/monticore/cdassociation/cocos/CDAssociationCoCos.java index 1d93b5339..455c9c2d5 100644 --- a/cdlang/src/main/java/de/monticore/cdassociation/cocos/CDAssociationCoCos.java +++ b/cdlang/src/main/java/de/monticore/cdassociation/cocos/CDAssociationCoCos.java @@ -6,19 +6,19 @@ import de.monticore.cdassociation.cocos.ebnf.*; public class CDAssociationCoCos extends CoCoParent { - + @Override public CDAssociationCoCoChecker createNewChecker() { return new CDAssociationCoCoChecker(); } - + @Override protected void addCheckerForAllCoCos(CDAssociationCoCoChecker checker) { addCheckerForEbnfCoCos(checker); addCheckerForMcgCoCos(checker); addCheckerForMcg2EbnfCoCos(checker); } - + @Override protected void addEbnfCoCos(CDAssociationCoCoChecker checker) { // CDAssociation @@ -28,6 +28,7 @@ protected void addEbnfCoCos(CDAssociationCoCoChecker checker) { checker.addCoCo(new CDAssociationOrderedCardinalityGreaterOne()); checker.addCoCo(new CDAssociationRoleNameLowerCase()); checker.addCoCo(new CDAssociationRoleNameNoConflictWithLocalAttribute()); + checker.addCoCo(new CDAssociationModifierCoCo()); } - + } diff --git a/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java b/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java new file mode 100644 index 000000000..6b5c3c1f5 --- /dev/null +++ b/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java @@ -0,0 +1,40 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cdassociation.cocos.ebnf; + +import de.monticore.cdassociation._ast.ASTCDAssociation; +import de.monticore.cdassociation._cocos.CDAssociationASTCDAssociationCoCo; +import de.monticore.umlmodifier._ast.ASTModifier; +import de.se_rwth.commons.logging.Log; + +public class CDAssociationModifierCoCo implements CDAssociationASTCDAssociationCoCo { + + @Override + public void check(ASTCDAssociation a) { + + //----------------------------------check the visibility modifiers----------------------------------------------------------- + //main modifier + boolean hasMainVisibility=false; + if(a.getModifier()!=null) { + ASTModifier mainModifier = a.getModifier(); + hasMainVisibility= mainModifier.isPublic()||mainModifier.isProtected()||mainModifier.isPrivate(); + } + + //left modifier + boolean hasLeftVisibility=false; + if(a.getLeft().getModifier()!=null) { + ASTModifier leftModifier = a.getLeft().getModifier(); + hasLeftVisibility=leftModifier.isPublic()||leftModifier.isProtected()||leftModifier.isPrivate(); + } + + //right modifier + boolean hasRightVisibility=false; + if(a.getRight().getModifier()!=null) { + ASTModifier rightModifier = a.getRight().getModifier(); + hasRightVisibility=rightModifier.isPublic()||rightModifier.isProtected()||rightModifier.isPrivate(); + } + + if(hasMainVisibility && (hasLeftVisibility || hasRightVisibility)) { + Log.error("Association cannot combine a main visibility modifier with individual role visibility modifiers.", a.get_SourcePositionStart()); + } + } +} From d5edace4829e4e3246b620db6009ab8ab68e938c Mon Sep 17 00:00:00 2001 From: Leonie Dahle Date: Fri, 4 Sep 2026 14:32:59 +0200 Subject: [PATCH 2/5] in progress: support additional modifiers for Associations in CDAssociationSymbolTableCompleter.java --- .../CDAssociationSymbolTableCompleter.java | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java b/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java index df5f1d757..a35dabb54 100644 --- a/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java +++ b/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java @@ -21,19 +21,19 @@ public class CDAssociationSymbolTableCompleter implements CDAssociationVisitor2, CDAssociationHandler { - + protected CDAssociationTraverser traverser; - + protected ISynthesize typeSynthesizer; - + public CDAssociationSymbolTableCompleter(ISynthesize typeSynthesizer) { this.typeSynthesizer = typeSynthesizer; } - + public CDAssociationSymbolTableCompleter() { this(new FullSynthesizeFromMCBasicTypes()); } - + @Override public void handle(ASTCDAssociation node) { if (node.getLeft().isPresentSymbol()) { @@ -44,31 +44,31 @@ public void handle(ASTCDAssociation node) { } endVisit(node); } - + public void initialize_CDRole(CDRoleSymbol symbol, ASTCDAssociation ast, boolean isLeft) { final ASTCDAssocSide side = isLeft ? ast.getLeft() : ast.getRight(); - + symbol.setAssocSide(side); final Optional typeResult = getSymTypeExpression(ast, side); if (!typeResult.isPresent()) { return; } symbol.setType(typeResult.get()); - + setupModifiers(side.getModifier(), ast.getModifier(), symbol); - + symbol.setIsDefinitiveNavigable(isLeft ? ast.getCDAssocDir().isDefinitiveNavigableLeft() : ast .getCDAssocDir().isDefinitiveNavigableRight()); - + if (side.isPresentCDCardinality()) { symbol.setCardinality(side.getCDCardinality()); } - + handleQualifier(symbol, side); symbol.setIsOrdered(side.isPresentCDOrdered()); symbol.setIsLeft(isLeft); } - + protected Optional getSymTypeExpression(ASTCDAssociation ast, ASTCDAssocSide side) { final TypeCheckResult typeResult = getTypeSynthesizer().synthesizeType(side @@ -79,12 +79,12 @@ protected Optional getSymTypeExpression(ASTCDAssociation ast, .getMCQualifiedType().get_SourcePositionStart()); return Optional.empty(); } - + // check if the type can be resolved - + return Optional.of(typeResult.getResult()); } - + protected void handleQualifier(CDRoleSymbol symbol, ASTCDAssocSide side) { if (side.isPresentCDQualifier()) { if (side.getCDQualifier().isPresentByType()) { @@ -109,17 +109,17 @@ else if (side.getCDQualifier().isPresentByAttributeName()) { } } } - + @Override public void endVisit(ASTCDAssociation node) { final ASTCDAssocLeftSide l = node.getLeft(); final ASTCDAssocRightSide r = node.getRight(); - + final TypeCheckResult rType = getTypeSynthesizer().synthesizeType(r.getMCQualifiedType() .getMCQualifiedName()); final TypeCheckResult lType = getTypeSynthesizer().synthesizeType(l.getMCQualifiedType() .getMCQualifiedName()); - + if (l.isPresentSymbol()) { if (rType.isPresentResult() && !rType.getResult().isObscureType() && rType.getResult() .hasTypeInfo()) { @@ -143,7 +143,7 @@ public void endVisit(ASTCDAssociation node) { } } } - + public void setupModifiers(ASTModifier assocSideModifier, ASTModifier assocModifier, CDRoleSymbol roleSymbol) { roleSymbol.setIsPublic(assocSideModifier.isPublic() || assocModifier.isPublic()); @@ -152,31 +152,34 @@ public void setupModifiers(ASTModifier assocSideModifier, ASTModifier assocModif roleSymbol.setIsStatic(assocSideModifier.isStatic() || assocModifier.isStatic()); roleSymbol.setIsFinal(assocSideModifier.isFinal() || assocModifier.isFinal()); roleSymbol.setIsDerived(assocSideModifier.isDerived() || assocModifier.isDerived()); + //TODO: add isOrdered()-getter + //roleSymbol.setIsOrdered(assocSideModifier.isOrdered() ||assocModifier.isOrdered()); + roleSymbol.setIsReadOnly(assocSideModifier.isReadonly()||assocModifier.isReadonly()); } - + public static void addRoleToTheirType(CDRoleSymbol symbol, TypeSymbol otherType) { // move the RoleSymbol to their Type final ICDAssociationScope spannedScope = (ICDAssociationScope) otherType.getSpannedScope(); - + // remove the role from its current scope(s) symbol.getEnclosingScope().remove(symbol); - + if (!spannedScope.getCDRoleSymbols().containsKey(symbol.getName())) { // add the symbol to the type; add to all relevant lists spannedScope.add(symbol); } } - + public ISynthesize getTypeSynthesizer() { return typeSynthesizer; } - + public void setTypeSynthesizer(ISynthesize typeSynthesizer) { this.typeSynthesizer = typeSynthesizer; } - + @Override public CDAssociationTraverser getTraverser() { return traverser; } - + @Override public void setTraverser(CDAssociationTraverser traverser) { this.traverser = traverser; } - + } From 027b75a5ecc579b0a4fcadc145cf239ab797f4ba Mon Sep 17 00:00:00 2001 From: Leonie Dahle Date: Wed, 9 Sep 2026 13:47:13 +0200 Subject: [PATCH 3/5] forbid static modifier for associations in CDAssociationModifierCoCo.java --- .../CDAssociationSymbolTableCompleter.java | 8 +++----- .../cocos/ebnf/CDAssociationModifierCoCo.java | 13 ++++++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java b/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java index a35dabb54..dc877fabc 100644 --- a/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java +++ b/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java @@ -146,14 +146,12 @@ public void endVisit(ASTCDAssociation node) { public void setupModifiers(ASTModifier assocSideModifier, ASTModifier assocModifier, CDRoleSymbol roleSymbol) { - roleSymbol.setIsPublic(assocSideModifier.isPublic() || assocModifier.isPublic()); - roleSymbol.setIsPrivate(assocSideModifier.isPrivate() || assocModifier.isPrivate()); - roleSymbol.setIsProtected(assocSideModifier.isProtected() || assocModifier.isProtected()); + roleSymbol.setIsPublic(assocSideModifier.isPublic()); + roleSymbol.setIsPrivate(assocSideModifier.isPrivate() ); + roleSymbol.setIsProtected(assocSideModifier.isProtected()); roleSymbol.setIsStatic(assocSideModifier.isStatic() || assocModifier.isStatic()); roleSymbol.setIsFinal(assocSideModifier.isFinal() || assocModifier.isFinal()); roleSymbol.setIsDerived(assocSideModifier.isDerived() || assocModifier.isDerived()); - //TODO: add isOrdered()-getter - //roleSymbol.setIsOrdered(assocSideModifier.isOrdered() ||assocModifier.isOrdered()); roleSymbol.setIsReadOnly(assocSideModifier.isReadonly()||assocModifier.isReadonly()); } diff --git a/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java b/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java index 6b5c3c1f5..1c5dfff62 100644 --- a/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java +++ b/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java @@ -34,7 +34,18 @@ public void check(ASTCDAssociation a) { } if(hasMainVisibility && (hasLeftVisibility || hasRightVisibility)) { - Log.error("Association cannot combine a main visibility modifier with individual role visibility modifiers.", a.get_SourcePositionStart()); + Log.warn("Association cannot combine a main visibility modifier with individual role visibility modifiers.", a.get_SourcePositionStart()); + } + + //check whether the association or symbolRole use the static modifier + boolean mainStatic = a.getModifier() != null && a.getModifier().isStatic(); + boolean leftStatic = a.getLeft().getModifier() != null && a.getLeft().getModifier().isStatic(); + boolean rightStatic = a.getRight().getModifier() != null && a.getRight().getModifier().isStatic(); + + if(mainStatic || leftStatic || rightStatic) { + String assocName = a.isPresentName() ? a.getName() : "unnamed"; + Log.error(String.format("0xCDA61: The association '%s' must not use static modifiers.", assocName), + a.get_SourcePositionStart()); } } } From 5aef8b311451e4b0e3c4538cabf18e6378de9f2c Mon Sep 17 00:00:00 2001 From: Leonie Dahle Date: Mon, 21 Sep 2026 12:28:14 +0200 Subject: [PATCH 4/5] still in progress: omit build() from BuilderDecorator.java to prevent compilation errors --- .../codegen/decorators/BuilderDecorator.java | 197 +++++++++--------- 1 file changed, 100 insertions(+), 97 deletions(-) diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/BuilderDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/BuilderDecorator.java index c0deebdf1..42bc60901 100644 --- a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/BuilderDecorator.java +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/BuilderDecorator.java @@ -78,108 +78,111 @@ public void visit(ASTCDClass node) { // Get decorated pojo class ASTCDClass decClazz = this.decoratorData.getAsDecorated(node); - // Create a new class with the "Builder" suffix - ASTCDClassBuilder builderClassB = CD4CodeMill.cDClassBuilder(); - builderClassB.setName(node.getName() + "Builder"); - builderClassB.setModifier(node.getModifier().deepClone()); - ASTCDClass builderClass = builderClassB.build(); - // Add the builder class to the decorated CD - addElementToParent(decParent, builderClass); - - // Add Log import to the builder class - CD4C.getInstance().addImport(builderClass, "de.se_rwth.commons.logging.Log"); - - // Add builder attribute for TOP safety - builderClass.addCDMember(CDAttributeFacade.getInstance().createAttribute(CD4CodeMill - .modifierBuilder().PROTECTED().build(), builderClass.getName(), "realBuilder")); - - // Add a constructor to the builder class - ASTCDConstructor constructor = CDConstructorFacade.getInstance().createConstructor(CD4CodeMill - .modifierBuilder().PUBLIC().build(), builderClass.getName()); - glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, constructor, new StringHookPoint( - "this.realBuilder = (" + builderClass.getName() + ") this;"))); - addToClass(builderClass, constructor); - - // Add a isValid() method to the builder class - List allAttributeList = getAllCDAttributes(node); - String staticErrorCode = "0x16725"; - ASTCDMethod isValidMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill - .modifierBuilder().PRIVATE().build(), MCTypeFacade.getInstance().createBooleanType(), - "isValid"); - glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, isValidMethod, - new TemplateHookPoint("methods.builder.isValid", allAttributeList, staticErrorCode))); - addToClass(builderClass, isValidMethod); - - // Add a build() method to the builder class - ASTCDMethod buildMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill - .modifierBuilder().PUBLIC().build(), node.getName(), "build"); - glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, buildMethod, new TemplateHookPoint( - "methods.builder.build", node.getName()))); - addToClass(builderClass, buildMethod); - - // Add the unsafeBuild() method to the builder class - ASTCDMethod unsafeBuildMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill - .modifierBuilder().PUBLIC().build(), node.getName(), "unsafeBuild"); - glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, unsafeBuildMethod, - new TemplateHookPoint("methods.builder.unsafeBuild", node.getName()))); - addToClass(builderClass, unsafeBuildMethod); - - // Add attributes to the builder class - for (ASTCDAttribute attribute : allAttributeList) { + if (!(node.getModifier().isAbstract())) { + // Create a new class with the "Builder" suffix + ASTCDClassBuilder builderClassB = CD4CodeMill.cDClassBuilder(); + builderClassB.setName(node.getName() + "Builder"); + builderClassB.setModifier(node.getModifier().deepClone()); + ASTCDClass builderClass = builderClassB.build(); + // Add the builder class to the decorated CD + addElementToParent(decParent, builderClass); + + // Add Log import to the builder class + CD4C.getInstance().addImport(builderClass, "de.se_rwth.commons.logging.Log"); + + // Add builder attribute for TOP safety builderClass.addCDMember(CDAttributeFacade.getInstance().createAttribute(CD4CodeMill - .modifierBuilder().PROTECTED().build(), attribute.getMCType(), attribute.getName())); - } - - // Add setter methods to the builder class - for (ASTCDAttribute attribute : allAttributeList) { - ASTCDParameter param = CD4CodeMill.cDParameterBuilder().setName(attribute.getName()) - .setMCType(attribute.getMCType()).build(); - if (dispatcher.isMCCollectionTypesASTMCOptionalType(attribute.getMCType())) { - //set of optional with type directly and not with optional - ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()) - .deepClone(); - param = CD4CodeMill.cDParameterBuilder().setName(attribute.getName()).setMCType(type) - .build(); - } - ASTCDMethod setMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill - .modifierBuilder().PUBLIC().build(), builderClass.getName(), "set" - + StringTransformations.capitalize(attribute.getName()), param); - glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, setMethod, new TemplateHookPoint( - "methods.builder.set", attribute))); - addToClass(builderClass, setMethod); + .modifierBuilder().PROTECTED().build(), builderClass.getName(), "realBuilder")); - // it is required to check if a setter method exists by checking the methods of the SetterDecorator for - // an exact match of "set" + attribute.getName() - // if this method does not exist, - // we need to reference the attribute directly in the build method - boolean hasSetterMethod; - List methods = decoratorData.getDecoratorData( - SetterDecorator.class) != null ? decoratorData.getDecoratorData(SetterDecorator.class) - .getMethods(attribute) : List.of(); - hasSetterMethod = methods.stream().anyMatch(mi -> mi.getKind() - == SetterDecorator.SetterMethodKind.SET_MANDATORY_OR_OPT); + // Add a constructor to the builder class + ASTCDConstructor constructor = CDConstructorFacade.getInstance().createConstructor( + CD4CodeMill.modifierBuilder().PUBLIC().build(), builderClass.getName()); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, constructor, new StringHookPoint( + "this.realBuilder = (" + builderClass.getName() + ") this;"))); + addToClass(builderClass, constructor); - // Add set attributes in the build method - glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.build:Inner", buildMethod, - new TemplateHookPoint("methods.builder.setAttribute", attribute, hasSetterMethod))); + // Add a isValid() method to the builder class + List allAttributeList = getAllCDAttributes(node); + String staticErrorCode = "0x16725"; + ASTCDMethod isValidMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PRIVATE().build(), MCTypeFacade.getInstance().createBooleanType(), + "isValid"); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, isValidMethod, + new TemplateHookPoint("methods.builder.isValid", allAttributeList, staticErrorCode))); + addToClass(builderClass, isValidMethod); - // Add set attributes in the unsafeBuild method - glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.unsafeBuild:Inner", - unsafeBuildMethod, new TemplateHookPoint("methods.builder.setAttribute", attribute, - hasSetterMethod))); - } - - // Add isAbsent methods for all attributes with cardinality != 1 - for (ASTCDAttribute attribute : allAttributeList) { - if (dispatcher.isMCCollectionTypesASTMCListType(attribute.getMCType()) || dispatcher - .isMCCollectionTypesASTMCOptionalType(attribute.getMCType()) || dispatcher - .isMCCollectionTypesASTMCSetType(attribute.getMCType())) { - ASTCDMethod setAbsentMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + // Add a build() method to the builder class + ASTCDMethod buildMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), node.getName(), "build"); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, buildMethod, + new TemplateHookPoint("methods.builder.build", node.getName()))); + addToClass(builderClass, buildMethod); + + // Add the unsafeBuild() method to the builder class + ASTCDMethod unsafeBuildMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), node.getName(), "unsafeBuild"); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, unsafeBuildMethod, + new TemplateHookPoint("methods.builder.unsafeBuild", node.getName()))); + addToClass(builderClass, unsafeBuildMethod); + + // Add attributes to the builder class + for (ASTCDAttribute attribute : allAttributeList) { + builderClass.addCDMember(CDAttributeFacade.getInstance().createAttribute(CD4CodeMill + .modifierBuilder().PROTECTED().build(), attribute.getMCType(), attribute.getName())); + } + + // Add setter methods to the builder class + for (ASTCDAttribute attribute : allAttributeList) { + ASTCDParameter param = CD4CodeMill.cDParameterBuilder().setName(attribute.getName()) + .setMCType(attribute.getMCType()).build(); + if (dispatcher.isMCCollectionTypesASTMCOptionalType(attribute.getMCType())) { + //set of optional with type directly and not with optional + ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()) + .deepClone(); + param = CD4CodeMill.cDParameterBuilder().setName(attribute.getName()).setMCType(type) + .build(); + } + ASTCDMethod setMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill .modifierBuilder().PUBLIC().build(), builderClass.getName(), "set" - + StringTransformations.capitalize(attribute.getName()) + "Absent"); - glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, setAbsentMethod, - new TemplateHookPoint("methods.builder.setAbsent", attribute))); - addToClass(builderClass, setAbsentMethod); + + StringTransformations.capitalize(attribute.getName()), param); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, setMethod, + new TemplateHookPoint("methods.builder.set", attribute))); + addToClass(builderClass, setMethod); + + // it is required to check if a setter method exists by checking the methods of the SetterDecorator for + // an exact match of "set" + attribute.getName() + // if this method does not exist, + // we need to reference the attribute directly in the build method + boolean hasSetterMethod; + List methods = decoratorData.getDecoratorData( + SetterDecorator.class) != null ? decoratorData.getDecoratorData(SetterDecorator.class) + .getMethods(attribute) : List.of(); + hasSetterMethod = methods.stream().anyMatch(mi -> mi.getKind() + == SetterDecorator.SetterMethodKind.SET_MANDATORY_OR_OPT); + + // Add set attributes in the build method + glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.build:Inner", + buildMethod, new TemplateHookPoint("methods.builder.setAttribute", attribute, + hasSetterMethod))); + + // Add set attributes in the unsafeBuild method + glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.unsafeBuild:Inner", + unsafeBuildMethod, new TemplateHookPoint("methods.builder.setAttribute", attribute, + hasSetterMethod))); + } + + // Add isAbsent methods for all attributes with cardinality != 1 + for (ASTCDAttribute attribute : allAttributeList) { + if (dispatcher.isMCCollectionTypesASTMCListType(attribute.getMCType()) || dispatcher + .isMCCollectionTypesASTMCOptionalType(attribute.getMCType()) || dispatcher + .isMCCollectionTypesASTMCSetType(attribute.getMCType())) { + ASTCDMethod setAbsentMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), builderClass.getName(), "set" + + StringTransformations.capitalize(attribute.getName()) + "Absent"); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, setAbsentMethod, + new TemplateHookPoint("methods.builder.setAbsent", attribute))); + addToClass(builderClass, setAbsentMethod); + } } } From bf7892fecc936006d6adbee5cd3a6f3e2b49830f Mon Sep 17 00:00:00 2001 From: Leonie Dahle Date: Mon, 28 Sep 2026 12:15:40 +0200 Subject: [PATCH 5/5] spotlessApply --- .../CDAssociationSymbolTableCompleter.java | 58 +++++++++---------- .../cocos/CDAssociationCoCos.java | 8 +-- .../cocos/ebnf/CDAssociationModifierCoCo.java | 51 +++++++++------- 3 files changed, 62 insertions(+), 55 deletions(-) diff --git a/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java b/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java index dc877fabc..ec1e7b6fd 100644 --- a/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java +++ b/cdlang/src/main/java/de/monticore/cdassociation/_symboltable/CDAssociationSymbolTableCompleter.java @@ -21,19 +21,19 @@ public class CDAssociationSymbolTableCompleter implements CDAssociationVisitor2, CDAssociationHandler { - + protected CDAssociationTraverser traverser; - + protected ISynthesize typeSynthesizer; - + public CDAssociationSymbolTableCompleter(ISynthesize typeSynthesizer) { this.typeSynthesizer = typeSynthesizer; } - + public CDAssociationSymbolTableCompleter() { this(new FullSynthesizeFromMCBasicTypes()); } - + @Override public void handle(ASTCDAssociation node) { if (node.getLeft().isPresentSymbol()) { @@ -44,31 +44,31 @@ public void handle(ASTCDAssociation node) { } endVisit(node); } - + public void initialize_CDRole(CDRoleSymbol symbol, ASTCDAssociation ast, boolean isLeft) { final ASTCDAssocSide side = isLeft ? ast.getLeft() : ast.getRight(); - + symbol.setAssocSide(side); final Optional typeResult = getSymTypeExpression(ast, side); if (!typeResult.isPresent()) { return; } symbol.setType(typeResult.get()); - + setupModifiers(side.getModifier(), ast.getModifier(), symbol); - + symbol.setIsDefinitiveNavigable(isLeft ? ast.getCDAssocDir().isDefinitiveNavigableLeft() : ast .getCDAssocDir().isDefinitiveNavigableRight()); - + if (side.isPresentCDCardinality()) { symbol.setCardinality(side.getCDCardinality()); } - + handleQualifier(symbol, side); symbol.setIsOrdered(side.isPresentCDOrdered()); symbol.setIsLeft(isLeft); } - + protected Optional getSymTypeExpression(ASTCDAssociation ast, ASTCDAssocSide side) { final TypeCheckResult typeResult = getTypeSynthesizer().synthesizeType(side @@ -79,12 +79,12 @@ protected Optional getSymTypeExpression(ASTCDAssociation ast, .getMCQualifiedType().get_SourcePositionStart()); return Optional.empty(); } - + // check if the type can be resolved - + return Optional.of(typeResult.getResult()); } - + protected void handleQualifier(CDRoleSymbol symbol, ASTCDAssocSide side) { if (side.isPresentCDQualifier()) { if (side.getCDQualifier().isPresentByType()) { @@ -109,17 +109,17 @@ else if (side.getCDQualifier().isPresentByAttributeName()) { } } } - + @Override public void endVisit(ASTCDAssociation node) { final ASTCDAssocLeftSide l = node.getLeft(); final ASTCDAssocRightSide r = node.getRight(); - + final TypeCheckResult rType = getTypeSynthesizer().synthesizeType(r.getMCQualifiedType() .getMCQualifiedName()); final TypeCheckResult lType = getTypeSynthesizer().synthesizeType(l.getMCQualifiedType() .getMCQualifiedName()); - + if (l.isPresentSymbol()) { if (rType.isPresentResult() && !rType.getResult().isObscureType() && rType.getResult() .hasTypeInfo()) { @@ -143,41 +143,41 @@ public void endVisit(ASTCDAssociation node) { } } } - + public void setupModifiers(ASTModifier assocSideModifier, ASTModifier assocModifier, CDRoleSymbol roleSymbol) { roleSymbol.setIsPublic(assocSideModifier.isPublic()); - roleSymbol.setIsPrivate(assocSideModifier.isPrivate() ); + roleSymbol.setIsPrivate(assocSideModifier.isPrivate()); roleSymbol.setIsProtected(assocSideModifier.isProtected()); roleSymbol.setIsStatic(assocSideModifier.isStatic() || assocModifier.isStatic()); roleSymbol.setIsFinal(assocSideModifier.isFinal() || assocModifier.isFinal()); roleSymbol.setIsDerived(assocSideModifier.isDerived() || assocModifier.isDerived()); - roleSymbol.setIsReadOnly(assocSideModifier.isReadonly()||assocModifier.isReadonly()); + roleSymbol.setIsReadOnly(assocSideModifier.isReadonly() || assocModifier.isReadonly()); } - + public static void addRoleToTheirType(CDRoleSymbol symbol, TypeSymbol otherType) { // move the RoleSymbol to their Type final ICDAssociationScope spannedScope = (ICDAssociationScope) otherType.getSpannedScope(); - + // remove the role from its current scope(s) symbol.getEnclosingScope().remove(symbol); - + if (!spannedScope.getCDRoleSymbols().containsKey(symbol.getName())) { // add the symbol to the type; add to all relevant lists spannedScope.add(symbol); } } - + public ISynthesize getTypeSynthesizer() { return typeSynthesizer; } - + public void setTypeSynthesizer(ISynthesize typeSynthesizer) { this.typeSynthesizer = typeSynthesizer; } - + @Override public CDAssociationTraverser getTraverser() { return traverser; } - + @Override public void setTraverser(CDAssociationTraverser traverser) { this.traverser = traverser; } - + } diff --git a/cdlang/src/main/java/de/monticore/cdassociation/cocos/CDAssociationCoCos.java b/cdlang/src/main/java/de/monticore/cdassociation/cocos/CDAssociationCoCos.java index 455c9c2d5..e5c568382 100644 --- a/cdlang/src/main/java/de/monticore/cdassociation/cocos/CDAssociationCoCos.java +++ b/cdlang/src/main/java/de/monticore/cdassociation/cocos/CDAssociationCoCos.java @@ -6,19 +6,19 @@ import de.monticore.cdassociation.cocos.ebnf.*; public class CDAssociationCoCos extends CoCoParent { - + @Override public CDAssociationCoCoChecker createNewChecker() { return new CDAssociationCoCoChecker(); } - + @Override protected void addCheckerForAllCoCos(CDAssociationCoCoChecker checker) { addCheckerForEbnfCoCos(checker); addCheckerForMcgCoCos(checker); addCheckerForMcg2EbnfCoCos(checker); } - + @Override protected void addEbnfCoCos(CDAssociationCoCoChecker checker) { // CDAssociation @@ -30,5 +30,5 @@ protected void addEbnfCoCos(CDAssociationCoCoChecker checker) { checker.addCoCo(new CDAssociationRoleNameNoConflictWithLocalAttribute()); checker.addCoCo(new CDAssociationModifierCoCo()); } - + } diff --git a/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java b/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java index 1c5dfff62..240b7a7fc 100644 --- a/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java +++ b/cdlang/src/main/java/de/monticore/cdassociation/cocos/ebnf/CDAssociationModifierCoCo.java @@ -7,45 +7,52 @@ import de.se_rwth.commons.logging.Log; public class CDAssociationModifierCoCo implements CDAssociationASTCDAssociationCoCo { - + @Override public void check(ASTCDAssociation a) { - + //----------------------------------check the visibility modifiers----------------------------------------------------------- //main modifier - boolean hasMainVisibility=false; - if(a.getModifier()!=null) { + boolean hasMainVisibility = false; + if (a.getModifier() != null) { ASTModifier mainModifier = a.getModifier(); - hasMainVisibility= mainModifier.isPublic()||mainModifier.isProtected()||mainModifier.isPrivate(); + hasMainVisibility = mainModifier.isPublic() || mainModifier.isProtected() || mainModifier + .isPrivate(); } - + //left modifier - boolean hasLeftVisibility=false; - if(a.getLeft().getModifier()!=null) { + boolean hasLeftVisibility = false; + if (a.getLeft().getModifier() != null) { ASTModifier leftModifier = a.getLeft().getModifier(); - hasLeftVisibility=leftModifier.isPublic()||leftModifier.isProtected()||leftModifier.isPrivate(); + hasLeftVisibility = leftModifier.isPublic() || leftModifier.isProtected() || leftModifier + .isPrivate(); } - + //right modifier - boolean hasRightVisibility=false; - if(a.getRight().getModifier()!=null) { + boolean hasRightVisibility = false; + if (a.getRight().getModifier() != null) { ASTModifier rightModifier = a.getRight().getModifier(); - hasRightVisibility=rightModifier.isPublic()||rightModifier.isProtected()||rightModifier.isPrivate(); + hasRightVisibility = rightModifier.isPublic() || rightModifier.isProtected() || rightModifier + .isPrivate(); } - - if(hasMainVisibility && (hasLeftVisibility || hasRightVisibility)) { - Log.warn("Association cannot combine a main visibility modifier with individual role visibility modifiers.", a.get_SourcePositionStart()); + + if (hasMainVisibility && (hasLeftVisibility || hasRightVisibility)) { + Log.warn( + "Association cannot combine a main visibility modifier with individual role visibility modifiers.", + a.get_SourcePositionStart()); } - + //check whether the association or symbolRole use the static modifier boolean mainStatic = a.getModifier() != null && a.getModifier().isStatic(); boolean leftStatic = a.getLeft().getModifier() != null && a.getLeft().getModifier().isStatic(); - boolean rightStatic = a.getRight().getModifier() != null && a.getRight().getModifier().isStatic(); - - if(mainStatic || leftStatic || rightStatic) { + boolean rightStatic = a.getRight().getModifier() != null && a.getRight().getModifier() + .isStatic(); + + if (mainStatic || leftStatic || rightStatic) { String assocName = a.isPresentName() ? a.getName() : "unnamed"; - Log.error(String.format("0xCDA61: The association '%s' must not use static modifiers.", assocName), - a.get_SourcePositionStart()); + Log.error(String.format("0xCDA61: The association '%s' must not use static modifiers.", + assocName), a.get_SourcePositionStart()); } } + }