Created
July 10, 2010 10:51
-
-
Save sma/470630 to your computer and use it in GitHub Desktop.
Jinja2 and/or Django like template engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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