diff --git a/cdlang/src/main/java/de/monticore/cdgen/CDGenTool.java b/cdlang/src/main/java/de/monticore/cdgen/CDGenTool.java index 0d137d979..ebd1f4276 100644 --- a/cdlang/src/main/java/de/monticore/cdgen/CDGenTool.java +++ b/cdlang/src/main/java/de/monticore/cdgen/CDGenTool.java @@ -13,12 +13,14 @@ import de.monticore.cd4analysis.trafo.CDAssociationCreateFieldsFromAllRoles; import de.monticore.cd4analysis.trafo.CDAssociationCreateFieldsFromNavigableRoles; import de.monticore.cd4code.CD4CodeMill; +import de.monticore.cd4code._cocos.CD4CodeCoCoChecker; import de.monticore.cd4code._symboltable.ICD4CodeArtifactScope; import de.monticore.cd4code._visitor.CD4CodeTraverser; import de.monticore.cdbasis.CDBasisMill; import de.monticore.cdbasis._ast.ASTCDClass; import de.monticore.cdbasis._ast.ASTCDCompilationUnit; import de.monticore.cdbasis.trafo.CDBasisDefaultPackageTrafo; +import de.monticore.cdgen.cocos.CD2JavaGenCoCos; import de.monticore.cdinterfaceandenum._ast.ASTCDEnum; import de.monticore.cdinterfaceandenum._ast.ASTCDInterface; import de.monticore.generating.GeneratorSetup; @@ -388,7 +390,8 @@ public void runBeforeSTCoCos(ASTCDCompilationUnit ast) { * @param ast the original ast */ public void runCoCos(ASTCDCompilationUnit ast) { - super.runCoCos(ast); + CD4CodeCoCoChecker checker = new CD2JavaGenCoCos().getCheckerForAllCoCos(); + checker.checkAll(ast); } @Override diff --git a/cdlang/src/main/java/de/monticore/cdgen/cocos/CD2JavaGenCoCos.java b/cdlang/src/main/java/de/monticore/cdgen/cocos/CD2JavaGenCoCos.java index 587fe0559..de1da55b1 100644 --- a/cdlang/src/main/java/de/monticore/cdgen/cocos/CD2JavaGenCoCos.java +++ b/cdlang/src/main/java/de/monticore/cdgen/cocos/CD2JavaGenCoCos.java @@ -10,8 +10,6 @@ public class CD2JavaGenCoCos extends CD4CodeCoCosDelegator { protected void addCheckerForAllCoCos(CD4CodeCoCoChecker checker) { super.addCheckerForAllCoCos(checker); checker.addCoCo(new CDAssociationUniqueInHierarchy()); - checker.addCoCo(new CDNoAttributesInInterfaces()); - checker.addCoCo(new CDNoOutgoingAssocs4Interfaces()); checker.addCoCo(new CDSingleClassInheritance()); } diff --git a/cdlang/src/main/java/de/monticore/cdgen/cocos/CDAssociationUnique.java b/cdlang/src/main/java/de/monticore/cdgen/cocos/CDAssociationUnique.java index f4144774b..2c302aba6 100644 --- a/cdlang/src/main/java/de/monticore/cdgen/cocos/CDAssociationUnique.java +++ b/cdlang/src/main/java/de/monticore/cdgen/cocos/CDAssociationUnique.java @@ -17,9 +17,6 @@ */ public class CDAssociationUnique implements CDBasisASTCDDefinitionCoCo { - /** - * @param node class to check. - */ @Override public void check(ASTCDDefinition node) { @@ -37,40 +34,44 @@ public void check(ASTCDDefinition node) { // if they share a left role-name, the referenced types on the right should not be the // same - if (deriveRoleName(assoc1, AssocSide.LEFT).equals(deriveRoleName(assoc2, - AssocSide.LEFT))) { - checkRef(node, findTypeByFullName(assoc1, assoc1.getRightQualifiedName().getQName()), - findTypeByFullName(assoc2, assoc2.getRightQualifiedName().getQName()), assoc1); + if (deriveRoleName(assoc1, AssocSide.LEFT).equals(deriveRoleName(assoc2, AssocSide.LEFT)) + && assoc1.getCDAssocDir().isDefinitiveNavigableLeft() && assoc2.getCDAssocDir() + .isDefinitiveNavigableLeft()) { + checkRef(assoc1, assoc2, findTypeByFullName(assoc1, assoc1.getRightQualifiedName() + .getQName()), findTypeByFullName(assoc2, assoc2.getRightQualifiedName() + .getQName())); } // if they share a right role-name, the referenced types on the left should not be the // same if (deriveRoleName(assoc1, AssocSide.RIGHT).equals(deriveRoleName(assoc2, - AssocSide.RIGHT))) { - checkRef(node, findTypeByFullName(assoc1, assoc1.getLeftQualifiedName().getQName()), - findTypeByFullName(assoc2, assoc2.getLeftQualifiedName().getQName()), assoc1); + AssocSide.RIGHT)) && assoc1.getCDAssocDir().isDefinitiveNavigableRight() && assoc2 + .getCDAssocDir().isDefinitiveNavigableRight()) { + checkRef(assoc1, assoc2, findTypeByFullName(assoc1, assoc1.getLeftQualifiedName() + .getQName()), findTypeByFullName(assoc2, assoc2.getLeftQualifiedName().getQName())); } // We also consider a left-to-right role name match ... - if (deriveRoleName(assoc1, AssocSide.LEFT).equals(deriveRoleName(assoc2, - AssocSide.RIGHT))) { - checkRef(node, findTypeByFullName(assoc1, assoc1.getRightQualifiedName().getQName()), - findTypeByFullName(assoc2, assoc2.getLeftQualifiedName().getQName()), assoc1); + if (deriveRoleName(assoc1, AssocSide.LEFT).equals(deriveRoleName(assoc2, AssocSide.RIGHT)) + && assoc1.getCDAssocDir().isDefinitiveNavigableLeft() && assoc2.getCDAssocDir() + .isDefinitiveNavigableRight()) { + checkRef(assoc1, assoc2, findTypeByFullName(assoc1, assoc1.getRightQualifiedName() + .getQName()), findTypeByFullName(assoc2, assoc2.getLeftQualifiedName().getQName())); } // ... as well as a right-to-left match - if (deriveRoleName(assoc1, AssocSide.RIGHT).equals(deriveRoleName(assoc2, - AssocSide.LEFT))) { - checkRef(node, findTypeByFullName(assoc1, assoc1.getLeftQualifiedName().getQName()), - findTypeByFullName(assoc2, assoc2.getRightQualifiedName().getQName()), assoc1); + if (deriveRoleName(assoc1, AssocSide.RIGHT).equals(deriveRoleName(assoc2, AssocSide.LEFT)) + && assoc1.getCDAssocDir().isDefinitiveNavigableRight() && assoc2.getCDAssocDir() + .isDefinitiveNavigableLeft()) { + checkRef(assoc1, assoc2, findTypeByFullName(assoc1, assoc1.getLeftQualifiedName() + .getQName()), findTypeByFullName(assoc2, assoc2.getRightQualifiedName() + .getQName())); } } } } } - /** - * helper-method to find types by full-name - */ + /** helper-method to find types by full-name */ protected ASTCDType findTypeByFullName(ASTCDAssociation node, String fullName) { Optional optSymbol = node.getEnclosingScope().resolveCDType(fullName); @@ -78,18 +79,20 @@ protected ASTCDType findTypeByFullName(ASTCDAssociation node, String fullName) { return optSymbol.get().getAstNode(); } - Log.error("0xCDCE2: Could not find: " + fullName + "."); + Log.error("0xCDCE2: Could not find: " + fullName + ".", node.get_SourcePositionStart()); return null; } /** Check if type2 is the same as type1. */ - protected void checkRef(ASTCDDefinition node, ASTCDType type1, ASTCDType type2, - ASTCDAssociation assoc1) { + protected void checkRef(ASTCDAssociation assoc1, ASTCDAssociation assoc2, ASTCDType type1, + ASTCDType type2) { if (type1.equals(type2)) { - Log.error(String.format("0xCDCE1: %s has a duplicate association to %s", type1.getName(), - type2.getName()), assoc1.isPresent_SourcePositionStart() ? assoc1 - .get_SourcePositionStart() : null, assoc1.isPresent_SourcePositionEnd() ? assoc1 - .get_SourcePositionEnd() : null); + Log.error(String.format("0xCDCE1: %s has duplicate associations %s at %s and %s, " + + "i.e. 2 different associations with the same target role-name for a given source-type." + + "This may lead to conflicts in the generated code and is therefore not permitted " + + "for code generation purposes.", type1.getName(), assoc1.getPrintableName(), assoc1 + .get_SourcePositionStart(), assoc2.get_SourcePositionStart()), assoc2 + .get_SourcePositionStart()); } } @@ -114,7 +117,7 @@ else if (assoc.isPresentName()) { } } - private enum AssocSide { + protected enum AssocSide { LEFT, RIGHT; } diff --git a/cdlang/src/main/java/de/monticore/cdgen/cocos/CDAssociationUniqueInHierarchy.java b/cdlang/src/main/java/de/monticore/cdgen/cocos/CDAssociationUniqueInHierarchy.java index 60921f77b..66d0d8fc2 100644 --- a/cdlang/src/main/java/de/monticore/cdgen/cocos/CDAssociationUniqueInHierarchy.java +++ b/cdlang/src/main/java/de/monticore/cdgen/cocos/CDAssociationUniqueInHierarchy.java @@ -2,7 +2,6 @@ package de.monticore.cdgen.cocos; import de.monticore.cdassociation._ast.ASTCDAssociation; -import de.monticore.cdbasis._ast.ASTCDDefinition; import de.monticore.cdbasis._ast.ASTCDType; import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; import de.se_rwth.commons.logging.Log; @@ -16,16 +15,17 @@ public class CDAssociationUniqueInHierarchy extends CDAssociationUnique { @Override - protected void checkRef(ASTCDDefinition node, ASTCDType type1, ASTCDType type2, - ASTCDAssociation assoc1) { - super.checkRef(node, type1, type2, assoc1); + protected void checkRef(ASTCDAssociation assoc1, ASTCDAssociation assoc2, ASTCDType type1, + ASTCDType type2) { + super.checkRef(assoc1, assoc2, type1, type2); // We now also check if the types are in a sub/super-type relation - checkSuper(type1, type2); - checkSuper(type2, type1); + checkSuper(assoc1, assoc2, type1, type2); + checkSuper(assoc2, assoc1, type2, type1); } /** Check if type2 is a super-type of type1. */ - protected void checkSuper(ASTCDType type1, ASTCDType type2) { + protected void checkSuper(ASTCDAssociation assoc1, ASTCDAssociation assoc2, ASTCDType type1, + ASTCDType type2) { Stack typesToVisit = new Stack<>(); @@ -38,8 +38,10 @@ protected void checkSuper(ASTCDType type1, ASTCDType type2) { while (!typesToVisit.isEmpty()) { final TypeSymbol nextType = typesToVisit.pop(); if (nextType.getFullName().equals(type2.getSymbol().getFullName())) { - Log.error(String.format("0xCDCE6: %s redefines an association of %s.", type1.getName(), - type2.getName())); + Log.error(String.format("0xCDCE6: %s redefines an association of %s from %s at %s." + + "Redefining associations are not supported by code generators.", type1.getName(), + type2.getName(), assoc2.get_SourcePositionStart(), assoc1.get_SourcePositionStart()), + assoc1.get_SourcePositionStart()); return; } diff --git a/cdlang/src/test/java/de/monticore/cdgen/cocos/CDAssociationUniqueTest.java b/cdlang/src/test/java/de/monticore/cdgen/cocos/CDAssociationUniqueTest.java index bf2ce95dd..cb144640f 100644 --- a/cdlang/src/test/java/de/monticore/cdgen/cocos/CDAssociationUniqueTest.java +++ b/cdlang/src/test/java/de/monticore/cdgen/cocos/CDAssociationUniqueTest.java @@ -56,4 +56,60 @@ public void testUniqueAssocName() throws IOException { runTest(model, false); } + @Test + public void testOnlyNavigable() throws IOException { + String model = "classdiagram DuplicateAssocs {" + " class A; class B;" + + " association A -> B;" + " association A -> (r) B;" + "}"; + runTest(model, false); + } + + @Test + public void testOnlyNavigableReverse() throws IOException { + String model = "classdiagram DuplicateAssocs {" + " class A; class B;" + + " association A -> B;" + " association B (r) <- A;" + "}"; + runTest(model, false); + } + + @Test + public void testOpposedAssocs() throws IOException { + String model = "classdiagram DuplicateAssocs {" + " class A; class B;" + + " association A -> B;" + " association A <- B;" + "}"; + runTest(model, false); + } + + @Test + public void testOpposedAssocs2() throws IOException { + String model = "classdiagram DuplicateAssocs {" + " class A; class B;" + + " association A -> B;" + " association B -> A;" + "}"; + runTest(model, false); + } + + @Test + public void testNonOpposedAssocs() throws IOException { + String model = "classdiagram DuplicateAssocs {" + " class A; class B;" + + " association A -> B;" + " association A <-> B;" + "}"; + runTestForErrorCode(model, ERROR_CODE); + } + + @Test + public void testNonOpposedAssocs2() throws IOException { + String model = "classdiagram DuplicateAssocs {" + " class A; class B;" + + " association A -> B;" + " association A <-> B;" + "}"; + runTestForErrorCode(model, ERROR_CODE); + } + + @Test + public void testNonOpposedAssocs3() throws IOException { + String model = "classdiagram DuplicateAssocs {" + " class A; class B;" + + " association A -> B;" + " association B <- A;" + "}"; + runTestForErrorCode(model, ERROR_CODE); + } + + @Test + public void testNonOpposedAssocsUnspecified() throws IOException { + String model = "classdiagram DuplicateAssocs {" + " class A; class B;" + + " association A -> B;" + " association A -- B;" + "}"; + runTest(model, false); + } + }