Academia.eduAcademia.edu

De-/re-constructing model transformation languages

2010

The diversity of today's model transformation languages makes it hard to compare their expressiveness and provide a framework for interoperability. De-constructing and then re-constructing model transformation languages by means of a unique set of most primitive constructs facilitates both. We thus introduce T-Core, a collection of primitives for model transformation. Combining T-Core with a (programming or modelling) language enables the design of model transformation formalisms. We show how basic and more advanced features from existing model transformation languages can be re-constructed using T-Core primitives. Finally, we design a new primitive language for model transformation by combining T-Core with an existing asynchronous and timed modelling language. Figure 1: The T-Core module

De-/Re-constructing Model Transformation Languages Eugene Syriani and Hans Vangheluwe Abstract The diversity of today’s model transformation languages makes it hard to compare their expressiveness and provide a framework for interoperability. De-constructing and then re-constructing model transformation languages by means of a unique set of most primitive constructs facilitates both. We thus introduce T-Core, a collection of primitives for model transformation. Combining T-Core with a (programming or modelling) language enables the design of model transformation formalisms. We show how basic and more advanced features from existing model transformation languages can be re-constructed using T-Core primitives. Finally, we design a new primitive language for model transformation by combining T-Core with an existing asynchronous and timed modelling language. 1 Introduction A plethora of different rule-based model transformation languages and supporting tools exist today. They cover all (or a subset of) the well-known essential features of model transformation [1]: atomicity, sequencing, branching, looping, non-determinism, recursion, parallelism, back-tracking, hierarchy, and time. For such languages, the semantics (and hence implementation) of a transformation rule consists of the appropriate combination of building blocks implementing primitive operations such as matching, rewriting, and often a validation of consistent application of the rule. The abovementioned essential features of transformation languages are achieved by implicitly or explicitly specifying “rule scheduling”. Languages such as ATL [2], FUJABA [3], GReAT [4], MoTif [5], and VIATRA [6] include constructs to specify the order in which rules are applied. This often takes the form of a control flow language. Without loss of generality, we consider transformation languages where models are encoded as typed, attributed graphs. The diversity of transformation languages makes it hard, on the one hand, to compare their expressiveness and, on the other hand, to provide a framework for interoperability (i.e., meaningfully combining transformation units specified in different transformation languages). One approach is to express model transformation at the level of primitive building blocks. De-constructing and then re-constructing model transformation languages by means of a small set of most primitive constructs offers a common basis to compare the expressiveness transformation languages. It may also help in the discovery of novel, possibly in domain-specific, model transformation constructs by combining the building blocks in new ways. Furthermore, it allows implementers to focus on maximizing the efficiency of the primitives in isolation, leading to more efficient transformations overall. Lastly, once re-constructed, different transformation languages can seamlessly interoperate as they are built on the same primitives. This use of common primitives in turn allows for global as well as inter-rule optimization We introduce T-Core, a collection of transformation language primitives for model transformation and motivate the choice of its primitives in Section 2. Section 3 shows how transformation entities, common as well as more esoteric, can be re-constructed. Section 4 explains how, in particular, timed and asynchronous model transformation languages can be built. Section 5 describes related work and section 6 draws conclusions and presents directions for future work. 2 De-constructing Transformation Languages We propose here a collection of model transformation primitives. The class diagram in Figure 1 presents the module TCore encapsulating model transformation primitives. T-Core consists of eight primitive constructs (Primitive objects): 1 Figure 1: The T-Core module 2 a Matcher, Iterator, Rewriter, Resolver, Rollbacker, Composer, Selector, and Synchronizer. The first five are RulePrimitive elements and represent the building blocks of a single transformation unit. T-Core is not restricted to any form of specification of a transformation unit. In fact, we consider only PreConditionPatterns and PostConditionPatterns. For example, in rule-based model transformation, the transformation unit is a rule. The PreConditionPattern determines its applicability: it is usually described with a left-hand side (LHS) and optional negative application conditions (NACs). It also consists of a PostConditionPattern which imposes a pattern to be found after the rule was applied: it is usually described with a right-hand side (RHS). RulePrimitives are to be distinguished from the ControlPrimitives, which are used in the design of the rule scheduling part of the transformation language. A meaningful composition of these different constructs in a Composer object allows modular encapsulation of and communication between Primitive objects. Primitives exchange three different types of messages: Packet, Cancel, and Exception. A packet π represents the host model together with sufficient information for inter- and intra-rule processing of the matches. π thus holds the current model (graph in our case) graph, the matchSet, and a reference to the current PreConditionPattern identifying a MatchSet. A MatchSet refers to a condition pattern and contains the actual matches as well as a reference to the matchToRewrite. Note that each MatchSet of a packet have a unique condition, used identifying the set of matches. A Match consists of a sub-graph of the graph in π where each element is bound to an element in graph. Some elements (Nodes) of the match may be labelled as pivots, which allows certain elements of the model to be identified and passed between rules. A cancel message ϕ is meant to cancel the activity of an active primitive element (especially used in the presence of a Selector). Finally, specific exceptions χ can be explicitly thrown, carrying along the currently processed packet π1 . All the primitive constructs can receive packets by invoking either their packetIn, nextIn, successIn, or failIn methods. The result of calling one of these methods sets the primitive in success or failure mode as recorded by the isSuccess attribute. Cancel messages are received from the cancelIn method. Next, we describe in detail the behaviour of the different methods supported by each of the eight primitive elements. 2.1 Matcher The Matcher finds all possible matches of the condition pattern c on the graph embedded in the packet it receives from its packetIn method. The transformation modeller may optimize the matching by setting the findAll attribute to false when he a priori knows that at most one match of this matcher will be processed in the overall transformation. The matching also considers the pivot mapping (if present) of the current match of π. Some implementations may, for example, parametrize the Matcher by the condition or embed it directly in the Matcher. The transformation units (e.g., rules) may be compiled in pre/post-condition patterns or interpreted, but this is a tool implementation issue which we do not discuss here. After matching the graph, the Matcher stores the different matches in the packet as described in Algorithm 1. 2.2 Rewriter As described in Algorithm 2, the Rewriter applies the required transformation for its condition on the match specified in the packet it receives from its packetIn method. That match is consumed by the Rewriter: no other operation can be further applied on it. Some validations are made in the Rewriter to verify, for example, that π.current.condition = condition.pre or that no error occurred during the transformation. In our approach, a modification (update or delete) of an element in {M| hc, Mi ∈ π.matchSets} is automatically propagated to the other matches, if applicable. 1π φ is used to represent the empty packet. 3 Algorithm 1 Matcher.packetIn(π) M ← (all) matches of condition found in π.graph if ∃ hcondition, M ′ i ∈ π.matchSets then M′ ← M′ ∪ M else add hcondition, Mi to π.matchSets end if π.current ← condition isSuccess ← M 6= 0/ return π Algorithm 2 Rewriter.packetIn(π) if π is invalid then isSuccess ← false exception ← χ(π) return π end if apply transformation on M.matchToRewrite for which hcondition.pre, Mi ∈ π.matchSets if transformation failed then isSuccess ← false exception ← χ(π) return π end if set all modified nodes in M to dirty remove hcondition, Mi from π.matchSets isSuccess ← true return π 2.3 Iterator The Iterator chooses a match among the set of matches of the current condition of the packet it receives from its packetIn method, as described in Algorithm 3. The match is chosen randomly in a Monte-Carlo sense, repeatable using sampling from a uniform distribution to provide a reproducible, fair sampling. When its nextIn method is called, the Iterator chooses another match as long as the maximum number of iterations maxIterations (possibly infinite) is not yet reached, as described in Algorithm 4. Algorithm 3 Iterator.packetIn(π) if hπ.current, Mi ∈ π.matchSets then choose m ∈ M M.matchToRewrite ← m remIterations ← maxIterations − 1 isSuccess ← true return π else isSuccess ← false return π end if Algorithm 4 Iterator.nextIn(π) if hπ.current, Mi ∈ π.matchSets and remIterations > 0 then choose m ∈ M M.matchToRewrite ← m remIterations ← remIterations − 1 isSuccess ← true return π else isSuccess ← false return π end if 2.4 Rollbacker The Rollbacker is only used to provide back-tracking capabilities to its transformation rule r. Consequently, it keeps a stack of packets packetStack Π, as described in Algorithms 5 and 6. The packetIn method stores the received 4 packet and the nextIn method ensures the roll-back of the packet to its previous state. Again, a maximum number of iterations can be specified. Algorithm 5 Rollbacker.packetIn(π) push π onto Π remIterations ← maxIterations − 1 isSuccess ← true return π Algorithm 6 Rollbacker.nextIn(π) if hπ.current, Mi ∈ π.matchSets and remIterations > 0 then remIterations ← maxIterations − 1 isSuccess ← true return π else if remIterations > 0 and Π 6= 0/ then π̂ ← pop Π remIterations ← maxIterations − 1 isSuccess ← true return π̂ else isSuccess ← false return π end if 2.5 Resolver The Resolver resolves a potential conflict between matches and rewritings as described in Algorithm 7. For the moment, the Resolver detects conflicts in a simple conservative way: it prohibits any change to other matches in the packet (check for dirty nodes). However, it does not verify if a modified match is still valid with respect to its precondition pattern. The externalMatchesOnly attribute specifies whether the conflict detection should also consider matches from its identified by π.current or not. In the case of conflict, a default resolution function is provided but the user may also override it. 5 Algorithm 7 Resolver.packetIn(π) for all condition c ∈ {c| hc, Mi ∈ π.matchSets} do if externalMatchesOnly and c = π.current then continue end if for all match m ∈ M do if m has a dirty node then if customResolution(π) then isSuccess ← true return π else if defaultResolution(π) then isSuccess ← true return π else isSuccess ← false exception ← χ(π) return π end if end if end for end for isSuccess ← false exception ← χ(π) return π 2.6 Selector The Selector is used when a choice needs to be made between multiple packets processed concurrently by different constructs. It allows exactly one of them to be processed further. When its successIn (or failIn) method is called, the received packet is stored in its success (or fail) collection, respectively. Note that, unlike the previous described methods, it is only when the select method in Algorithm 8 is called that a packet is returned, chosen from success. The selection is random in the same way as in the Iterator. When the cancel method is invoked, the two collections are cleared and a cancel message ϕ is returned where the exclusions set consists of the singleton π.current (meaning that operations of the chosen condition should not be cancelled). 2.7 Synchronizer The Synchronizer is used when multiple packets processed in parallel need to be synchronized. It is parametrized by the number of threads to synchronize. This number is known at design-time. Its successIn and failIn methods behave exactly like those of the Selector. The Synchronizer is in success mode only if all threads have terminated by never invoking failIn. The merge method “merges” the packets in success, as described in Algorithm 9. A trivial default merge function is provided by unifying and “gluing” the set of packets. Nevertheless, it first conservatively verifies the validity of the received packets by prohibiting overlapping matches between them. If it fails, the user can specify a custom merge function. This avoids the need for static parallel independence detection. Instead it is done at 6 run-time and the transformation modeller must explicitly describe the handler. One pragmatic use of that solution is, for instance, to let the transformation run once to detect the possible conflicts and then the modeller may handle these cases by modifying the transformation model. Algorithm 8 Selector.select() if success 6= 0/ then π̂ ← choose from success isSuccess ← true else if fail 6= 0/ then π̂ ← choose from fail isSuccess ← false else π̂ ← πφ isSuccess ← false exception ← χ(πφ ) end if success ← 0/ fail ← 0/ return π̂ Algorithm 9 Synchronizer.merge() if |success| = threads then if defaultMerge() then π̂ ← the merged packet in success isSuccess ← true success ← 0/ fail ← 0/ return π̂ else if customMerge() then π̂ ← the merged packet in success isSuccess ← true success ← 0/ fail ← 0/ return π̂ else isSuccess ← false exception ← χ(πφ ) return πφ end if else if |success| + |fail| = threads then π̂ ← choose from fail isSuccess ← false return π̂ else isSuccess ← false exception ← χ(πφ ) return πφ end if 2.8 Composer The Composer serves as a modular encapsulation interface of the elements in its primitives list. When one of its packetIn or nextIn methods is invoked, it is up to the user to manage subsequent method invocations to its primitives. Nevertheless, when the cancelIn method is called, the Composer invokes the cancelIn method of all its primitives. Whenever the cancelIn method is called on a RulePrimitive object, its current action is cancelled only if it is actively processing a packet π such that the current condition of π is not in ϕ.exclusions, where ϕ is the received cancel message. In the case of a Matcher, since the current condition of the packet may not already be set, the cancelIn also verifies that the condition of the the Matcher is not in the exclusions list. The interruption of activity can, for instance, be implemented as pre-emptive asynchronous method call of cancelIn. 7 2.9 T-Core: a minimal collection of transformation primitives In the de-construction process of transformation languages into a collection of primitives, questions like “up to what level?” or “what to include and what to exclude?” arise. The proposed T-Core module answers these questions in the following way. In a model transformation language, the smallest transformation unit is traditionally the rule. A rule is a complex structure with a declarative part and an operational part. The declarative part of a rule consists of the specification of the rule (e.g., LHS/RHS and optionally NAC in graph transformation rules). However, T-Core is not restricted to any form of specification let it be rule-based, constraint-based, or function-based. In fact, some languages require units with only a pre-condition to satisfy, while other with a pre- and a post-condition. Some even allow arbitrary permutations of repetitions of the two. In T-Core, either a PreConditionPattern or both a Pre- and a PostConditionPattern must be specified. For example, a graph transformation rule can be represented in T-Core as a couple of a pre- and a postcondition pattern, where the latter has a reference to the former to satisfy the semantics of the interface K (in the L ← K → R algebraic graph transformation rules) and be able to perform the transformation. Transformation languages where rules are expressed bidirectionally or as functions are supported in T-Core as long as they can be represented as pre- and post-condition patterns. The operational part of a rule describes how it executes. This operation is often encapsulated in the form of an algorithm (with possibly local optimizations). Nevertheless, it always consists of a matching phase, i.e., finding instances of the model that satisfy the pre-condition and of a transformation phase, i.e., applying the rule such that the resulting model satisfies the post-condition. T-Core distinguishes these two phases by offering a Matcher and a Rewriter as primitives. Consequently, the Matcher’s condition only consists of a pre-condition pattern and the Rewriter then needs a post-condition pattern that can access the pre-condition pattern to perform the rewrite. Combinations of Matchers and Rewriters in sequence can then represent a sequence of simple graph transformation rule: matchrewrite-match-rewrite. Moreover, because of the separation of these two phases, more complex constructs may be built, such as: match-match-match or match-match-rewrite-rewrite. The former is a query where each Matcher filters the conditions of the query. The latter is a nesting of transformation rules. In this case, however, overlapping matches between different Matchers and then rewrites on the overlapping elements may lead to inconsistent transformations or even non-sense. This is why a Resolver can be used from T-Core to safely allow match-rewrite combinations. The data structure exchanged between T-Core RulePrimitives in the form of packets contains sufficient information for each primitive to process it as described in the various algorithms in Section 2. The Match allows to refer to all model elements that satisfy a pre-condition pattern. The pivot mappings allow elements of certain matches to be bound to elements of previously matched elements. The pivot mapping is equivalent to passing parameters between rules as will be shown in the example in Section 3.1. The MatchSet allows to delay the rewriting phase instead of having to rewrite directly after matching. Packets conceptually carry the complete model (optimized implementation may relax this) which allows concurrent execution of transformations. The Selector and the Synchronizer both permit to join branches or threads of concurrent transformations. Also, having separated the matching from the rewriting enables to manage the matches and the results of a rewrite by further operators. Advanced features such as iteration over multiple matches or backtracking to a previous state in the transformation are also supported in T-Core. Since T-Core is a low-level collection of model transformation primitives, combining its primitives to achieve relevant and useful transformations may involve a large number of these primitive operators. Therefore, it is necessary to provide a “grouping” mechanism. The Composer allows to modularly organize T-Core primitives. It serves as an interface to the primitives it encapsulates. This then enables scaling of transformations built on T-Core to large and complex model transformations designs. T-Core is presented here as an open module which can be extended, through inheritance for example. One could add other primitive model transformation building blocks. For instance, a conformance check operator may be useful to verify if the resulting transformed model still conforms to its meta-model. It can be interleaved between sequences of rewrites or used at the end of the overall transformation as suggested in [SLE]. We believe however that such new constructs should either be part of the (programming or modelling) language or the tool in which T-Core is integrated, 8 to keep T-Core as primitive as possible. 3 Re-constructing Transformation Languages (a) (b) (c) (d) Figure 2: Combining T-Core with other languages allows to re-construct existing and new languages Having de-constructed model transformation languages in a collection of model transformation primitives makes it easier to reason about transformation languages. In fact, properly combining T-Core primitives with an existing well-formed programming or modelling language allows us to re-construct some already existing transformation languages and even construct new ones. Figure 2 shows some examples of combinations of T-Core with other languages. Figure 2(a) and Figure 2(b) combine a subset of T-Core with a simple (programming) language which offers sequencing, branching, and looping mechanisms (as proposed in Jocapini’s structured program theorem). We will refer to such a language as an SBL language. The first combination only involves the Matcher and its PreConditionPattern, Packet messages to exchange, and the Composer to organize the primitives. These T-Core primitives integrated in an SBL language leads to a query language. Since only matching operations can be performed on the model, they represent queries where the resulting packet holds the set of all elements (sub-graph) of the model (graph) that satisfy the desired pre-conditions. Including other T-Core primitives such as the Rewriter promote the query language to a transformation language. Figure 2(b) enumerates the necessary T-Core primitives combined with an SBL language to design a complete sequential model transformation language. Replacing the SBL language by another one, such as UML Activity Diagrams [7] in Figure 2(c), allows us to re-construct Story Diagrams [3], for example, since Story Diagrams are defined as a combination of UML Activity and Collaboration Diagrams with graph transformation features. Other combinations involving the whole T-Core module may lead to novel transformation language with exception handling and the notion of timed model transformations when combined with a discrete-event modelling language. 9 We now present the re-construction of two transformation features using the combination of an SBL language with T-Core as in Figure 2(b). 3.1 Re-constructing Story Diagrams Figure 3: The FUJABA doSubDemo transformation showing a for-all Pattern and two statement activities In the context of object-oriented reverse-engineering, the FUJABA tool allows the user to specify the content of a class method by means of Story Diagrams, an extension of UML Activity Diagrams. A Story Diagram organizes the behaviour of a method with activities and transitions. An activity can be a Story Pattern or a statement activity. The former consists of a graph transformation rule and the latter is Java code. Figure 3 shows such a story diagram taken from the doDemo method example in [3]. This snippet represents an elevator loading people on a given floor of a house who wish to go to another level. The rule in the pattern is specified in a UML Collaboration Diagram-like notation [7] with objects and associations. Objects with implicit types (e.g., this, l2, and e1) are bound objects from previous patterns or variables in the context of the current method. The Story Pattern 6 is a for-all Pattern. Its rule is applied on all matches found looping over the unbound objects (e.g., p4, and l4). The outgoing transition labelled each time applies statement 7 after each iteration of the for-all Pattern. This activity allows the pattern to simulate random choices of levels for different people. When all iterations have been completed, the flow proceeds with statement 8 reached by the transition labelled end. The latter activity simulates the elevator going one level up. We now show how to re-construct this non-trivial story diagram transformation from an SLB language combined with T-Core. An instance of that combination is called a T-Core model. First, we design the rules needed for the conditions of rule primitives. Figure 4 describes the three necessary rules corresponding to the three Story Diagram activities. We use the visual concrete syntax of MoTif [5] where the central compartment is the LHS, the compartment on the right of the arrow head is the RHS and the compartment(s) on the left of dashed lines are the NAC(s). The concrete syntax for representing the pattern was chosen to be intuitively close enough to the FUJABA graphical representation. Numeric labels are used to uniquely identify different elements across compartments. Elements with an alpha-numeric label between parentheses denote pivots. A right-directed arrow on top of the label depicts that the model element matched for this pattern element is assigned to a pivot (e.g., p4 and l4). A left-directed arrow on top of the label depicts that the model element matched for this pattern element is bound to the specified pivot (e.g., this and e1). The T-Core model equivalent to the original doSubDemo transformation consists of a Composer doSubDemoC. The hierarchy of its sub-primitives is illustrated in the Collaboration Diagram in Figure 5. It is composed of two Composers loadC and nextStepC each containing a Matcher, an Iterator, a Rewriter, and a Resolver. The packetIn method of doSubDemoC first calls the corresponding method of loadC and then feeds the returned packet to the packetIn method of nextStepC. This ensures that the output packet of the overall transformation is the result of first loading all the Person objects and then moving the elevator by one step. makeChoiceC and nextStepC behave as simple transformation rules. Their packetIn method behaves as specified in Algorithm 12. First, the matcher is tried on the input packet. Note that the conditions of the matchers makeChoiceM and nextStepM are the LHSs of rules makeChoice and nextStep, respectively. If it fails, the composer goes into failure mode and the packet is returned. Then, the 10 Figure 4: The three MoTif rules for the doSubDemo transformation Figure 5: The object hierarchy of the doSubDemo composer 11 iterator chooses a match. Subsequently, the rewriter attempts to transform this match. Note that the conditions of the rewriters makeChoiceW and nextStepW are the RHSs of rules makeChoice and nextStep, respectively. If it fails, an exception is thrown and the transformation stops. Otherwise, the resolver verifies the application of this pattern with respect to other matches in the transformed packet. The behaviour of the resolution function will be elaborated on later. Finally, on a successful resolution, the resulting packet is output and the composer is put in success mode. loadC is the composer that emulates the for-all Pattern of the example. Algorithm 11 specifies that behaviour. After finding all matches with loadM (whose condition is the LHS and the NAC of rule load), the packet is forwarded to the iterator loadI to choose a match. The iteration is emulated by a loop with the failure mode of loadI as the breaking condition. Inside the loop, loadW rewrites the chosen match and loadR resolves possible conflicts. Then, the resulting packet is sent to makeChoiceC to fulfil the each time transition of the story digram. After that, the nextIn method of loadI is invoked with the new packet to choose a new match and proceed in the loop. Algorithm 10 doSubDemoC.packetIn(π) π ← loadC.packetIn(π) π ← nextStepC.packetIn(π) isSuccess ← true return π Having seen the overall T-Core transformation model, let us inspect how the different Resolvers should behave in order to provide a correct and complete transformation. The first rewriter called is loadR and the first time it receives a packet is when a transformation is applied on one of the matches of loadM. Therefore each match consists of the same House (since it is a bound node), two Levels, an Elevator, and the associations between them. On the other hand, loadW only adds a Person and links it to a Level. Therefore the default resolution function of loadR applies successfully, since no matched element is modified nor is the NAC violated in any other match. The next resolver is makeChoiceR which is in the same loop as loadR. There, the House is conflicting with all the matches in the packet according to the conservative default resolution function. Note that makeChoiceM finds at most one match (the bound House element). However, makeChoiceW does not really conflict with matches found in loadM. We therefore specify a custom resolution function for makeChoiceR that always succeeds. The same applies for nextStepR. 12 Algorithm 11 loadC.packetIn(π) π ← loadM.packetIn(π) if not loadM.isSuccess then isSuccess ← false return π end if π ← loadI.packetIn(π) while true do if not loadI.isSuccess then isSuccess ← true return π end if π ← loadW.packetIn(π) if not loadW.isSuccess then isSuccess ← false return π end if π ← loadR.packetIn(π) if not loadR.isSuccess then isSuccess ← false return π end if π ← makeChoiceC.packetIn(π) π ← loadI.nextIn(π) end while Algorithm 12 makeChoiceC.packetIn(π) π ← makeChoiceM.packetIn(π) if not makeChoiceM.isSuccess then isSuccess ← false return π end if π ← makeChoiceI.packetIn(π) if not makeChoiceI.isSuccess then isSuccess ← true return π end if π ← makeChoiceW.packetIn(π) if not makeChoiceW.isSuccess then isSuccess ← false return π end if π ← makeChoiceR.packetIn(π) if not makeChoiceW.isSuccess then isSuccess ← false return π end if isSuccess ← true return π 3.2 Re-constructing amalgamated rules In a recent paper, Rensink et al. claim that the Repotting the Geraniums example is inexpressible in most transformation formalisms [8]. The authors propose a transformation language that uses an amalgamation scheme for nested graph transformation rules. As we have seen in the previous example, nesting transformation rules is possible in TCore and will be used to solve the problem. It consists of repotting all flowering geraniums whose pots have cracked. Figure 6 illustrates the two nested graph transformation rules involved and Algorithm 13 demonstrates the composition of primitive T-Core elements encoding these rules. baseM (with, as condition, the LHS of rule base) finds all broken pots containing a flowering geranium, given the input packet containing the input graph. The set of matches resulting in the packet are the combination of all flowering geraniums and their pot container. From then on starts the loop. First, baseI chooses a match. If one is chosen, baseW transforms this match and baseR resolves any conflicts. In this case, baseW only creates a new unbroken pot and assigns pivots. Therefore, baseR’s resolution function always succeeds. In fact, the resolver is not needed here, but we include it for consistency. The innerC composer encodes the inner rule which finds the two bound pots and moves a flourishing flower in the broken pot to the unbroken one. In order to iterate over all the flowers in the broken pot, the innerC.packetIn method has the exact same behaviour as loadC.packetIn in Algorithm 11, with the exception of not calling a sub-composer (like makeChoiceC). Note that an always successful custom resolution function for innerR is required. After the Resolver successfully outputs the packet, the inner rule is applied. Then (and also if baseI had failed) baseM.packetIn is called again with the resulting packet. The loop ends when the baseM.packetIn method call inside the loop fails, which entails baseC to return the final packet in success mode. 13 Algorithm 13 baseC.packetIn(π) π ← baseM.packetIn(π) if not baseM.isSuccess then isSuccess ← false return π end if while true do π ← baseI.packetIn(π) if baseI.isSuccess then π ← baseW.packetIn(π) if not baseW.isSuccess then isSuccess ← false return π end if π ← baseR.packetIn(π) Figure 6: The transformation rules for the Repotting Geraif not baseR.isSuccess then niums example isSuccess ← false return π end if π ← innerC.packetIn(π) end if π ← baseM.packetIn(π) if not baseM.isSuccess then isSuccess ← false return π end if end while 4 Re-constructing Asynchronous & Timed Transformation Languages In the context of rule-based graph transformation, the Discrete Event System Specification (DEVS) formalism [9] can be used as an underlying basis for rule scheduling in transformation languages [10]. DEVS is a compositional, timed discrete-event language and is thus an attractive framework for generalize primitive model transformation language. 4.1 The MoTif-Core Language MoTif-Core is a primitive transformation language which combines DEVS and T-Core (see Figure 2(d)). Its metamodel in Figure 7 shows how MoTif-Core is an extension of DEVS integrating all T-Core contructs. The so-called T-Core primitives can be found encapsulated in the state of different AtomicDEVS elements, (except for the Composer). Those classes are prefixed by “TC”, depicting that they are semantically identical to their T-Core counterparts (e.g., TCMatcher from MoTif-Core and Matcher from TCore). However, the messages sent through their method calls use Event objects from MoTif-Core instead of T-Core. All the RulePrimitive elements have two inports (PacketIn and CancelIn) from which packets and cancel events are respectively received and two outports (SuccessOut and FailOut) from which packets are sent. Both the Iterator and the Rollbacker have an extra NextIn inport. For compositionality and transparency reasons, the Composer has the 14 Figure 7: The meta-model of MoTif-Core 15 same ports, as well as an extra ExceptionOut outport. On the other hand, the ControlPrimitive elements have SuccessIn and FailIn as inports and SuccessOut and FailOut as outports. When a MoTif-Core primitive (any sub-class RulePrimitive, ControlPrimitive, or CompositePrimitive from Figure 7) receives an event from an inport, its external transition function invokes the appropriate method of its state (T-Core primitive) corresponding to this inport as illustrated in Algorithm 14. In the ControlPrimitive elements, the Selector’s select method is no longer used. It is the select function of the Composer that takes care of the selection of the appropriate primitive to output. In MoTif-Core, the Composer inherently composes other DEVSBlock elements (MoTif-Core primitives as well as pure DEVSBlocks) as it is a CoupledDEVS and hence stateless. Different types of Composers can be specified for re-using recurring composers in different transformation models. This CompositePrimitive specifies the connection between the different in/outports to ensure a proper flow of the transformation. Algorithm 14 primitive.externalTransition(e) // Some pre-processing... if e received from ACancelIn then state.cancelIn(e) end if if e received from APacketIn then state.packetIn(e) else if e received from ANextIn then // if defined state.nextIn(e) end if // ... some post-processing MoTif-Core is a completely meta-modelled primitive language for model transformation. A MoTif-Core model is a DEVS model specialized for transformation purposes. It can therefore be combined with AtomicDEVS and CoupledDEVS models. The implementation of a virtual machine makes use of this advantage to specify the interface for the input of the host model in a similar way as in [10]. The interface for outputting the result of the transformation is also modelled. Hence, MoTif-Core is a closed system. MoTif-Core is inherently a timed event-based language. This allows transformation languages to specify the time duration for the matching phase (Matcher) and for the rewriting phase (Rewriter) separately, which gives the opportunity to express real-time transformations. Note that the remaining AtomicDEVS elements do not consume time (their time advance is 0). It also offers the possibility to add an orthogonal dimension to the causality of the flow of a transformation. An example of such a transformation language can be found in [11]. Being discrete-event based, MoTif-Core allows to specify asynchronous transformation languages and can provide a data flow of execution. MoTif-Core models can be executed by a DEVS simulator. Looking more closely at the behaviour of the different RulePrimitive elements, only packets or exception events are exchanged among them. Consequently, when an exception is thrown by a RulePrimitive, it can be propagated up in the coupling hierarchy of CRules and be presented to the user. It is then possible to trace back the complete path where the exception arose as well as the exact time. Therefore, one can extrapolate that the result of the execution of a MoTif-Core model is either the new model or an exception, if the elements are combined accordingly. This may be a lead towards model transformation debugging. FUJABA’s maybe statement is used for exceptions, but it is only available for statement activities (i.e., Java code). MoTif-Core primitives are independent from one another as they do not share any information in common. Furthermore, the packets they exchange contain enough information for each primitive to process it. The ControlPrimitive provide join points to parallel execution of other MoTif-Core primitives. Therefore, a distributed execution of MoTifCore models is possible with an appropriate distributed DEVS simulator. 16 Figure 8: A basic MoTif ARule re-constructed with MoTif-Core primitives 4.2 Applications of MoTif-Core The simplest rule-based model transformation entity is the rule itself. Figure 8 shows how an ARule in the MoTif transformation language is mapped onto a MoTif-Core model. The original semantics of an ARule can be found in [5]. The pattern on the right-hand side of the figure preserves its semantics expressed in terms of MoTif-Core. The graphical syntax of MoTif-Core primitives is a rounded rectangle labelled on the top right by its type (M for Matcher, I for Iterator, W for Rewriter, R for Resolver, B for Rollbacker, S for Selector, and Y for Synchronizer). Inside it, the optional alias followed by a column and then a name identify the primitive. The Composer is represented by a double-lined rounded rectangle labelled with the composer’s type. In this figure, the composition of the different primitive MoTif-Core elements are encapsulated in a Composer of type ARule. First, when a packet is received by the Matcher, all matches in the graph of the graph transformation rule it encodes are found and stored in the packet. If none were found, the Composer outputs the unchanged packet from the failure outport CFailOut. Otherwise, the Iterator receives the modified packet output from the Matcher and selects one match. Then, the rewriter receives this packet and transforms the graph according to the selected match (specified in the packet). If the rewriting fails, an exception is sent outside the Composer through CExceptionOut. Otherwise, the selected match is removed and the packet is sent to the Resolver. It validates the consistency of the transformation that just occurred with respect to possibly other pending matches in the packet from other rules. In the case of a failure, an exception is again sent whereas in case of a successful application, the newly modified packet is sent through the success outport CSuccessOut. The channels between the CCancelIn inport and all the ACancelIn inports were omitted for clarity in the figure. The time of the Matcher and Rewriter are those specified in the original ARule since MoTif supports timed transformations. Although the concept of exceptions was not originally present in MoTif, the process of de-constructing and re-constructing its elements allowed us to enhance the transformation language. MoTif-Core is not only a timed language, but also allows parallel composition of models. Since the different primitive models are designed such that they wait for an input event to arrive at an inport, they can inherently run in parallel inside a Composer. GReAT [4] is a well-known graph transformation language with asynchronous behaviour. For example, Figure 9(a)2 presents a Test block where two Cases (atomic or composite rules) can be applied. When a Test block receives a packet in GReAT, the packet is tested on all the Cases. If multiple Cases succeed, only one will be applied non-deterministically. Figure 9(a) shows how a Test block can be re-constructed using MoTif-Core primitives. The MoTif-Core model consists of a Composer composing two ARules (one for each Case, assuming they each consists of an atomic rule). However, a Selector (Cut) ensures that at most one of them will get applied 2 The figure is taken from [4]. 17 (a) (b) Figure 9: A Test block in GReAT showing two cases in (a) and its de-construction in MoTif-Core in (b) by sending the appropriate cancel event to the ARules as soon as a packet is received. Since GReAT is not a timed model transformation language, we assume that the Matcher and the Rewriter of both ARules consume the same time. First, the two Matchers each receive a clone of the packet that the Composer received. Then, assuming both are in success mode, the select function of the Composer will choose one of them to output. Without loss of generality, suppose Case1 is chosen. Consequently the Selector receives the packet from ASuccessIn and instantaneously sends it through ASuccessOut. However, both Iterators receive it. To solve this problem, the Selector also sends a cancel event through ACancelOut which both Iterators receive and only the one mentioned in the exclusion list (in this case Case1) does not cancel its activity. The same behaviour as previously described for the ARule follows from there. For the case where one of Matchers fails, the same scenario applies since the Composer will still select the successful one. Finally, in the case where both Matchers fail the Composer selects one of them and the Selector sends the packet it receives through AFailOut. Therefore the MoTif-Core model is indeed semantically equivalent to the asynchronous Test block in GReAT. Again, a side-effect of this re-construction is that it allows one to extend the GReAT language with exception handling. 5 Related work The closer work to our knowledge is [12]. In the context of global model management, the authors define type system offering a set of primitives for model transformation. Their approach, however, does not deal with exceptions nor with concept of time. Nevertheless, their framework is able to achieve higher-order transformations. This is in fact realized in our approach by transforming a MoTif transformation entities into MoTif-Core models, the transformation itself being expressed in MoTif-Core. The GP graph transformation language [13] consists of transformation primitives at a higher-level of abstraction then MoTif-Core. Although it is performs very efficiently, it is a small language that does not scale for multi-paradigm modelling in the domain of transformations. Other graph transformation tools, such as VIATRA [6] and GReAT [4], have their own virtual machine used as an API. In our approach, since the primitive operations are modelled, they are completely compatible with other existing 18 model transformation frameworks. 6 Conclusion In this report, we have motivated the need for providing a collection of primitives for model transformation languages. We have defined T-Core which describes precisely each of these primitive constructs. The de-construction process of model transformation languages enabled us to re-construct existing model transformation features by combining TCore with, for example, an SBL language. This allowed us to compare different model transformation languages using a common basis. Finally, we have shown how the combination of T-Core with certain existing modelling languages, such as DEVS, can lead to novel transformation languages. An example is MoTif-Core, which allowed us to enhance and introduce new model transformation features, such as exception handling and the dimension of time. Now that these primitives are well-defined, efficiently implementing each of them might lead to more efficient model transformation languages. Also, for future work, we would like to closely compare MoTif-Core with QVT-Core as they seem to express transformations at the same level of abstraction. We would also like to investigate further on the notion of exceptions and error handling in the context of model transformation. References [1] Syriani, E. and Vangheluwe, H. (2009) Matters of model transformation. Technical Report SOCS-TR-2009.2. McGill University, School of Computer Science. [2] Jouault, F. and Kurtev, I. (2006) Transforming models with ATL. MTiP’05, January, LNCS, 3844, pp. 128–138. Springer-Verlag. [3] Fischer, T., Niere, J., Turunski, L., and Zündorf, A. (2000) Story diagrams: A new graph rewrite language based on the Unified Modelling Language and Java. In Ehrig, H., Engels, G., Kreowski, H.-J., and Rozenberg, G. (eds.), Theory and Application of Graph Transformations, Paderborn (Germany), November, LNCS, 1764, pp. 296–309. Springer-Verlag. [4] Agrawal, A., Karsai, G., Kalmar, Z., Neema, S., Shi, F., and Vizhanyo, A. (2006) The design of a language for model transformations. SoSym, 5, 261–288. [5] Syriani, E. and Vangheluwe, H. (2009) Discrete-Event Modeling and Simulation: Theory and Applications. CRC Press, Boca Raton (USA). [6] Varró, D. and Balogh, A. (2007) The model transformation language of the VIATRA2 framework. Science of Computer Programming, 68, 214–234. [7] Object Management Group (2009) Unified Modeling Language Superstructure. [8] Rensink, A. and Kuperus, J.-H. (2009) Repotting the geraniums: On nested graph transformation rules. In Margaria, T., Padberg, J., and Taentzer, G. (eds.), GT-VMT’09, York (UK), March EASST. [9] Zeigler, B. P. (1984) Multifacetted Modelling and Discrete Event Simulation. Academic Press. [10] Syriani, E. and Vangheluwe, H. (2007) Programmed graph rewriting with DEVS. In Nagl, M. and Schürr, A. (eds.), AGTIVE 2007, Kassel (Germany), LNCS, 5088, pp. 136–152. Springer-Verlag. [11] Syriani, E. and Vangheluwe, H. (2008) Programmed graph rewriting with time for simulation-based design. In Pierantonio, A., Vallecillo, A., Bézivin, J., and Gray, J. (eds.), ICMT 2008, Zürich (Switzerland), July, LNCS, 5063, pp. 91–106. Springer-Verlag. [12] Vignaga, A., Jouault, F., Bastarrica, M. C., and Brunelière, H. (2009) Typing in model management. In Paige, R. (ed.), Theory and Practice of Model Transformations (ICMT’09), Zürich (Switzerland), June, LNCS, 5563, pp. 197–212. Springer-Verlag. 19 [13] Manning, G. and Plump, D. (2008) The GP programming system. GT-VMT’08, Budapest (Hungary), March ECEASST, pp. 235–247. 20