JIT Classes
In my previous blog entries, I already talked a little about the structure of project BigNumbers: I introduced the ExpressionBuilder and Expression class and interface, and said that project BigNumbers already provides two implementations of ExpressionBuilder: one that generates an Expression implementation that provides an 'interpreter' of the expression, and one that generates an Expression implementation that wraps an instance of a class that has been dynamically created, on the fly, at runtime.
This has not been my first attempt at generating Java classes dynamically. A couple of years ago, I toyed around with BCEL for a while, in order to create my own JDO-alike persistency mechanism. Now, even though BCEL is incredibly powerful - it is definitely not the most user friendly byte code engineering library around.
Javassist
So when I started to work on my JIT ExpressionBuilder, I figured I would look around to see if something of interest happened in this space in the last few years. And my o my, I ran into so many projects that I can't see straight anymore. However, the only tool that really stood apart from the rest is Javassist.
The cool thing about Javassist is that it does not require you to write the bytecode yourself if you are constructing a method. Instead, it allows you to specify the body of your operation in plain old Java. Needless to say that that really makes life easier.
StringTemplate
With all of this beautiness readily available, I started to generate the Java code from source code, by an endless stream of writer.writeln("......"); statements. It didn't take me very long to figure out that this would become unmanageable very rapidly, so I needed something else: I needed a template engine. Again, there are plenty of template engines out there, but none of them fitted my three requirements:
- I wanted it to support generating Java source code in a sincere way, and not just XML-alike files;
- I prefered to have a template engine that would compile templates into classes, in order to limit the runtime dependencies of my
ExpressionBuilder; - I wanted a mechanism that allowed me to reuse only parts of the entire template. In fact, I wanted the template to generate an entire source file, and (because of the way Javassist works) I would only use the subtemplates that generated the relevant methods in my
ExpressionBuilder.
I looked at a couple of well-known template engines (Velocity, FreeMarker), and a couple of not-so-well-known template engines, like Jamon. Jamon was a special case, and I kind of liked it. Jamon is a JSP-alike engine. The syntax slightly resembles the JSP syntax, and it generates Java sources to be compiled before you can actually use the template. (Jamon is based on strong typing, unlike many other template languages.) Unfortunately, the generated code still relies on runtime artifacts. And since I was using Maven 2, and Jamon does not yet provide a Maven plugin yet, I would have to write that myself before I would have integrated Jamon into my project.
So I continued searching, and finally ran into a library called StringTemplate. And it appears that StringTemplate is actually a great template engine to be used in combination with Javassist. Why is that?
- First of all, StringTemplate is really convenient for generating source code. You do not end up in escape sequence hell.
- StringTemplate is fast.
- StringTemplate allows you to reuse only some subtemplates if you want to.
- StringTemplate appears to be incredible convenient if you want to generate readable source code. (Like the mechanism for dealing with indent.)
Putting it Together
So here's an example of my BigNumbers StringTemplate source code. If you are like me, then it will take quite a while to figure out what's going on here. In that sense it may also be different from the other template languages. It seems that the other template languages are working very hard to make templates easy for non programmers. StringTemplate is a little different: it seems to be a template engine dedicated for programmers. So you actually need to understand the language quite well in order to figure out what is going on. But then it really gives a lot of control on what is happening.
group operations;
evaluate(variableNames, operations, roundingMode) ::= <<
public java.math.BigDecimal evaluate(com.agilejava.bignumbers.core.VariableResolver resolver) {
$variableNames:resolve(); separator="\n"$
int roundingMode = $roundingMode$;
java.math.BigDecimal intermediate;
java.util.Stack stack = new java.util.Stack();
$operations:operation(); separator="\n"$
return (java.math.BigDecimal) stack.pop();
}
>>
operation() ::= <<
$it:(it.type)()$
>>
plus() ::= <<
stack.push(((java.math.BigDecimal) stack.pop()).add((java.math.BigDecimal) stack.pop()));
>>
minus() ::= <<
intermediate = (java.math.BigDecimal) stack.pop();
stack.push(((java.math.BigDecimal) stack.pop()).subtract(intermediate));
>>
....
This is how you I use this code from Java:
private final static String getBodyEvaluate(String[] variableNames,
Object[] operations, int roundingMode) throws BuilderException {
ClassLoader loader = JITExpressionBuilder.class.getClassLoader();
InputStream stream = loader.getResourceAsStream("operations.stg");
Reader reader = new InputStreamReader(stream);
StringTemplateGroup group = new StringTemplateGroup(reader);
StringTemplate template = group.getInstanceOf("evaluate");
template.setAttribute("variableNames", variableNames);
template.setAttribute("operations", operations);
template.setAttribute("roundingMode", roundingMode);
return template.toString();
}
( Jul 22 2005, 09:36:08 AM CEST )
Permalink
Comments [0]
Big freaking numbers II
After I finished my blog entry yesterday, I kept staring at the code for a while, thinking if it could be simplified by changing the API of BigNumbers. I already had a convenience class (Function) that didn't feel very convenient at all, so I fixed that. With the new API, the snippet given yesterday:
/**
* Calculates (a / 20) * (b + 123.90)
*/
public void calculateIt(BigDecimal a, BigDecimal b) {
ExpressionBuilder builder = ....;
Expression expression = builder.create("(a / 20) * (b + 123.90)");
VariableMap map = new VariableMap();
return expression.evaluate(map.assign(a, "a").assign(b, "b"));
}
can be rewritten as:
/**
* Calculates (a / 20) * (b + 123.90)
*/
public void calculateIt(BigDecimal a, BigDecimal b) {
ExpressionBuilder builder = ....;
Expression expression = builder.create("(a / 20) * (b + 123.90)");
Function function = new Function(expression, new String[] { "a", "b" });
return function.evaluate(a, b);
}
Basically, Function is a convenience wrapper of Expression. The constructor accepts both the Expression, as well as an array of variable names. The order of the variable names is the order in which the Function will expect the arguments to arrive.


