ModelJoin integration
This document introduces the ModelJoin integration built into the CodeGenerator. It will describe the driving abstraction in depth and also take a look at how this abstraction will then be used to generate specific views on different Synchronization models for our Synchronization UI.
To provide a broad overview of how things work together, here is the general CodeGenerator workflow as well as how ModelJoin fits into that process:
About ModelJoin
ModelJoin is a simple DSL used to specify unified views on different heterogeneous models. That is, instances from multiple models may be merged according to a number of simple rules such as the equality of attribute values. In order to get familiar with ModelJoin we highly recommend reading its technical report [1].
From now on, we will assume you are familiar with ModelJoin and its general structure.
ModelJoin abstraction
The ModelJoin abstraction is our logical representation of ModelJoin files. At a conceptual level it is nothing more than a reification of the formal components of a ModelJoin description. That is, all parts of the ModelJoin grammar are made explicit through corresponding Java classes.
Although the CodeGenerator is written in Scala for the most part, we decided to use Java for the ModelJoin abstraction. This decision was mainly driven by two reasons:
|
In the end, the whole abstraction simply boils down to the above mentioned simple concept - although of course there are some implementation specific details. This quickly becomes clear when taking a look at (an excerpt of the) implementation diagram:
The grammar
package contains all reified elements of the ModelJoin grammar. Yet, in
order to keep things together, a number of helper classes are necessary. These are located under
core
and mainly act as wrappers for other elements of ModelJoin descriptions. However these
classes are of no real further interest for the other parts of our abstraction. Therefore they
mostly act as mere data-holders without much functionality attached.
Some examples for these helper classes are OCLConstraint
or ClassResource
which act as wrappers
for arbitrary OCL expressions [2] and fully qualified references to classes within a source model.
Both the ModelJoin
as well as the JoinExpression
classes act as mere data-holders so a look at
their respective JavaDoc pages should suffice to get an idea of how they work. Things get more
interesting when looking at the keep
expressions.
Composite structure of keep
expressions
In general, there are six different types of keep
expressions. Those are:
-
keep aggregate
-
keep attributes
-
keep calculated
-
keep reference
-
keep subtype
-
keep supertype
The last three of these expression are structured compositely. That is, they in turn contain an
arbitrary number of nested keep
expressions.
To accommodate for this structure, the Visitor
Pattern is used [3]. See the JavaDoc of KeepExpression.KeepExpressionVisitor
for details.
Generating the ModelJoin representation
In order to instantiate all elements of a ModelJoin description through a natural and fluid
interface, a number of Builder implementations [4] are available. Those are
either located directly within their corresponding classes, or in the util
package if they span
multiple classes. The ModelJoinBuilder
is available to generate entire ModelJoin descriptions
whereas the JoinFactory
takes care of the creation process for individual joins.
Putting things together, a complete construction process may look like this:
ClassResource person = ClassResource.fromQualifiedName("contact.Person");
ClassResource employee = ClassResource.fromQualifiedName("company.Employee");
AttributePath dateOfBirth = AttributePath.from(personClass, "dateOfBirth");
ModelJoinBuilder.createNewModelJoin()
.add(JoinFactory.createNew()
.natural()
.join(person)
.with(employee)
.as(ClassResource("unified.Person")
.keep(KeepAttributesExpression
.keepAttributes(dateOfBirth))
.done()
)
.build();