Skip to content

Instantly share code, notes, and snippets.

@sma
Created July 10, 2010 10:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sma/470630 to your computer and use it in GitHub Desktop.
Save sma/470630 to your computer and use it in GitHub Desktop.
Jinja2 and/or Django like template engine
package ingwer;
import org.springframework.expression.*;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class Ingwer {
private static final Pattern TOKENS = Pattern.compile("\\{%\\s*(.+?)\\s*%}|\\{\\{(.+?)}}|([^{]+)");
private static final Map<String, Class<? extends Node>> TAGS = new HashMap<String, Class<? extends Node>>();
static {
TAGS.put("if", If.class);
TAGS.put("for", For.class);
}
private Block root;
public Ingwer(String source) {
root = parseBlock(TOKENS.matcher(source), new Block(), "");
}
public String render(Map<String, Object> values) {
StringWriter w = new StringWriter();
try {
StandardEvaluationContext c = new StandardEvaluationContext();
c.registerFunction("range", Functions.class.getDeclaredMethod("range", Integer.TYPE));
c.setVariables(values);
root.render(w, c);
} catch (Exception e) {
throw new RuntimeException(e);
}
return w.toString();
}
private static Block parseBlock(Matcher m, Block block, String end) {
boolean b = true;
while (m.find()) {
if (m.group(1) != null) {
if (m.group(1).equals(end)) break;
if (m.group(1).equals("else")) b = !b;
else block.add(parseTag(m), b);
} else if (m.group(2) != null) {
block.add(new Var(m.group(2)), b);
} else {
block.add(new Text(m.group(3)), b);
}
}
return block;
}
private static Node parseTag(Matcher m) {
String[] tokens = m.group(1).split(" ", 2);
Class<? extends Node> tagClass = TAGS.get(tokens[0]);
try {
return tagClass.getDeclaredConstructor(Matcher.class, String.class).newInstance(m, tokens[1]);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static class Functions {
public static List<Integer> range(int n) {
List<Integer> list = new ArrayList<Integer>(n);
for (int i = 0; i < n; i++) {
list.add(i);
}
return list;
}
}
static abstract class Node {
abstract void render(Writer w, EvaluationContext c) throws IOException;
static Expression parse(String expression) {
return new SpelExpressionParser().parseExpression(expression);
}
}
static class Text extends Node {
private final String text;
public Text(String text) {
this.text = text;
}
void render(Writer w, EvaluationContext c) throws IOException {
w.write(text);
}
}
static class Var extends Node {
private final Expression e;
public Var(String expression) {
e = parse(expression);
}
void render(Writer w, EvaluationContext c) throws IOException {
Object value = e.getValue(c);
if (value != null) {
w.write(value.toString());
}
}
}
static class Block extends Node {
private List<Node> nodes = new ArrayList<Node>();
void add(Node node, boolean b) {
nodes.add(node);
}
void render(Writer w, EvaluationContext c) throws IOException {
for (Node node : nodes) {
node.render(w, c);
}
}
}
static class ElseBlock extends Block {
private Block other = new Block();
void add(Node node, boolean b) {
if (b) {
super.add(node, true);
} else {
other.add(node, true);
}
}
}
static class If extends ElseBlock {
private final Expression e;
If(Matcher m, String argument) {
e = parse(argument);
parseBlock(m, this, "endif");
}
void render(Writer w, EvaluationContext c) throws IOException {
Object value = e.getValue(c);
if (value instanceof Boolean && (Boolean) value) {
super.render(w, c);
} else {
super.other.render(w, c);
}
}
}
static class For extends ElseBlock {
private final String name;
private final Expression e;
For(Matcher m, String argument) {
String[] tokens = argument.split(" ", 3);
name = tokens[0];
e = parse(tokens[2]);
parseBlock(m, this, "endfor");
}
void render(Writer w, EvaluationContext c) throws IOException {
Object value = e.getValue(c);
if (value instanceof Iterable) {
for (Object object : (Iterable) value) {
c.setVariable(name, object);
super.render(w, c);
}
} else {
super.other.render(w, c);
}
}
}
public static void main(String[] args) {
Ingwer i = new Ingwer("Hallo {{#name}}\n{% for i in #range(10) %}" +
"{{#i}} is {% if #i % 2 == 0 %}even{% else %}odd{% endif %}\n{% endfor %}Done.");
System.out.println(i.render(Collections.<String, Object>singletonMap("name", "sma")));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment