Model Converter

A model converter transforms one object graph (the input model) into another object graph (the output model), which are both held in memory. That output model not necessarily needs to be a completely different, new object graph. It could well be the same graph with only a few modifications being applied.
You can either use the basic model converter API, or you can use the model converter framework that is found in the open source component com.gs.gapp.converter.

Basic Virtual Developer API

You either have to implement org.jenerateit.modelconverter.ModelConverterI or extend org.jenerateit.modelconverter.MessageProviderModelConverter in order to provide a model converter. The relevant methods that need to be implemented are: clientConvert() or convert()

The following picture shows the API:

Model Converter API

Advanced API of com.gs.gapp.converter

The purpose of this API is to have a uniform way of developing an object graph transformation and to be able to reuse and extend parts of the transformation logic. The following picture shows the most important parent classes for model converters and model element converters, AbstractConverter and AbstractModelElementConverter

Advanced Model and Model Element Converters

An input model to be processed by a model converter and an output model to be created by a model converter contain zero or more model elements:

input:  M(i)   = {e(i,0), e(i,1), ..., e(i,n)}

output: M(i+1) = {e(i+1,0), e(i+1,1), ..., e(i+1,n)}

A model converter T that transforms M(i) to M(i+1) contains one or more model element converters c:

T = {c(0), c(1), ..., c(n)}

The 4 most important methods of a model element converter are:

/**
 * This method checks if this converter is responsible for conversion.
 * This method may be overwritten by clients.
 * By default it checks if the source class is identical.
 *
 * @param origElement
 * @param prevElement
 * @return true if this element converter is responsible for the conversion
 */
boolean isResponsibleFor(Object origElement, ModelElementI prevElement);

/**
 * @param origElement
 * @param prevElement
 * @return the just created element
 */
T onCreateModelElement(S origElement, ModelElementI prevElement);

/**
 * This method holds the actual implementation of the conversion logic.
 *
 * @param origElement
 * @param newElement
 */
onConvert(S origElement, T newElement);

/**
 * Call this method from within an onCreateModelElement() or
 * onConvert() in order to let the framework execute an
 * appropriate model element converter for the given origElement.
 *
 * @param resultClass
 * @param origElement
 * @param converterClasses
 * @return
 */
M convertWithOtherConverter(Class<M> resultClass,
                            Object origElement,
                            Class<?>... converterClasses);

When extending AbstractModelElementConverter, you typically only need to implement onCreateModelElement() and onConvert().

A model element converter c determines, whether it is responsible for the conversion of a model element e(i,k) of the input model or not. You can visualize the model conversion rules in a matrix, where ‘+’ means that a model element converter is responsible to convert a model element e.

e → c c(0) c(1) c(2) c(n)
e(i,0) + - - -
e(i,1) - - + -
e(i,2) - + - -
e(i,3) - - - -
e(i,m) - - - +

The behavior of a model element converter can be parameterized by using one of the entries of the enum com.gs.gapp.metamodel.converter.ModelElementConverterBehavior in its constructor. The parameterization has an effect on …

  • whether an element converter can only be indirectly used, by calling convertWithOtherConverter()
  • whether onCreateModelElement() and onConvert() are both called right after one another
  • whether onCreateModelElement() needs a second method parameter be passed in order to have sufficient context for the element creation

The parameterization allows to solve the following issues:

  • avoid infinite loops by circular method calls
  • let a model element converter return a previously created output element when it gets called more than once with one and the same input element
  • a constructor for a to-be-created model element might need another model element being passed to it

The process of a model conversion starts by identifying all responsible model element converters for all elements of the input model. Then all identified model element converters are called, one after the other. Indirect calls of other model element converters are done by calling convertWithOtherConverter().

Order of Model Converter Execution

If several model converters form a tree of model converters, there is a predefined order of execution of those converters (pre-order traversal). When you have a look at the genset tree of transformation steps, the order is left-to-right and then top-to-bottom. Often it is important to know about this order so you can depend on certain model elements already having been created.