ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Template-Based Code Generation with Apache Velocity, Part 2
Pages: 1, 2, 3, 4

Implementing the Exporter Using Velocity

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
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
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
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 "*".

Pages: 1, 2, 3, 4

Next Pagearrow