Template-Based Code Generation with Apache Velocity, Part 2
Pages: 1, 2, 3, 4
The Platform-Specific Model
Using the Internal Object Model as the input data model for Velocity
makes, as we have seen, the Exporter implementation very
straightforward. The downside of this approach is that all of the logic to
access the internal model is delegated to the template. That's not so
good, because a template language is less powerful that an imperative
language like Java. Thus, you'll have a number of limitations in
manipulating the data model. In production work, when the code to be
generated is very complex, the template has to perform lots of checks
and interpret the logic behind the design. As result, we get messy
templates, which are very difficult to handle.
For this reason, many generators, along with the platform-independent model, also uses a Platform-Specific Model. It is written by using an imperative language but, unlike the internal model, the PSM knows about the target platform. It has the duty to break out all of the data into the fields required for the templates, which do little more than placement and formatting.
The exporter module I'm going to describe will use a PSM, which
contains information from the Internal Object Model in a ready-to-use
simplified format. The PSM will be the data model for the Velocity
transformation; therefore, it will be associated to a Velocity context.
Figure 7 shows that architecture.

Figure 7. Code generation with a Platform-Specific Model
Figure 8 shows the class diagram related to the PSM.

Figure 8. Class diagram for the Platform-Specific Model
The PSMClass class is aggregated to the PSMAttribute
class. It has also two associations to the PSMAssociation
class: singleAssociations and multiAssociations.
This means that the template, without performing any check, will
receive separate lists for single or multiple associations. The PSMAttribute
class represents an attribute; it has a name and a type
and also another member called upperName, which contains
the name of the attribute with the first letter in upper case. That
will prevent the template from invoking a context function for doing
that operation in generating setXxx/getXxx methods. The PSMAssociation
class contains the attribute targetClass (the target
class name) and paramClass, which is the same as targetClass
but with the first letter in lower case; this will be used as an
identifier for instances and parameters of targetClass.
You can find the implementation of the PSM classes inside of the source
code for this article.
In order to place the PSM in the picture, you have to implement a
new exporter that converts the IOM into the PSM and starts the
transformation process using Velocity. This exporter, called PSMVelocityExporter,
is implemented as follows:
package com.codegenerator.velocity;
import com.codegenerator.*;
import java.io.*;
import java.util.*;
import org.apache.velocity.app.*;
import org.apache.velocity.*;
import org.apache.velocity.exception.*;
public class PSMVelocityExporter implements Exporter {
private final static String TEMPLATE = "PSMTemplate.vm";
private PSMClass currentClass = null;;
public void initialize() throws Exception {
Velocity.init();
}
public void startClass(IOMClass cl) throws Exception {
currentClass = new PSMClass();
currentClass.setName(cl.getName());
}
public void endClass(IOMClass cl) throws Exception {
VelocityContext context = new VelocityContext();
context.put("class", currentClass);
Template template = Velocity.getTemplate(TEMPLATE);
BufferedWriter writer =
new BufferedWriter(new FileWriter(cl.getName() +
".java"));
template.merge(context, writer);
writer.flush();
writer.close();
System.out.println("Class " + cl.getName() +
" generated!");
}
public void startAttribute(IOMAttribute attr)throws Exception {
PSMAttribute at = new PSMAttribute();
at.setName(attr.getName());
at.setNameUpper(GeneratorUtility.firstToUpperCase(attr.getName()));
at.setType(GeneratorUtility.getJavaType(attr.getType()));
currentClass.addAttribute(at);
}
public void startAssociation(IOMAssociation association,
IOMClass sourceClass) throws Exception {
IOMRole role = association.getStartRole();
if (role.getClassInvolved()==sourceClass)
role = association.getEndRole();
if (role.isNavigable()) {
PSMAssociation ast = new PSMAssociation();
ast.setTargetClass(role.getClassInvolved().getName());
ast.setRefClass(GeneratorUtility.firstToLowerCase(
role.getClassInvolved().getName()));
if (role.getMultiplicity().equals("1"))
currentClass.addSingleAssociation(ast);
else
currentClass.addMultiAssociation(ast);
}
}
/* Non-implemented method not reported */
}
The currentClass data member is a reference to the PSMClass
currently in process. The startClass method takes the
name from the IOMClass in input and assigns it to currentClass.
The endClass method will be invoked once the PSMClass
is ready; therefore, it starts the transformation by using currentClass
as the Velocity context. The startAttribute
method creates a PSMAttribute from the IOMAttribute
and assigns it to currentClass. The startAssociation
method creates a PSMAssociation instance and performs all of
the needed checks to judge whether or not the association is navigable
and to find out the multiplicity. As you will see, those checks will no
longer be inside of the template.
The template used to work with this exporter is implemented as follows:
## PSMTemplate.vm
// Generated by PSMVelocityExporter
import java.util.*;
public abstract class $class.name {
#foreach( $att in $class.Attributes )
// $att.Name
private $att.Type $att.Name;
public $att.Type get${att.NameUpper}() {
return this.$att.Name;
}
public void set${att.NameUpper}($att.Type $att.Name) {
this.$att.Name = $att.Name;
}
#end
#foreach( $assoc in $class.SingleAssociations)
// Association
// $class.Name -- $assoc.TargetClass
public abstract $assoc.TargetClass get${assoc.TargetClass}();
public abstract void set${assoc.TargetClass}($assoc.TargetClass $assoc.paramClass);
#end
#foreach( $assoc in $class.MultiAssociations)
// Association
// $class.Name -- $assoc.TargetClass
public abstract ArrayList getAll${assoc.TargetClass}();
public abstract void add${assoc.TargetClass}($assoc.TargetClass $assoc.paramClass);
#end
}
It is very simple and does not perform any checks. Since the data model is the PSM, the template can just retrieve all of the attributes, single associations, and multiple associations, and create the methods to handle them.
You can launch the generator using the PSMVelocityExporter
like this:
java com.codegenerator.Generator order.xml XML PSMVelocityExporter
It creates the Order, OrderItem, and Article classes.
Conclusion
A real-world code generator must use templates. In these two articles, we have analyzed the huge benefits of using templates and provided examples with Java and Apache Velocity. In particular, in this second part, we saw how to integrate templates with a IOM-based code generator by using the IOM itself as data model for the transformation or resorting to a Platform-Specific Model. The second solution, which we can call a hybrid model, is better than the hard-coded model, because you don't have the formatting portion embedded as print statements in the generator code. It is also better than templates alone, because the PSM is written with an imperative language, which is better for doing complex model transformations and validations.
Resources
[1] Apache Velocity
[2] F. Aliverti-Piuri, "Bug Prevention with Code Generation: A J2EE Case Study," ONJava.com, March 2004.
[3] J. Herrington, "Code-Generation Techniques for Java," ONJava.com, September 2003.
[4] J. Herrington, Code Generation In Action, Manning, 2003.
[6] G. Naccarato, "Writing a Code Generator in Java," JavaPro Online, March 2004.
[7] G. Naccarato, "Implement an IOM-Based Code Generator," JavaPro Online, March 2004.
[8] G. Naccarato, "Template-Based Code Generation with Apache Velocity, Part 1," ONJava.com, May 2004.
[9] Source code for this article
Giuseppe Naccarato has a degree in computer science and works as software developer for an IT company based in Glasgow (UK). His main interests are J2EE- and .Net-related technologies. Contact Giuseppe at http://www.giuseppe-naccarato.com.
Return to ONJava.com.
-
Will it works as plugin
2008-08-07 06:29:51 Srinath.K [View]
-
Eclipse plugin
2005-07-21 03:37:30 ntony [View]
-
Open Source Template Engines
2004-06-16 18:09:50 fj_rodriguez [View]
-
It's proven to work in practice
2004-06-11 06:34:25 Jesco [View]
-
It's proven to work in practice
2006-01-10 08:07:32 mustafabulut [View]