Scripting Java with JSR 223 - Example of writing a 20-line template engine from personal experience

The article is devoted to Java developers, whom life has made or so far only makes move forward, towards a bright agile future. It is supposed that the reader is familiar with Java, Javascript and heard about JSR 223.

Many very often had to argue about "which is better, X or Y". Previously, it was a Java / C ++ pair, now the emphasis has shifted more towards scripting languages.

Just the other day, colleagues were discussing with their colleagues whether it would be worthwhile to teach all these innovations to those who, with their favorite language (whether Java or C ++) passed fire, water and copper pipes and it seems that now they can do everything. I think it's worth it.

There are so many examples of elegant code written in any language, but nothing is more memorable than good code written in a functional language. It will be difficult to surprise those who use Javascript every day, but for adherents of pure Java, without "harmful" scripting impurities, something may seem like a revelation. A confirmation of this is a simple example that I had to face.

The small Java project I'm working on is to write a simple Swing interface, it should be distributed through Web Start. And here it turns out that a person accustomed to an abundance of additional libraries on the server side (apache commons, spring, freemarker) left alone with Java should make a difficult choice for himself: adding third-party libraries to the project simply because there is a “delicious” class or write something yourself. Experience seems to allow you to choose the second path, but at the same time the complexity of the project increases significantly.

For example, I had to deal with the task of generating fairly complex HTML. The data is taken from the model, it seemed like this, here you can use Velocity or Freemarker, but the thought that I would add hundreds of classes to the project just to beautifully draw the page did not bother me. On the other hand, I understand that writing the parser itself is not half an hour, plus testing ... although stop! But what if you write it in Javascript? Fortunately, Javascript is quite easy to execute in JRE, moreover, in Java7 it includes a Javascript Engine that knows and knows Javascript 1.8, which means a lot of tasty "goodies." And I decided to try.

The syntax is chosen like JSP: <%%> is a collection of statements, and <% =%> is expression, respectively. For testing, I used the jrunscript script console. The idea is very simple and completely repeats what JSP does: generate a certain code or function from the template, the execution of which will return the finished page. The simplest example of a test template:

js> var string = "Variable x is <% if ( 'undefined' == typeof x ) { %> undefined <%} else { %> <%= x %><%}%>"

In order to process such a template, it is enough to break it using the split function:

js> string.split(/<%(.*?)%>/).forEach(function(x,i)print((i+1)+': '+x))
1: Variable x is
2:  if ( 'undefined' == typeof x ) {
3:  undefined
4: } else {
5:
6: = x
7:
8: }
9:


The forEach call was needed just to nicely print the array. As you can see, the lines are odd, and the even between the expressions. And we don’t need another, now it’s a matter of technology from this array to form the body of the function.

js> string.split(/<%(.*?)%>/).map(function(s,i) (i % 2
  >     ? (s[0] == '=' ? 'this.push(' + s.slice(1) + ');' : s)
  >     : (s && 'this.push(' + uneval(s)  + ');')
  > )).join('\n')
this.push("Variable x is ");
 if ( 'undefined' == typeof x ) {
this.push(" undefined ");
} else {
this.push(" ");
this.push( x );
}


And that moment for which I started all this. We need to write a function generator, in fact it will compile our template:

js> function compile(template) new Function(template.split(/<%(.*?)%>/).map(function(s,i) (i % 2
  >      ? (s[0] == '=' ? ['this.push(', ');'].join(s.slice(1)) : s)
  >      : (s && ['this.push(', ');'].join(uneval(s)))
  > )).join('\n'))
js> compiled = compile(string)
function anonymous() {
    this.push("Variable x is ");
    if ("undefined" == typeof x) {
        this.push(" undefined ");
    } else {
        this.push(" ");
        this.push(x);
    }
}


Our story is drawing to a close, an experienced reader has probably already guessed that we will add the resulting lines to an array using the push function.
It remains to test the result:

js> var array = []; compiled.call(array); array.join('')
Variable x is  undefined
js> x = 5
js> compiled.call(array = []); array.join('')
Variable x is  5


It remains to decorate the code so that it is convenient to use and the template handler is ready.

Conclusion: I needed about half an hour to write a fairly powerful template handler in Javascript, while using Java, I even find it difficult to say if it would take a day or a week. As for the week, this is of course a joke, but it is not far from the truth.

Also popular now: