📜 ⬆️ ⬇️

Scripting Java using JSR 223 - an example of writing a template engine in 20 lines from personal experience

The article is dedicated to Java developers, whom life has forced, or is only forcing, to move forward, to a bright agile future. It is assumed that the reader is familiar with Java, Javascript and heard about JSR 223.

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

Literally the other day it was discussed with colleagues whether it was worthwhile to teach all these innovations to those who, with their favorite language (be it Java or C ++), went through fire, water and copper pipes and now everything seems to be able to. I think it's worth it.

There are so many examples of elegant code written in any language, but nothing is remembered as good code written in a functional language. Those who use Javascript every day will be difficult to surprise with something, but for devotees of pure Java, without “harmful” script impurities, something may seem like a revelation. Proof of this is a simple example with which I had to face.
')
A small Java project I am working on is writing a simple Swing interface, it should be distributed via Web Start. And here it turns out that the person who is used to the abundance of additional libraries on the server side (apache commons, spring, freemarker) left alone with Java must make difficult choices for himself: add third-party libraries to the project simply because there is a “tasty” 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 face the task of generating quite complex HTML. The data is taken from the model, it seemed it was here, you can use Velocity or Freemarker, but the idea that I would add hundreds of classes to the project just to draw a beautiful page would not let me rest. On the other hand, I understand that writing the parser itself is not half an hour, plus testing ... although stop! and what if you write it in Javascript? Fortunately, Javascript is fairly easy to execute in the JRE, and moreover in Java7 it includes the Javascript Engine, which knows and is able to use Javascript 1.8, which means a lot of delicious "plush". And I decided to try.

The syntax chose to be like JSP: <%%> is a set of statements, and <% =%> is expression. For testing, I used the jrunscript scripting console. The idea is very simple and completely repeats what the JSP does: generate a code or function from the template, the execution of which will return the finished page. The simplest example of a template for testing:

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 call forEach was needed just to beautifully print the array. As you can see, the lines are odd, and the even ones are expressions between them. And we don’t need another one; now it’s up to the technology from this array to form the function body.

 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 it all. 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 coming to an end, an experienced reader probably already guessed that we’ll add the resulting strings 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: it will take me about half an hour to write a sufficiently powerful template handler to Javascript, while using Java I even find it difficult to say for sure that 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.

Source: https://habr.com/ru/post/151976/


All Articles