Issue 19017: Improving the association direction notation (uml2-rtf) Source: NASA (Dr. Nicolas F. Rouquette, nicolas.f.rouquette(at)jpl.nasa.gov) Nature: Uncategorized Issue Severity: Summary: The UML 2.5 notation for associations in section 11.5.4 states (4th paragraph): On a binary Association drawn as a solid line, a solid triangular arrowhead next to or in place of the name of the Association and pointing along the line in the direction of one end indicates that end to be the last in the order of the ends of the Association. The arrow indicates that the Association is to be read as associating the end away from the direction of the arrow with the end to which the arrow is pointing (see Figure 11.27). This notation is for documentation purposes only and has no general semantic interpretation. It is used to capture some application-specific detail of the relationship between the associated Classifiers. In practice, the order of association ends is not very useful. Deriving the direction of an association based on association end cardinality, aggregation type and navigability, which is a function of ownership (see Property::isNavigable()) would be more useful. I propose the following criteria (written in QVT Operational): modeltype uml uses 'http://www.nomagic.com/magicdraw/UML/2.4.1'; /** * @author nicolas.f.rouquette@jpl.nasa.gov * October 2013 - UML2.6 Improving the association direction notation. */ transformation AssociationDirectionCheck(in selectedAssociations:uml); property associations : Set(uml::Association) = selectedAssociations.rootObjects()[uml::Association]; main() { log('Analyzing ' + associations->size().repr() + ' associations'); associations->sortedBy(qualifiedName)->forEach(a) { var p := a.memberEnd![name='p']; var q := a.memberEnd![name='q']; log('Association ' + a.name + ' : ' + p.type.name + ' -- ' + q.type.name); p.describe('end1'); q.describe('end2'); } } helper uml::Property::describe(in prefix:String) { var a := self.association; assert fatal (a.oclIsKindOf(uml::Association)); var other := a.memberEnd->excluding(self)->any(true); var dir := 'n/a'; if (self.isMemberEndLogicallyDirectedToOtherEnd()) then dir := self.name + '>>' + other.name endif; if (other.isMemberEndLogicallyDirectedToOtherEnd()) then dir := other.name + '>>' + self.name endif; log(prefix + ': ' + self.namespace.name + '::' + self.name + ' : ' + self.type.name + '[' + self.lower.repr() + '..' + (if self.upper < 0 then '*' else self.upper.repr() endif) + ']' + ' {memberEnd#' + a.memberEnd->indexOf(self).repr() + ', aggregation=' + self.aggregation.repr() + ', isNavigable=' + self.isNavigable().toString() + ', direction=' + dir + '}'); } helper uml::Property::isMemberEndLogicallyDirectedToOtherEnd() : Boolean { var a := self.association; assert fatal (a.oclIsKindOf(uml::Association)); var other := a.memberEnd->excluding(self)->any(true); var fwdDirByClassOrNav := ((self.owner = a) and (other.owner <> a)) or (not self.isNavigable()) and other.isNavigable(); var fwdDirByComposition := (not self.isComposite) and other.isComposite; var fwdDirByCardinality := (not self.isComposite) and (not other.isComposite) and (self.upper <= 1) and (other.upper < 0 or other.upper > 1); return fwdDirByClassOrNav or ((not fwdDirByClassOrNav) and (fwdDirByComposition or fwdDirByCardinality)); } query Boolean::toString() : String { if (self) then return 'Y' endif; return 'F'; } For a representative set of test cases varying all combinations of association end aggregation type, cardinality, ownership, navigability, member end order, the above criteria suffices to determine whether an association with ends p and q is in the forward direction (p>>q) or reverse (q>>p): [10/13/13 2:03 PM] Analyzing 22 associations [10/13/13 2:03 PM] Association AB'0 : A'0 -- B'0 [10/13/13 2:03 PM] end1: AB'0::p : A'0[1..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: A'0::q : B'0[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association AB'1 : A'1 -- B'1 [10/13/13 2:03 PM] end1: AB'1::p : A'1[1..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: AB'1::q : B'1[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association AB0 : A0 -- B0 [10/13/13 2:03 PM] end1: AB0::p : A0[1..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: A0::q : B0[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association AB1 : A1 -- B1 [10/13/13 2:03 PM] end1: AB1::p : A1[1..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: AB1::q : B1[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD'0 : C'0 -- D'0 [10/13/13 2:03 PM] end1: CD'0::p : C'0[0..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: C'0::q : D'0[1..1] {memberEnd#2, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD'1 : C'1 -- D'1 [10/13/13 2:03 PM] end1: CD'1::p : C'1[0..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: CD'1::q : D'1[1..1] {memberEnd#2, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD0 : C0 -- D0 [10/13/13 2:03 PM] end1: CD0::p : C0[0..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: C0::q : D0[1..1] {memberEnd#1, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD1 : C1 -- D1 [10/13/13 2:03 PM] end1: CD1::p : C1[0..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: CD1::q : D1[1..1] {memberEnd#1, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF'0 : E'0 -- F'0 [10/13/13 2:03 PM] end1: EF'0::p : E'0[1..1] {memberEnd#1, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: E'0::q : F'0[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF'1 : E'1 -- F'1 [10/13/13 2:03 PM] end1: EF'1::p : E'1[1..1] {memberEnd#1, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: EF'1::q : F'1[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF0 : E0 -- F0 [10/13/13 2:03 PM] end1: EF0::p : E0[1..1] {memberEnd#2, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: E0::q : F0[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF1 : E1 -- F1 [10/13/13 2:03 PM] end1: EF1::p : E1[1..1] {memberEnd#2, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: EF1::q : F1[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH'0 : G'0 -- H'0 [10/13/13 2:03 PM] end1: GH'0::p : G'0[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G'0::q : H'0[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH'1 : G'1 -- H'1 [10/13/13 2:03 PM] end1: GH'1::p : G'1[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: GH'1::q : H'1[0..*] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH'2 : G'2 -- H'2 [10/13/13 2:03 PM] end1: H'2::p : G'2[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G'2::q : H'2[0..*] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH0 : G0 -- H0 [10/13/13 2:03 PM] end1: GH0::p : G0[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G0::q : H0[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH1 : G1 -- H1 [10/13/13 2:03 PM] end1: GH1::p : G1[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: GH1::q : H1[0..*] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH2 : G2 -- H2 [10/13/13 2:03 PM] end1: H2::p : G2[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G2::q : H2[0..*] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ'0 : I'0 -- J'0 [10/13/13 2:03 PM] end1: J'0::p : I'0[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I'0::q : J'0[1..1] {memberEnd#2, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ'1 : I'1 -- J'1 [10/13/13 2:03 PM] end1: J'1::p : I'1[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I'1::q : J'1[0..*] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ0 : I0 -- J0 [10/13/13 2:03 PM] end1: J0::p : I0[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I0::q : J0[1..1] {memberEnd#1, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ1 : I1 -- J1 [10/13/13 2:03 PM] end1: J1::p : I1[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I1::q : J1[0..*] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} To facilitate reviewing this criteria, these associations are shown in the attached class diagram. Resolution: Revised Text: Actions taken: October 13, 2013: received issue Discussion: End of Annotations:===== iler: QUALCOMM Windows Eudora Version 7.1.0.9 Date: Mon, 14 Oct 2013 11:13:42 -0400 To: issues@omg.org, uml2-rtf@omg.org From: Juergen Boldt Subject: issue 19017 -- UML 2.6 RTF issue X-Virus-Scanned: amavisd-new at omg.org From: "Rouquette, Nicolas F (313D)" To: "issues@omg.org" CC: "uml2-rtf@omg.org" Subject: Improving the association direction notation. Thread-Topic: Improving the association direction notation. Thread-Index: AQHOyFe/6FotLq+zwEeEHZvK7eHpsQ== Date: Sun, 13 Oct 2013 21:04:04 +0000 Accept-Language: en-US X-MS-Has-Attach: yes X-MS-TNEF-Correlator: user-agent: Microsoft-MacOutlook/14.3.4.130416 x-originating-ip: [128.149.137.114] X-Source-Sender: nicolas.f.rouquette@jpl.nasa.gov X-AUTH: Authorized X-Virus-Scanned: amavisd-new at omg.org The UML 2.5 notation for associations in section 11.5.4 states (4th paragraph): On a binary Association drawn as a solid line, a solid triangular arrowhead next to or in place of the name of the Association and pointing along the line in the direction of one end indicates that end to be the last in the order of the ends of the Association. The arrow indicates that the Association is to be read as associating the end away from the direction of the arrow with the end to which the arrow is pointing (see Figure 11.27). This notation is for documentation purposes only and has no general semantic interpretation. It is used to capture some application-specific detail of the relationship between the associated Classifiers. In practice, the order of association ends is not very useful. Deriving the direction of an association based on association end cardinality, aggregation type and navigability, which is a function of ownership (see Property::isNavigable()) would be more useful. I propose the following criteria (written in QVT Operational): modeltype uml uses ' http://www.nomagic.com/magicdraw/UML/2.4.1'; /** * @author nicolas.f.rouquette@jpl.nasa.gov * October 2013 - UML2.6 Improving the association direction notation. */ transformation AssociationDirectionCheck(in selectedAssociations:uml); property associations : Set(uml::Association) = selectedAssociations.rootObjects()[uml::Association]; main() { log('Analyzing ' + associations->size().repr() + ' associations'); associations->sortedBy(qualifiedName)->forEach(a) { var p := a.memberEnd![name='p']; var q := a.memberEnd![name='q']; log('Association ' + a.name + ' : ' + p.type.name + ' -- ' + q.type.name); p.describe('end1'); q.describe('end2'); } } helper uml::Property::describe(in prefix:String) { var a := self.association; assert fatal (a.oclIsKindOf(uml::Association)); var other := a.memberEnd->excluding(self)->any(true); var dir := 'n/a'; if (self.isMemberEndLogicallyDirectedToOtherEnd()) then dir := self.name + '>>' + other.name endif; if (other.isMemberEndLogicallyDirectedToOtherEnd()) then dir := other.name + '>>' + self.name endif; log(prefix + ': ' + self.namespace.name + '::' + self.name + ' : ' + self.type.name + '[' + self.lower.repr() + '..' + (if self.upper < 0 then '*' else self.upper.repr() endif) + ']' + ' {memberEnd#' + a.memberEnd->indexOf(self).repr() + ', aggregation=' + self.aggregation.repr() + ', isNavigable=' + self.isNavigable().toString() + ', direction=' + dir + '}'); } helper uml::Property::isMemberEndLogicallyDirectedToOtherEnd() : Boolean { var a := self.association; assert fatal (a.oclIsKindOf(uml::Association)); var other := a.memberEnd->excluding(self)->any(true); var fwdDirByClassOrNav := ((self.owner = a) and (other.owner <> a)) or (not self.isNavigable()) and other.isNavigable(); var fwdDirByComposition := (not self.isComposite) and other.isComposite; var fwdDirByCardinality := (not self.isComposite) and (not other.isComposite) and (self.upper <= 1) and (other.upper < 0 or other.upper > 1); return fwdDirByClassOrNav or ((not fwdDirByClassOrNav) and (fwdDirByComposition or fwdDirByCardinality)); } query Boolean::toString() : String { if (self) then return 'Y' endif; return 'F'; } For a representative set of test cases varying all combinations of association end aggregation type, cardinality, ownership, navigability, member end order, the above criteria suffices to determine whether an association with ends p and q is in the forward direction (p>>q) or reverse (q>>p): [10/13/13 2:03 PM] Analyzing 22 associations [10/13/13 2:03 PM] Association AB'0 : A'0 -- B'0 [10/13/13 2:03 PM] end1: AB'0::p : A'0[1..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: A'0::q : B'0[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association AB'1 : A'1 -- B'1 [10/13/13 2:03 PM] end1: AB'1::p : A'1[1..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: AB'1::q : B'1[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association AB0 : A0 -- B0 [10/13/13 2:03 PM] end1: AB0::p : A0[1..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: A0::q : B0[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association AB1 : A1 -- B1 [10/13/13 2:03 PM] end1: AB1::p : A1[1..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: AB1::q : B1[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD'0 : C'0 -- D'0 [10/13/13 2:03 PM] end1: CD'0::p : C'0[0..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: C'0::q : D'0[1..1] {memberEnd#2, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD'1 : C'1 -- D'1 [10/13/13 2:03 PM] end1: CD'1::p : C'1[0..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: CD'1::q : D'1[1..1] {memberEnd#2, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD0 : C0 -- D0 [10/13/13 2:03 PM] end1: CD0::p : C0[0..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: C0::q : D0[1..1] {memberEnd#1, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD1 : C1 -- D1 [10/13/13 2:03 PM] end1: CD1::p : C1[0..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: CD1::q : D1[1..1] {memberEnd#1, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF'0 : E'0 -- F'0 [10/13/13 2:03 PM] end1: EF'0::p : E'0[1..1] {memberEnd#1, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: E'0::q : F'0[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF'1 : E'1 -- F'1 [10/13/13 2:03 PM] end1: EF'1::p : E'1[1..1] {memberEnd#1, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: EF'1::q : F'1[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF0 : E0 -- F0 [10/13/13 2:03 PM] end1: EF0::p : E0[1..1] {memberEnd#2, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: E0::q : F0[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF1 : E1 -- F1 [10/13/13 2:03 PM] end1: EF1::p : E1[1..1] {memberEnd#2, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: EF1::q : F1[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH'0 : G'0 -- H'0 [10/13/13 2:03 PM] end1: GH'0::p : G'0[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G'0::q : H'0[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH'1 : G'1 -- H'1 [10/13/13 2:03 PM] end1: GH'1::p : G'1[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: GH'1::q : H'1[0..*] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH'2 : G'2 -- H'2 [10/13/13 2:03 PM] end1: H'2::p : G'2[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G'2::q : H'2[0..*] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH0 : G0 -- H0 [10/13/13 2:03 PM] end1: GH0::p : G0[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G0::q : H0[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH1 : G1 -- H1 [10/13/13 2:03 PM] end1: GH1::p : G1[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: GH1::q : H1[0..*] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH2 : G2 -- H2 [10/13/13 2:03 PM] end1: H2::p : G2[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G2::q : H2[0..*] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ'0 : I'0 -- J'0 [10/13/13 2:03 PM] end1: J'0::p : I'0[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I'0::q : J'0[1..1] {memberEnd#2, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ'1 : I'1 -- J'1 [10/13/13 2:03 PM] end1: J'1::p : I'1[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I'1::q : J'1[0..*] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ0 : I0 -- J0 [10/13/13 2:03 PM] end1: J0::p : I0[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I0::q : J0[1..1] {memberEnd#1, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ1 : I1 -- J1 [10/13/13 2:03 PM] end1: J1::p : I1[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I1::q : J1[0..*] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} To facilitate reviewing this criteria, these associations are shown in the attached class diagram. - Nicolas. Binary Association Direction1.jpg Juergen Boldt Director, Member Services 109 Highland Ave Needham, MA 02494 USA Tel: 781 444 0404 x 132 fax: 781 444 0320 www.omg.org [] DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s1024; t=1381771072; bh=st5y4/u4D4FKz6b+AwmUyxbT+6245VHzaenClt1mpTs=; h=X-Yahoo-Newman-Id:X-Yahoo-Newman-Property:X-YMail-OSG:X-Yahoo-SMTP:X-Rocket-Received:From:To:References:In-Reply-To:Subject:Date:Message-ID:MIME-Version:Content-Type:X-Mailer:Thread-Index:Content-Language; b=wlcFdP2+CNdjqjcwZr0zri4M2yLf2K244xnvvKS0tJljpymWn6wk9KrdaPchwwDlb2y+wyu+a7f1WkqUdeIimE8N//37sU/xG1SUDaupC1DNOocHoBbwqz132czGKgQeDblffuMWYUx7RAV68TO4pdMLUFKgvcO1CkeYZJvgO1U= X-Yahoo-Newman-Id: 444199.26476.bm@smtp213.mail.bf1.yahoo.com X-Yahoo-Newman-Property: ymail-3 X-YMail-OSG: A35wWTwVM1ksU.mRjrDW1U7IbTEQpirD6o4WMgpLxG7JLSM EExUj2USst_03UnkK8R3Ebljl15qHUlYy.lvOgCcgis9bhVC0GqwsmV52PUN 9_kchPoruqVES0Nt_JHZ8x6cHkZZnsD3ASsweoXljctN0GDu7x1PSxucxV5k kEjFUO5JW.WKwtsvrwjMZ9qkXvkuSpbmu2V3L20WIJXHf4le6AL4eYeKRzNW X8rcM_zwkotEcFl3TgTJg92btZCyDuZ0f062ouUy.26bR7GCVHgYIn55Nb91 gC3IDQ6n9UFHUGnQmgPQDViPgsIig5R0SrGXr184bZtuwn8EJxPuY77ZTM2V YPRaI4DVdr5A9HNblWR8lB7ilOeYHGtmHtwv3U3MtC7ykduZG91FfLRZeehV PWlwFSgG4Ws16hv5kee9YiT_eL7H7gGY5nBLlZ4BWF5OMP6dV8KZKo5ADo7L 8Si46p19twPO5nhgBE_pN4JWwDDLyZ5oX6GZ1tnySUENgIpTKMykwok8TV7Y 9CaUXgIJ5KX7jza.0i5Lluh2smcwEkyPwKNbtIOnBZ7CRjz2wuN3he9TlnDY trUbQ.73QnFz04NEbRviq.8a4GyThugXzMavbpPVvPsk0HGUVTc_jPW6uqT_ ZbUFsvJMmGFLSlby1wfDpp0dsRC1K3dMDQtLKjqBMjrOjmvjvrxFBzSKoxIE uL25s_iEk7eEpjaU5dg5JCiIrVH2E4TnaLIs3DARx48SJMFjjq.bIGHHFXoM lCYESKcXphn9IbEkEv45WWcMUiYZoAK3EEBoTYduYsKvbA7NJBn0GGxO49DW NeATAb5FiQd2u5.HtOiUYdrJK2YHuvBMi8dfZnnPYx8lqHeuQLZsIyeXgUOz JkE4KK9lnYne2 X-Yahoo-SMTP: BHehp.2swBCs4PqecFo6LCqjUcnFjw4- X-Rocket-Received: from mjchonolesHP (mjchonoles@71.225.92.207 with ) by smtp213.mail.bf1.yahoo.com with SMTP; 14 Oct 2013 10:17:52 -0700 PDT From: "Michael Chonoles" To: "'Juergen Boldt'" , , Subject: RE: issue 19017 -- UML 2.6 RTF issue Date: Mon, 14 Oct 2013 13:17:49 -0400 X-Mailer: Microsoft Outlook 15.0 Thread-Index: AQGu41WPnEtW4qDKeu1zv94QcJ7Lhpo0OpsA X-Virus-Scanned: amavisd-new at omg.org Hmm 1) Why would be it more useful? There is no explanation of the problem 2) Expressing the fix in QVT Operational is as useful as expressing it in Klingon. Certainly, when we update the spec, we would need to also express it in English. 3) It is unclear what this does to the read-direction triangle? Possibly the triangle direction should be independent of the order of the ends and the navigability of the ends. MIchael From: Juergen Boldt [mailto:juergen@omg.org] Sent: Monday, October 14, 2013 11:14 AM To: issues@omg.org; uml2-rtf@omg.org Subject: issue 19017 -- UML 2.6 RTF issue From: "Rouquette, Nicolas F (313D)" To: "issues@omg.org" CC: "uml2-rtf@omg.org" Subject: Improving the association direction notation. Thread-Topic: Improving the association direction notation. Thread-Index: AQHOyFe/6FotLq+zwEeEHZvK7eHpsQ== Date: Sun, 13 Oct 2013 21:04:04 +0000 Accept-Language: en-US X-MS-Has-Attach: yes X-MS-TNEF-Correlator: user-agent: Microsoft-MacOutlook/14.3.4.130416 x-originating-ip: [128.149.137.114] X-Source-Sender: nicolas.f.rouquette@jpl.nasa.gov X-AUTH: Authorized X-Virus-Scanned: amavisd-new at omg.org The UML 2.5 notation for associations in section 11.5.4 states (4th paragraph): On a binary Association drawn as a solid line, a solid triangular arrowhead next to or in place of the name of the Association and pointing along the line in the direction of one end indicates that end to be the last in the order of the ends of the Association. The arrow indicates that the Association is to be read as associating the end away from the direction of the arrow with the end to which the arrow is pointing (see Figure 11.27). This notation is for documentation purposes only and has no general semantic interpretation. It is used to capture some application-specific detail of the relationship between the associated Classifiers. In practice, the order of association ends is not very useful. Deriving the direction of an association based on association end cardinality, aggregation type and navigability, which is a function of ownership (see Property::isNavigable()) would be more useful. I propose the following criteria (written in QVT Operational): modeltype uml uses ' http://www.nomagic.com/magicdraw/UML/2.4.1'; /** * @author nicolas.f.rouquette@jpl.nasa.gov * October 2013 - UML2.6 Improving the association direction notation. */ transformation AssociationDirectionCheck(in selectedAssociations:uml); property associations : Set(uml::Association) = selectedAssociations.rootObjects()[uml::Association]; main() { log('Analyzing ' + associations->size().repr() + ' associations'); associations->sortedBy(qualifiedName)->forEach(a) { var p := a.memberEnd![name='p']; var q := a.memberEnd![name='q']; log('Association ' + a.name + ' : ' + p.type.name + ' -- ' + q.type.name); p.describe('end1'); q.describe('end2'); } } helper uml::Property::describe(in prefix:String) { var a := self.association; assert fatal (a.oclIsKindOf(uml::Association)); var other := a.memberEnd->excluding(self)->any(true); var dir := 'n/a'; if (self.isMemberEndLogicallyDirectedToOtherEnd()) then dir := self.name + '>>' + other.name endif; if (other.isMemberEndLogicallyDirectedToOtherEnd()) then dir := other.name + '>>' + self.name endif; log(prefix + ': ' + self.namespace.name + '::' + self.name + ' : ' + self.type.name + '[' + self.lower.repr() + '..' + (if self.upper < 0 then '*' else self.upper.repr() endif) + ']' + ' {memberEnd#' + a.memberEnd->indexOf(self).repr() + ', aggregation=' + self.aggregation.repr() + ', isNavigable=' + self.isNavigable().toString() + ', direction=' + dir + '}'); } helper uml::Property::isMemberEndLogicallyDirectedToOtherEnd() : Boolean { var a := self.association; assert fatal (a.oclIsKindOf(uml::Association)); var other := a.memberEnd->excluding(self)->any(true); var fwdDirByClassOrNav := ((self.owner = a) and (other.owner <> a)) or (not self.isNavigable()) and other.isNavigable(); var fwdDirByComposition := (not self.isComposite) and other.isComposite; var fwdDirByCardinality := (not self.isComposite) and (not other.isComposite) and (self.upper <= 1) and (other.upper < 0 or other.upper > 1); return fwdDirByClassOrNav or ((not fwdDirByClassOrNav) and (fwdDirByComposition or fwdDirByCardinality)); } query Boolean::toString() : String { if (self) then return 'Y' endif; return 'F'; } For a representative set of test cases varying all combinations of association end aggregation type, cardinality, ownership, navigability, member end order, the above criteria suffices to determine whether an association with ends p and q is in the forward direction (p>>q) or reverse (q>>p): [10/13/13 2:03 PM] Analyzing 22 associations [10/13/13 2:03 PM] Association AB'0 : A'0 -- B'0 [10/13/13 2:03 PM] end1: AB'0::p : A'0[1..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: A'0::q : B'0[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association AB'1 : A'1 -- B'1 [10/13/13 2:03 PM] end1: AB'1::p : A'1[1..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: AB'1::q : B'1[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association AB0 : A0 -- B0 [10/13/13 2:03 PM] end1: AB0::p : A0[1..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: A0::q : B0[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association AB1 : A1 -- B1 [10/13/13 2:03 PM] end1: AB1::p : A1[1..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: AB1::q : B1[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD'0 : C'0 -- D'0 [10/13/13 2:03 PM] end1: CD'0::p : C'0[0..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: C'0::q : D'0[1..1] {memberEnd#2, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD'1 : C'1 -- D'1 [10/13/13 2:03 PM] end1: CD'1::p : C'1[0..1] {memberEnd#1, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: CD'1::q : D'1[1..1] {memberEnd#2, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD0 : C0 -- D0 [10/13/13 2:03 PM] end1: CD0::p : C0[0..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: C0::q : D0[1..1] {memberEnd#1, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association CD1 : C1 -- D1 [10/13/13 2:03 PM] end1: CD1::p : C1[0..1] {memberEnd#2, aggregation=none, isNavigable=F, direction=p>>q} [10/13/13 2:03 PM] end2: CD1::q : D1[1..1] {memberEnd#1, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF'0 : E'0 -- F'0 [10/13/13 2:03 PM] end1: EF'0::p : E'0[1..1] {memberEnd#1, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: E'0::q : F'0[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF'1 : E'1 -- F'1 [10/13/13 2:03 PM] end1: EF'1::p : E'1[1..1] {memberEnd#1, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: EF'1::q : F'1[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF0 : E0 -- F0 [10/13/13 2:03 PM] end1: EF0::p : E0[1..1] {memberEnd#2, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: E0::q : F0[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association EF1 : E1 -- F1 [10/13/13 2:03 PM] end1: EF1::p : E1[1..1] {memberEnd#2, aggregation=composite, isNavigable=F, direction=q>>p} [10/13/13 2:03 PM] end2: EF1::q : F1[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH'0 : G'0 -- H'0 [10/13/13 2:03 PM] end1: GH'0::p : G'0[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G'0::q : H'0[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH'1 : G'1 -- H'1 [10/13/13 2:03 PM] end1: GH'1::p : G'1[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: GH'1::q : H'1[0..*] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH'2 : G'2 -- H'2 [10/13/13 2:03 PM] end1: H'2::p : G'2[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G'2::q : H'2[0..*] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH0 : G0 -- H0 [10/13/13 2:03 PM] end1: GH0::p : G0[1..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G0::q : H0[1..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH1 : G1 -- H1 [10/13/13 2:03 PM] end1: GH1::p : G1[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: GH1::q : H1[0..*] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association GH2 : G2 -- H2 [10/13/13 2:03 PM] end1: H2::p : G2[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: G2::q : H2[0..*] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ'0 : I'0 -- J'0 [10/13/13 2:03 PM] end1: J'0::p : I'0[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I'0::q : J'0[1..1] {memberEnd#2, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ'1 : I'1 -- J'1 [10/13/13 2:03 PM] end1: J'1::p : I'1[0..1] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I'1::q : J'1[0..*] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ0 : I0 -- J0 [10/13/13 2:03 PM] end1: J0::p : I0[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I0::q : J0[1..1] {memberEnd#1, aggregation=composite, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] Association IJ1 : I1 -- J1 [10/13/13 2:03 PM] end1: J1::p : I1[0..1] {memberEnd#2, aggregation=none, isNavigable=Y, direction=p>>q} [10/13/13 2:03 PM] end2: I1::q : J1[0..*] {memberEnd#1, aggregation=none, isNavigable=Y, direction=p>>q} To facilitate reviewing this criteria, these associations are shown in the attached class diagram. - Nicolas.