Skip to content

Commit 6d1dcfc

Browse files
committed
refactor the builder so it is easier to override behavior
1 parent 18c4e3a commit 6d1dcfc

19 files changed

Lines changed: 893 additions & 676 deletions

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

Lines changed: 0 additions & 638 deletions
This file was deleted.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import java.util.concurrent.atomic.AtomicInteger;
1515

1616
import com.google.common.base.Charsets;
17+
import com.sampullara.mustache.code.BuilderCodeFactory;
18+
import com.sampullara.mustache.code.WriteCode;
1719

1820
/**
1921
* A pseudo interpreter / compiler. Instead of compiling to Java code, it compiles to a
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.sampullara.mustache.code;
2+
3+
import com.sampullara.mustache.Code;
4+
import com.sampullara.mustache.CodeFactory;
5+
import com.sampullara.mustache.Mustache;
6+
import com.sampullara.mustache.MustacheException;
7+
import com.sampullara.mustache.Scope;
8+
9+
import java.util.List;
10+
import java.util.concurrent.atomic.AtomicInteger;
11+
12+
import static com.sampullara.mustache.Mustache.truncate;
13+
14+
/**
15+
* The builder code factory is designed to work well with the default Mustache implementation.
16+
* In many cases where you significantly change the behavior of the default Mustache class you
17+
* may want to have your own CodeFactory that works with it.
18+
*/
19+
public class BuilderCodeFactory implements CodeFactory {
20+
21+
@Override
22+
public Code iterable(Mustache m, String variable, List<Code> codes, String file, int line) {
23+
return new IterableCode(m, variable, codes, file, line);
24+
}
25+
26+
@Override
27+
public Code function(Mustache m, String variable, List<Code> codes, String file, int line) {
28+
return new FunctionCode(m, variable, codes, file, line);
29+
}
30+
31+
@Override
32+
public Code ifIterable(Mustache m, String variable, List<Code> codes, String file, int line) {
33+
return new IfIterableCode(m, variable, codes, file, line);
34+
}
35+
36+
@Override
37+
public Code notIterable(Mustache m, String variable, List<Code> codes, String file, int line) {
38+
return new InvertedIterableCode(m, variable, codes, file, line);
39+
}
40+
41+
@Override
42+
public Code partial(Mustache m, String variable, String file, int line) throws MustacheException {
43+
return new PartialCode(m, variable, file, line);
44+
}
45+
46+
@Override
47+
public Code value(Mustache m, String name, boolean encode, int line) {
48+
return new WriteValueCode(m, name, encode, line);
49+
}
50+
51+
@Override
52+
public Code write(String s, int line) {
53+
return new DefaultWriteCode(s, line);
54+
}
55+
56+
@Override
57+
public Code eof(int line) {
58+
return new EOFCode(line);
59+
}
60+
61+
@Override
62+
public Code extend(Mustache m, String variable, List<Code> codes, String file, int i) throws MustacheException {
63+
return new ExtendCode(m, variable, codes, file, i);
64+
}
65+
66+
@Override
67+
public Code name(Mustache m, String variable, List<Code> codes, String file, int i) {
68+
return new ExtendNameCode(m, variable, codes, file, i);
69+
}
70+
71+
public static String unexecuteValueCode(Mustache m, Scope current, String text, AtomicInteger position, Code[] next, boolean encoded) throws MustacheException {
72+
AtomicInteger probePosition = new AtomicInteger(position.get());
73+
Code[] truncate = truncate(next, 1, null);
74+
Scope result = null;
75+
int lastposition = position.get();
76+
while (next.length > 0 && probePosition.get() < text.length()) {
77+
lastposition = probePosition.get();
78+
result = next[0].unexecute(current, text, probePosition, truncate);
79+
if (result == null) {
80+
probePosition.incrementAndGet();
81+
} else {
82+
break;
83+
}
84+
}
85+
if (result != null) {
86+
String value = text.substring(position.get(), lastposition);
87+
if (encoded) {
88+
value = m.decode(value);
89+
}
90+
position.set(lastposition);
91+
return value;
92+
}
93+
return null;
94+
}
95+
96+
public static void put(Scope result, String name, Object value) {
97+
String[] splits = name.split("[.]");
98+
Scope depth = result;
99+
for (int i = 0; i < splits.length; i++) {
100+
if (i < splits.length - 1) {
101+
Scope tmp = (Scope) result.get(splits[i]);
102+
if (tmp == null) {
103+
tmp = new Scope();
104+
}
105+
depth.put(splits[i], tmp);
106+
depth = tmp;
107+
} else {
108+
depth.put(splits[i], value);
109+
}
110+
}
111+
}
112+
113+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.sampullara.mustache.code;
2+
3+
import com.sampullara.mustache.Code;
4+
import com.sampullara.mustache.MustacheException;
5+
import com.sampullara.mustache.Scope;
6+
import com.sampullara.util.FutureWriter;
7+
8+
import java.io.IOException;
9+
import java.util.concurrent.atomic.AtomicInteger;
10+
11+
/**
12+
* Writes a string.
13+
* <p/>
14+
* User: sam
15+
* Date: 11/27/11
16+
* Time: 10:46 AM
17+
*/
18+
public class DefaultWriteCode implements WriteCode {
19+
private final StringBuffer rest;
20+
private final int line;
21+
22+
public DefaultWriteCode(String rest, int line) {
23+
this.rest = new StringBuffer(rest);
24+
this.line = line;
25+
}
26+
27+
public void execute(FutureWriter fw, Scope scope) throws MustacheException {
28+
try {
29+
fw.write(rest.toString());
30+
} catch (IOException e) {
31+
throw new MustacheException("Failed to write", e);
32+
}
33+
}
34+
35+
@Override
36+
public int getLine() {
37+
return line;
38+
}
39+
40+
@Override
41+
public Scope unexecute(Scope current, String text, AtomicInteger position, Code[] next) throws MustacheException {
42+
if (position.get() + rest.length() <= text.length()) {
43+
String substring = text.substring(position.get(), position.get() + rest.length());
44+
if (rest.toString().equals(substring)) {
45+
position.addAndGet(rest.length());
46+
return current;
47+
}
48+
}
49+
return null;
50+
}
51+
52+
@Override
53+
public void identity(FutureWriter fw) throws MustacheException {
54+
execute(fw, null);
55+
}
56+
57+
public void append(String append) {
58+
rest.append(append);
59+
}
60+
61+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.sampullara.mustache.code;
2+
3+
import com.sampullara.mustache.Code;
4+
import com.sampullara.mustache.MustacheException;
5+
import com.sampullara.mustache.Scope;
6+
import com.sampullara.util.FutureWriter;
7+
8+
import java.util.concurrent.atomic.AtomicInteger;
9+
10+
/**
11+
* Represents the end of file.
12+
* <p/>
13+
* User: sam
14+
* Date: 11/27/11
15+
* Time: 10:40 AM
16+
*/
17+
public class EOFCode implements Code {
18+
19+
private final int line;
20+
21+
public EOFCode(int line) {
22+
this.line = line;
23+
}
24+
25+
@Override
26+
public void execute(FutureWriter fw, Scope scope) throws MustacheException {
27+
// NOP
28+
}
29+
30+
@Override
31+
public int getLine() {
32+
return line;
33+
}
34+
35+
@Override
36+
public Scope unexecute(Scope current, String text, AtomicInteger position, Code[] next) throws MustacheException {
37+
// End of text
38+
position.set(text.length());
39+
return current;
40+
}
41+
42+
@Override
43+
public void identity(FutureWriter fw) throws MustacheException {
44+
// NOP
45+
}
46+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.sampullara.mustache.code;
2+
3+
import com.sampullara.mustache.Code;
4+
import com.sampullara.mustache.Mustache;
5+
6+
import java.util.List;
7+
8+
/**
9+
* Implementation strategy:
10+
* - Load extension template codes
11+
* - Load local template codes
12+
* - Execute extension template codes, replacing named sections with local replacements
13+
*/
14+
15+
public abstract class ExtendBaseCode extends SubCode {
16+
17+
public ExtendBaseCode(Mustache m, String variable, List<Code> codes, String file, int line) {
18+
super("<", m, variable, codes, file, line);
19+
}
20+
21+
public String getName() {
22+
return variable;
23+
}
24+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.sampullara.mustache.code;
2+
3+
import com.sampullara.mustache.Code;
4+
import com.sampullara.mustache.Mustache;
5+
import com.sampullara.mustache.MustacheException;
6+
import com.sampullara.mustache.Scope;
7+
import com.sampullara.util.FutureWriter;
8+
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
12+
import java.util.concurrent.atomic.AtomicInteger;
13+
14+
import static com.sampullara.mustache.Mustache.truncate;
15+
16+
/**
17+
* Extending a template.
18+
* <p/>
19+
* User: sam
20+
* Date: 11/27/11
21+
* Time: 10:39 AM
22+
*/
23+
public class ExtendCode extends ExtendBaseCode {
24+
25+
private Mustache partial;
26+
27+
public ExtendCode(Mustache m, String variable, List<Code> codes, String file, int line) throws MustacheException {
28+
super(m, variable, codes, file, line);
29+
Map<String, ExtendNameCode> replaceMap = new HashMap<String, ExtendNameCode>();
30+
for (Code code : codes) {
31+
if (code instanceof ExtendNameCode) {
32+
// put name codes in the map
33+
ExtendNameCode erc = (ExtendNameCode) code;
34+
replaceMap.put(erc.getName(), erc);
35+
} else if (code instanceof WriteCode) {
36+
// ignore text
37+
} else {
38+
// fail on everything else
39+
throw new IllegalArgumentException(
40+
"Illegal code in extend section: " + code.getClass().getName());
41+
}
42+
}
43+
Map<String, ExtendNameCode> debugMap = null;
44+
if (Mustache.debug) {
45+
debugMap = new HashMap<String, ExtendNameCode>(replaceMap);
46+
}
47+
partial = m.partial(variable);
48+
Code[] supercodes = partial.getCompiled();
49+
// recursively replace named sections with replacements
50+
replaceCode(supercodes, replaceMap, debugMap);
51+
if (Mustache.debug) {
52+
if (debugMap != null && debugMap.size() > 0) {
53+
throw new MustacheException(
54+
"Replacement sections failed to match named sections: " + debugMap.keySet());
55+
}
56+
}
57+
}
58+
59+
private void replaceCode(Code[] supercodes, Map<String, ExtendNameCode> replaceMap, Map<String, ExtendNameCode> debugMap) {
60+
for (int i = 0; i < supercodes.length; i++) {
61+
Code code = supercodes[i];
62+
if (code instanceof ExtendNameCode) {
63+
ExtendNameCode enc = (ExtendNameCode) code;
64+
ExtendNameCode extendReplaceCode = replaceMap.get(enc.getName());
65+
if (extendReplaceCode != null) {
66+
supercodes[i] = extendReplaceCode;
67+
} else {
68+
if (Mustache.debug) {
69+
debugMap.remove(enc.getName());
70+
}
71+
replaceCode(enc.codes, replaceMap, debugMap);
72+
}
73+
} else if (code instanceof SubCode) {
74+
SubCode subcode = (SubCode) code;
75+
replaceCode(subcode.codes, replaceMap, debugMap);
76+
}
77+
}
78+
}
79+
80+
@Override
81+
public void execute(FutureWriter fw, Scope scope) throws MustacheException {
82+
Code[] supercodes = partial.getCompiled();
83+
for (Code code : supercodes) {
84+
code.execute(fw, scope);
85+
}
86+
}
87+
88+
@Override
89+
public Scope unexecute(Scope current, String text, AtomicInteger position, Code[] next) throws MustacheException {
90+
Code[] supercodes = partial.getCompiled();
91+
for (int i = 0; i < supercodes.length; i++) {
92+
Code[] truncate = truncate(supercodes, i + 1, next);
93+
supercodes[i].unexecute(current, text, position, truncate);
94+
}
95+
return current;
96+
}
97+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.sampullara.mustache.code;
2+
3+
import com.sampullara.mustache.Code;
4+
import com.sampullara.mustache.Mustache;
5+
import com.sampullara.mustache.MustacheException;
6+
import com.sampullara.mustache.Scope;
7+
import com.sampullara.util.FutureWriter;
8+
9+
import java.util.Arrays;
10+
import java.util.List;
11+
import java.util.concurrent.atomic.AtomicInteger;
12+
13+
import static com.sampullara.mustache.Mustache.truncate;
14+
15+
/**
16+
* The named reference.
17+
* <p/>
18+
* User: sam
19+
* Date: 11/27/11
20+
* Time: 10:39 AM
21+
*/
22+
public class ExtendNameCode extends ExtendBaseCode {
23+
24+
public ExtendNameCode(Mustache m, String variable, List<Code> codes, String file, int line) {
25+
super(m, variable, codes, file, line);
26+
}
27+
28+
@Override
29+
public void execute(FutureWriter fw, Scope scope) throws MustacheException {
30+
execute(fw, Arrays.asList(scope));
31+
}
32+
33+
@Override
34+
public Scope unexecute(Scope current, String text, AtomicInteger position, Code[] next) throws MustacheException {
35+
for (int i = 0; i < codes.length; i++) {
36+
if (Mustache.debug) {
37+
Mustache.line.set(codes[i].getLine());
38+
}
39+
Code[] truncate = truncate(codes, i + 1, next);
40+
current = codes[i].unexecute(current, text, position, truncate);
41+
}
42+
return current;
43+
}
44+
}

0 commit comments

Comments
 (0)