Annotation Processor 201 — Generate code with template

Emma Suzuki
3 min readSep 19, 2016

In the last blog (https://medium.com/@emmasuzuki/annotation-processor-101-your-first-custom-annotation-a3db9ae48046#.geoazobi0), I introduced how to setup an annotation processor and in the bottom of the blog, I left two points to improve the annotation processor class I wrote. In this article, I address the first point.

Recap

This is the process method of the original GreetProcessor class.

At 2. I am writing a class to be generated. There are static code and logic mixing up. It is not easy to read nor write and I may easily miss “}” or “;”.

How would I improve it?

Apache Velocity

Apache Velocity is the Java-based template engine. It reads a template written in Velocity Template Language (VTL) and injects variables into the template and renders to an output. It provides a clear separation of static contents and logic.

Let’s change the GreetProcessor class to use the Velocity.

  1. Add the Velocity dependency to processor/build.gradle
dependencies {
...
compile group: 'org.apache.velocity', name: 'velocity', version: '1.7'
}

2. Create “template” folder under processor/src/main/resources

3. Create a velocity template (greeter.vm) under the template folder.

.vm is the file extension for the velocity template. This is how greeter.vm looks like.

$something is a parameter that to be passed into the template. Besides that, static code are exactly how you want to see in an output file.

4. Modify 2. part of the process method.

First, initialize the VelocityEngine.

VelocityEngine velocityEngine = new VelocityEngine();
velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, "./processor/src/main/resources/template");
velocityEngine.init();

I put the greeter.vm to a specific path so I tell the VelocityEngine to look for my template in the specific path.

Secondly, initialize the VelocityContext and add dynamic values to it.

VelocityContext velocityContext = new VelocityContext();
velocityContext.put("packageName", packageName);
velocityContext.put("className", "Greeter");
velocityContext.put("methodName", "hello");
velocityContext.put("names", getFormattedNames(names));

This VelocityContext’s each key name should match with $-prefixed parameters in the template.

In this example, I format names in Java, but alternatively you can use VTL’s #foreach syntax and put for loop logic inside the template.

At last, get the template.

Template template = velocityEngine.getTemplate("greeter.vm");

5. Write the output.

Change

writer.write(builder.toString());totemplate.merge(velocityContext, writer);

template.merge will inject parameters in velocityContext into the template.

Let’s Run

Same output is generated using Velocity.

Conclusion

With Velocity template, the process method got cleaner and code has clear separation of static and dynamic contents. As code which to be generated becoming longer and larger, Velocity will help you with maintainability.

Now, Let’s Accelerate Your Coding with Velocity!

--

--

Emma Suzuki

A bit of many technology, full of curiosity. Mobile, Web, Data Engineer.