Mapping the model to the template
The mapping file can contain a number of different mappings. This chapter will explain each mapping available.
Value
The value mapping is the simplest mapping. It creates a variable to use in the template by defining a name and an expression to calculate it:
<value name="component" eval="dataElement.component.name"/>
Expressions in mapping files are written in OGNL.
ConditionalValue
A conditionalValue will allow you to define a value which differs based on a set of conditions:
In this example, the value fileName can be overwritten by defining the option 'demo.filename'.
<conditionalValue name="fileName">
<option if="dataElement.getOption('demo.fileName').defined" eval="dataElement.getOption('demo.fileName').value"/>
<defaultOption eval="dataElement.name"/>
</conditionalValue>
A conditionalValue can contain multiple options. The expander will resolve the conditions top-down. Once a condition returns true, the value will be calculated with the eval expression.
Group
A group mapping will group values into an object.
E.g. in this example the values in the group can be accessed in the template as <targetElement.packageName>
and <targetElement.name>
:
<group name="targetElement" if="field.linkField neq null">
<value name="name" eval="field.linkField.targetElement.name"/>
<value name="packageName" eval="field.linkField.targetElement.packageName"/>
</group>
An if condition can be added to only create the group if that condition is fulfilled.
Let
The let mapping does not have any direct effect on the template. Rather, it's used to define variables which can be used in other mappings.
See Extending the scope.
List
The list mapping iterates over a collection and creates a list of mapped objects.
<!--
Get a list containing the names of the Fields in a DataElement.
-->
<list name="fields">
<foreach name="field" in="dataElement.fields"/>
<value name="name" eval="field.name"/>
</list>
The foreach syntax has been introduced in expanders-core:8.1.0
A list mapping can contain multiple <foreach>
mappings.
The iterations will be nested, but the end result will be a single flat list.
<!--
Obtain every field in a component.
-->
<list name="fields">
<foreach name="dataElement" in="component.dataElements"/>
<foreach name="field" in="dataElement.fields"/>
<value name="dataElementName" eval="dataElement.name"/>
<value name="name" eval="field.name"/>
</list>
Filtering List
Use the <filter>
mapping to select which items to include.
<list name="fields">
<foreach name="field" in="dataElement.fields"/>
<!-- Only take fields where the valueField is defined. -->
<filter name="isValueField" eval="field.valueField neq null"/>
<value name="name" eval="field.name"/>
</list>
Duplicates can be removed by using <distinct>
.
<list name="fieldTypes">
<foreach name="field" in="dataElement.fields"/>
<filter name="isValueField" eval="field.valueField neq null"/>
<!-- Remove all items that are duplicates based on the value presented in the eval. -->
<distinct name="distinctTypes" eval="field.valueField.type"/>
<value name="name" eval="field.valueField.type.name"/>
</list>
Sorting
Lists can be sorted with <sorted>
.
<list name="fields">
<foreach name="field" in="dataElement.fields"/>
<value name="name" eval="field.name"/>
<!-- Sort by field name -->
<sorted eval="field.name"/>
</list>
List Composition
Use <item>
to add a single static item to a list.
<list name="fields">
<!-- Always add an id field -->
<item>
<value name="name" eval="'id'"/>
</item>
<foreach name="field" in="dataElement.fields"/>
<value name="name" eval="field.name"/>
</list>
Use <items>
to combine lists.
<!--
Create a separate mapping for LinkFields and ValueFields and then combine them.
-->
<list name="fields">
<items>
<foreach name="field" in="dataElement.fields"/>
<filter name="isValueField" eval="field.valueField neq null"/>
<value name="name" eval="field.name"/>
<value name="type" eval="field.valueField.javaType"/>
</items>
<items>
<foreach name="field" in="dataElement.fields"/>
<filter name="isLinkField" eval="field.linkField neq null"/>
<value name="name" eval="field.name"/>
<value name="type" eval="field.linkField.linkFieldType.dataType"/>
</items>
</list>
Assert
You can check if the model is valid by using asserts:
<assert name="versionField_isValueFieldType" message="Option `isVersion` can only be used in combination with a valueFieldType"
eval="field.valueField neq null"/>
Include
Lastly, the include mapping will include another mapping file and add it to the mapping.
<include path="SomeCommonMapping.xml"/>
<!-- or -->
<include path="/path/to/SomeCommonMapping.xml"/>
Extended OGNL functionality
String formatting
A few attributes have been added to help with java.lang.String
formatting.
Attribute | Example |
---|---|
.upper ,.toUpper | dataElement to DATAELEMENT |
.lower ,.toLower | DataElement to dataelement |
.firstToUpper | dataElement to DataElement |
.firstToLower | DataElement to dataElement |
.path ,toPath | net.demo.package to net/demo/package |
.kebab | dataElement to data-element |
.camelCase | data_element to dataElement |
.snakeCase | dataElement to data_element |
.pascalCase | dataElement to DataElement |
String interpolation
The mapping file supports string interpolation to create Strings in a compact way.
<value name="path" eval="format('${component.name}/${dataElement.name}')"/>
List helpers
The java.util.List
class has been extended to provide the following commonly used operations.
Attribute | Description & Example |
---|---|
.exists(predicate) | List contains an element matching the predicate.dataElement.fields.exists(:[ #this.linkField neq null ]) "The dataElement has linkFields" |
.notEmpty | List has one or more elementscomponent.dataElements.notEmpty "component has dataElements" |
.flatten | Flatten a List<List<T>> to List<T> application.components.{ dataElement }.flatten "get a list of dataElements in an application" |
Lambda support
Ognl lambdas (:[ ... ]
) can be used to define Function
, Supplier
and Predicate
from the java.util.function
package.
Class | Example |
---|---|
Function | optional.map(:[ #this.name ]) |
Supplier | optional.orElseGet(:[ #root.dataElement.name ]) |
Predicate | list.exists(:[ #this.getOption('option').defined ]) |