Permalink
Browse files

Merge pull request #982 from umple/fix961

Fixing issue 961. Check to make sure a subclass does not have an asso…
  • Loading branch information...
2 parents 61a14ab + a28f9f4 commit 12f6b32a49d8abe41f8017636fb7cb5a43248121 @vahdat-ab vahdat-ab committed on GitHub Feb 4, 2017
@@ -0,0 +1,17 @@
+E180 Duplicate Association Name Class Hierarchy
+Errors and Warnings
+noreferences
+
+@@description
+
+<h2>Umple semantic error reported when a subclass has an association that is not a specialization named the same as a superclass association</h2>
+
+<p>A subclass can not have an association with the same role name as an association of its superclass, unless it is a valid specialization.
+</p>
+
+
+@@example
+@@source manualexamples/E180DuplicateRoleNameSubclass.ump
+@@endexample
+
+
@@ -356,6 +356,7 @@ class UmpleInternalParser
//required for parsing specializedAssociations
createSpecializedLinks();
checkDuplicateAssociationNames();
+ checkDuplicateAssociationNamesClassHierarchy();
checkExtendsForCycles();
checkSortedAssociations();
checkClassInterfaceAssocations();
@@ -1955,6 +1956,86 @@ private Boolean checkIsDistributed(UmpleInterface uInterface)
visited.add(A);
}
}
+
+ // Issue #961. Ensure that all superclasses and subclasses do not have associations to two different
+ // classes that use the same role name, and is not a specialization.
+ private void checkDuplicateAssociationNamesClassHierarchy()
+ {
+ for (UmpleClass uClass : model.getUmpleClasses())
+ {
+ List<String> visitedSupers = new ArrayList<String>();
+ UmpleClass superClass = uClass.getExtendsClass();
+
+ while (superClass != null)
+ {
+ // Prevent cycles.
+ if (visitedSupers.contains(superClass.getName()))
+ {
+ break;
+ }
+ visitedSupers.add(superClass.getName());
+
+ ArrayList<String> duplicateNames = new ArrayList<String>();
+
+ for(Association assoc : uClass.getAssociations())
+ {
+ AssociationEnd assocEnd = getDifferingEnd(assoc, uClass.getName());
+
+ ArrayList<String> existingNames = new ArrayList<String>();
+
+ String roleName = assocEnd.getRoleName();
+
+ // Check only associations that have a user-defined role name, and is not a specialization.
+ if (!assocEnd.getIsDefaultRoleName() && !assoc.getIsSpecialization() && !duplicateNames.contains(roleName))
+ {
+ for (Association superAssoc : superClass.getAssociations())
+ {
+ AssociationEnd superAssocEnd = getDifferingEnd(superAssoc, superClass.getName());
+ String superAssocRoleName = superAssocEnd.getRoleName();
+
+ if (superAssocEnd != null && superAssocRoleName.equals(roleName) &&
+ !existingNames.contains(superAssocRoleName) && !superAssocEnd.getClassName().equals(assocEnd.getClassName()))
+ {
+ getParseResult().addErrorMessage(new ErrorMessage(180,assoc.getTokenPosition(),
+ uClass.getName(),superClass.getName(),roleName));
+ duplicateNames.add(roleName);
+ }
+ existingNames.add(superAssocRoleName);
+ }
+ }
+ }
+ superClass = superClass.getExtendsClass();
+ }
+ }
+ }
+
+ // Get the end of an association opposite to the given class. If the association is reflexive,
+ // return the end with a user defined role name.
+ private AssociationEnd getDifferingEnd(Association assoc, String className)
+ {
+ AssociationEnd firstEnd = assoc.getEnd(0);
+ AssociationEnd secondEnd = assoc.getEnd(1);
+
+ Boolean checkFirstEnd = !firstEnd.getClassName().equals(className);
+ Boolean checkSecondEnd = !secondEnd.getClassName().equals(className);
+
+ if (!checkFirstEnd && !checkSecondEnd)
+ {
+ // Association is reflexive. Both ends are the same, but we want the one with a user defined role name.
+ if (!firstEnd.getIsDefaultRoleName())
+ {
+ return firstEnd;
+ }
+ return secondEnd;
+ }
+
+ if (checkFirstEnd)
+ {
+ return firstEnd;
+ }
+ return secondEnd;
+
+ }
private void checkDuplicateAssociationNames()
{
@@ -111,6 +111,7 @@
160: 2, "http://cruise.eecs.uottawa.ca/umple/WE1xxIdentifierInvalid.html", Constant name '{0}' must be alphanumeric, starting with a upper-case letter ;
161: 4, "http://cruise.eecs.uottawa.ca/umple/WE1xxIdentifierInvalid.html", Constant name '{0}' should start with a upper-case letter ;
+180: 2, "http://cruise.eecs.uottawa.ca/umple/PageBeingDeveloped.html", Class '{0}' is a subclass of class '{1}' and may not have multiple associations with the same name '{2}' ;
# Messages related to traits
200: 2, "http://cruise.eecs.uottawa.ca/umple/E200TraitIdentifierInvalid.html", Trait name '{0}' must be alphanumeric and start with an alpha character, or _ ;
@@ -184,6 +185,8 @@
1503 : 1, "http://cruise.eecs.uottawa.ca/umple/E15xxParsingError.html", Parsing error: {0}, did you mean {1} ;
1510 : 1, "http://cruise.eecs.uottawa.ca/umple/E1510UseFileMissing.html", File '{0}' referred to in use statement was not found ;
+
+
# Messages to be emitted when embedded code from another language is compiled and you are passing on the error
2001: 5, "http://cruise.eecs.uottawa.ca/umple/W20xxErrorinEmbeddedCode.html", Error in Java embedded in Umple: '{0}' ;
2002: 5, "http://cruise.eecs.uottawa.ca/umple/W20xxErrorinEmbeddedCode.html", Error in C++ embedded in Umple: '{0}' ;
@@ -0,0 +1,14 @@
+class A{
+ 0..1 -> *C assoc;
+}
+
+class B{
+ isA A;
+ 0..1 -> *D assoc;
+}
+
+class C{
+}
+
+class D{
+}
@@ -0,0 +1,11 @@
+class Person {
+ * -> * Person friends;
+}
+
+class Student {
+ isA Person;
+ * -> * Dog friends;
+}
+
+class Dog {
+}
@@ -0,0 +1,18 @@
+class A {
+* -> * D assoc;
+}
+
+class B {
+isA A;
+}
+
+class C {
+isA B;
+* -> 0..1 E assoc;
+}
+
+class D {
+}
+
+class E {
+}
@@ -0,0 +1,8 @@
+class A{
+ 0..1 -> * B;
+}
+class B{
+ isA A;
+ 0..1 -> * D bs;
+}
+class D{}
@@ -0,0 +1,11 @@
+class A{
+ 0..1 -> * B;
+}
+class B{
+ isA A;
+}
+class D{
+ isA B;
+ 0..1 -> * D bs;
+}
+class C{}
@@ -2280,6 +2280,16 @@ public void duplicateAssociationNames()
assertParse("024_multipleUnnamedOneWayAssociationsToSameClass.ump");
}
+
+ @Test
+ public void duplicateAssociationNamesClassHierarchy()
+ {
+ assertFailedParse("024_roleNameSameSubclassSuperclass.ump", 180);
+ assertFailedParse("024_roleNameSameSubclassSuperclass1.ump", 180);
+ assertFailedParse("024_roleNameSameSubclassSuperclass2.ump", 180);
+ assertFailedParse("024_roleNameSameSubclassSuperclass3.ump", 180);
+ assertFailedParse("024_roleNameSameSubclassSuperclass4.ump", 180);
+ }
@Test
public void duplicateAttributesNames(){
@@ -0,0 +1,11 @@
+class Person {
+ * -> * Person friends;
+}
+
+class Student {
+ isA Person;
+ * -> * Dog friends;
+}
+
+class Dog {
+}

0 comments on commit 12f6b32

Please sign in to comment.