| -- @atlcompiler atl2006 |
| -- author : Gabriel Barbier, Mia-Software, gbarbier@mia-software.com |
| -- recreates bidirectionnal associations from unidirectionnal associations |
| -- @nsURI uml=http://www.eclipse.org/uml2/2.1.0/UML |
| |
| module BidirectionalAssociation; --superimpose UML2Copy |
| |
| create umlOutput:uml from umlInput:uml; |
| |
| helper def : bidirectionalAssociations : Set(uml!Association) = uml!Association.allInstancesFrom('umlInput')->select( |
| association | association->isBidirectional()); |
| helper def : uselessProperties : Set(uml!Property) = thisModule.bidirectionalAssociations->collect(association | association.ownedEnd)->flatten(); |
| helper def : uselessAssociations : Set(uml!Association) = OrderedSet {}; |
| |
| helper context uml!Association def: isBidirectional() : Boolean = |
| let sourceClass:uml!Class = self.endType->first() in ( |
| if (sourceClass.oclIsUndefined()) then |
| false |
| else |
| let targetClass:uml!Class = self.endType->last() in ( |
| if (targetClass.oclIsUndefined()) then |
| false |
| else |
| if (sourceClass = targetClass) then |
| -- self association, so to have a bidirectional association |
| -- we have to have a collection of property which type is sourceClass |
| -- and which is linked to an association |
| -- and this collection shall have a size equals to 2 |
| (sourceClass.attribute->select( |
| attribute | (attribute.type = sourceClass) |
| and (not attribute.association.oclIsUndefined()))->size() = 2) |
| else |
| -- so we are looking to have exactly one property |
| -- which type is the opposite class in each class |
| (sourceClass.attribute->select( |
| attribute | (attribute.type = targetClass) |
| and (not attribute.association.oclIsUndefined()))->size() = 1) |
| and |
| (targetClass.attribute->select( |
| attribute | (attribute.type = sourceClass) |
| and (not attribute.association.oclIsUndefined()))->size() = 1) |
| endif |
| endif |
| ) |
| endif); |
| |
| helper context uml!Association def: getOppositeAssociation() : uml!Association = |
| if (self->isBidirectional()) then |
| thisModule.bidirectionalAssociations->select(association | |
| (not (association = self)) and (self.endType->includesAll(association.endType)) |
| )->first() |
| else |
| self |
| endif; |
| |
| -- initialize collection of useless association and property |
| entrypoint rule init() { |
| do { |
| for (association in thisModule.bidirectionalAssociations) { |
| if (thisModule.uselessAssociations->excludes(association) |
| and thisModule.uselessAssociations->excludes(association->getOppositeAssociation())) { |
| --association->toString().output(); |
| --association->getOppositeAssociation()->toString().output(); |
| |
| -- finally, we will flag useless association and property |
| -- (only one association, but useless property of both associations) |
| thisModule.uselessAssociations <- thisModule.uselessAssociations->append(association); |
| --thisModule.uselessProperties <- thisModule.uselessProperties->union(association.ownedEnd); |
| --thisModule.uselessProperties <- thisModule.uselessProperties->union(association->getOppositeAssociation().ownedEnd); |
| } -- else nothing to do |
| } |
| } |
| } |
| -- copy association that are not bidirectional |
| rule Association { |
| from s : uml!"uml::Association" ( |
| if (thisModule.inElements->includes(s)) |
| then |
| if (s.oclIsTypeOf(uml!"uml::Association")) |
| then |
| -- add specific test |
| not s->isBidirectional() |
| else |
| false |
| endif |
| else false |
| endif) |
| to t : uml!"uml::Association" mapsTo s ( |
| name <- s.name, |
| visibility <- s.visibility, |
| isLeaf <- s.isLeaf, |
| isAbstract <- s.isAbstract, |
| isDerived <- s.isDerived, |
| eAnnotations <- s.eAnnotations, |
| ownedComment <- s.ownedComment, |
| clientDependency <- s.clientDependency, |
| nameExpression <- s.nameExpression, |
| elementImport <- s.elementImport, |
| packageImport <- s.packageImport, |
| ownedRule <- s.ownedRule, |
| templateParameter <- s.templateParameter, |
| templateBinding <- s.templateBinding, |
| ownedTemplateSignature <- s.ownedTemplateSignature, |
| generalization <- s.generalization, |
| powertypeExtent <- s.powertypeExtent, |
| redefinedClassifier <- s.redefinedClassifier, |
| substitution <- s.substitution, |
| representation <- s.representation, |
| collaborationUse <- s.collaborationUse, |
| ownedUseCase <- s.ownedUseCase, |
| useCase <- s.useCase, |
| ownedEnd <- s.ownedEnd, |
| memberEnd <- s.memberEnd, |
| navigableOwnedEnd <- s.navigableOwnedEnd) |
| } |
| -- copy bidirectional association |
| rule BidirectionalAssociation { |
| from s : uml!"uml::Association" ( |
| if (thisModule.inElements->includes(s)) |
| then |
| if (s.oclIsTypeOf(uml!"uml::Association")) |
| then |
| (s->isBidirectional() and thisModule.uselessAssociations->excludes(s)) |
| else |
| false |
| endif |
| else false |
| endif) |
| using { |
| -- if they are same, it may not be a problem |
| sourceClass:uml!Class = s.memberEnd->select(element | s.ownedEnd->includes(element))->first().type; |
| targetClass:uml!Class = s.memberEnd->select(element | s.ownedEnd->excludes(element))->first().type; |
| opposite:uml!Property = targetClass.attribute->select(attribute | attribute.type = sourceClass)->first(); |
| } |
| |
| to t : uml!"uml::Association" mapsTo s ( |
| name <- sourceClass->toString() + ' to ' + targetClass->toString(), |
| visibility <- s.visibility, |
| isLeaf <- s.isLeaf, |
| isAbstract <- s.isAbstract, |
| isDerived <- s.isDerived, |
| eAnnotations <- s.eAnnotations, |
| ownedComment <- s.ownedComment, |
| clientDependency <- s.clientDependency, |
| nameExpression <- s.nameExpression, |
| elementImport <- s.elementImport, |
| packageImport <- s.packageImport, |
| ownedRule <- s.ownedRule, |
| templateParameter <- s.templateParameter, |
| templateBinding <- s.templateBinding, |
| ownedTemplateSignature <- s.ownedTemplateSignature, |
| generalization <- s.generalization, |
| powertypeExtent <- s.powertypeExtent, |
| redefinedClassifier <- s.redefinedClassifier, |
| substitution <- s.substitution, |
| representation <- s.representation, |
| collaborationUse <- s.collaborationUse, |
| ownedUseCase <- s.ownedUseCase, |
| useCase <- s.useCase, |
| -- do not retrieve elements of collection 'ownedEnd' |
| --ownedEnd <- s.ownedEnd, |
| -- and filter elements of collection 'memberEnd' to eliminate elements in collection 'ownedEnd' |
| memberEnd <- s.memberEnd->select(element | s.ownedEnd->excludes(element)), |
| -- and now we will add opposite property from other unidirectionnal association |
| memberEnd <- opposite, |
| navigableOwnedEnd <- s.navigableOwnedEnd) |
| do { |
| --('opposite property = ' + opposite->toString())->output(); |
| t.name->output(); |
| } |
| } |
| |
| -- to filter useless property |
| rule Property { |
| from s : uml!"uml::Property" ( |
| if thisModule.inElements->includes(s) then |
| if (s.oclIsTypeOf(uml!"uml::Property")) |
| then thisModule.uselessProperties->excludes(s) |
| else false |
| endif |
| else false endif) |
| to t : uml!"uml::Property" mapsTo s ( |
| name <- s.name, |
| visibility <- s.visibility, |
| isLeaf <- s.isLeaf, |
| isStatic <- s.isStatic, |
| isOrdered <- s.isOrdered, |
| isUnique <- s.isUnique, |
| isReadOnly <- s.isReadOnly, |
| isDerived <- s.isDerived, |
| isDerivedUnion <- s.isDerivedUnion, |
| aggregation <- s.aggregation, |
| eAnnotations <- s.eAnnotations, |
| ownedComment <- s.ownedComment, |
| clientDependency <- s.clientDependency, |
| nameExpression <- s.nameExpression, |
| type <- s.type, |
| upperValue <- s.upperValue, |
| lowerValue <- s.lowerValue, |
| templateParameter <- s.templateParameter, |
| end <- s.end, |
| deployment <- s.deployment, |
| redefinedProperty <- s.redefinedProperty, |
| defaultValue <- s.defaultValue, |
| subsettedProperty <- s.subsettedProperty, |
| -- test to avoid link to deleted Association |
| association <- if (thisModule.uselessAssociations->excludes(s.association)) then |
| s.association |
| else |
| OclUndefined |
| endif, |
| qualifier <- s.qualifier) |
| } |