Skip to content

Commit 1fb8c19

Browse files
author
Sam Pullara
committed
introduce the concept of a TemplateFunction that operates before the template has been compiled. this lets you do things like reorganize template code to meet i18n requirements.
1 parent d980161 commit 1fb8c19

File tree

8 files changed

+171
-27
lines changed

8 files changed

+171
-27
lines changed

builder/src/main/java/com/sampullara/mustache/DefaultCodeFactory.java

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ public Object call() throws Exception {
112112
public int getLine() {
113113
return line;
114114
}
115+
116+
protected void identity(String marker, FutureWriter fw, Scope scope) throws IOException, MustacheException {
117+
fw.append("{{").append(marker).append(variable).append("}}");
118+
execute(fw, m.iterable(scope, variable));
119+
fw.append("{{/").append(variable).append("}}");
120+
}
115121
}
116122

117123
private static class IterableCode extends SubCode {
@@ -121,7 +127,15 @@ public IterableCode(Mustache m, String variable, List<Code> codes, String file,
121127

122128
@Override
123129
public void execute(FutureWriter fw, Scope scope) throws MustacheException {
124-
execute(fw, m.iterable(scope, variable));
130+
try {
131+
if (scope == IdentityScope.one) {
132+
identity("#", fw, scope);
133+
} else {
134+
execute(fw, m.iterable(scope, variable));
135+
}
136+
} catch (IOException e) {
137+
throw new MustacheException(e);
138+
}
125139
}
126140

127141
@Override
@@ -156,13 +170,21 @@ public FunctionCode(Mustache m, String variable, List<Code> codes, String file,
156170

157171
@Override
158172
public void execute(FutureWriter fw, Scope scope) throws MustacheException {
159-
Object function = m.getValue(scope, variable);
160-
if (function instanceof Function) {
161-
execute(fw, m.function(scope, (Function) function));
162-
} else if (function == null) {
163-
execute(fw, Lists.newArrayList(scope));
164-
} else {
165-
throw new MustacheException("Not a function: " + function);
173+
try {
174+
if (scope == IdentityScope.one) {
175+
identity("_", fw, scope);
176+
} else {
177+
Object function = m.getValue(scope, variable);
178+
if (function instanceof Function) {
179+
execute(fw, m.function(scope, (Function) function));
180+
} else if (function == null) {
181+
execute(fw, Lists.newArrayList(scope));
182+
} else {
183+
throw new MustacheException("Not a function: " + function);
184+
}
185+
}
186+
} catch (IOException e) {
187+
throw new MustacheException(e);
166188
}
167189
}
168190

@@ -218,7 +240,15 @@ public void execute(FutureWriter fw, Scope scope) throws MustacheException {
218240
m.actual.set(fw);
219241
fw = m.capturedWriter.get();
220242
}
221-
execute(fw, m.ifiterable(scope, variable));
243+
try {
244+
if (scope == IdentityScope.one) {
245+
identity("?", fw, scope);
246+
} else {
247+
execute(fw, m.ifiterable(scope, variable));
248+
}
249+
} catch (IOException e) {
250+
throw new MustacheException(e);
251+
}
222252
}
223253

224254
@Override
@@ -250,7 +280,15 @@ public void execute(FutureWriter fw, Scope scope) throws MustacheException {
250280
m.actual.set(fw);
251281
fw = m.capturedWriter.get();
252282
}
253-
execute(fw, m.inverted(scope, variable));
283+
try {
284+
if (scope == IdentityScope.one) {
285+
identity("^", fw, scope);
286+
} else {
287+
execute(fw, m.inverted(scope, variable));
288+
}
289+
} catch (IOException e) {
290+
throw new MustacheException(e);
291+
}
254292
}
255293

256294
@Override
@@ -286,17 +324,21 @@ public PartialCode(Mustache m, String variable, String file, int line) {
286324
}
287325

288326
@Override
289-
public void execute(FutureWriter fw, final Scope s) throws MustacheException {
327+
public void execute(FutureWriter fw, final Scope scope) throws MustacheException {
290328
try {
291-
final Mustache partial = m.partial(variable);
292-
fw.enqueue(new Callable<Object>() {
293-
@Override
294-
public Object call() throws Exception {
295-
FutureWriter fw = new FutureWriter();
296-
partial.partial(fw, s, variable, partial);
297-
return fw;
298-
}
299-
});
329+
if (scope == IdentityScope.one) {
330+
fw.append("{{>").append(variable).append("}}");
331+
} else {
332+
final Mustache partial = m.partial(variable);
333+
fw.enqueue(new Callable<Object>() {
334+
@Override
335+
public Object call() throws Exception {
336+
FutureWriter fw = new FutureWriter();
337+
partial.partial(fw, scope, variable, partial);
338+
return fw;
339+
}
340+
});
341+
}
300342
} catch (IOException e) {
301343
throw new MustacheException("Execution failed: " + file + ":" + line, e);
302344
}
@@ -333,7 +375,17 @@ public WriteValueCode(Mustache m, String name, boolean encoded, int line) {
333375

334376
@Override
335377
public void execute(FutureWriter fw, Scope scope) throws MustacheException {
336-
m.write(fw, scope, name, encoded);
378+
if (scope == IdentityScope.one) {
379+
try {
380+
if (!encoded) fw.append("{");
381+
fw.append("{{").append(name).append("}}");
382+
if (!encoded) fw.append("}");
383+
} catch (IOException e) {
384+
throw new MustacheException(e);
385+
}
386+
} else {
387+
m.write(fw, scope, name, encoded);
388+
}
337389
}
338390

339391
@Override

builder/src/test/java/com/sampullara/mustache/InterpreterTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.common.base.Function;
44
import com.google.common.util.concurrent.SettableFuture;
55
import com.sampullara.util.FutureWriter;
6+
import com.sampullara.util.TemplateFunction;
67
import com.sampullara.util.http.JSONHttpRequest;
78
import junit.framework.TestCase;
89
import org.codehaus.jackson.JsonFactory;
@@ -54,6 +55,17 @@ int taxed_value() {
5455
assertEquals(getContents(root, "simple.txt"), sw.toString());
5556
}
5657

58+
public void testIdentitySimple() throws MustacheException, IOException, ExecutionException, InterruptedException {
59+
MustacheBuilder c = new MustacheBuilder(root);
60+
Mustache m = c.parseFile("simple.html");
61+
StringWriter sw = new StringWriter();
62+
FutureWriter writer = new FutureWriter(sw);
63+
m.execute(writer, IdentityScope.one);
64+
writer.flush();
65+
assertEquals(getContents(root, "simple.html").replaceAll("\\s+", ""), sw.toString().replaceAll(
66+
"\\s+", ""));
67+
}
68+
5769
public void testClassLoaderSimple() throws MustacheException, IOException, ExecutionException, InterruptedException {
5870
MustacheBuilder c = new MustacheBuilder();
5971
classLoaderTest(c);
@@ -218,6 +230,27 @@ public String apply(String input) {
218230
assertEquals(getContents(root, "lambda.txt"), sw.toString());
219231
}
220232

233+
public void testTemplateLamda() throws MustacheException, IOException {
234+
MustacheBuilder c = new MustacheBuilder(root);
235+
Mustache m = c.parseFile("templatelambda.html");
236+
StringWriter sw = new StringWriter();
237+
FutureWriter writer = new FutureWriter(sw);
238+
m.execute(writer, new Scope(new Object() {
239+
String name = "Sam";
240+
TemplateFunction translate = new TemplateFunction() {
241+
@Override
242+
public String apply(String input) {
243+
if (input.equals("Hello {{name}}")) {
244+
return "{{name}}, Hola!";
245+
}
246+
return null;
247+
}
248+
};
249+
}));
250+
writer.flush();
251+
assertEquals(getContents(root, "templatelambda.txt"), sw.toString());
252+
}
253+
221254
public void testSimpleWithSave() throws MustacheException, IOException, ExecutionException, InterruptedException {
222255
MustacheBuilder c = init();
223256
Mustache m = c.parseFile("simple.html");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.sampullara.mustache;
2+
3+
import com.google.common.collect.Iterables;
4+
import com.google.common.collect.Lists;
5+
6+
/**
7+
* Used to reproduce markup from code.
8+
*/
9+
public class IdentityScope extends Scope {
10+
public final static IdentityScope one = new IdentityScope();
11+
12+
private IdentityScope() {}
13+
14+
@Override
15+
public Object get(Object o) {
16+
return one;
17+
}
18+
}

core/src/main/java/com/sampullara/mustache/Mustache.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
import com.google.common.base.Function;
44
import com.google.common.collect.Iterables;
5+
import com.google.common.collect.Lists;
6+
57
import com.sampullara.util.FutureWriter;
8+
import com.sampullara.util.TemplateFunction;
69
import org.codehaus.jackson.JsonNode;
710

811
import java.io.File;
@@ -16,7 +19,6 @@
1619
import java.util.NoSuchElementException;
1720
import java.util.concurrent.Callable;
1821
import java.util.concurrent.ConcurrentHashMap;
19-
import java.util.concurrent.ExecutionException;
2022
import java.util.concurrent.Future;
2123
import java.util.concurrent.atomic.AtomicInteger;
2224
import java.util.logging.Level;
@@ -333,6 +335,9 @@ public Object call() throws Exception {
333335
* @return
334336
*/
335337
protected Iterable<Scope> iterable(final Scope s, final String name) {
338+
if (s == IdentityScope.one) {
339+
return Lists.newArrayList(s);
340+
}
336341
MustacheTrace.Event event = null;
337342
if (trace) {
338343
Object parent = s.getParent();
@@ -409,7 +414,17 @@ public void remove() {
409414
};
410415
}
411416

412-
public Iterable<Scope> function(final Scope s, final Function f) {
417+
// Memory leak if you abuse TemplateFunction
418+
private static Map<String, Mustache> templateFunctionCache = new ConcurrentHashMap<String, Mustache>();
419+
420+
public Iterable<Scope> function(final Scope scope, final Function f) {
421+
final boolean templateFunction = f instanceof TemplateFunction;
422+
final Scope s;
423+
if (templateFunction) {
424+
s = IdentityScope.one;
425+
} else {
426+
s = scope;
427+
}
413428
return new Iterable<Scope>() {
414429
@Override
415430
public Iterator<Scope> iterator() {
@@ -426,7 +441,19 @@ public synchronized boolean hasNext() {
426441
capturedWriter.get().flush();
427442
capturedWriter.set(null);
428443
Object apply = f.apply(writer.toString());
429-
actual.get().write(apply == null ? null : String.valueOf(apply));
444+
String applyString = apply == null ? null : String.valueOf(apply);
445+
if (templateFunction) {
446+
if (applyString != null) {
447+
Mustache mustache = templateFunctionCache.get(applyString);
448+
if (mustache == null) {
449+
mustache = mj.parse(applyString);
450+
templateFunctionCache.put(applyString, mustache);
451+
}
452+
mustache.execute(actual.get(), scope);
453+
}
454+
} else {
455+
actual.get().write(applyString);
456+
}
430457
actual.set(null);
431458
} catch (Exception e) {
432459
logger.log(Level.SEVERE, "Could not apply function: " + f, e);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.sampullara.util;
2+
3+
import com.google.common.base.Function;
4+
5+
/**
6+
* Use this function if you want to transform template code rather than the results
7+
* of template code.
8+
*/
9+
public interface TemplateFunction extends Function<String, String> {
10+
}

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<module>builder</module>
1313
<module>handlebar</module>
1414
<module>network</module>
15-
<module>java7-support</module>
15+
<!-- <module>java7-support</module>-->
1616
</modules>
1717
<packaging>pom</packaging>
1818

@@ -71,7 +71,7 @@
7171
<encoding>UTF-8</encoding>
7272
</configuration>
7373
</plugin>
74-
<plugin>
74+
<!-- <plugin>
7575
<groupId>org.apache.maven.plugins</groupId>
7676
<artifactId>maven-gpg-plugin</artifactId>
7777
<executions>
@@ -83,7 +83,7 @@
8383
</goals>
8484
</execution>
8585
</executions>
86-
</plugin>
86+
</plugin>-->
8787
</plugins>
8888
</build>
8989

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{{#translate}}Hello {{name}}{{/translate}}
2+
{{_translate}}Hello {{name}}{{/translate}}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Sam, Hola!
2+
Sam, Hola!

0 commit comments

Comments
 (0)