Model Transformation (via Graph Transformation) with eNeo
A hands-on introduction to unidirectional model transformation with eNeo by @Anthony A.

eMoflon::Neo (referred to as just eNeo as from now) is part of the eMoflon tool suite.  eNeo supports model management as a layer over Neo4j, a fairly well-known graph database.  

This handbook focusses on unidirectional model transformation as a model management task.  I assume you have worked through +(Meta-)Modelling with eNeo and have eNeo installed and your workspace setup accordingly.  You should continue directly with the project from +(Meta-)Modelling with eNeo.

Preparing the beginning of the simulation

As a first preparatory step, let’s make sure the model is in a state representing a valid start of the game.  The raven must be on the first path segment, each fruit tree must have four fruits, and the fruit basket must be empty.  The model we created in +(Meta-)Modelling with eNeo and shall import into the database is not in this state as the raven is on segment 2 and the fruit basket already contains some fruits.

Setting the raven back to the first segment
Create a new file Rules.msl  in the project eneo.example.Obstgarten (parallel to the existing Language.msl) with the following contents:

import "platform:/resource/eneo.example.Obstgarten/src/Language.msl"

rule resetRaven {
  raven:Raven {
    -- -on->segment
    ++ -on->first
  }
  
  path:Pathway {
    -segments->first {
      .position : 0
    }
  }
  
  segment:PathSegment
  first:PathSegment
}

  • A rule in eMSL is conceptually similar to a pattern, but has green and red elements.  A rule is actually two patterns merged together:  a precondition pattern consisting of all black and red elements, and a postcondition pattern consisting of all black and green elements.  A rule is applied by matching the precondition pattern, then deleting all red elements and creating all green elements to establish the postcondition pattern.

In the concrete rule resetRaven, the raven is matched together with the current path segment it’s on, and the first path segment we want to transfer it to.  We are able to access all types from the Obstgarten metamodel via the import on Line 1.  The change realised by the rule is straightforward:  we delete the connection to the former segment (-- operator on Line 2), and create the connection to the first segment (++  operator on Line 3).  In the visualisation of the rule depicted to the right, retained elements are black, deleted red, and created green.

To ensure that the first path segment is really the first, an attribute condition is used on Line 11.  The segments edge from path to first must have its position attribute equal to 0.  

  • The equals operator : is used here; a few other simple operators are supported including >, >=, <, and <=.

Note that this rule assumes that the raven is already on one of the segments.  If this assumption is violated, the precondition will not match (the edge from raven to segment cannot be found) and the rule will not be applied.  Another point to note is that application of the rule also fails if the raven is already on the first segment.  To understand why it is important to know that eNeo always matches all patterns injectively, i.e., two separate elements in a pattern are never matched to the same element in a model.  For the rule to be applicable when the raven is already on the first path segment, the elements first and segment would have to be matched to the exact same element (the first path segment) in the model.  As this is impossible with eNeo, the rule will never match.  In this case, this is desirable as (re)setting the raven to the first segment when it is already there would be a waste of time.
 
  • While non-injective pattern matching can be very useful, our practical experience with teaching model transformation via graph transformation rules indicates that most people expect separate elements in a pattern/rule to match to separate elements in a model.  Some tools allow switching injective pattern matching on and off; we find it simpler to just explicitly specify an additional pattern/rule for each desired case.  Finally, there are some subtle and confusing details concerning deletion and creation for non-injectively matched patterns, which can all be avoided by restricting all application to strictly injective matches.

To take our very first rule for a spin, create a new Xtend file RunSimulation.xtend parallel to the existing Application.xtend with the following contents:

package eneo.example.Obstgarten

import java.math.RoundingMode