Skip to main content

Using StringTemplate: Part 1 "An introduction to StringTemplate"

Posted by aberrant on May 25, 2010 at 7:48 PM PDT

 

Please note: I put forth SQL examples because they can be short and are easy to understand. As other have pointed out it is not advisable to build SQL with Strings, regardless of the technique used.

What is StringTemplate?

To quote the StringTemplate home page:
          “StringTemplate is a java template engine for generating source code, web pages, emails, or any other formatted text output.”

Great, but what is a “template engine”?
          A template engine (also known as a template processor or a template parser) is a software component that is designed to combine one or more templates with a data model to produce one or more result documents (Wikipedia). To you and me that means that a template engine is an alternate way to generate complicated text. One that I feel is particularity useful. Before we dive into that, lets take a look at a few examples of traditional String construction in Java.

First we have the trusty old “+” operator.



String message = "Hello" + " World";


While convenient for small concatenations. It becomes a real pain when the embedded variable count of the amount of text increases.   



String strSQL = "UPDATE customers SET customerName='" + name
     +
"', customerAddress='" + address
     +
"', customerEmail = '”+ email + "
     +
"customerNumber = " + phone
     + " customerPurchase = " + purchase
     +
"' WHERE id=" + 12;




String new419 = "Hello "
     +
"Dear, " + name + ", \n"
     + "Permit me to inform you of my desire of "
     +
"going into business relationship with you. "
     + "I have the believe you are a reputable "
     + "and responsible and trustworthy person \n"
     + "I can do business with from the little"
     + "information so far I gathered about you "
     + "during my search for a partner and by matter "
     + "of trust I must not hesitate to confide "
     +
"in you for this simple and sincere business. ";

Next we have StringBuilder. In general it's considered a better practice to use StringBuilder because of it's performance profile.



StringBuilder sqlBuilder = new StringBuilder(
     "UPDATE customers SET customerName='").append(name).append(
     "', customerAddress='").append(address).append(
     "', customerEmail = '").append(email).append(
     "', customerNumber = '").append(phone).append(
     "', customerPurchase = '"
).append(purchase).append(
     "' WHERE id=").append(12);

Unfortunately it doesn't make building up a string significantly easier.
 

Finally we have formatted text

// Explicit argument indices may be used to re-order output.
String.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d");
>> d c b a

// Optional locale as the first argument can be used to get
// locale-specific formatting of numbers. The precision and width can be given
// to round and align the value.

String.format(Locale.FRANCE, "e = %+10.4f", Math.E);

>> e = +2,7183

// The '(' numeric flag may be used to format negative numbers with
// parentheses rather than a minus sign. Group separators are
// automatically inserted.

String.format("Amount gained or lost since last statement: $ %(,.2f",balanceDelta);

>> Amount gained or lost since last statement: $ (6,217.58)

Format is powerful but only scales up to, at most, a handful of parameters. This is not because there is some programmatic limit to the Format class, but more one of practicality. Tracking a hundred parameters by their index would be quite challenging.

Templates, a better way.

A template is simply a document with “placeholders” in it. These “placeholders” are expressions that tell the template engine where to put data.  All (okay most) white space and line feed characters are respected. This also has the nice benefit of separating the formatting (view) from the data. Here is a simple single line example (with StringTemplate syntax in red):

StringTemplate query = new StringTemplate(
     "UPDATE customers SET customerName='$customer$'," +
     " customerAddress='$address$' WHERE id=$id$"
);

query.setAttribute("customer", "Frank");
query.setAttribute(
"address", "1313 Mocking Bird Lane");
query.setAttribute(
"id", "4453");

System.out.println(query);

Output:

UPDATE customers SET customerName='Frank', customerAddress='1313 Mocking Bird Lane' WHERE id=4453

As you can see “placeholders” are delimited by $...$ (or <...> if you so choose). In this way it is reminiscent of variable substitution in languages like PHP or bash. The name inside the delimiters is used to look up the correct piece of data. Having names is important for maintaining readability. It also allows data to be pushed into the template in any order. This was a single line example. When you get to multi-line text you will probably want to use a template file. A template file is a plain text document that contains the fully formatted version of your desired output. $...$ attributes are placed thought the document to indicate parts you want replaced later by StringTemplate.

Example template file (spam-419.st):

From Address $from_addr$
To Address $to_addr$
Dear $to$,
    Permit me to inform you of my desire of going into business relationship with you. I have
    the believe you are a reputable and responsible and trustworthy person I can do business
    with from the little information so far I gathered about you during my search for a partner
    and by matter of trust I must not hesitate to confide in you for this simple and sincere business
.
...
Best Regards, $from$

Populating this template involves loading a StringTemplateGroup and using that to create an instance of your StringTemplate.



StringTemplateGroup templateGroup = new StringTemplateGroup("spam group", "templates");
StringTemplate spam419 = templateGroup.getInstanceOf(
"spam419");
spam419.setAttribute(
"to", "Collin");
spam419.setAttribute(
"to_addr", "collin@bugmenot.com");
spam419.setAttribute(
"from", "Ima Spammer");
spam419.setAttribute(
"from_addr", "ima.spammer@spammers-paradise.com");

System.out.println(spam419.toString());

The StringTemplateGroup constructor in the code above takes two parameters. The first is a "group name". In this example it is not really important, feel free to use anything you like. The second is very important. This is the location of a directory (folder) that contains the template files you wish to load. In this example I am using a relative path. Relative to where I'm executing this code I expect to see a directory called "templates". The getInstanceOf() method takes the name (without the .st extension) of the that template file to be loaded. In this case I'm attempting to load "spam419.st".

From Address ima.spammer@spammers-paradise.com
To Address collin@bugmenot.com
Dear Collin,
    Permit me to inform you of my desire of going into business relationship with you. I have
    the believe you are a reputable and responsible and trustworthy person I can do business
    with from the little information so far I gathered about you during my search for a partner
    and by matter of trust I must not hesitate to confide in you for this simple and sincere business
.
...
Best Regards, Ima Spammer

I encourage you to experiment with this template. I think you will find adding or removing lines is trivial. Changing white space in the template is trivial. Even adding new attributes (placeholders) is straight forward. StringTemplate is a powerful tool that I can't hope to cover in just one blog post. For those who are chomping at the bit to learn more I'd recommend reading the official StringTemplate documentation which is a fantastic resource. 

Have fun,
Collin

Example sources

 

 

 

Related Topics >>

Comments

StringTemplate is good; MVEL is even better

In the interest of completeness, there are several templating engines out there (as I'm sure you know).

I've always liked StringTemplate.

There's also Freemarker: http://freemarker.sourceforge.net/

And Velocity: http://velocity.apache.org/

But my personal favorite is MVEL: http://mvel.codehaus.org/MVEL+2.0+Templating+Guide

Nice article.

Best,
Laird

SQL?

I concur that it's definitely a bad idea to use SQL as an example for this. It's just asking for trouble. Use PreparedStatements or similar to avoid escaping problems, eg suppose the name in your example was

haha' --

( haha followed by closing quote then a sql comment leader forcing (if it was Sybase) all customers to have their name changed )

Same goes for XML/HTML - there are more correct tools for the job.

For other string stuff though it's pretty good.

For the sake of completeness,

For the sake of completeness, it should be mentioned that Stringtemplate features specifying a renderer by type... You could put a renderer on any inbound string to perform the escaping.

No, I agree

Both of you are absolutely right. I was more concerned with using short understandable examples then best practices. I'm going to put a note at the top.

Thanks for reading!

Collin

What about SQL Injection?

Hi Collin, Thanks for showing people about this powerful functionality. However, I do think that your example showing parameter substitution in a SQL template is a particularly bad idea. To the best of my knowledge, StringTemplate doesn't perform any special quoting of the values that are substituted in, which would make your example just as bad as constructing SQL Statements through concatenation of snippets, which people like OWASP have been trying to teach people to avoid for several years now. Parameter substitution for SQL should be done by means of PreparedStatements, or some other DB-specific mechanism like Hibernate, that deals with SQL parameter quoting, and enforces the separation of logic (the SQL query) and data (the parameters). Please would you update this post to remove the example, or else use it as an example of what NOT to do.