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

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
Figure 7. Code generation with a Platform-Specific Model

Figure 8 shows the class diagram related to the PSM.

Figure 8
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.

[5] Code Generation Network

[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.