<\/a><\/p>\nFigure 4. Overview of the Gremlin-ATL Framework<\/strong><\/p>\n <\/p>\n
3.2. ATLtoGremlin Transformation<\/h3>\n3.2.1. Transformation Mapping<\/h4>\n
Table 1 shows the mapping used by Gremlin-ATL to translate ATL constructs into Gremlin steps. An ATL Module<\/em> is translated into a Gremlin script, that represents the top-level container storing the entire query to execute.<\/p>\nMatched Rule Definitions<\/em> inside the ATL module are mapped to a sequence of steps that access all the elements of the type of the rule. For example, the matched rule definition MethodToMethodUnit<\/em> in Listing 1 is translated into the Gremlin expression g.allOfKind(\"Method\")<\/tt> that searches in the input graph all the elements representing Method<\/em> instances. Abstract Rule Definitions<\/em> are not translated, because they are called only when a specialized rule is matched. Lazy rule definitions<\/em> are translated into function definitions named with the rule’s identifier and containing their translated body.<\/p>\nMatched rule bodies<\/em> are mapped to a transform step containing the translated expressions representing rule’s out pattern and bindings. This transform step is followed by an iterate step that tells the Gremlin engine to execute the query. Abstract rule bodies<\/em> are directly mapped without creating a transform step, and generated Gremlin steps are added to the ones of the translated bodies of the corresponding specialized rules<\/em>. This approach flattens the inheritance hierarchy of a transformation by duplicating parent code in each concrete sub-rule.<\/p>\nRule Guards<\/em> defining the set of elements matched by a rule are translated into a Gremlin filter step containing the translated condition to verify. For example, the guard of the rule MethodToMethodUnit<\/em> is translated into the following Gremlin expression that first navigates the reference typeParameters<\/em> and searches if it contains at least one value: filter{!(src.getRef(\"typeParameters\").hasNext())}<\/tt>.<\/p>\nRules’ body can contain two types of ATL constructs: out patterns<\/em> representing the target element to create, and attribute\/reference bindings<\/em> describing the attribute and references to set on the created element. Out patterns<\/em> are mapped to a variable definition storing the result of the createElement<\/em> function which creates the new instance and the associated trace links. This instruction is followed by a resolveTraces<\/em> call that tells the engine to resolve the potential trace links associated to the created element. In our example it generates the sequence tHelper.createElement(\"MethodUnit\", src)<\/tt> that creates a new MethodUnit<\/em> instance in the target model and associates it with the src<\/em> element from the source model. Attribute<\/em> and Reference Bindings<\/em> are respectively translated into the mapping operation setAtt<\/em> and a Transformation Helper’s link<\/em> call.<\/p>\nOur mapping translates helper definitions<\/em> into global methods, which define a self<\/em> parameter representing the context of the helper and a list of optional parameters. This global function is dynamically added to the Object<\/em> metaclass to allow method-like invocation, improving query readability. Global helper definitions<\/em> are also mapped to global methods, but do not define a self<\/em> parameter. Finally, Global Variables<\/em> are translated into unscoped Gremlin variables.<\/p>\nATL embeds its own specification of OCL, which is used to navigate the source elements to find the objects to transform, express the guard condition of the transformation rules, and define helpers’ body. We have adapted the mapping defined in the Mogwa\u00ef framework [21] to fit the OCL metamodel embedded in ATL. In addition, we integrated our Model Mapping Definition<\/em> component in the translation in order to provide a generic translation based on an explicit mapping. The current version of Gremlin-ATL supports an important part of the OCL constructs, allowing to express complex navigation queries over models. As an example, the inline if<\/em> construct used in MethodToMethodUnit<\/em> to check whether the source element represents a constructor can be translated into the equivalent Gremlin ternary operator: src.isKindOf(\"Constructor\") ? \"constructor\" : \"method\"<\/tt>.<\/p>\n\nTable 1. ATL to Gremlin Mapping<\/caption>\n\n\nATL Expression<\/strong><\/td>\nGremlin Step<\/strong><\/td>\n<\/tr>\n\nmodule<\/td>\n | Gremlin Script<\/td>\n<\/tr>\n | \nmatched_rule definition<\/td>\n | g.allOfType(type)<\/td>\n<\/tr>\n | \nabstract_rule definition<\/td>\n | not mapped<\/em><\/td>\n<\/tr>\n\nlazy_rule definition<\/td>\n | def name(type) { body }<\/td>\n<\/tr>\n | \nabstract_rule body<\/td>\n | body1<\/sup><\/td>\n<\/tr>\n\nspecialized_rule body<\/td>\n | transform{ body U<\/tt> parent.body }.iterate()<\/td>\n<\/tr>\n\nrule_guard(condition)<\/td>\n | filter{ condition }<\/td>\n<\/tr>\n | \nout_pattern(srcEl, tgtEl)<\/td>\n | var out = tHelper.createElement(tgtEl.type, srcEl);<\/td>\n<\/tr>\n | \n<\/td>\n | tHelper.resolveTraces(srcEl, tgtEl);<\/td>\n<\/tr>\n | \nattribute_binding<\/td>\n | el.setAtt(exp)<\/td>\n<\/tr>\n | \nreference_binding<\/td>\n | el.link(exp)<\/td>\n<\/tr>\n | \nhelper_definition<\/td>\n | def name(var self, params){ expression }<\/td>\n<\/tr>\n | \n<\/td>\n | Object.metaClass.name = {\u00a0(params) -> name(delegate, params) }<\/td>\n<\/tr>\n | \nobj.helper(params)<\/td>\n | obj.helper(params)<\/td>\n<\/tr>\n | \nglobal_helper defninition<\/td>\n | def name(params) { expression }<\/td>\n<\/tr>\n | \nglobal_helper computation<\/td>\n | name(params)<\/td>\n<\/tr>\n | \nglobal_variable<\/td>\n | def name = expression<\/td>\n<\/tr>\n | \nOCL_Expression<\/td>\n | Mogwa\u00ef2<\/sup><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n 1. The body of abstract rules is duplicated in the transform step of all its sub-rules. \n2. OCL expressions are translated by an improved version of the Mogwa\u00ef framework.<\/p>\n 3.2. Operation Composition<\/h4>\nThe above mappings explain how ATL constructs are mapped individually into the corresponding Gremlin steps. In this section we present an overview of the algorithm used to compose these generated steps into a complete query. Listing 2 shows the final Gremlin output for the transformation example shown in Listing 1.<\/p>\n In order to generate a complete Gremlin query, our transformation has to navigate the input ATL model and link the generated elements together. First, the transformation searches all the helper<\/em> definitions (including global ones) and translates them according to the mapping shown in Table 1. The generated functions are added to the Gremlin script container, making them visible for the translated ATL rules. This first step generates the lines 1 to 8 for the helpers getVisibility<\/em>. Note that this initial phase also generates the function that registers contextual helpers to the Object<\/em> metaclass (lines 10-13), allowing method-like invocation in generated expressions.<\/p>\nLazy\\_rule definitions<\/em> are then translated into global functions, and added to the Gremlin script container. Out pattern<\/em> and bindings<\/em> contained in the Lazy\\_rule body<\/em> are translated into their Gremlin equivalent following the mapping presented in the previous section and appended in the generated function body.<\/p>\nListing 2. Generated Gremlin Traversal<\/strong><\/p>\n1 \/\/ getVisibility() helper\r\n2 def getVisibility(var vertex) {\r\n3 var result = vertex.getAtt(\"visibility\");\r\n4 if(result == null)\r\n5 return \"unknown\";\r\n6 else\r\n7 return result;\r\n8 }\r\n9\r\n10 \/\/ Add getVisibility to Vertex method list\r\n11 Vertex.metaClass.getVisibility =\r\n12 { -> getVisibility(delegate) }\r\n13\r\n14 \/\/ MethodToMethodUnit\r\n15 g.allOfKind(Method).filter{\r\n16 def src = it;\r\n17 !(src.getRef(\"typeParameters\").hasNext())\r\n18 }.transform{\r\n19 def src = it;\r\n20 var tgt = tHelper.createElement(\"MethodUnit\", src);\r\n21 tHelper.resolveTraces(src, tgt);\r\n22 tgt.setAtt(\"kind\", src.isKindOf(\"Constructor\") ? \"constructor\" : \"method\");\r\n23 tgt.setAtt(\"export\", src.getVisibility());\r\n24 tgt.setRef(\"codeElement\", src.getRef(\"body\").toList() as Set);\r\n25 }.iterate();\r\n<\/pre>\nOnce this initial step has been performed the transformation searches all the matched rules definitions<\/em> and creates the associated Gremlin instructions. If the rule defines a guard the generated filter<\/em> step is directly linked after the allOfKind<\/em> operation in order to select the elements that satisfy the guard’s condition. Then, the transform step<\/em> (and the associated iterate<\/em> call) corresponding to the matched rule body<\/em> is created and added at the end of the traversal. In our example this operation generates lines 15 to 18.<\/p>\nOut pattern<\/em> elements contained in the matched rule body<\/em> are retrieved and the corresponding Gremlin instructions (lines 19 to 24) are added to the transform<\/em> step closure. Finally, Rule’s bindings<\/em> are transformed following the mapping and added in the closure’s instructions. Note that helper calls inside binding’s expressions are also translated into the corresponding method calls during this operation.<\/p>\n3.3. Auxiliary Components<\/h3>\n3.3.1 Model Mapping<\/h4>\nThe Model Mapping<\/em> library defines the basic modeling operations that can be computed by a given database storing a model. It provides a simple API allowing designers to express the implicit schema of their database with modeling primitives. Model Mapping<\/em> can be manually implemented for a given database, or automatically extracted using schema inference techniques such as the NoSQLDataEngineering framework [22]. This mapping is used within the generated Gremlin query to access all the elements of a given type, retrieve element’s attribute values, or navigate references.<\/p>\nTable 2 summarizes the mapping operations used in Gremlin-ATL and groups them into two categories: metamodel-level<\/strong> and model-level operations<\/strong>. The first group provides high-level operations that operate at the metamodel level, such as retrieving the type of an element, type conformance checks, new instances creation, and retrieve all the elements conforming to a type. The second group provides methods that compute element-based navigation, such as retrieving a referenced element, computing the parent\/children of an element, and access its attributes. Finally, these model-level methods allow to update and delete existing references and attributes.<\/p>\nNote that the ATLtoGremlin<\/em> transformation only relies on the definition of these operations to generate a Gremlin query, and is not tailored to a specific Model Mapping<\/em> implementation.<\/p>\n\nTable 2. Model Mapping API<\/caption>\n\n\nOperation<\/strong><\/td>\nDescription<\/strong><\/td>\n<\/tr>\n\nallOfType(type)<\/td>\n | Returns all the strict instances of the given type<\/em><\/td>\n<\/tr>\n\nallOfKind(type)<\/td>\n | Returns all the instances of the given type or one of its sub-types<\/td>\n<\/tr>\n | \ngetType(el)<\/td>\n | Returns the type of the element el<\/em><\/td>\n<\/tr>\n | | | | | | | | | |