It is often the case that you need additional properties in the templates; these properties should not be added to the metaclasses directly, since they are often specific to the specific code generation target and thus should not "pollute" the metamodel.
It is possible to define such extensions external to the metaclasses. For details see the Xtend Language Documentation , we provide an simple example here.
Assume we wanted to change the Attribute s part of the template as follows:
«FOREACH attribute AS a» private «a.type» «a.name»; public void «a.setterName()»( «a.type» value ) { this.«a.name» = value; } public «a.type» «a.getterName()»() { return this.«a.name»; } «ENDFOREACH»
To make this work, we need to define the
setterName()
and
getterName()
operations. We do this by writing
a so-called extension file; we call it java.ext
. It
must have the .ext
suffix to be recognized by
Xpand; the
Java
name is because it contains
Java-generation specific properties. We put this file directly into the
templates
directory under
src
, i.e. directly next to the
Root.xpt
file. The extension file looks as
follows:
First, we have to import the data metamodel; otherwise we would not be able to use the Attribute metaclass.
import data;
We can then define the two new operations
setterName
and
getterName
. Note that they take the type on
which they are called as their first parameter, a kind of "explicitly
this". After the colon we use an expression that returns the
to-be-defined value.
String setterName(Attribute ele) : 'set'+ele.name.toFirstUpper(); String getterName(Attribute ele) : 'get'+ele.name.toFirstUpper();
To make these extensions work, we have to add the following line
to the beginning of the Root.xpt
template
file:
«EXTENSION templates::java»
In case you cannot express the "business logic" for the expression
with the expression language, you can fall back to Java. Take a look at
the following extension definition file. It is called
util.ext
and is located in src/datamodel/generator/util
:
String timestamp() : JAVA datamodel.generator.util.TemplateUtils.timestamp();
Here, we define an extension that is independent of a specific model element, since it does not have a formal parameter! The implementation of the extension is delegated to a static operation of a Java class. Here is its implementation:
public class TemplateUtils { public static String timestamp() { return String.valueOf( System.currentTimeMillis() ); } }
This element can be used independent of any model element – it is available globally.
Sometimes, it is necessary to access extensions not just from
templates and other Xtend files but also from Java code. The following
example is of this kind: We want to define properties that derive the
name of the implementation class from the entity name itself. The best
practice for this use case is to implement the derived property as a
Java method, as above. The following piece of code declares properties
for Entity
:
package datamodel; import data.Entity; public class EntityHelper { public static String className( Entity e ) { return e.getName()+"Implementation"; } public static String classFileName( Entity e ) { return className(e)+".java"; } }
In addition, to access the properties from the template files, we
define an extension that uses the helper methods. The
helper.ext
file is located right next to the helper
class shown above, i.e. in the datamodel
package:
import data; String className( Entity e ) : JAVA datamodel.EntityHelper.className(data.Entity); String classFileName( Entity e ) : JAVA datamodel.EntityHelper.classFileName(data.Entity);
In addition to these new properties being accessible from Java
code by invoking EntityHelper.className(someEntity)
, we can
now write the following template:
«EXTENSION templates::java» «EXTENSION datamodel::generator::util::util» «EXTENSION datamodel::helper» «DEFINE Root FOR data::DataModel» «EXPAND Entity FOREACH entity» «ENDDEFINE» «DEFINE Entity FOR data::Entity» «FILE classFileName()» // generated at «timestamp()» public abstract class «className()» { «FOREACH attribute AS a» private «a.type» «a.name»; public void «a.setterName()»( «a.type» value ) { this.«a.name» = value; } public «a.type» «a.getterName()»() { return this.«a.name»; } «ENDFOREACH» } «ENDFILE» «ENDDEFINE»
For completeness, the following illustration shows the resulting directory and file structure.