Issue 16329: No unambiguous way in MOF 2.4 to serialize UML 2.4's StructuredActivityNode (mof2core-rtf) Source: No Magic, Inc. (Mr. Nerijus Jankevicius, nerijus(at)nomagic.com) Nature: Revision Severity: Critical Summary: We just recently had discussion with Ed about an issue with Activity::node and Activity::group. Both are composite non-derived properties and it causes problems with all StructuredActivityNodes, which are ActivityNodes and ActivityGroups at the same time. MagicDraw or Eclipse implementation of UML does not allow to own the same element in two composites , even if owner element is the same. Does XMI support that? So, ExpansionRegion or any other StructuredActivityNode appears in Activity::group only. fUML spec/engine expects to find them in Activity::node , as all owned nodes should be there. Any suggestions? Don't you think we should fix that somehow Resolution: resolved as an urgent issue Revised Text: Actions taken: June 10, 2011: received issue January 12, 2012: closed issue Discussion: Resolution: The problem is that the combination of the UML, MOF and XMI specs taken together do not provide an adequate specification of how to serialize StructuredActivityNodes, which leads to ambiguity. Currently, the ownership of StructuredActivityNode by Activity is derived, and is a subset of Activity::node and Activity::group. This leads to the question of whether a StructuredActivityNode should be serialized as a node, as a group, or both. This has obvious impacts on model interchange, and also impacts fUML. The solution is to make the ownership of StructuredActivityNode by Activity non-derived, and then fix the MOF semantics and XMI serialization rules to ensure that a StructuredActivityNode is serialized in a structuredNode element, and not in a node or a group element. This resolution fixes the MOF part; there are two counterpart issues corresponding to the UML and XMI parts. MOF 2.4 currently states that an Element may only have a single slot that refers to its container. In the MOF semantics, it states for ClassInstance “At most one slot for an isComposite property may have a value. (This needs more work if the owner reference is not navigable)”. In fact this ought to say “At most one owning slot, i.e. a slot whose property is opposite an isComposite property, may have a value”. The semantics also specifies Object::owningProperty(), unambiguously declaring that an object only has one owning slot at a time. The issue is that there is no correct specification of which property provides the owning slot where there is redefinition. In the case of StructuredActivityNode::activity, it redefines ActivityNode::activity and ActivityGroup::inactivity. MOF needs to be clear that in such a case it is the slot for the redefining property that is the actual owning slot. Armed with that information, the XMI spec can then say that the correct element to serialize is the one opposite the actual owning slot. The semantics correctly states for ObjectInstance “the stored StructuralFeatures … exclude Properties that have been redefined”. However the main problem is in the definition of Instance::propertySlot(), which is supposed to return the slot corresponding to a particular property. This in turn calls a function called originalDefinition() which incorrectly climbs up the chain of redefinitions and subsettings to find the highest property in the chain. This is wrong on two counts: firstly it is the wrong algorithm in principle, because it conflicts with “excluding properties that have been redefined”, and secondly it is wrong to assume that there is only one redefinition/subsetting chain, because in general there are several. The function originalDefinition is misnamed as well as being ill-conceived. Instead, we need a function applicableDefinition that determines, for a given instance and a given property, which slot is applicable. The semantic function allSlottableProperties is incorrect; it misunderstands and reverses the role of redefinition, it confuses subsetting and redefinition, and it has an incorrect “not” in its attempt to exclude association-owned ends. The semantic function owningProperty is incorrect because it does not take into account the effect of redefinition. The semantic function allProperties is extensively used but has no definition. Note: there are many other syntactic inconsistencies in the semantic definition of MOF 2.4, such as the inconsistent use of body conditions vs postconditions, the inconsistent use of operation names vs result, and the omission of operation call brackets. This urgent resolution does not attempt to fix those issues. Revised text: In Section 15.2, under ClassInstance: Replace: 2. At most one Slot for an isComposite property may have a value. (This needs more work if the owner reference is not navigable). by: 2. At most one owning Slot, i.e. a Slot whose property is opposite an isComposite property, may have a value. In Section 15.8, Additional Operations: Replace the following definition: [1] This gives all of the properties in the namespace of the class (including inherited) that require a slot: it excludes properties owned by Associations, derived properties and those that subset or redefine another property (in which case that property will provide the slot and the redefinition will just restrict the values) Class::allSlottableProperties(): Set(Property); allSlottableProperties = member->select(p| p.oclIsKindOf(Property) and not (self.allParents() includes p.namespace) -- excludes foreign association ends not(p.isDerived) and isEmpty(p.redefinedProperty) and isEmpty(p.subsettedProperty) ) Replace it by: [1] This gives all of the owned properties of the class (including inherited) that require a slot: it excludes derived properties and those that are redefined in the same classproperties. Class::allSlottableProperties(): Set(Property); allSlottableProperties = self.allNonDerivedProperties ()->reject( p | (self.allNonDerivedProperties()->select(r | r.allRedefinedProperties()->intersection(self.allProperties())->includes(p)) [2] All the non-derived owned properties (including inherited) of a class. Class::allNonDerivedProperties(): Set(Property); allNonDerivedProperties = self.allProperties() -> select( p | not(p.isDerived)) [3 [2] All the non-redefined properties (including inherited) of a class. Class::allProperties(): Set(Property); allProperties = member->select(p | p.oclIsKindOf(Property) and p.owner = self)n | [4 n.oclIsKindOf(Property))->collect( p | p.oclAsType(Property)) [3] All of the properties directly or indirectly redefined by a property. Property::allRedefinedProperties() : Set(Property) allRedefinedProperties = if self.redefinedProperty->isEmpty then Set{} else self.redefinedProperty->union( self.redefinedProperty->collect(r | r.allRedefinedProperties())())) Replace the following definition: [2] This returns the slot corresponding to the supplied property. For redefining properties it will be the redefined one. Note that derived properties will only have slots if the redefine a non-derived one so the result may be null. Instance::propertySlot(Property p): Slot propertySlot = self.slot->select(definingFeature = p.originalDefinition)) Replace it by: [54] This returns the slot corresponding to the supplied property. For redefined properties it will be the slot corresponding to the redefining one. Note that derived properties will only have slots if they are redefined by a non-derived one so the result may be null. ObjectInstance::propertySlot(Property p): Slot propertySlot = if self.oclIsKindOf(ClassInstance) then self.slot->any(definingFeature = p.applicableDefinition(self.oclAsType(ClassInstance).classifier)) else self.slot->any(definingFeature = p) Replace the following definition: [3] This returns the original definition of a Property through any number of redefinitions and subsetting. Property::originalDefinition(): Property post: (isEmpty(redefinedProperty) and isEmpty(subsettedProperty) and result = self) or (notEmpty(redefinedProperty) and result = redefinedProperty.originalDefinition()) or (notEmpty(subsettedProperty) and result = subsettedProperty.originalDefinition()) Replace it by: [65] This returns the property that defines the slot that will carry the data for the requesting property in an instance of the class c. Property::applicableDefinition(Class c): Property applicableDefinition = c.allSlottableProperties().any(p | p.allRedefinedProperties()->includes(self)) Replace the following definition: [4] This returns the single Property that represents the current owner of the Object based on current instance values; may be null for top level objects Object::owningProperty(): Property post: result = self.allProperties->select(op| op.opposite <> null and op.opposite.isComposite and self.get(op)<> null) Replace it by: [76] This returns the single Property with a slot that represents the current owner of the Object based on current instance values; may be null for top level objects. Object::owningProperty(): Property modeled as ClassInstance::owningProperty(): Property owningProperty = self.classifier.allSlottableProperties()->select(op | op.opposite <> null and op.opposite.isComposite and self.get(op)<> null) Add the following definition: [7] All the non-redefined properties of an object. Object::allProperties(): Set(Property) modeled as ClassInstance:: allProperties (): Set(Property) allProperties = self.classifier.allProperties() Renumber all of the remaining definitions to follow on from [8]. End of Annotations:===== MG Issue No: 16329TBD Title: No unambiguous way in MOF 2.4 to serialize UML 2.4.s StructuredActivityNode Source: Nerijus Jankevicius Summary: We just recently had discussion with Ed about an issue with Activity::node and Activity::group. Both are composite non-derived properties and it causes problems with all StructuredActivityNodes, which are ActivityNodes and ActivityGroups at the same time. MagicDraw or Eclipse implementation of UML does not allow to own the same element in two composites , even if owner element is the same. Does XMI support that? So, ExpansionRegion or any other StructuredActivityNode appears in Activity::group only. fUML spec/engine expects to find them in Activity::node , as all owned nodes should be there. Any suggestions? Don't you think we should fix that somehow? Resolution: The problem is that the combination of the UML, MOF and XMI specs taken together do not provide an adequate specification of how to serialize StructuredActivityNodes, which leads to ambiguity. Currently, the ownership of StructuredActivityNode by Activity is derived, and is a subset of Activity::node and Activity::group. This leads to the question of whether a StructuredActivityNode should be serialized as a node, as a group, or both. This has obvious impacts on model interchange, and also impacts fUML. The solution is to make the ownership of StructuredActivityNode by Activity non-derived, and then fix the MOF semantics and XMI serialization rules to ensure that a StructuredActivityNode is serialized in a structuredNode element, and not in a node or a group element. This resolution fixes the MOF part; there are two counterpart issues corresponding to the UML and XMI parts. MOF 2.4 currently states that an Element may only have a single slot that refers to its container. In the MOF semantics, it states for ClassInstance .At most one slot for an isComposite property may have a value. (This needs more work if the owner reference is not navigable).. In fact this ought to say .At most one owning slot, i.e. a slot whose property is opposite an isComposite property, may have a value.. The semantics also specifies Object::owningProperty(), unambiguously declaring that an object only has one owning slot at a time. The issue is that there is no correct specification of which property provides the owning slot where there is redefinition. In the case of StructuredActivityNode::activity, it redefines ActivityNode::activity and ActivityGroup::inactivity. MOF needs to be clear that in such a case it is the slot for the redefining property that is the actual owning slot. Armed with that information, the XMI spec can then say that the correct element to serialize is the one opposite the actual owning slot. The semantics correctly states for ObjectInstance .the stored StructuralFeatures . exclude Properties that have been redefined.. However the main problem is in the definition of Instance::propertySlot(), which is supposed to return the slot corresponding to a particular property. This in turn calls a function called originalDefinition() which incorrectly climbs up the chain of redefinitions and subsettings to find the highest property in the chain. This is wrong on two counts: firstly it is the wrong algorithm in principle, because it conflicts with .excluding properties that have been redefined., and secondly it is wrong to assume that there is only one redefinition/subsetting chain, because in general there are several. The function originalDefinition is misnamed as well as being ill-conceived. Instead, we need a function applicableDefinition that determines, for a given instance and a given property, which slot is applicable. The semantic function allSlottableProperties is incorrect; it misunderstands and reverses the role of redefinition, it confuses subsetting and redefinition, and it has an incorrect .not. in its attempt to exclude association-owned ends. The semantic function owningProperty is incorrect because it does not take into account the effect of redefinition. The semantic function allProperties is extensively used but has no definition. Note: there are many other syntactic inconsistencies in the semantic definition of MOF 2.4, such as the inconsistent use of body conditions vs postconditions, the inconsistent use of operation names vs result, and the omission of operation call brackets. This urgent resolution does not attempt to fix those issues. Revised text: In Section 15.2, under ClassInstance: Replace: 2. At most one Slot for an isComposite property may have a value. (This needs more work if the owner reference is not navigable). by: 2. At most one owning Slot, i.e. a Slot whose property is opposite an isComposite property, may have a value. In Section 15.8, Additional Operations: Replace the following definition: [1] This gives all of the properties in the namespace of the class (including inherited) that require a slot: it excludes properties owned by Associations, derived properties and those that subset or redefine another property (in which case that property will provide the slot and the redefinition will just restrict the values) Class::allSlottableProperties(): Set(Property); allSlottableProperties = member->select(p| p.oclIsKindOf(Property) and not (self.allParents() includes p.namespace) -- excludes foreign association ends not(p.isDerived) and isEmpty(p.redefinedProperty) and isEmpty(p.subsettedProperty) ) Replace it by: [1] This gives all of the owned properties of the class (including inherited) that require a slot: it excludes derived properties and those that are redefined in the same classproperties. Class::allSlottableProperties(): Set(Property); allSlottableProperties = self.allNonDerivedProperties ()->reject( p | (self.allNonDerivedProperties()->select(r | r.allRedefinedProperties()->intersection(self.allProperties())->includes(p)) [2] All the non-derived owned properties (including inherited) of a class. Class::allNonDerivedProperties(): Set(Property); allNonDerivedProperties = self.allProperties() -> select( p | not(p.isDerived)) [3 [2] All the non-redefined properties (including inherited) of a class. Class::allProperties(): Set(Property); allProperties = member->select(p | p.oclIsKindOf(Property) and p.owner = self)n | [4 n.oclIsKindOf(Property))->collect( p | p.oclAsType(Property)) [3] All of the properties directly or indirectly redefined by a property. Property::allRedefinedProperties() : Set(Property) allRedefinedProperties = if self.redefinedProperty->isEmpty then Set{} else self.redefinedProperty->union( self.redefinedProperty->collect(r | r.allRedefinedProperties())())) Replace the following definition: [2] This returns the slot corresponding to the supplied property. For redefining properties it will be the redefined one. Note that derived properties will only have slots if the redefine a non-derived one so the result may be null. Instance::propertySlot(Property p): Slot propertySlot = self.slot->select(definingFeature = p.originalDefinition)) Replace it by: [54] This returns the slot corresponding to the supplied property. For redefined properties it will be the slot corresponding to the redefining one. Note that derived properties will only have slots if they are redefined by a non-derived one so the result may be null. ObjectInstance::propertySlot(Property p): Slot propertySlot = if self.oclIsKindOf(ClassInstance) then self.slot->any(definingFeature = p.applicableDefinition(self.oclAsType(ClassInstance).classifier)) else self.slot->any(definingFeature = p) Replace the following definition: [3] This returns the original definition of a Property through any number of redefinitions and subsetting. Property::originalDefinition(): Property post: (isEmpty(redefinedProperty) and isEmpty(subsettedProperty) and result = self) or (notEmpty(redefinedProperty) and result = redefinedProperty.originalDefinition()) or (notEmpty(subsettedProperty) and result = subsettedProperty.originalDefinition()) Replace it by: [65] This returns the property that defines the slot that will carry the data for the requesting property in an instance of the class c. Property::applicableDefinition(Class c): Property applicableDefinition = c.allSlottableProperties().any(p | p.allRedefinedProperties()->includes(self)) Replace the following definition: [4] This returns the single Property that represents the current owner of the Object based on current instance values; may be null for top level objects Object::owningProperty(): Property post: result = self.allProperties->select(op| op.opposite <> null and op.opposite.isComposite and self.get(op)<> null) Replace it by: [76] This returns the single Property with a slot that represents the current owner of the Object based on current instance values; may be null for top level objects. Object::owningProperty(): Property modeled as ClassInstance::owningProperty(): Property owningProperty = self.classifier.allSlottableProperties()->select(op | op.opposite <> null and op.opposite.isComposite and self.get(op)<> null) Add the following definition: [7] All the non-redefined properties of an object. Object::allProperties(): Set(Property) modeled as ClassInstance:: allProperties (): Set(Property) allProperties = self.classifier.allProperties() Renumber all of the remaining definitions to follow on from [8].