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 1
Pages: 1, 2

Apache Velocity

In the previous examples, I used a trivial hypothetical template engine, the transform application, which replaces the $ labels of the template with data coming from the model. Now, it is time to use a real template engine in order to generate code.



The Apache Velocity template engine is a Jakarta open source tool. It works with templates written in VLT (Velocity Template Language), a very simple template-style language. Velocity has been developed mainly to be used with Java. In fact, it uses normal Java classes, associated to a specific context, as the data model. The transformation process has as input a template and a context, and produces output in the format specified by the template. During the transformation process, labels are replaced by data coming from the context.

I have written and tested the sample code with J2SE 1.4.2 and Apache Velocity 1.4. In order to compile and execute it, the CLASSPATH environment variable must include references to velocity-1.4-rc1.jar and velocity-dep-1.4-rc1.jar.

Velocity in Action

The next example is a simple code generator using Velocity, which builds Java classes with data members and related accessor methods (getXxx()/SetXxx()) methods. The data model will be an XML document containing the names of the classes and their attributes. Here is the input model:

<?xml version="1.0" encoding="UTF-8"?>
<!-- order.xml --!>
<Content>  
	
  <Class name="Customer">
    <Attribute name="code" type="int"/>
    <Attribute name="description" type="String"/>
  </Class>
    
  <Class name="Order">
    <Attribute name="number" type="int"/>
    <Attribute name="date" type="Date"/>        
    <Attribute name="customer" type="Customer"/>        
  </Class>    

</Content>

The XML structure is very straightforward -- the <Class> element describes a class and the <Attribute> elements a data member. The example defines the Customer class (with code and description) and the Order class (with number, date and customer). The code generator will read that document in order to create the Customer and Order Java classes.

Next we implement the generator. The first step is to design an internal structure to host the XML data. We need two classes, called descriptors, representing <Class> and <Attribute>. The descriptor related to the <Class> element is the following:

// ClassDescriptor.java
package com.codegenerator.example1;

import java.util.*;

public class ClassDescriptor {
  private String name;
  private ArrayList attributes = new ArrayList();
  public void setName(String name) {
    this.name = name;
  }
  public String getName() {
    return this.name;
  }
  public void addAttribute(AttributeDescriptor attribute) {
    attributes.add(attribute);
  }
  public ArrayList getAttributes() {
    return attributes;
  }
}

It defines the ClassDescriptor class that can host data described into the <Class> element. The name attribute represents the name of the class, while attributes is an ArrayList containing AttributeDescriptor objects. That class, which is the descriptor for the <Attribute> element, is implemented as follows:

// AttributeDescriptor.java

package com.codegenerator.example1;

import java.util.*;
public class AttributeDescriptor {
  private String name;
  private String type;
  public void setName(String name) {
    this.name = name;
  }
  public String getName() {
    return this.name;
  }
  public void setType(String type) {
    this.type = type;
  }
  public String getType() {
    return this.type;
  }
}

In order to import data from the XML to the descriptors, we use a SAX parser, as shown by the following code:

package com.codegenerator.example1;

import java.util.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class ClassDescriptorImporter extends DefaultHandler {

  private ArrayList classes = new ArrayList();

  public ArrayList getClasses() {
    return classes;
  }

  public void startElement(String uri, String name, 
    String qName, Attributes attr) throws SAXException {

    // Imports <Class>  
    if (name.equals("Class")) {
      ClassDescriptor cl = new ClassDescriptor();
      cl.setName(attr.getValue("name"));
      classes.add(cl);
    }

    // Imports <Attribute>
    else if (name.equals("Attribute")) {
      AttributeDescriptor at = new AttributeDescriptor();
      at.setName(attr.getValue("name"));
      at.setType(attr.getValue("type"));
      ClassDescriptor parent = 
        (ClassDescriptor) classes.get(classes.size()-1);
      parent.addAttribute(at);

    }

    else if (name.equals("Content")) {
    }

    else throw new SAXException("Element " + name + " not valid");
  }  
}

The duty of the ClassDescriptorImport class, which extends the SAX default handler, is to transfer data from the XML to the descriptors. As you can see, each time a <Class> element is processed, the class creates a new instance of ClassDescriptor and inserts it into the class's ArrayList. Where the parser processes a <Attribute> element, the class creates a new instance of ClassAttribute and adds it to the parent ClassDescriptor. At the end of the parsing process, classes will contain the descriptors for all of the classes found within the XML document. The getClasses method returns that ArrayList.

At this point, Velocity enters into the picture. The VTL template to generate a Java class with data members and getXxx/setXxx methods is the following:

## class.vm

import java.util.*;

public class $class.Name {

#foreach($att in $class.Attributes)	
  // $att.Name
  private $att.Type $att.Name;
  public $att.Type get$utility.firstToUpperCase($att.Name)() {
    return this.$att.Name;
  }	
  public void set$utility.firstToUpperCase($att.Name)($att.Type $att.Name) {
    this.$att.Name = $att.Name;
  }
	
#end
}

The $class label is a reference to a ClassDescriptor instance; therefore, $class.Name is the result of the ClassDescriptor.getName invocation (actually, $class.Name is a shortcut for $class.getName() -- in general, a similar shortcut can be applied to all of the methods starting with get). The #foreach statement executes a loop for all of the elements contained in $class.Attributes, which represents all of the AttributeDescriptor instances associated to the current ClassDescriptor. The statements inside of the loop define the data members and the getXxx/setXxx methods according to the values of $att.Name and $att.Type.

The $utility.firstToUpperCase label invokes a user-defined method that returns the same string in input, but with the first character in upper case. That method may be useful, for example, to obtain getNumber from the number data member.

The Code Generator

What is left to implement is the main application. It reads the XML, associates the descriptors to the template by means of the ClassDescriptorImporter, and calls Velocity to perform the transformation.

You can find the complete code of the generator (the ClassGenerator class) among the resources for this article. The most important method of that class is start(). Here is the implementation:

public static void start(String modelFile, String templateFile)
  throws Exception {

  // Imports XML
  FileInputStream input = new FileInputStream(modelFile);
  xmlReader.parse(new InputSource(input));
  input.close();
  classes = cdImporter.getClasses(); // ClassDescriptor Array

  // Generates Java classes source code
  // by using Apache Velocity
  GeneratorUtility utility = new GeneratorUtility();
  for (int i = 0; i < classes.size(); i++) {

    VelocityContext context = new VelocityContext();
    ClassDescriptor cl = (ClassDescriptor) classes.get(i);
    context.put("class", cl);
    context.put("utility", utility);

    Template template = Velocity.getTemplate(templateFile);

    BufferedWriter writer =
      new BufferedWriter(new FileWriter(cl.getName()+".java"));

    template.merge(context, writer);
    writer.flush();
    writer.close();

    System.out.println("Class " + cl.getName() + " generated!");
  }

}

The method has in input the XML and the template filenames. It imports the data by using the xmlReader object, which has been previously associated to the cdImporter object, which is a ClassDescriptorImporter instance. As you can see, by means of the getClasses method, you can get the descriptors of all of the classes that have to be generated (classes ArrayList). The context object created inside of the loop is particularly important because it represents the connection between descriptor instances and template. In fact, the Context.put method associates a Java object to a template label. After executing these statements:

context.put("class", cl);
context.put("utility", utility);

The cl object (current class descriptor) can be referred by the $class label of the template, and the utility object by $utility. That last object is an instance of GeneratorUtility, containing the firstInUpperCase() method.

After creating the context and invoking put, the start method, according to the template file in input, creates a Template object and calls the merge method. It performs the template-driven transformation, taking the data from the context object, and writes the output in the stream referenced by writer.

By launching the code generator with order.xml (data model) and class.vm (template) in input, you obtain Customer.java and Order.java, implemented as follows:

import java.util.*;

public class Customer {

  // code
  private int code;
  public int getCode() {
    return this.code;
  }	
  public void setCode(int code) {
    this.code = code;
  }
	
  // description
  private String description;
  public String getDescription() {
    return this.description;
  }	
  public void setDescription(String description) {
    this.description = description;
  }

}

import java.util.*;

public class Order {

  // number
  private int number;
  public int getNumber() {
    return this.number;
  }	
  public void setNumber(int number) {
    this.number = number;
  }
	
  // date
  private Date date;
  public Date getDate() {
    return this.date;
  }	
  public void setDate(Date date) {
    this.date = date;
  }

  // customer
  private Customer customer;
  public Customer getCustomer() {
    return this.customer;
  }	
  public void setCustomer(Customer customer) {
    this.customer = customer;
  }

}

Conclusion

After developing the template-based code generator, you can use it in two ways:

  • To alter the data model to generate other classes.
  • To replace the template to produce different language syntax, or even code for a different programming language.

Both of those operations don't require any changes to the generator itself; therefore, a template-based code generator is more flexible than one that embeds the target language syntax.

In part two of this article we'll look at a more complex scenario for template-based code generation. In particular, I'll show how to use templates along with the Internal Model Object generator discussed in [7] below, and a design pattern to decouple that language-independent internal model from a language-dependent Velocity context.

Resources

[1] Apache Velocity

[2] F. Aliverti-Piuri, "Bug Prevention with Code Generation: A J2EE Case Study," ONJava, March 2004.

[3] J. Herrington, "Code-Generation Techniques for Java," ONJava, Sept. 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] Article source code

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.