Issue 14550: Revise fUML to be based on UML 2.3 (fuml-ftf) Source: Model Driven Solutions (Mr. Ed Seidewitz, ed-s(at)modeldriven.com) Nature: Uncategorized Issue Severity: Summary: Specification: Semantics of a Foundational Subset for Executable UML Models (ptc/2008-11-03) The fUML syntax subset and its semantics are currently based on UML 2.2. The UML 2.3 resolved a number of issues relevant to execution semantics. The fUML specification should be updated so it is based on UML 2.3 rather than UML 2.2. Relevant semantic issues include, in particular: - 6111 Reentrancy 1 - 9863 Section: Actions – Output of read actions for no values - 9870 Actions on non-unique properties with location specified - 9858 Default weight - 9873 Section: Common Behavior – isReentrant should default to true - 10045 11.3.47 on StructuralFeatureAction (and related sections on subclasses) - 11646 StructuredActivityNode - 13898 what's the difference > between weight=1 and weight=*? - 13914 Clarify that input pins do not accept more tokens than their actions can immediately consume Resolution: In addition to the issues noted above, the following UML RTF Issues resolved in UML 2.3 make abstract syntax changes within the fUML subset. While these do not result in any semantic changes, they should be made in the fUML abstract syntax to ensure that it remains a proper subset of the UML 2.3 abstract syntax. · 8682 A test case cannot be empty · 10081 Section 13.2 (body property of OpaqueBehavior) · 10515 Section 7 (property isLeaf inherited by Class) · 12558 Section 13.3 (multiplicity of Reception::signal) For the issues identified in the issue, which do have semantic effect, the following summarizes the changes needed in the fUML Execution Model. · 6111 - The resolution to this issue adds the attribute isLocallyReentrant to Action. The default is false, meaning that an action cannot fire more than once concurrently within the same activity execution. The current fUML behavior effectively presumes isLocallyReentrant=true, that is, that all actions can fire multiple times concurrently. The fUML semantics should be updated to support isLocallyReentrant=false as the default, as well as isLocallyReentrant=true. This requires adding an attribute to the ActionActivation class to record that the action is firing and checking this in the isReady operation. The check at the end of an action firing for whether an action should fire again should trigger any firing that is still pending after being delayed by a lack of local reentrancy. · 9863 - The resolution of this issue clarifies that a read structural feature action will offer a null token if it reads an empty structural feature and that a read link action will offer a null token if there are no matching links. Currently, the fUML semantics are that no offers are made from an object node that contains no tokens, including the output pins of actions. This should be changed in general so that an object node offers a null token when it fires and holds no tokens. This can be done by overriding the sendOffers operation in ObjectNodeActivation. · 9858 - This issue suggested that the default activity edge weight should be * rather than 1. However, the resolution clarified that weight=1 actually does have the behavior the issue writer desired, so the issue was closed with no change. ActivityEdge::weight is defined in the CompleteActivities package, so it is not actually included in the fUML subset. However, this issue resolution means that the current fUML semantics are, in fact, consistent with the default weight=1, rather than weight=*, as previously thought. · 9870 - The resolution of this issue changes remove structural feature value actions so that they do not have a value pin if they have a removeAt pin. This requires an update to the RemoveStructuralFeatureValueActionActivation::doAction operation. · 9873 - The resolution of this issue changes the default to true for Behavior::isReentrant. However, the fUML subset already requires this, so no change is necessary. The fUML semantics are now consistent with the UML default, however. · 10045 - The resolution of this issue clarifies that a structural feature action can be used to read and write an association end of a binary association as if it was a feature of the opposite classifier, even if the end is not actually owned by that classifier. This requires updating the doAction operations of the various structural feature action activation classes so that the actions behave like the corresponding link action if their structural feature is an association end. (Note that association ends are never classifier-owned in fUML.) · 11646 - The resolution of this issue adds structuredNodeInput and structuredNodeOutput properties to StructuredActivityNode, subsetting the input and output properties of Action. This allows structured activity nodes to properly own input and output pins. However, this actually has no direct effect on the fUML Execution Model, which already presumed that structured activity nodes could own pins, but referenced them using the inherited Action::input and output properties. · 13898 - The resolution to this issue clarifies that the weight on an activity edge only specifies the minimum number of tokens that must be accepted for tokens to flow, not the maximum number that can flow. The fUML semantics are consistent with the default of weight=1. (See also the discussion of Issue 9858 above.) · 13914 - The resolution to this issue clarifies that an input pin cannot accept more tokens than their actions can immediately consume. Currently, fUML semantics allows an input pin to accept all tokens offered to it, which then can be consumed by its action over multiple firings. This can be corrected by overriding the takeOfferedTokens operation in PinActivation to take no more tokens than the multiplicity upper bound of the pin. (Note that by doing this in PinActivation rather than InputPinActivation, an output pin of a structured activity node is also restricted to not take more tokens than its multiplicity upper bound from its incoming flows - which is correct per UML Superstructure Subclause 11.3.27.) This also requires updating the takeOfferedTokens operation on ActivityEdgeInstance to allow taking a specific number of tokens, rather than just the next group of tokens offered together. Revised Text: Text Changes In Clause 3 Normative References, replace the three UML 2.2 references with the following : UML 2.3 Beta 2 Infrastructure Specification (ptc/09-09-11) UML 2.3 Beta 2 Superstructure convenience document (ptc/09-09-09) In Subclause 6.1 Changes to Adopted OMG Specifications: · Remove the first bullet after "The following areas have been identified as semantic inconsistencies at this time." · Replace the final paragraph with: NOTE: The foundational subset is based on UML 2.3. Particular changes made in UML 2.2 and UML 2.3 over previous UML versions that are incorporated into the foundational subset include changes to decisions nodes, structural feature actions and structured activity nodes, the addition of the "isLocallyReentrant" property to actions and the addition of the start object behavior action. In Subclause 8.5.2.1 Overview, remove the last paragraph (beginning "NOTE: The UML 2 Superstructure Specification (Subclause 12.3.6) states that…"). Abstract Syntax Changes In Figure 19 and Subclause 7.2.2.2.4, add the attribute Classifier::isFinalSpecification: Boolean. In Figure 38 and Subclause 7.5.2.2.1, add the attribute Action::isLocallyReentrant = false. In Figure 42 and Subclause 7.5.3.2.20, change the multiplicity of WriteStructuralFeatureAction::value from 1 to 0..1. In Figure 35 and Subclause 7.4.3.2.1, change the multiplicity of Clause::test to 1..*. In Figure 35 and Subclause 7.4.3.2.4, change the multiplicity of LoopNode::test to 1..*. In Figure 35 and Subclause 7.4.3.2.5, add the associations StructuredActivityNode::structuredNodeInput: InputPin[0..*] {subsets Action::input} and structuredNoteOutput: OutputPin[0..*] {subsets Action::output} associations. In the normative XMI, change ConditionalNode::result from {subsets Action::output} to {redefines StructuredActivityNode::structuredNodeOutput}. In the normative XMI, change LoopNode::loopVariableInput from {subsets Action::input} to {redefines StructuredActivityNode::structuredNodeInput} and result from {subsets Action::output} to {redefines StructuredActivityNode::structuredNodeOutput}. In Figure 26 and Subclause 7.3.2.2.4, change the default value for Behavior::isReentrant to true. In the normative XMI, add {nonunique} to OpaqueBehavior::body. In Figure 27 and in Subclause 7.3.3.2.3, change the multiplicity of Reception::signal from 0..1 to 1. Remove the constraint "A reception must have an associated signal." Change "a_signal_but_no_method" to "no_method", with the OCL "self.method -> isEmpty()". Execution Model Changes In Figure 68: · In ActivityEdgeInstance: o Rename the operation "countOfferedTokens" to "countOfferedValues". o Add the operation "takeOfferedTokens(maxCount: Integer): Token[*]". o Remove the operation "getNextOffer". · In ObjectNodeActivation: o Add the operation "sendOffers(tokens: Token[*])" o Add the operation "countOfferedValues". · In Offer: o Rename the operation "countOfferedTokens" to "countOfferedValues". o Add the operation "removeOfferedValues(count: Integer)". o Add the operation "hasTokens(): Boolean". In Figure 73: · Remove the operation "countUnofferedTokens" from ExpansionNodeActivation. In Figure 74: · Add the attribute "firing: Boolean" and the operation "isFiring():Boolean" to ActionActivation. · Remove the operation "countUnofferedTokens" from InputPinActivation. · Add the operation "takeOfferedTokens(): Tokens[*]" to PinActivation. In Figure 76: · Add the operations "getAssociation(feature: StructuralFeature): Association[0..1]", "getMatchingLinks(association: Association, end: StructuralFeature, oppositeValue: Value): Link[*]" and "getOppositeEnd(association: Association, end: StructuralFeature): Property" to StructuralFeatureActionActivation. In Subclause 8.3.2.2.11 Link: · In the operation destroy, after the initial comment add: // Shift the positions of the feature values of any remaining links in // the extent of the same association, for ends that are ordered. PropertyList ends = this.type.memberEnd; ExtensionalValueList extent = this.locus.getExtent(this.type); for (int i = 0; i < extent.size(); i++) { ExtensionalValue otherLink = extent.getValue(i); for (int j=0; j < ends.size(); j++) { Property end = ends.getValue(j); if (end.multiplicityElement.isOrdered) { FeatureValue featureValue = otherLink.getFeatureValue(end); if (this.getFeatureValue(end).position < featureValue.position) { featureValue.position = featureValue.position - 1; } } } } In Subclause 8.5.2.2.1 ActivityEdgeInstance: · Rename the operation "countOfferedTokens" to "countOfferedValues" and replace the code with: // Return the number of values being offered in object tokens. int count = 0; OfferList offers = this.offers; for (int i = 0; i < offers.size(); i++) { count = count + offers.getValue(i).countOfferedValues(); } return count; · Replace the code for the operation takeOfferedTokens with: // Take all the offered tokens and return them. TokenList tokens = new TokenList(); while (this.offers.size() > 0) { TokenList offeredTokens = this.offers.getValue(0).getOfferedTokens(); for (int i = 0; i < offeredTokens.size(); i++) { tokens.addValue(offeredTokens.getValue(i)); } this.offers.removeValue(0); } return tokens; · Add the operation takeOfferedTokens(maxCount: Integer): Token[*] with the code: // Take all the offered tokens, up to the given maximum count of non-null object tokens, and return them. TokenList tokens = new TokenList(); int remainingCount = maxCount; while (this.offers.size() > 0 & remainingCount > 0) { Offer offer = this.offer.getValue(0); TokenList offeredTokens = offer.getOfferedTokens(); int count = offer.countOfferedValues(); if (count <= remainingCount) { for (int i = 0; i < offeredTokens.size(); i++) { tokens.addValue(offeredTokens.getValue(i)); } remainingCount = remainingCount - count; this.offers.removeValue(0); } else { for (int i = 0; i < remainingCount; i++) { Token token = offeredTokens.getValue(i); if (token.getValue() != null) { tokens.addValue(token); } } remainingCount = 0; } } return tokens; · Replace the code for getOfferedTokens with: // Get the offered tokens (after which the tokens will still be offered). TokenList tokens = new TokenList(); OfferList offers = this.offers; for (int i = 0; i < offers.size(); i++) { TokenList offeredTokens = offers.getValue(i).getOfferedTokens(); for (int j = 0; j < offeredTokens.size(); j++) { tokens.addValue(offeredTokens.getValue(j)); } } return tokens; · Replace the code for hasOffer with: // Return true if there are any pending offers. boolean hasTokens = false; int i = 1; while (!hasTokens & i <= this.offers.size()) { hasTokens = this.offers.getValue(i-1).hasTokens(); i = i + 1; } return hasTokens; · Remove the operation getNextOffer. In Subclause 8.5.2.2.15 ObjectNodeActivation: · Add the operation sendOffers(tokens: Token[*]) with the code: // If the set of tokens to be sent is empty, then offer a null token instead. // Otherwise, offer the given tokens as usual. if (tokens.size() == 0) { tokens.addValue(new ObjectToken()); } super.sendOffers(tokens); · In the operation addToken, replace the lines // Transfer the given token to be held by this node only if it is a control token. // If it is a control token, consume it without holding it. if (token.getValue().isControl()) { with: // Transfer the given token to be held by this node only if it is a non-null object token. // If it is a control token or a null token, consume it without holding it. if (token.getValue() == null) { · Add the operation countTotalValues with the code: // Count the total number of non-null object tokens being offered to this node activation. int totalValueCount = 0; int i = 1; while (i <= this.incomingEdges.size()) { totalValueCount = totalValueCount + this.incomingEdges.getValue(i-1).countOfferedValues(); i = i + 1; } return totalValueCount; In Subclause 8.5.2.2.17 Offer: · Add the description: An offer is a group of tokens offered together. The grouping of offered tokens into offers usually does not matter for how the tokens may be accepted. However, control and object tokens may become grouped together in the same offer due to a join node that has both incoming control and object flows. In this case, the control tokens are implicitly accepted once all the object tokens in the same offer have been accepted. · Rename the operation "countOfferedTokens" to "countOfferedValues" and replace its code with: // Return the number of values being offered on object tokens. this.removeWithdrawnTokens(); int count = 0; for (int i = 0; i < this.offeredTokens.size(); i++) { if (this.offeredTokens.getValue(i).getValue() != null) { count = count + 1; } } return count; · In the operation getOfferedTokens, change the comment to // Get the offered tokens, removing any that have been withdrawn. and after that add: this.removeWithdrawnTokens(); · Add the operation removeOfferedValues(count: Integer), with the code: // Remove the given number of non-null object tokens from those in this offer. int n = count; int i = 1; while (n > 0) { if (this.offeredTokens.getValue(i-1).getValue() != null) { this.offeredTokens.removeValue(i-1); } else { i = i + 1; } n = n - 1; } · Add the operation "hasTokens(): Boolean", with the code: // Check whether this offer has any tokens that have not been withdrawn. this.removeWithdrawnTokens(); return this.offeredTokens.size() > 0; In Subclause 8.5.4.2.2 ExpansionNodeActivation: · Remove the operation countUnofferedTokens. In Subclause 8.5.4.2.3 ExpansionRegionActivation: · In the operations isReady and numberOfValues, replace occurrences of "countUnofferedTokens" with "countOfferedValues". In Subclause 8.6.2.2.1 Action Activation: · Under Attributes, add: firing: Boolean Whether this action activation is already firing. This attribute is only used if the action for this action activation has isLocallyReentrant = false (the default). If isLocallyReentrant=true, then firing always just remains false. · Add the operation isFiring():Boolean, with the code: // Indicate whether this action activation is currently firing or not. return firing; · In the run operation, add the following line at the end: this.firing = false; · In the isReady operation, change the initial comment line to // In addition to the default condition, check that, if the action has isLocallyReentrant=false, then the activation is not currently firing, // and that the sources of all incoming edges (control flows) have offers and all input pin activations are ready. Change the line: boolean ready = super.isReady(); to: boolean ready = super.isReady() & (((Action)this.node).isLocallyReentrant | !this.isFiring()); · Replace the code for the fire operation with: // Do the main action behavior then concurrently fire all output pin activations // and offer a single control token. Then activate the action again, // if there are still enough tokens available to the input pins // and the action has no incoming control flows. Debug.println("[fire] Action " + this.node.name + "..."); Action action = (Action)this.node; InputPinList inputPins = action.input; boolean fireAgain = false; do { this.doAction(); this.sendOffers(); Debug.println("[fire] Checking if " + this.node.name + " should fire again..."); fireAgain = inputPins.size() > 0 & action.incoming.size() == 0; int j = 1; while (fireAgain & j <= inputPins.size()) { PinActivation inputPinActivation = this.getPinActivation(inputPins.getValue(j-1)); fireAgain = inputPinActivation.isReady(); j = j + 1; } } while (fireAgain); // Note: If the action is not locally reentrant, then this is the only firing. // If the action is locally reentrant, then firing always remains false anyway. // In either case, isolation is not required. this.firing = false; · At the beginning of the takeOfferedTokens operation, add: // Note: This is included here to happen in the same isolation scope as the isReady test. this.firing = !((Action)this.node).isLocallyReentrant; In Subclause 8.6.2.2.5 InputPinActivation: · Replace the code of operation isReady with: // Return true if the total number of values already being offered by this pin plus those being offered // by the sources of incoming edges is at least equal to the minimum multiplicity of the pin. boolean ready = super.isReady(); if (ready) { int totalValueCount = this.countUnofferedTokens() + this.countOfferedValues(); int minimum = ((Pin)this.node).multiplicityElement.lower; ready = totalValueCount >= minimum; } return ready; · Remove operation countUnofferedTokens. In Subclause 8.6.2.2.8 PinActivation: · Add the operation takeOfferedTokens(): Tokens[*] with the code: // Take only a number of tokens only up to the limit allowed by // the multiplicity upper bound of the pin for this activation. int count = this.countUnofferedTokens(); int upper = -1; // Note: A pin activation used in an expansion activation group // will have this.node == null. if (this.node != null) { int upper = ((Pin)(this.node)).multiplicityElement.upper.naturalValue; } TokenList tokens = new TokenList(); // Note: upper < 0 indicates an unbounded upper multiplicity. if (upper < 0 | count < upper) { ActivityEdgeInstance incomingEdges = this.incomingEdges; for (int i=0; i<incomingEdges.size(); i++) { ActivityEdgeInstance edge = incomingEdges.getValue(i); int incomingCount = edge.countOfferedValues(); TokenList incomingTokens = new TokenList(); if (upper < 0 | incomingCount < upper - count) { incomingTokens = edge.takeOfferedTokens(); count = count + incomingCount; } else if (count < upper) { incomingTokens = edge.takeOfferedTokens(upper-count); count = upper; } for (int j = 0; j < incomingTokens.size(); j++) { Token token = incomingTokens.getValue(j); tokens.addValue(token); } } } return tokens; In Subclause 8.6.3.2.1 AddStructuralFeatureValueAction: · Replace the code for doAction with: // Get the values of the object and value input pins. // If the given feature is an association end, then create a link between the object and value inputs. // Otherwise, if the object input is a structural value, then add a value to the values for the feature. // If isReplaceAll is true, first remove all current matching links or feature values. // If isReplaceAll is false and there is an insertAt pin, insert the value at the appropriate position. AddStructuralFeatureValueAction action = (AddStructuralFeatureValueAction)(this.node); StructuralFeature feature = action.structuralFeature; Association association = this.getAssociation(feature); Value value = this.takeTokens(action.object).getValue(0); ValueList inputValues = this.takeTokens(action.value); // NOTE: Multiplicity of the value input pin is required to be 1..1. Value inputValue = inputValues.getValue(0); int insertAt = 0; if (action.insertAt != null) { insertAt = ((UnlimitedNaturalValue)this.takeTokens(action.insertAt).getValue(0)).value.naturalValue; } if (association != null) { LinkList links = this.getMatchingLinks(association, feature, value); Property oppositeEnd = this.getOppositeEnd(association, feature); int position = 0; if (oppositeEnd.multiplicityElement.isOrdered) { position = -1; } if (action.isReplaceAll) { for (int i = 0; i < links.size(); i++) { Link link = links.getValue(i); link.destroy(); } } else if (feature.multiplicityElement.isUnique) { for (int i=0; i < links.size(); i++) { Link link = links.getValue(i); FeatureValue featureValue = link.getFeatureValue(feature); if (featureValue.values.getValue(0).equals(inputValue)) { position = link.getFeatureValue(oppositeEnd).position; if (insertAt > 0 & featureValue.position < insertAt) { insertAt = insertAt - 1; } link.destroy(); } } } Link newLink = new Link(); newLink.type = association; // This necessary when setting a feature value with an insertAt position newLink.locus = this.getExecutionLocus(); newLink.setFeatureValue(feature, inputValues, insertAt); ValueList oppositeValues = new ValueList(); oppositeValues.addValue(value); newLink.setFeatureValue(oppositeEnd, oppositeValues, position); newLink.locus.add(newLink); } else if (value instanceof StructuredValue) { StructuredValue structuredValue = (StructuredValue)value; if (action.isReplaceAll) { structuredValue.setFeatureValue(feature, inputValues, 0); } else { FeatureValue featureValue = structuredValue.getFeatureValue(feature); if (featureValue.values.size() > 0 & insertAt == 0 ) { // *** If there is no insertAt pin, then the structural feature must be unordered, and the insertion position is immaterial. *** insertAt = ((ChoiceStrategy)this.getExecutionLocus().factory.getStrategy("choice")).choose(featureValue.values.size()); } if (feature.multiplicityElement.isUnique) { // Remove any existing value that duplicates the input value int j = position(inputValue, featureValue.values, 1); if (j > 0) { featureValue.values.remove(j-1); if (insertAt > 0 & j < insertAt) { insertAt = insertAt - 1; } } } if (insertAt <= 0) { // Note: insertAt = -1 indicates an unlimited value of "*" featureValue.values.addValue(inputValue); } else { featureValue.values.addValue(insertAt - 1, inputValue); } } } if (action.result != null) { this.putToken(action.result, value); } In Subclause 8.6.3.2.3 ClearStructuralFeatureActionActivation: · Replace the code for doAction with: // Get the value of the object input pin. // If the given feature is an association end, then // destroy all links that have the object input on the opposite end. // Otherwise, if the object input is a structured value, then // set the appropriate feature of the input value to be empty. ClearStructuralFeatureAction action = (ClearStructuralFeatureAction)(this.node); StructuralFeature feature = action.structuralFeature; Association association = this.getAssociation(feature); Value value = this.takeTokens(action.object).getValue(0); if (association != null) { LinkList links = this.getMatchingLinks(association, feature, value); for (int i = 0; i < links.size(); i++) { link.destroy(); } } else if (value instanceof StructuredValue) { ((StructuredValue)value).setFeatureValue(action.structuralFeature, new ValueList(), 0); } if (action.result != null) { this.putToken(action.result, value); } In Subclause 8.6.3.2.4 CreateLinkActionActivation: · Replace the code for doAction with: // Get the extent at the current execution locus of the association for which a link is being created. // Destroy all links that have a value for any end for which isReplaceAll is true. // Create a new link for the association, at the current locus, with the given end data values, // inserted at the given insertAt position (for ordered ends). CreateLinkAction action = (CreateLinkAction)(this.node); LinkEndCreationDataList endDataList = action.endData; Association linkAssociation = this.getAssociation(); ExtensionalValueList extent = this.getExecutionLocus().getExtent(linkAssociation); Link oldLink = null; for (int i = 0; i < extent.size(); i++) { ExtensionalValue value = extent.getValue(i); Link link = (Link)value; boolean nomatch = true; int j = 1; while (noMatch & j <= endDataList.size()) { LinkEndCreationData endData = endDataList.getValue(j-1); if (endData.isReplaceAll & this.endMatchesEndData(link, endData)) { oldLink = link; link.destroy(); noMatch = false; } j = j + 1; } } Link newLink = new Link(); newLink.type = linkAssociation; // This necessary when setting a feature value with an insertAt position newLink.locus = this.getExecutionLocus(); for (int i = 0; i < endDataList.size(); i++) { LinkEndCreationData endData = endDataList.getValue(i); int insertAt; if (endData.insertAt == null) { insertAt = 0; } else { insertAt = ((UnlimitedNaturalValue)(this.takeTokens(endData.insertAt).getValue(0))).value.naturalValue; if (oldLink != null) { if (oldLink.getFeatureValue(endData.end).position < insertAt) { insertAt = insertAt - 1; } } } newLink.setFeatureValue(endData.end, this.takeTokens(endData.value), insertAt); } this.getExecutionLocus().add(newLink); In Subclause 8.6.3.2.9 ReadLinkActionActivation: · In the operation doAction, replace the first for loop with: FeatureValueList featureValues = new FeatureValueList(); for (int j = 0; j < extent.size(); j++) { ExtensionalValue value = extent.getValue(j); Link link = (Link)value; if (this.linkMatchesEndData(link, endDataList)) { FeatureValue featureValue = link.getFeatureValue(openEnd.end); if (!openEnd.end.multiplicityElement.isOrdered | featureValues.size() == 0) { featureValues.addValue(featureValue); } else { int n = featureValue.position; boolean continueSearching = true; int k = 0; while (continueSearching & k < featureValues.size()) { k = k + 1; continueSearching = featureValues.getValue(k-1).position < n; } featureValues.addValue(k-1, featureValue); } } } for (int j = 0; j < featureValues.size(); j++) { FeatureValue featureValue = featureValues.getValue(j); this.putToken(action.result, featureValue.values.getValue(0)); } In Subclause 8.6.3.2.11 ReadStructuralFeatureActionActivation: · Replace the code for the operation doAction with: // Get the value of the object input pin. // If the given feature is an association end, then get all values of the that end // for which the opposite end has the object input value and place them on the result pin. // Otherwise, if the object input value is a structural value, then get the values // of the appropriate feature of the input value and place them on the result output pin. ReadStructuralFeatureAction action = (ReadStructuralFeatureAction)(this.node); StructuralFeature feature = action.structuralFeature; Association association = this.getAssociation(feature); Value value = this.takeTokens(action.object).getValue(0); ValueList resultValues = new ValueList(); if (association != null) { LinkList links = this.getMatchingLinks(association, feature, value); for (int i = 0; i < links.size(); i++) { Link link = links.getValue(i); resultValues.addValue(link.getFeatureValue(feature).values.getValue(0)); } } else if (value instanceof StructuredValue) { resultValues = ((StructuredValue)value).getFeatureValue(feature).values; } this.putTokens(action.result, resultValues); In Subclause 8.6.3.2.12 RemoveStructuralFeatureValueActionActivation: · Replace the code for the operation doAction with: // Get the values of the object and value input pins. // If the given feature is an association end, then destroy any matching links. // Otherwise, if the object input is a structural value, remove values from the given feature. // If isRemoveDuplicates is true, then destroy all current matching links or remove all values equal to the input value. // If isRemoveDuplicates is false and there is no removeAt input pin, remove any one feature value equal to the input value (if there are any that are equal). // If isRemoveDuplicates is false, and there is a removeAt input pin remove the feature value at that position. RemoveStructuralFeatureValueAction action = (RemoveStructuralFeatureValueAction)(this.node); StructuralFeature feature = action.structuralFeature; Association association = this.getAssociation(feature); Value value = this.takeTokens(action.object).getValue(0); Value inputValue = null; if (action.value != null) { // NOTE: Multiplicity of the value input pin is required to be 1..1. inputValue = this.takeTokens(action.value).getValue(0); } int removeAt = 0; if (action.removeAt != null) { removeAt = ((UnlimitedNaturalValue)this.takeTokens(action.removeAt).getValue(0)).value.naturalValue; } if (association != null) { LinkList links = this.getMatchingLinks(association, feature, value); if (action.isRemoveDuplicates) { for (int i = 0; i < links.size(); i++) { Link link = links.getValue(i); link.destroy(); } } else if (action.removeAt == null) { // *** If there is more than one matching link, non-deterministically choose one. *** if (links.size() > 0) { int i = ((ChoiceStrategy)this.getExecutionLocus().factory.getStrategy("choice")).choose(links.size()); links.getValue(i-1).destroy(); } } else { boolean notFound = true; int i = 1; while (notFound & i < links.size()) { Link link = links.getValue(i-1); if (link.getFeatureValue(feature).position == removeAt) { notFound = false; link.destroy(); } } } } else if (value instanceof StructuredValue) { FeatureValue featureValue = ((StructuredValue)value).getFeatureValue(action.structuralFeature); if (action.isRemoveDuplicates) { int j = this.position(inputValue, featureValue.values, 1); while (j > 0) { featureValue.values.remove(j-1); j = this.position(inputValue, featureValue.values, j); } } else if (action.removeAt == null) { intList positions = new intList(); int j = this.position(inputValue, featureValue.values, 1); while (j > 0) { positions.addValue(j); j = this.position(inputValue, featureValue.values, j); } if (positions.size()>0) { // *** Nondeterministically choose which value to remove. *** int k = ((ChoiceStrategy)this.getExecutionLocus().factory.getStrategy("choice")).choose(positions.size()); featureValue.values.remove(positions.getValue(k-1) - 1); } } else { if (featureValue.values.size()<= removeAt) { featureValue.values.remove(removeAt-1); } } } if (action.result != null) { this.putToken(action.result, value); } In Subclause 8.6.3.2.13 StructuralFeatureActionActivation: · Add the operation getAssociation(feature: StructuralFeature): Association[0..1] with the code: // If the structural feature for the action of this activation is an association end, // then get the associated association. Association association = null; if (feature instanceof Property) { association = ((Property)feature).association; } return association; · Add the operation getMatchingLinks(association: Association, end: StructuralFeature, oppositeValue: Value): Link[*] with the code: // Get the links of the given binary association whose end opposite // to the given end has the given value Property oppositeEnd = this.getOppositeEnd(association, end); ExtensionalValueList extent = this.getExecutionLocus().getExtent(association); LinkList links = new LinkList(); for (int i = 0; i<extent.size(); i++) { ExtensionalValue link = extent.getValue(i); if (link.getFeatureValue(oppositeEnd).values.getValue(0).equals(oppositeValue)) { if (!end.multiplicityElement.isOrdered | links.size() == 0) { links.addValue((Link)link); } else { int n = link.getFeatureValue(end).position; boolean continueSearching = true; int j = 0; while (continueSearching & j < links.size()) { j = j + 1; continueSearching = links.getValue(j-1).getFeatureValue(end).position < n; } links.addValue(j-1, (Link)link); } } } return links; · Add the operation getOppositeEnd(association: Association, end: StructuralFeature): Property with the code: // Get the end of a binary association opposite to the given end. Property oppositeEnd = association.memberEnd.getValue(0); if (oppositeEnd == end) { oppositeEnd = association.memberEnd.getValue(1); } return oppositeEnd; Actions taken: December 18, 2008: received issue October 8, 2009: received issue July 23, 2010: closed issue July 23, 2010: closed issue Discussion: End of Annotations:===== ubject: Revise fUML to be based on UML 2.3 Date: Thu, 8 Oct 2009 22:07:56 -0400 X-MS-Has-Attach: X-MS-TNEF-Correlator: Thread-Topic: Revise fUML to be based on UML 2.3 thread-index: AcpIhVE+UO3VaVZMQuGaUtDkO3hD7Q== From: "Ed Seidewitz" To: Specification: Semantics of a Foundational Subset for Executable UML Models (ptc/2008-11-03) The fUML syntax subset and its semantics are currently based on UML 2.2. The UML 2.3 resolved a number of issues relevant to execution semantics. The fUML specification should be updated so it is based on UML 2.3 rather than UML 2.2. Relevant semantic issues include, in particular: - 6111 Reentrancy 1 - 9863 Section: Actions . Output of read actions for no values - 9870 Actions on non-unique properties with location specified - 9858 Default weight - 9873 Section: Common Behavior . isReentrant should default to true - 10045 11.3.47 on StructuralFeatureAction (and related sections on subclasses) - 11646 StructuredActivityNode - 13898 what's the difference > between weight=1 and weight=*? - 13914 Clarify that input pins do not accept more tokens than their actions can immediately consume