The JSON/XML mapper Piriti is heavily based on deferred binding and code generation.
As I started to implement code generators in Piriti I looked around how other projects deal with it and read through
the official documentation on the
GWT site. The usual way to generate code is to extend com.google.gwt.core.ext.Generator
and then call
GeneratorContext.tryCreate(TreeLogger, String, String)
. The returned PrintWriter is then often wrapped into
some kind of IndentedWriter
like the one used by GWT itself. This class adds methods to indent and unindent code and supports printf()
like
behaviour. Finally the writer is used to generate all code. This in turn results in code which looks like that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
I can’t help, but this code somehow reminds me of the old times, where we generated HTML code in servlets. This approach might work as long as the amount of generated code is small. In Piriti the code generation process is somewhat complex and distributed over several classes. Changing the generated code became very difficult and error-prone. Only the correct use of writer.indent() and writer.outdent() is not a trivial task. To some extent this problem can be solved by the use of an abstract base class, which contains common code. The generated class would extend from the abstract base class. But at the end of the day you have to generate some code in the concrete subclass.
Velocity to the rescue
Velocity is a Java-based template engine. It permits anyone to use a simple yet powerful template language to reference objects defined in Java code. Velocity supports for loops, if-then-else conditions and custom macros. Templates can include other templates. This way you can put common code in extra templates and reuse it in other templates. Velocity is mainly used in web projects for HTML generation. Another common use case is to generate email bodies. But there’s no reason not to use Velocity for code generation in GWT.
Doing so the above code snippet becomes something like that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
If you compare the two code snippets you get the idea! The velocity based code is much more readable. As you can see the velocity template contains variable references like $elements. Before the template is rendered all necessary variables must be put into the so-called Velocity context which is more or less a big map. If the variable refers to a java object you can use its properties and even call methods.
To use Velocity for code generation you have to setup the Velocity engine, create the Velocity context and merge the template. In Piriti the engine is configured with the following properties:
1 2 3 4 5 6 7 8 9 |
|
Finally the code generation process is reduced to the following lines:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
If you want to delve deeper into the code generation process in Piriti, check out the trunk and take a look into the code.