| Sign In/My Account | View Cart |
Template-Based Code Generation with Apache Velocity, Part 2
Pages: 1, 2, 3, 4
As you have seen in part one, a VTL template references information
belonging to a Velocity context. Therefore, in order to
generate Java classes, you can insert instances of IOMClass
into the context so that they can provide data such as
the names of the classes, the attributes, etc. Proceeding like that, the
transformation data model for the engine will be the Internal Object
Model, as shown in Figure 3.

Figure 3. IOM as data model for Velocity
Thus, since the template knows the IOM, it will be possible extract the information for each class and generate the proper code.
Basically, we just need to implement the startClass
method of the Exporter interface, assigning the current IOMClass instance
to the Velocity context and invoking the transformation.
The code is as follows:
public class IOMVelocityExporter implements Exporter {
private final static String TEMPLATE = "IOMTemplate.vm";
private GeneratorUtility utility = null;
public void initialize() throws Exception {
Velocity.init();
utility = new GeneratorUtility();
}
public void startClass(IOMClass cl) throws Exception {
VelocityContext context = new VelocityContext();
context.put("class", cl);
context.put("utility", utility);
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!");
}
/* Non-implemented methods not reported */
}
Note: The utility object is an instance of GeneratorUtility,
containing the firstInUpperCase method, which will be
used by the template. That way, it is also associated with the context.
As you'll see, techniques and syntax to generate code are delegated to the template. Before explaining how you can write that template, I'd like to clarify the way in which we want to implement the associations. In this example, I consider four different cases of relations between two classes. Figure 4 shows those cases.

Figure 4. Different types of multiplicity
As you can see, when the class A has a bidirectional
association with just one class, B, the generator creates
the setXxx/getXxx methods for both A and B.
If the association is unidirectional, B won't have the setA
and getA methods, because that navigation is not allowed.
When the A class can be associated to more B
classes, A contains addB and getAllB,
while B contains setA and getA,
if the association is bidirectional, and no method otherwise.
The template has the responsibility to judge, for each
association, the right case, and then to generate the proper code
according to Figure 4. To do that, it can use the isNavigable
and multiplicity attributes of the IOMRole
classes aggregated to IOMAssociation (see the detailed class
diagram in Figure 5).

Figure 5. Detailed class diagram for IOMAssociation
Here is the template:
## IOMTemplate.vm
// Generated by IOMVelocityExporter
import java.util.*;
public abstract class $class.Name {
#foreach( $att in $class.Attributes )
#set($javaType = $utility.getJavaType($att.Type))
// $att.Name
private $javaType $att.Name;
public $javaType get$utility.firstToUpperCase($att.Name)() {
return this.$att.Name;
}
public void set$utility.firstToUpperCase($att.Name)($javaType $att.Name) {
this.$att.Name = $att.Name;
}
#end
#set($associations = $class.getMyAssociations())
#foreach( $assoc in $associations )
#if($assoc.getStartRole().getClassInvolved()==$class)
#set($role=$assoc.getEndRole())
#else
#set($role=$assoc.getStartRole())
#end
#set($targetClass=$role.getClassInvolved().Name)
#set($paramClass=$utility.firstToLowerCase($targetClass))
#if($role.isNavigable())
// Association
// $class.getName() -- $targetClass
// Navigable : $role.isNavigable()
// Multiplicity: $role.getMultiplicity()
#if($role.Multiplicity=="1")
public abstract $targetClass get$targetClass();
public abstract void set$targetClass($targetClass $paramClass);
#else
public abstract ArrayList getAll$targetClass();
public abstract void add$targetClass($targetClass $paramClass);
#end
#end
#end
}
The template creates attribute definitions and related accessor
methods in the same way we saw in part one. Then it defines the way to
create methods for handling the associations. First, it figures out
whether the current class is the end or the start role of the
association; eventually $targetClass will be a reference
to the associated class, and $paramClass contains the
identifier used for instances and parameters of the target class (if $targetClass="Order",
then $paramClass="order").
Then the template checks whether or not the association is
navigable. If it isn't, no method will be generated. If it is, and
multiplicity is "1", the template defines the getXxx and setXxx
methods. Otherwise, it defines the getAllXxx and addXxx
methods, because the multiplicity is "*".