Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "VIATRA2/Activity Diagrams to Petri Nets"

(Implementation using GT rules)
(Implementation using GT rules)
Line 65: Line 65:
 
=== Control Flow ===
 
=== Control Flow ===
 
[[Image:VIATRA2_Examples_A2PN_GP_ControlFlow.png|thumb|120 px| Illustration of a graph pattern, consisting of a control flow element and the place corresponding to it.]]
 
[[Image:VIATRA2_Examples_A2PN_GP_ControlFlow.png|thumb|120 px| Illustration of a graph pattern, consisting of a control flow element and the place corresponding to it.]]
Specifying graph transformation rules requires specifying patterns. Our first pattern expresses a control flow element that has already been mapped into the Petri Net domain. Thus the pattern consists of a control flow element from a UML Activity, the Petri Net place corresponding to it, and a special '''traceability link''' between them. Such links will be useful later as they help to remember which Petri Net node corresponds to which UML model element, preserving a mapping between source and target models, thereby achieving [[VIATRA2/UseCases/TraceabilityModels|traceability]].  
+
Specifying graph transformation rules requires specifying graph patterns. Our first pattern expresses a control flow element that has already been mapped into the Petri Net domain. Thus the pattern consists of a control flow element from a UML Activity, the Petri Net place corresponding to it, and a special '''traceability link''' between them. Such links will be useful later as they help to remember which Petri Net node corresponds to which UML model element, preserving a mapping between source and target models, thereby achieving [[VIATRA2/UseCases/TraceabilityModels|traceability]].  
  
 
The visual illustration of this pattern looks admittedly strange, as the traceability link connects to the control flow element, which is an edge. It is good to remember that for this and similar purposes, [[VIATRA2/Model_Space|VIATRA2 models]] tolerate edges (relations) connecting to other edges. Furthermore in this case, the control flow element is actually a node in the abstract UML model, despite being denoted by an edge on the Activity Diagram. It is useful to keep in mind that transformations always operate on the abstract syntax, from which the concrete syntax may differ significantly. As a further deviation from the visual illustration of this pattern, we actually constructed the pattern with an additional node for the Petri Net itself, and an edge that identifies the place as belonging to this net. While not apparent right now, this addition will be useful later.
 
The visual illustration of this pattern looks admittedly strange, as the traceability link connects to the control flow element, which is an edge. It is good to remember that for this and similar purposes, [[VIATRA2/Model_Space|VIATRA2 models]] tolerate edges (relations) connecting to other edges. Furthermore in this case, the control flow element is actually a node in the abstract UML model, despite being denoted by an edge on the Activity Diagram. It is useful to keep in mind that transformations always operate on the abstract syntax, from which the concrete syntax may differ significantly. As a further deviation from the visual illustration of this pattern, we actually constructed the pattern with an additional node for the Petri Net itself, and an edge that identifies the place as belonging to this net. While not apparent right now, this addition will be useful later.
Line 79: Line 79:
 
The pattern imposes three constraints on these variables. The first one identifies the variable <code>ControlFlow</code> to be an entity of the type also called <code>ControlFlow</code>. Normally, only variable names are capitalized. As the name of the type starts with a capital letter, it has to be enclosed in single apostrophes to mark it as the name of a (meta)model element, as opposed to the name of a variable. The second constraint identifies an "<code>activityEdgeMapping</code>" between the activity edge (<code>ControlFlow</code>) and its image in the target model (<code>PetriPlace</code>); this is the part of the pattern that finds the traceability link. Finally, asserting "<code>placeOfNet</code>" ensures that <code>PetriPlace</code> is a place, <code>PetriNet</code> is a net, and the former is contained in the latter.  
 
The pattern imposes three constraints on these variables. The first one identifies the variable <code>ControlFlow</code> to be an entity of the type also called <code>ControlFlow</code>. Normally, only variable names are capitalized. As the name of the type starts with a capital letter, it has to be enclosed in single apostrophes to mark it as the name of a (meta)model element, as opposed to the name of a variable. The second constraint identifies an "<code>activityEdgeMapping</code>" between the activity edge (<code>ControlFlow</code>) and its image in the target model (<code>PetriPlace</code>); this is the part of the pattern that finds the traceability link. Finally, asserting "<code>placeOfNet</code>" ensures that <code>PetriPlace</code> is a place, <code>PetriNet</code> is a net, and the former is contained in the latter.  
  
How does this single line assert all these? Actually, "<code>placeOfNet</code>" is not an edge (relation) of the model (and neither is <code>activityEdgeMapping</code>). They are graph patterns themselves, and the '''find''' keyword indicates '''pattern composition''' (or pattern invocation), which basically means reusing a predefined pattern as part of a different pattern. This utility pattern is defined this way:
+
How does this single line assert all these? Actually, "<code>placeOfNet</code>" is not an edge (relation) of the model (and neither is "<code>activityEdgeMapping</code>"). They are graph patterns themselves, and the '''find''' keyword indicates '''pattern composition''' (or pattern invocation), which basically means reusing a predefined pattern as part of a different pattern. This utility pattern is defined this way:
 
+
  '''pattern''' placeOfNet(PetriPlace, PetriNet) = {
 
+
    net(PetriNet);
 +
    place(PetriPlace);
 +
    net.places(Places, PetriNet, PetriPlace);
 +
  }
 +
The first constraint asserts that variable <code>PetriNet</code> is an entity of type <code>net</code>. The second constraint declares the variable <code>PetriPlace</code> is an entity of type <code>place</code>. The final constraint assert that the variable <code>Places</code> is a relation (note the ternary structure) of the type <code>net.places</code>, that connects from <code>PetriNet</code> to <code>PetriPlace</code>. This simple pattern thus matches Petri places belonging to Petri nets. Notice the absence of pattern variable <code>Places</code> from the list of '''parameters''' of the pattern: it is deliberately hidden from the outside.
  
 
[[Image:VIATRA2_Examples_A2PN_RuleControlFlow.png|thumb|350 px|Illustration of a GT Rule for mapping Control Flow.]]
 
[[Image:VIATRA2_Examples_A2PN_RuleControlFlow.png|thumb|350 px|Illustration of a GT Rule for mapping Control Flow.]]
Now, we need to create Graph Transformation (GT) rules that map a control flow edge into a Petri place. This will be our first GT rule, and luckily it is a quite simple one.
+
Now, we can finally create Graph Transformation (GT) rule that map a control flow edge into a Petri place. This will be our first GT rule, and luckily it is a quite simple one.
 
+
The LHS would be a pattern that recognizes a control flow edge. The RHS contains the same activity edge, a place, and a traceability link between them. By the [[VIATRA2/GettingStarted/Creating_Transformations#Language_features:_GTASM|definition of GT rules]], the rule will be applicable on each control flow, and the result of the application would be a new Petri place, with a traceability link established between them.
+
 
+
It is possible for the control structure of the transformation program to guarantee that the GT rule is applied on each control flow element exactly once. Yet, it is often a good idea to formulate GT rules in such a way that it is immediately apparent that they cannot be applied on the same spot repeatedly. Here we achieve this by a so-called '''Negative Application Condition''' (NAC). A NAC is an extra constraint attached on the LHS pattern that, when satisfied, will invalidate a match of the pattern. NACs can also be described as graph patterns. In our case, the NAC pattern consists of the same control flow element already having a traceability link. Since it is a negative condition, the LHS will only match control flow elements that have not already been mapped into.
+
 
+
 
+
 
+
  
 +
The LHS would be a trivial pattern that recognizes a control flow edge. The RHS is the graph pattern <code>mappedControlFlow</code> defined above, so it contains an activity edge (actually, ''the same'' activity edge), a place, and a traceability link between them. By the [[VIATRA2/GettingStarted/Creating_Transformations#Language_features:_GTASM|definition of GT rules]], the rule will be applicable on each control flow, and the result of the application would be a new Petri place, with a traceability link established between them.
  
 +
It is possible for the control structure of the transformation program to guarantee that the GT rule is applied on each control flow element exactly once. Yet, it is often a good idea to formulate GT rules in such a way that it is immediately apparent that they cannot be applied on the same spot repeatedly. Here we achieve this by a so-called '''Negative Application Condition''' (NAC). A NAC is an extra constraint attached on the LHS pattern that, when satisfied, will invalidate a match of the pattern. NACs can also be described as graph patterns. In our case, the NAC pattern consists of the same control flow element already having a traceability link. Since it is a negative condition, the LHS will only match control flow elements that have not already been mapped into the Petri Net domain. In various graph pattern figures, NACs are indicated by red crosses, by entirely red visual elements, or by red boxes enclosing the pattern with NAC or NEG written on them. In the VTCL textual syntax, they are indicated by the keyword '''neg''', followed by the invocation of the pattern constituting the NAC:
 +
 
   '''gtrule''' transformControlFlow(out ControlFlow, in PetriNet) = {
 
   '''gtrule''' transformControlFlow(out ControlFlow, in PetriNet) = {
 
     '''precondition pattern''' unmappedControlFlow(ControlFlow) = {
 
     '''precondition pattern''' unmappedControlFlow(ControlFlow) = {
Line 108: Line 108:
 
     }
 
     }
 
   }
 
   }
 +
 +
Here we can identify the simple LHS (precondition) pattern consisting of a control flow element and a NAC; our familiar RHS (postcondition) pattern; and finally an '''action sequence''' that is responsible for naming the place the same way as the control flow element was named (and [[VIATRA2/UseCases/TransformationBestPractices#Naming_elements|giving descriptive names to elements is important]]). This name copying is defined in an imperative ASM rule called <code>copyName</code>, that is simply invoked here. It is worth pointing out that the LHS and RHS share the <code>ControlFlow</code> variable, therefore it will mean the same model element in both cases. Also, the action sequence has access to the value of <code>PetriPlace</code>, which is the place created by the RHS (or more precisely, by applying the declarative part of the rule).
 +
 +
The parameters of the GT rule are the variables <code>ControlFlow</code> and <code>PetriNet</code>. The latter is an '''input parameter''': it is meant to be explicitly specified before applying the GT rule. This is important: we do not want to create a new Petri Net with each rule application (even though it it in the RHS but not the LHS), but use the same target model as the container of results everywhere. The control structure of the transformation will have to provide the identity of the target Petri Net when invoking this GT rule. The <code>ControlFlow</code> variable, on the other hand, is an '''output parameter'''; its value is meant to be determined in the GT rule, by the pattern matching process of the LHS, and propagated to the outside. Parameters can also be specified with the '''inout''' keyword, indicating that they can either be fixed or left for pattern matching to identify. Variables such as <code>PetriPlace</code>, however, are meant to be created by within the rule application, therefore if they appear as a parameter, it only makes sense to mark them as output parameters.
  
 
== Exercise: decision and merge ==
 
== Exercise: decision and merge ==

Revision as of 15:36, 19 March 2010

Aim

This tutorial example presents a transformation from a core subset of UML2 Activities to the domain of Petri-Nets (also known as Place/Transition Nets). This simple transformation imitates a set of complex, real-world model transformations that map high-level engineering models such as BPEL into semantical domains (i.e. mathematical formalisms) such as transition systems, for formal verification and analysis by tools such as SAL.

The example is meant to be the first one to read after completing the Hello World tutorial. As an important point, the tutorial includes an exercise for readers, to help them learn transformation development.

How to obtain

TODO

Background

VIATRA2

The tutorial assumes that the readers have already familiarized themselves with the basic features of the user interface of VIATRA2, the notion of model transformation and the basic concepts of the formalism GTASM. The Getting Started block is recommended reading before starting this tutorial. In particular, the Using Transformations page introduces how to load and use this transformation.

Source domain: UML Activities

Core set of UML2 Activity Diagram elements

UML Activities form a standard workflow description language, with countless modeling tools and diagram editors available. This tutorial will use the core elements of UML Activity modeling.

An Activity is the model of the process, represented in an Activity Diagram (AD) by a large box containing nodes and edges. An activity consists of several action nodes (also called executable nodes, more precisely opaque actions), denoted by smaller rounded blocks; each represent some action that is performed for some time during this process. We will only be interested in the most common kind of activity edges: control flow edges connect actions to define their sequence of execution. This sequence starts at the initial node, and terminates at the flow final node. These two are not actual actions, but special control nodes.

Further control nodes include the decision node, that represents a branching into two or more possible paths of execution according to some criterion (the criterion itself is not represented in our subset of UML Activities). These alternative paths meet at control nodes called merge nodes, from which point they proceed with the same sequence. A similar pair of control nodes is fork and join, which denote respectively a point where the execution is split into several concurrent threads, and a point where these threads of events catch up with each other to proceed as a single sequence.

Target domain: Petri Nets

Example Petri Net

Petri Nets (PN) are a mathematical formalism for specifying the behaviour of discrete systems. This formalism excels at modeling concurrency and synchronization, resource constraints and loosely dependent subsystems. There are efficient analysis (model checking) tools for Petri Nets, capable of e.g. finding deadlocks, deciding the reachability of states with given properties, proving the conservation of resources or the cyclic nature of a sequence of events.

In the core subset of the formalism, a PN is a directed bipartite graph. One partition of nodes are called places, and represented by circles in diagrams. The other type of nodes is the transition, commonly represented by an elongated rectangle. Edges can flow either from a transition to a place, or from a place to a transition, and will be called arcs to distinguish from UML Activity Edges.

A state of the PN is defined by a marking, which assigns a natural number (0, 1, or more) to each place. This number is visualized as the number of tokens contained in the place, indicated by small dots in the circle. The marking of the initial state is an important part of the definition of the Petri Net.

A transition is said to be enabled or fireable in a state if each of its incoming arcs come from marked places (i.e. containing at least one token). A state change of the Petri Net is the firing of an enabled transition; this consists of removing a token from each incoming place, and adding a token to each place at the end of outgoing arcs from the transition.

Transformation design: mapping Activities into Petri Nets

Action and control flow

We interpret UML Activity semantics in a way such that executing and action is not instantaneous, but can last for some time. Also, there is no obligation for an action to start as soon as the previous one in the sequence is finished, some time can pass in-between. Thus for each Action, the resulting Petri Net should have one or more states where the action is being executed. Likewise for each control flow edge, there should be PN states where the edge is active, i.e. execution has passed the action (or control node) at the source of the control flow edge, but has not yet reached the Activity Node at the target of the edge.

Illustrating the principle of mapping a UML Activity Control Flow into the Petri Net domain.
Illustrating the principle of mapping a UML Activity Action into the Petri Net domain.

This goal can be achieved by creating a Petri place for each action and each control flow in the Activity model. In the marking belonging to a state of the Petri net, a token in the place corresponding to an action means that an instance of the process is currently executing the action. Similarly, a token in the place corresponding to a control flow means that the activity edge is currently active.

Obviously, we also need transitions, otherwise the Petri Net would be static. For each control flow incoming in an action, a transition should be created to represent the start of execution of the action. This transition has an incoming arc from the place representing the control flow, and an outgoing arc to the place representing the action. Thus the transition is enabled if the activity edge is active, and firing it will start the execution of the action and cease the activation of the edge. Similarly, a transition should represent the termination of the action, with an incoming arc from the place of the action, and an outgoing arc to the place of the edge.

This argument suggests the following principle for the transformation. Each control flow edge will be mapped into a place representing when the edge is active. Each action should be mapped into two transitions and a place in-between, with two arcs linking the three Petri nodes. The first transition represents entering the action, the place (more precisely, a token in the place) represents that the state is during the execution of the action, and the second transition represents exiting the action. Then the entering transition of the action should be linked by an incoming arc to the place representing the incoming activity edge, and the exiting transition should be linked by an outgoing arc to the place representing the outgoing activity edge.


Initial and flow final nodes

Illustrating the principle of mapping a Flow Final Node of a UML Activity model into the Petri Net domain.

When execution reaches a flow final node in the Activity, the instance of the process terminates. More precisely, if there are several threads of execution (due to a fork node), the one that reaches the flow final node terminates, while others are unaffected (this is the difference between the flow final node and a different type of terminating node). In Petri Net terminology, the token representing the activation of the incoming control flow edge should be consumed by the flow final node, without putting a token in any place representing an activity edge or action. This is achieved by a transition that has an incoming arc from the place corresponding to the control flow edge pointing to the flow final node, and no outgoing arcs.

Illustrating the principle of mapping an Initial Node of a UML Activity model into the Petri Net domain.

There are at least two ways to interpret an initial node. One possibility is that the node can activate the outgoing control flow edge exactly once, thereby starting the process. In some contexts, however, it makes more sense to assume that the initial node can activate the control flow an arbitrary number of times, launching multiple process instances that are executed concurrently. Both versions can be represented by Petri Nets (although in the second case there would be a semantic issue with join nodes, which is out of scope for this tutorial). In both cases, the initial node is mapped into a transition, with an outgoing arc to the place corresponding to the outgoing control flow edge. We opted to choose the first interpretation; a place marked initially with one token (the only marked place in the initial state) is created and connected by an arc to the transition corresponding to the initial node. This marked place ascertains that the transition can fire only once, as the token will be removed upon the firing, rendering the transition disabled from that time on. The other interpretation can be achieved by simply omitting this place and the arc; in this case the transition would be able to fire any time, an arbitrary number of times.

Fork and join

Illustrating the principle of mapping a Fork Node of a UML Activity model into the Petri Net domain.
Illustrating the principle of mapping a Join Node of a UML Activity model into the Petri Net domain.

Fork nodes have one incoming and several outgoing activity edges. When the incoming edge is active, the fork node activates all outgoing edges, so that several flows are created that will proceed concurrently. A straightforward Petri Net representation is a single transition, with incoming and outgoing arcs to the places representing incoming and outgoing control flow edges, respectively.

Join nodes mark a point of execution that can only proceed (i.e. activate the single outgoing edge) when all joined flows have reached it, i.e. all incoming activity edges are active. The Petri Net representation, much like that of the fork node, is a single transition, with incoming and outgoing arcs to the places representing incoming and outgoing control flow edges, respectively.

Implementation using GT rules

Once we have designed the overall principle of the transformation, we can formalize it using GT rules, and implement them in VTCL, the transformation language of VIATRA2.

Control Flow

Illustration of a graph pattern, consisting of a control flow element and the place corresponding to it.

Specifying graph transformation rules requires specifying graph patterns. Our first pattern expresses a control flow element that has already been mapped into the Petri Net domain. Thus the pattern consists of a control flow element from a UML Activity, the Petri Net place corresponding to it, and a special traceability link between them. Such links will be useful later as they help to remember which Petri Net node corresponds to which UML model element, preserving a mapping between source and target models, thereby achieving traceability.

The visual illustration of this pattern looks admittedly strange, as the traceability link connects to the control flow element, which is an edge. It is good to remember that for this and similar purposes, VIATRA2 models tolerate edges (relations) connecting to other edges. Furthermore in this case, the control flow element is actually a node in the abstract UML model, despite being denoted by an edge on the Activity Diagram. It is useful to keep in mind that transformations always operate on the abstract syntax, from which the concrete syntax may differ significantly. As a further deviation from the visual illustration of this pattern, we actually constructed the pattern with an additional node for the Petri Net itself, and an edge that identifies the place as belonging to this net. While not apparent right now, this addition will be useful later.

The following VTCL snippet defines the graph pattern mappedControlFlow.

   pattern mappedControlFlow(ControlFlow, PetriPlace, PetriNet) = {
     'ControlFlow'(ControlFlow);
     find activityEdgeMapping(ControlFlow, PetriPlace);
     find placeOfNet(PetriPlace, PetriNet);
   }

The three pattern variables are ControlFlow, PetriPlace, and PetriNet; they are intended to be mapped onto the control flow element, the place in the Petri Net, and the net itself, respectively.

The pattern imposes three constraints on these variables. The first one identifies the variable ControlFlow to be an entity of the type also called ControlFlow. Normally, only variable names are capitalized. As the name of the type starts with a capital letter, it has to be enclosed in single apostrophes to mark it as the name of a (meta)model element, as opposed to the name of a variable. The second constraint identifies an "activityEdgeMapping" between the activity edge (ControlFlow) and its image in the target model (PetriPlace); this is the part of the pattern that finds the traceability link. Finally, asserting "placeOfNet" ensures that PetriPlace is a place, PetriNet is a net, and the former is contained in the latter.

How does this single line assert all these? Actually, "placeOfNet" is not an edge (relation) of the model (and neither is "activityEdgeMapping"). They are graph patterns themselves, and the find keyword indicates pattern composition (or pattern invocation), which basically means reusing a predefined pattern as part of a different pattern. This utility pattern is defined this way:

 pattern placeOfNet(PetriPlace, PetriNet) = {
   net(PetriNet);
   place(PetriPlace);
   net.places(Places, PetriNet, PetriPlace);
 }

The first constraint asserts that variable PetriNet is an entity of type net. The second constraint declares the variable PetriPlace is an entity of type place. The final constraint assert that the variable Places is a relation (note the ternary structure) of the type net.places, that connects from PetriNet to PetriPlace. This simple pattern thus matches Petri places belonging to Petri nets. Notice the absence of pattern variable Places from the list of parameters of the pattern: it is deliberately hidden from the outside.

Illustration of a GT Rule for mapping Control Flow.

Now, we can finally create Graph Transformation (GT) rule that map a control flow edge into a Petri place. This will be our first GT rule, and luckily it is a quite simple one.

The LHS would be a trivial pattern that recognizes a control flow edge. The RHS is the graph pattern mappedControlFlow defined above, so it contains an activity edge (actually, the same activity edge), a place, and a traceability link between them. By the definition of GT rules, the rule will be applicable on each control flow, and the result of the application would be a new Petri place, with a traceability link established between them.

It is possible for the control structure of the transformation program to guarantee that the GT rule is applied on each control flow element exactly once. Yet, it is often a good idea to formulate GT rules in such a way that it is immediately apparent that they cannot be applied on the same spot repeatedly. Here we achieve this by a so-called Negative Application Condition (NAC). A NAC is an extra constraint attached on the LHS pattern that, when satisfied, will invalidate a match of the pattern. NACs can also be described as graph patterns. In our case, the NAC pattern consists of the same control flow element already having a traceability link. Since it is a negative condition, the LHS will only match control flow elements that have not already been mapped into the Petri Net domain. In various graph pattern figures, NACs are indicated by red crosses, by entirely red visual elements, or by red boxes enclosing the pattern with NAC or NEG written on them. In the VTCL textual syntax, they are indicated by the keyword neg, followed by the invocation of the pattern constituting the NAC:

 gtrule transformControlFlow(out ControlFlow, in PetriNet) = {
   precondition pattern unmappedControlFlow(ControlFlow) = {
     'ControlFlow'(ControlFlow);
     neg find activityEdgeMapping(ControlFlow, NoPetriPlace);
   }
   postcondition pattern mappedControlFlow(ControlFlow, PetriPlace, PetriNet) = {
     'ControlFlow'(ControlFlow);
     find activityEdgeMapping(ControlFlow, PetriPlace);
     find placeOfNet(PetriPlace, PetriNet);
   }
   action {
     call copyName(ControlFlow, PetriPlace);
   }
 }

Here we can identify the simple LHS (precondition) pattern consisting of a control flow element and a NAC; our familiar RHS (postcondition) pattern; and finally an action sequence that is responsible for naming the place the same way as the control flow element was named (and giving descriptive names to elements is important). This name copying is defined in an imperative ASM rule called copyName, that is simply invoked here. It is worth pointing out that the LHS and RHS share the ControlFlow variable, therefore it will mean the same model element in both cases. Also, the action sequence has access to the value of PetriPlace, which is the place created by the RHS (or more precisely, by applying the declarative part of the rule).

The parameters of the GT rule are the variables ControlFlow and PetriNet. The latter is an input parameter: it is meant to be explicitly specified before applying the GT rule. This is important: we do not want to create a new Petri Net with each rule application (even though it it in the RHS but not the LHS), but use the same target model as the container of results everywhere. The control structure of the transformation will have to provide the identity of the target Petri Net when invoking this GT rule. The ControlFlow variable, on the other hand, is an output parameter; its value is meant to be determined in the GT rule, by the pattern matching process of the LHS, and propagated to the outside. Parameters can also be specified with the inout keyword, indicating that they can either be fixed or left for pattern matching to identify. Variables such as PetriPlace, however, are meant to be created by within the rule application, therefore if they appear as a parameter, it only makes sense to mark them as output parameters.

Exercise: decision and merge

TODO

 ERROR - Could not transform activity node: uml.models.activity_complex_uml.uN596_b4.ComplexActivity.DecisionNode
 ERROR - Could not transform activity node: uml.models.activity_complex_uml.uN596_b4.ComplexActivity.MergeNode
 *** Cound not transform UML Activity: ComplexActivity
 --- Transformation terminated.

Copyright © Eclipse Foundation, Inc. All Rights Reserved.