-- @atlcompiler emftvm -- @nsURI UML2=http://www.eclipse.org/uml2/3.0.0/UML -- $Id$ -- Implements the Observer profile. module UML2Observer; create OUT : UML2 from IN : UML2, OBSERVER : UML2, UML2TYPES : UML2; uses "lib::Strings"; uses "lib::UML2"; uses "lib::Mappings"; uses UML2Copy; -- ====================================================================== -- model-specific helpers begin -- ====================================================================== helper def : language : String = 'language'.value(); helper def : separator : String = 'separator'.value(); helper def : Observer : UML2!"uml::Interface" = 'observer::Observer'.interface(); helper def : ObserverName : String = thisModule.Observer.qualifiedName('separator'.value()); helper def : oclString : UML2!"uml::PrimitiveType" = 'String'.primitiveType(); helper def : oclAny : UML2!"uml::PrimitiveType" = 'OclAny'.primitiveType(); helper context UML2!"uml::Classifier" def : isObservable : Boolean = not self.getAppliedStereotype('Observer::Observable').oclIsUndefined(); helper context UML2!"uml::Classifier" def : isObserver : Boolean = not self.getAppliedStereotype('Observer::Observer').oclIsUndefined(); helper context UML2!"uml::Association" def : isSubscribe : Boolean = not self.getAppliedStereotype('Observer::subscribe').oclIsUndefined(); helper context UML2!"uml::Dependency" def : isAccessor : Boolean = not self.getAppliedStereotype('Accessors::accessor').oclIsUndefined(); helper context UML2!"uml::Dependency" def : accessor : UML2!"uml::Stereotype" = self.getAppliedStereotype('Accessors::accessor'); helper context UML2!"uml::Class" def : update() : String = self.handlers()->iterate(o; acc : String = ''|acc + o.update()); helper context UML2!"uml::Class" def : handlers() : Sequence(UML2!"uml::Operation") = self.ownedOperation->select(o| o.name.startsWith('on') and o.name.endsWith('Change') and o.name.size() > 8); helper context UML2!"uml::Operation" def : update() : String = self.name.updateBody(self.propertyName, self.parTypeName); helper context UML2!"uml::Operation" def : propertyName : String = self.name.substring(3, self.name.size() - 6); -- onPropertyChange -> Property helper context UML2!"uml::Operation" def : parTypeName : String = self.ownedParameter->first().type.qualifiedName(thisModule.separator); helper context UML2!"uml::Behavior" def : isNotifyMethod : Boolean = self.specification.isAccessor and self.owner.isObservableKind; helper context UML2!"uml::Behavior" def : isSubscribeMethod : Boolean = self.specification.isAccessor and self.owner.isObserverKind and self.specification.isSubscriber; helper context UML2!"uml::Operation" def : accessorFor : UML2!"uml::Property" = self.accessorDependency.supplier->any(p| p.oclIsKindOf(UML2!"uml::Property")); helper context UML2!"uml::Operation" def : accessorDependency : UML2!"uml::Dependency" = self.clientDependency->any(d|d.isAccessor); helper context UML2!"uml::Operation" def : isAccessor : Boolean = self.clientDependency->exists(d|d.isAccessor); helper context UML2!"uml::Classifier" def : isObservableKind : Boolean = self.isObservable or self.generalization->exists(g|g.general.isObservableKind); helper context UML2!"uml::Classifier" def : isObserverKind : Boolean = self.isObserver or self.generalization->exists(g|g.general.isObserverKind); helper context UML2!"uml::Operation" def : isSubscriber : Boolean = self.isAccessor and self.accessorFor.hasSubscribesAssociation; helper context UML2!"uml::Property" def : hasSubscribesAssociation : Boolean = not self.association.oclIsUndefined() and self.association.isSubscribe; helper context UML2!"uml::Operation" def : notify(body : String) : String = let d : UML2!"uml::Dependency" = self.accessorDependency in -- UML has its own enumeration literal implementations, which are NamedElements let kind : String = d.getValue(d.accessor, 'kind').name in if kind = 'remove' then self.accessorFor.removerNotify(body) else if kind = 'add' or kind = 'insert' then self.accessorFor.adderNotify(body) else if kind = 'set' then self.accessorFor.setterNotify(body) else body endif endif endif; helper context UML2!"uml::Property" def : setterNotify(body : String) : String = self.name.setterNotifyBody( self.type.qualifiedName(thisModule.separator), body, false); helper context UML2!"uml::Property" def : adderNotify(body : String) : String = self.name.adderNotifyBody( self.type.qualifiedName(thisModule.separator), body, self.isUnique, false); helper context UML2!"uml::Property" def : removerNotify(body : String) : String = self.name.removerNotifyBody( self.type.qualifiedName(thisModule.separator), body, false); helper context UML2!"uml::Operation" def : subscribe(body : String) : String = let d : UML2!"uml::Dependency" = self.accessorDependency in -- UML has its own enumeration literal implementations, which are NamedElements let kind : String = d.getValue(d.accessor, 'kind').name in if kind = 'remove' then self.accessorFor.name.removerSubscribeBody(body, false) else if kind = 'add' or kind = 'insert' then self.accessorFor.name.adderSubscribeBody(body) else if kind = 'set' then self.accessorFor.name.setterSubscribeBody(body, false) else body endif endif endif; -- ====================================================================== -- model-specific helpers end -- ====================================================================== -- ====================================================================== -- transformation rules begin -- ====================================================================== rule Class { from s : UML2!"uml::Class" in IN ( s.oclIsTypeOf(UML2!"uml::Class")) to t : UML2!"uml::Class" ( __xmiID__ <- s.__xmiID__, name <- s.name, visibility <- s.visibility, isLeaf <- s.isLeaf, isAbstract <- s.isAbstract, isActive <- s.isActive, 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, ownedAttribute <- s.ownedAttribute, ownedConnector <- s.ownedConnector, interfaceRealization <- s.interfaceRealization, ownedOperation <- s.ownedOperation, ownedBehavior <- s.ownedBehavior ->union(if s.isObserver then Sequence{thisModule.ObserverClass(s)} else Sequence{} endif), classifierBehavior <- s.classifierBehavior, ownedTrigger <- s.ownedTrigger, nestedClassifier <- s.nestedClassifier, ownedReception <- s.ownedReception) } rule ObservableClass extends Class { from s : UML2!"uml::Class" ( s.isObservable) to t : UML2!"uml::Class" ( name <- s.name.debug('ObservableClass'), ownedAttribute <- s.ownedAttribute->including(observers), ownedBehavior <- s.ownedBehavior ->union(Sequence{getBehavior, addBehavior, removeBehavior, notifyBehavior}) ->union(if s.isObserver then Sequence{thisModule.ObserverClass(s)} else Sequence{} endif)), -- Observable functionality -- observers : UML2!"uml::Property" ( name <- 'observer', type <- thisModule.Observer, visibility <- #private, isUnique <- true, isOrdered <- true, upperValue <- observersUpper, lowerValue <- observersLower), observersUpper : UML2!"uml::LiteralUnlimitedNatural" ( value <- 0-1), observersLower : UML2!"uml::LiteralInteger" ( value <- 0), -- Get -- getOp : UML2!"uml::Operation" ( name <- 'getObservers', class <- s, visibility <- #public, ownedParameter <- Sequence{getPar}, concurrency <- #sequential), getPar : UML2!"uml::Parameter" ( name <- 'return', lowerValue <- getParLow, upperValue <- getParUp, type <- thisModule.Observer, effect <- #read, direction <- #return), getParLow : UML2!"uml::LiteralInteger" ( value <- 0), getParUp : UML2!"uml::LiteralUnlimitedNatural" ( value <- 0-1), getBehavior : UML2!"uml::OpaqueBehavior" ( specification <- getOp, name <- 'getObserversBehavior', ownedParameter <- Sequence{getBPar}, language <- Sequence{thisModule.language}, body <- Sequence{'observer'.multiGetterBody()}), getBPar : UML2!"uml::Parameter" ( name <- 'return', lowerValue <- getBParLow, upperValue <- getBParUp, type <- thisModule.Observer, effect <- #read, direction <- #return), getBParLow : UML2!"uml::LiteralInteger" ( value <- 0), getBParUp : UML2!"uml::LiteralUnlimitedNatural" ( value <- 0-1), -- Add -- addOp : UML2!"uml::Operation" ( name <- 'addObserver', class <- s, visibility <- #public, ownedParameter <- Sequence{addPar}, concurrency <- #sequential), addPar : UML2!"uml::Parameter" ( name <- 'observer', type <- thisModule.Observer, effect <- #update, direction <- #"in"), addBehavior : UML2!"uml::OpaqueBehavior" ( specification <- addOp, name <- 'addObserverBehavior', ownedParameter <- Sequence{addBPar}, language <- Sequence{thisModule.language}, body <- Sequence{'observer'.adderBody(true)}), addBPar : UML2!"uml::Parameter" ( name <- 'observer', type <- thisModule.Observer, effect <- #update, direction <- #"in"), -- Remove -- removeOp : UML2!"uml::Operation" ( name <- 'removeObserver', class <- s, visibility <- #public, ownedParameter <- Sequence{removePar}, concurrency <- #sequential), removePar : UML2!"uml::Parameter" ( name <- 'observer', type <- thisModule.Observer, effect <- #update, direction <- #"in"), removeBehavior : UML2!"uml::OpaqueBehavior" ( specification <- removeOp, name <- 'removeObserverBehavior', ownedParameter <- Sequence{removeBPar}, language <- Sequence{thisModule.language}, body <- Sequence{'observer'.removerBody()}), removeBPar : UML2!"uml::Parameter" ( name <- 'observer', type <- thisModule.Observer, effect <- #update, direction <- #"in"), -- Notify -- notifyOp : UML2!"uml::Operation" ( name <- 'notifyObservers', class <- s, visibility <- #public, ownedParameter <- Sequence{notifyPar1, notifyPar2}, concurrency <- #sequential), notifyPar1 : UML2!"uml::Parameter" ( name <- 'name', type <- thisModule.oclString, effect <- #read, direction <- #"in"), notifyPar2 : UML2!"uml::Parameter" ( name <- 'value', type <- thisModule.oclAny, effect <- #update, direction <- #"in"), notifyBehavior : UML2!"uml::OpaqueBehavior" ( specification <- notifyOp, name <- 'notifyObserversBehavior', ownedParameter <- Sequence{notifyBPar1, notifyBPar2}, language <- Sequence{thisModule.language}, body <- Sequence{thisModule.ObserverName.notifyObserversBody()}), notifyBPar1 : UML2!"uml::Parameter" ( name <- 'name', type <- thisModule.oclString, effect <- #read, direction <- #"in"), notifyBPar2 : UML2!"uml::Parameter" ( name <- 'value', type <- thisModule.oclAny, effect <- #update, direction <- #"in") } lazy rule ObserverClass { from s : UML2!"uml::Class" to real : UML2!"uml::InterfaceRealization" ( contract <- thisModule.Observer, implementingClassifier <- s), -- Update -- updateOp : UML2!"uml::Operation" ( name <- 'update'.debug('ObserverClass : ' + s.name), class <- s, visibility <- #public, ownedParameter <- Sequence{updatePar1, updatePar2}, concurrency <- #sequential), updatePar1 : UML2!"uml::Parameter" ( name <- 'name', type <- thisModule.oclString, effect <- #read, direction <- #"in"), updatePar2 : UML2!"uml::Parameter" ( name <- 'value', type <- thisModule.oclAny, effect <- #update, direction <- #"in"), updateBehavior : UML2!"uml::OpaqueBehavior" ( specification <- updateOp, name <- 'updateBehavior', ownedParameter <- Sequence{updateBPar1, updateBPar2}, language <- Sequence{thisModule.language}, body <- Sequence{s.update()}), updateBPar1 : UML2!"uml::Parameter" ( name <- 'name', type <- thisModule.oclString, effect <- #read, direction <- #"in"), updateBPar2 : UML2!"uml::Parameter" ( name <- 'value', type <- thisModule.oclAny, effect <- #update, direction <- #"in") do { updateBehavior; } } rule OpaqueBehavior { from s : UML2!"uml::OpaqueBehavior" in IN ( s.oclIsTypeOf(UML2!"uml::OpaqueBehavior")) to t : UML2!"uml::OpaqueBehavior" ( __xmiID__ <- s.__xmiID__, name <- s.name, visibility <- s.visibility, isLeaf <- s.isLeaf, isAbstract <- s.isAbstract, isActive <- s.isActive, isReentrant <- s.isReentrant, body <- if s.isNotifyMethod then if s.isSubscribeMethod then s.body->collect(b|s.specification.notify(s.specification.subscribe(b))) else s.body->collect(b|s.specification.notify(b)) endif else if s.isSubscribeMethod then s.body->collect(b|s.specification.subscribe(b)) else s.body endif endif, language <- s.language, 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, ownedAttribute <- s.ownedAttribute, ownedConnector <- s.ownedConnector, ownedBehavior <- s.ownedBehavior, classifierBehavior <- s.classifierBehavior, interfaceRealization <- s.interfaceRealization, ownedTrigger <- s.ownedTrigger, nestedClassifier <- s.nestedClassifier, ownedOperation <- s.ownedOperation, ownedReception <- s.ownedReception, redefinedBehavior <- s.redefinedBehavior, ownedParameter <- s.ownedParameter, precondition <- s.precondition, postcondition <- s.postcondition, ownedParameterSet <- s.ownedParameterSet, specification <- s.specification) } -- ====================================================================== -- transformation rules end -- ======================================================================