Monday, July 9, 2012

Simple(r) String Templates with Commons Lang

Whenever I have a need to use string templating in a project, I reflexively turn to Velocity, which works great, but it:
  • is "heavy": can be difficult to get into a project and functional, has some quirks with classloaders and logger systems, etc
  • is old: last updates were back in 2010
Velocity is also loaded with features like control constructs, loops, branches, etc, which seem like overkill when all I really need to do is format email notifications with some variables (I've already gone the route of client-side view templates for my UI stuff these days).

I know there are a million ways to solve this using homegrown regex's and other such things, but I found a convenient helper in Commons Lang3: StrSubstitutor. It allows you to replace variables in a string, denoted by:
${varName}
I've written a simple wrapper around it, which can pull a template string from a file on the classpath, caching it using a simple WeakHashMap.  This could be extended to use the nicer caching framework found in the fantastic Google Guava project, perhaps.

package com.sungard.testtracker.util;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.text.StrSubstitutor;
import com.google.common.collect.Maps;
public class Templates {
private static final WeakHashMap<String, String> templateCache = new WeakHashMap<String, String>();
private String template;
private Map<String, String> values;
private Templates(String template) {
this.template = template;
this.values = Maps.newHashMap();
}
public static Templates template(String template) {
return new Templates(template);
}
public Templates value(String name, String value) {
values.put(name, value);
return this;
}
public String render() {
String actualTemplate = loadTemplate(template);
return StrSubstitutor.replace(actualTemplate, values);
}
String loadTemplate(String template) {
String t = templateCache.get(template);
if (t == null) {
try {
t = IOUtils.toString(getClass().getClassLoader()
.getResourceAsStream(template));
templateCache.put(template, t);
} catch (Exception e) {
throw new IllegalStateException("Error loading template: "
+ template, e);
}
}
return t;
}
}
view raw Templates.java hosted with ❤ by GitHub
An example use of this class might be to stick a template text file into src/main/resources/templates/foo.template (for all of you fellow Maven'rs out there!), and invoke it like:
String output = Templates.template("templates/foo.template")
                         .value("foo", "bar")
                         .value("other", "val")
                         .render();

No comments: