-
Notifications
You must be signed in to change notification settings - Fork 284
Expand file tree
/
Copy pathDeferringMustacheFactory.java
More file actions
138 lines (119 loc) · 4.42 KB
/
DeferringMustacheFactory.java
File metadata and controls
138 lines (119 loc) · 4.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package com.github.mustachejava;
import com.github.mustachejava.codes.PartialCode;
import com.github.mustachejava.util.GuardException;
import com.github.mustachejava.util.InternalArrayList;
import com.github.mustachejava.util.Wrapper;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
/**
* This allows you to automatically defer evaluation of partials. By default
* it generates HTML but you can override that behavior.
*/
public class DeferringMustacheFactory extends DefaultMustacheFactory {
public static final Object DEFERRED = new Object();
public DeferringMustacheFactory() {
}
public DeferringMustacheFactory(String resourceRoot) {
super(resourceRoot);
}
public DeferringMustacheFactory(File fileRoot) {
super(fileRoot);
}
private static class Deferral {
final long id;
final Future<Object> future;
Deferral(long id, Future<Object> future) {
this.id = id;
this.future = future;
}
}
public static class DeferredCallable implements Callable<String> {
private List<Deferral> deferrals = new ArrayList<>();
public void add(Deferral deferral) {
deferrals.add(deferral);
}
@Override
public String call() throws Exception {
StringBuilder sb = new StringBuilder();
for (Deferral deferral : deferrals) {
Object o = deferral.future.get();
if (o != null) {
writeDeferral(sb, deferral, o);
}
}
return sb.toString();
}
}
@Override
public MustacheVisitor createMustacheVisitor() {
final AtomicLong id = new AtomicLong(0);
return new DefaultMustacheVisitor(this) {
@Override
public void partial(TemplateContext tc, final String variable, final String indent) {
TemplateContext partialTC = new TemplateContext("{{", "}}", tc.file(), tc.line(), tc.startOfLine());
final Long divid = id.incrementAndGet();
list.add(new PartialCode(partialTC, df, variable) {
Wrapper deferredWrapper;
@Override
public Writer execute(Writer writer, final List<Object> scopes) {
final Object object = get(scopes);
final DeferredCallable deferredCallable = getDeferred(scopes);
if (object == DEFERRED && deferredCallable != null) {
try {
writeTarget(writer, divid);
writer.append(appended);
} catch (IOException e) {
throw new MustacheException("Failed to write", e, tc);
}
// Make a copy of the scopes so we don't change them
List<Object> scopesCopy = new InternalArrayList<>(scopes);
deferredCallable.add(
new Deferral(divid, getExecutorService().submit(() -> {
try {
StringWriter sw = new StringWriter();
partial.execute(sw, scopesCopy).close();
return sw.toString();
} catch (IOException e) {
throw new MustacheException("Failed to writer", e, tc);
}
})));
return writer;
} else {
return appendText(partial.execute(writer, scopes));
}
}
private DeferredCallable getDeferred(List<Object> scopes) {
try {
if (deferredWrapper == null) {
deferredWrapper = getObjectHandler().find("deferred", scopes);
}
return (DeferredCallable) deferredWrapper.call(scopes);
} catch (GuardException e) {
deferredWrapper = null;
return getDeferred(scopes);
}
}
});
}
};
}
protected void writeTarget(Writer writer, Long divid) throws IOException {
writer.append("<div id=\"");
writer.append(divid.toString());
writer.append("\"></div>");
}
protected static void writeDeferral(StringBuilder sb, Deferral deferral, Object o) {
sb.append("<script>document.getElementById(\"");
sb.append(deferral.id);
sb.append("\").innerHTML=\"");
sb.append(o.toString().replace("<", "<").replace("\"", "\\\"").replace("\n", "\\n"));
sb.append("\";</script>");
}
}