This repository hosts a set of Eclipse plugins enabling the convenient definition of GTS morphisms as well as the amalgamation of GTSs based on such morphisms.
We currently do not yet have an update site or fully functional feature. As a result, the plugins can only be used in development mode, following the steps below:
- Install Eclipse, making sure to install EMF, Xtext, and Henshin (in the latest version). You may also wish to include an installation of EMFatic for easier editing of Ecore metamodels.
- Clone the repository and import all projects except for the example project into your workspace.
- Right-click on the GenerateXDsmlCompose.mwe2 file and choose
Run As/MWE2 workflow
to ensure all implementation files are correctly generated (this may be helpful to do also when pulling a new version of the repository). If Eclipse is showing errors in the projects, ignore them and run the generation anyway. After the generation and the rebuild of the workspace, all errors except for one on N2.ecore should have disappeared. The remaining error is expected. - There may be some missing source folders in some of the plugin projects (marked by a red exclamation mark). To fix this issue, simply right-click on the plugin project and choose
New/Folder
, then type the name of the missing folder. - Choose
Run/Eclipse Application
to start a fresh Eclipse with the plugins installed. - Create a new project (or import the example projects in the repository) and add a file with extension
.gts
. In this file, you will be able to specify your GTS morphisms.
GTSs and GTS morphisms are expressed in .gts
files. These are text files using the syntax below (syntax completion is available throughout the Eclipse editor).
A GTS consists of a type graph (an Ecore metamodel) and an optional Henshin module with Henshin graph-transformation rules (note that Henshin units are not currently supported by the tool). You can specify a GTS using a GTS literal as below:
gts name {
metamodel: "XXX"
behaviour: "YYY"
}
Here, name
can be an arbitrary, optional name for the GTS that may later be used to reference the GTS. The metamodel
clause references an Ecore package (which must be found in a .ecore
file on the classpath of the containing project) defining the metamodel (or typegraph) of the GTS. The behaviour
clause references a Henshin module (which must be found in a .henshin
file on the classpath of the containing project) the rules of which are considered to be the rules of the GTS. We currently only support Henshin (although we have plans to support other graph-transformation engines in the future) and do not support Henshin units. It is acceptable to leave out the behaviour
clause.
Some alternative forms of specifying GTSs exist; these all differ primarily by what is specified between the curly braces. We will discuss GTS families and GTS amalgamation further down in this documentation. Furthermore, wherever the keyword gts
is used, you can also use the keyword xdsml
instead.
Any GTS specification may be annotated with two modifiers:
export
in front of thegts
keyword indicates that the.ecore
(and optionally the.henshin
) file of the GTS should be generated into thesrc-gen
folder of the containing project. This currently works only for amalgamated GTSs, but we expect to also support family-based GTSs in the future.interface_of
GTSs are formed from the original metamodel and rules by only considering a sub-GTS typable over the metamodel elements explicitly annotated with@Interface
. This is particularly useful for GTS amalgamation as described below.
Finally, a GTS specification can reference another named GTS. This is particularly useful when referencing a pre-defined GTS from a mapping specification (see below). The example below shows how:
gts MyGTS {
metamodel: "A"
behaviour: "B"
}
gts MyReferencingGTS interface_of {
MyGTS
}
Here, MyReferencingGTS
is the same as MyGTS
except that it only uses elements annotated @Interface
in the metamodel.
A GTS morphism is specified as a mapping between two GTSs, using a map
clause:
map {
from {
metamodel: "YYY"
behaviour: "YYY"
}
to {
metamodel: "XXX"
behaviour: "XXX"
}
type_mapping {
class YYY => XXX
reference YYY => XXX
attribute YYY => XXX
...
}
behaviour_mapping {
rule XXX to YYY {
param yyy => xxx
object yyy => xxx
link yyy => xxx
slot yyy => xxx
...
}
...
}
}
Here, from
and to
each specify a GTS. The block in curly braces after from
and to
is actually a GTS specification (see above) with the gts keyword left out. If you want to name a GTS in such an in-line position, you can simply add the gts
keyword and the name between from
/to
and {
. If your GTSs do not have rules the behaviour_mapping
clause should also be left out and the file only specifies a clan-morphism between the metamodels.
The mandatory type_mapping
section describes the type-graph morphism part of the GTS morphism by providing a clan morphism between the two metamodels. This is achieved through a list of mapping statements that map a class
, reference
, or attribute
.
Similarly, the optional behaviour_mapping
section describes rule mappings. Each rule mapping is started using the keyword rule
followed by the name of the rule in the source GTS, the keyword to
, and the name of the rule in the target GTS. Note that in the papers, the order rule names are mapped from target to source, but as this is only a technical formality, we decided that it would be more appropriate to use the more intuitive direction for the specification of GTS morphisms.
Each rule mapping again contains a list of mappings for objects, links, and slots (attribute constraints) in the LHS and RHS of the rule as well as for rule parameters. NACs or PACs are currently not yet supported and neither are other forms of constraints. Each mapping is again in the direction source to target. Only named objects can be mapped; therefore it is recommended that all objects in the GTS's rules be named. Objects and links that occur in the kernel of the rule only need to be mapped once. Link names are synthesised following this pattern: [<source_object_name>-><target_object_name>:<reference_name>]
.
Extensive validation is performed for any mapping specification, including to check whether it represents a (potential) GTS morphism. Eclipse error and warning markers provide information and hints about the results of these checks. Slot mappings are considered valid if the associated expressions are syntactically identical, subject to parameter renaming.
The system will create error markers if type or behaviour mappings are not complete. As it can be quite tedious to type out all parts of the mapping, it is possible to ask the system to automatically complete a partial mapping. To do so, simply add the keyword auto-complete
at the start of the specification:
auto-complete map {
...
}
As long as the mappings specified do not break the conditions for a GTS morphism, the system will attempt to complete the morphism automatically. You can request for the completed morphisms to be exported as .gts
files for inspection.
It is possible to claim that only a unique auto-completion to a morphism exists. This is done by adding the unique
keyword like so:
auto-complete unique map {
...
}
The editor will add a warning marker to the unique
keyword to show that this claim has not been checked yet. To check unique completability, explicitly request a validation by running the first Validate
item from the editor's context menu. If auto-completion is not unique, an error marker will be added to the file. This provides quick-fix suggestions for mappings to add to sufficiently constrain the possible auto-completions. Suggestions are provided in order of potential impact; the top suggestion should offer the quickest path to unique auto-completion.
To see the results of auto-completion, right-click on your .gts
file and select "Generate auto-completions". The auto-completions will be saved in files in the src-gen/
folder of your project.
A unique auto-completion can be requested to produce an inclusion mapping by adding the keyword inclusion
:
auto-complete unique inclusion map {
...
}
This will require that the to
and from
GTS have the same metamodel and rules (module filtering by interface_of
). inclusion
cannot be used with the various virtual-rule commands (see below).
An inclusion-autocompletion will create an inclusion morphism by mapping elements by EMF identity.
When a rule in the source GTS cannot be mapped to any rule in the target GTS, it can be mapped to a virtual rule. To indicate that a rule should be mapped to a virtual rule, write a rule mapping of the following form:
rule init to virtual
Note that virtual
is a language keyword, rules named "virtual"
are not supported. From such a rule mapping, the tool will generate a virtual rule with the same structure as the source rule and use that in the mapping. Note that to-virtual rule mappings cannot specify any element mappings; these are all implicit. This is so because the rule is dynamically generated only when needed. At the same time, there is only one valid mapping between source rule and virtual rule, so there is no need to specify it explicitly.
Mapping to arbitrary virtual rues may affect behaviour-preservation properties of the morphism. To help with this, it is possible to constrain virtual rules to be identity rules; that is their left- and right-hand sides must be identical. Only identity rules can be mapped to virtual identity rules, of course, and the tool will check this. To specify a rule mapping to a virtual identity rule use the following form of rule mappings (where init
is the name of a rule in the source GTS):
rule init to virtual identity
Note that the word identity
is a keyword in the morphism language. It is therefore not possible to map rules named "identity"
.
Where possible, auto-completion will consider completing by introducing to-virtual or even to-identity rule mappings. This behaviour can be restricted by claiming auto-completion is possible using only to-identity rule mappings or without using to-virtual mappings at all. To do so, use one of the following forms:
auto-complete to-identity-only map { ... }
to claim that only to-identity mappings might need to be introduced and
auto-complete without-to-virtual map { ... }
to claim that no to-virtual mappings will need to be introduced to complete the morphism.
Conversely, you can also establish rule mappings from virtual empty source rules. Note that there is no need to consider identity source rules or any other more complex source rules: morphisms of the graphs constituting a rule will be from the source rule to the target rule, so for empty source rules such morphisms will trivially exist.
To define a rule mapping from an empty source rule write:
rule empty to do
where do
is the name of a rule in the target GTS. "empty"
is a keyword in the language and cannot be the name of a rule.
You can ask auto-completion to consider introducing from-empty rule mappings automatically. Note that this is very likely to reduce the chances of producing unique auto-completions as from-empty mappings can be trivially introduced and can be trivially complemented with to-virtual mappings to ensure all rules in both GTSs have a mapping. In order to produce somewhat more intuitive behaviour, the tool will (1) not try to introduce from-empty mappings if a mapping with an actual source rule can be found, and (2) only try to introduce from-empty rule mappings if explicitly instructed to do so. To allow from-empty mappings to be included, use the following syntax:
auto-complete allow-from-empty map { ... }
You can specify that the source or target of a GTS morphism should be taken from a GTS family by providing the definition of the family and the sequence of transformers to apply to the family's root GTS when picking the GTS you actually want. Our FASE paper [2] has more information on GTS families.
To specify a GTS family, replace the GTS specification with one that follows this format:
{
family: {
metamodel: "XXX"
behaviour: "YYY"
transformers: "ZZZ"
}
using [
unitName(param1, param2, ...),
unitName2(param1, param2, ...)
]
}
Here, metamodel
and behaviour
describe the root GTS of the family as usual. transformers
references a Henshin module (this must be typed over Ecore and Henshin) with the transformer rules of the GTS family. Finally, the using
clause indicates the sequence of transformer applications, including their actual parameters, to be used in deriving the correct GTS from inside the family. Currently, two types of parameters are supported: qualified names can be used to refer to classes, references, rules, or graph elements in the current GTS and string literals in double quotes can be used to provide string parameters. Other types of parameters are currently not supported, but may be added in future versions of the tool. Scoping isn't implemented in a fully dynamic manner at the moment, so there is no code-completion support for qualified-name parameters yet.
GTS family specifications as above can be used anywhere a GTS is expected to be provided.
Once a valid morphism has been described (either as a complete map or by using unique auto-completion), GTS amalgamation can be performed (as per [1]). Where the source GTS is declared using interface_of
, amalgamation will assume an inclusion to be defined by the @Interface
annotations. It is currently not checked whether this is also an extension, so use at your own peril. interface_of
for the target GTS is currently not supported when amalgamating GTSs.
To specify GTS amalgamation, use a special form of GTS specification:
gts name {
weave: {
map1: interface_of(A)
map2: AB
}
}
Here, A
is a reference to an existing named GTS. AB
is a reference to a named mapping (name mappings by adding the name just after the map
keyword). No further checks of the morphisms are undertaken and no guarantees are given wrt semantics preservation of the amalgamation step. Both map1
and map2
can be defined either by referencing an existing named mapping or by using the interface_of
keyword.
The weave
clause can be extended with parameters specifying the rules to use when generating names for the amalgamated model elements. By default, weaving will preserve the names of all model elements that contributed to a given woven element. If these names are all identical, the new model element will have the same name. Otherwise, all names will be joined together using _
as the separator. Names of model elements that are not mapped from the kernel GTS will be prefixed with left__
(for map1
) or right__
(for map2
), respectively, to indicate their provenance.
The following parameters can be used to change this behaviour. These parameters are given as a comma-separated list in parentheses after the weave
keyword and before the :
.
preferMap1TargetNames
/preferMap2TargetNames
can be used to indicate that only the names from the respective mapping should be preserved.preferKernelNames
can be used to indicate that the names from the kernel GTS should be preserved.dontLabelNonKernelElements
can be used to indicate that names of elements not from the kernel GTS should be left unchanged.
If any naming option leads to names that are not unique within their scope, the weaver will fall back to the default naming strategy for these elements.
For any amalgamated GTS that is labeled export
, the automatic builder will generate a corresponding .ecore
and (possibly) .henshin
file in the src-gen/
folder.
[1] Francisco Durán, Antonio Moreno-Delgado, Fernando Orejas, and Steffen Zschaler: Amalgamation of Domain Specific Languages with Behaviour. Journal of Logical and Algebraic Methods in Programming, 86(1): 208--235, Jan. 2017. [pdf] [http]
[2] Steffen Zschaler and Francisco Durán: GTS Families for the Flexible Composition of Graph Transformation Systems. 20th International Conference on Fundamental Approaches to Software Engineering (FASE'17), 2017. [pdf ((c) Springer)] [slides] [http]
[3] Steffen Zschaler, Francisco Durán: GTSMorpher: Safely Composing Behavioural Analyses Using Structured Operational Semantics. Robert Heinrich, Francisco Durán, Carolyn Talcott, and Steffen Zschaler (eds.), Composing Model-Based Analysis Tools, Springer, 2021. [http]