Bookmark this on Hatena Bookmark
Share on LinkedIn
Pocket

I have been recently writings to convert in . This is a technical post to compare some frameworks to generate .

  • using a template engine
  • using an AST/Source to code
  • others

Each solution has it drawbacks and strengths.

Template engine

Well with a template engine like Velocity or JET, you basically can write anything you want.

You won’t be constrained by an API and you may also generate syntaxic invalid files.

The main drawback is that you can’t write in a one shot your implementation. You will have to  store your template in your resources, write a code to access it before generating your code.

Depending of the template engine, it can be a real pain.

public class HelloVelocity {
 public static void main(String[] args) {
// Boring code to initialize the template engine
 VelocityEngine ve = new VelocityEngine();
 ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
 ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());

 ve.init();
 // Try to find the code template
 Template t = ve.getTemplate("codeTemplate.vm");
 VelocityContext ctx = new VelocityContext();
 // Initialize the context.
 ctx.put("name", "velocity");
 ctx.put("date", (new Date()).toString());
 
 List temp = new ArrayList();
 temp.add("1");
 temp.add("2");
 ctx.put("list", temp);
 
 StringWriter sw = new StringWriter();
 // Finally render
 t.merge(ctx, sw);
 
 System.out.println(sw.toString());
 }
}

Using an AST/Source to source

I will present you an interesting framework based on JDT Java compiler.

This framework is called Roaster, is open-source and hosted on Github.

This is a maven library which provides a fluent API to generate java classes. Here an example:

final JavaClassSource javaClass = Roaster.create(JavaClassSource.class);
javaClass.setPackage("com.company.example").setName("Person");

javaClass.addInterface(Serializable.class);
javaClass.addField()
  .setName("serialVersionUID")
  .setType("long")
  .setLiteralInitializer("1L")
  .setPrivate()
  .setStatic(true)
  .setFinal(true);

javaClass.addProperty(Integer.class, "id").setMutable(false);
javaClass.addProperty(String.class, "firstName");
javaClass.addProperty("String", "lastName");

javaClass.addMethod()
  .setConstructor(true)
  .setPublic()
  .setBody("this.id = id;")
  .addParameter(Integer.class, "id");

Where the framework differs, is that it relies on JDT. The source code (parsed or created programmatically is associated to a JDT Dom Tree). Basically we are using the Eclipse functionalities to build the Java source code and unparse it.

Therefore, an hybrid approach can be chosen mixing parsing and programmation to produce your source code.

JavaClassSource javaClass =
  Roaster.parse(JavaClassSource.class, "public class SomeClass {}");
javaClass.addMethod()
  .setPublic()
  .setStatic(true)
  .setName("main")
  .setReturnTypeVoid()
  .setBody("System.out.println(\"Hello World\");")
  .addParameter("java.lang.String[]", "args");
System.out.println(javaClass)

The main drawback of this solution is that I did not find any way to insert invalid java code (aka a plain String) into my class structure. The string is parsed by JDT and if its invalid, it throws an exception. Since I potentially have some invalid structures in Beanshell to be converted as Java, I have been blocked without solution.

Other frameworks

I want to present an outsider : JavaPoet.

This framework hosted on Github is half-way between a template engine and an AST generation framework,

JavaPoet is offering a nice Fluent interface to produce your Java source code.

All things are easy to understand and manipulate : structures, flow control statements, types.

MethodSpec main = MethodSpec.methodBuilder("main")
    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
    .returns(void.class)
    .addParameter(String[].class, "args")
    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
    .addMethod(main)
    .build();

JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
    .build();

javaFile.writeTo(System.out);

I have been using it in several projects and it’s possible to inject plain Strings directly inside a valid structure. Rather efficient (it generates the package folder structure for you), it has a main drawback, you cannot declare your own imports (at the exception of the static ones).

Therefore if  you are building your code using a mix of  Fluent blocks and String templates, you will be soon short of imports to have a perfectly compiling code.

And you know what, they don’t want to add the feature grr :

https://.com/square/javapoet/issues/512

https://github.com/square/javapoet/issues/507

Share on LinkedIn
Pocket


Sylvain Leroy

Senior Software Quality Manager and Solution Architect in Switzerland, I have previously created my own company, Tocea, in Software Quality Assurance. Now I am offering my knowledges and services in a small IT Consulting company : Byoskill and a website www.byoskill.com Currently living in Lausanne (CH)