Actually implemented delimiter change. Tidied things upalong the way.
This commit is contained in:
@@ -98,7 +98,7 @@ public class Mustache
|
|||||||
{
|
{
|
||||||
// a hand-rolled parser; whee!
|
// a hand-rolled parser; whee!
|
||||||
Accumulator accum = new Accumulator(compiler);
|
Accumulator accum = new Accumulator(compiler);
|
||||||
char start1 = '{', start2 = '{', end1 = '}', end2 = '}';
|
Delims delims = new Delims();
|
||||||
int state = TEXT;
|
int state = TEXT;
|
||||||
StringBuilder text = new StringBuilder();
|
StringBuilder text = new StringBuilder();
|
||||||
int line = 1;
|
int line = 1;
|
||||||
@@ -130,8 +130,8 @@ public class Mustache
|
|||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case TEXT:
|
case TEXT:
|
||||||
if (c == start1) {
|
if (c == delims.start1) {
|
||||||
if (start2 == -1) {
|
if (delims.start2 == NO_CHAR) {
|
||||||
accum.addTextSegment(text);
|
accum.addTextSegment(text);
|
||||||
state = TAG;
|
state = TAG;
|
||||||
} else {
|
} else {
|
||||||
@@ -143,12 +143,12 @@ public class Mustache
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MATCHING_START:
|
case MATCHING_START:
|
||||||
if (c == start2) {
|
if (c == delims.start2) {
|
||||||
accum.addTextSegment(text);
|
accum.addTextSegment(text);
|
||||||
state = TAG;
|
state = TAG;
|
||||||
} else {
|
} else {
|
||||||
text.append(start1);
|
text.append(delims.start1);
|
||||||
if (c != start1) {
|
if (c != delims.start1) {
|
||||||
text.append(c);
|
text.append(c);
|
||||||
state = TEXT;
|
state = TEXT;
|
||||||
}
|
}
|
||||||
@@ -156,12 +156,13 @@ public class Mustache
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TAG:
|
case TAG:
|
||||||
if (c == end1) {
|
if (c == delims.end1) {
|
||||||
if (end2 == -1) {
|
if (delims.end2 == NO_CHAR) {
|
||||||
if (text.charAt(0) == '=') {
|
if (text.charAt(0) == '=') {
|
||||||
// TODO: change delimiters
|
delims.updateDelims(text.substring(1, text.length()-1));
|
||||||
|
text.setLength(0);
|
||||||
} else {
|
} else {
|
||||||
sanityCheckTag(text, line, start1, start2);
|
sanityCheckTag(text, line, delims.start1, delims.start2);
|
||||||
accum = accum.addTagSegment(text, line);
|
accum = accum.addTagSegment(text, line);
|
||||||
skipNewline = accum.skipNewline();
|
skipNewline = accum.skipNewline();
|
||||||
}
|
}
|
||||||
@@ -175,13 +176,14 @@ public class Mustache
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MATCHING_END:
|
case MATCHING_END:
|
||||||
if (c == end2) {
|
if (c == delims.end2) {
|
||||||
if (text.charAt(0) == '=') {
|
if (text.charAt(0) == '=') {
|
||||||
// TODO: change delimiters
|
delims.updateDelims(text.substring(1, text.length()-1));
|
||||||
|
text.setLength(0);
|
||||||
} else {
|
} else {
|
||||||
// if we haven't remapped the delimiters, and the tag starts with {{{ then
|
// if we haven't remapped the delimiters, and the tag starts with {{{ then
|
||||||
// require that it end with }}} and disable HTML escaping
|
// require that it end with }}} and disable HTML escaping
|
||||||
if (start1 == '{' && start2 == '{' && text.charAt(0) == '{') {
|
if (delims.isDefault() && text.charAt(0) == delims.start1) {
|
||||||
try {
|
try {
|
||||||
// we've only parsed }} at this point, so we have to slurp in
|
// we've only parsed }} at this point, so we have to slurp in
|
||||||
// another character from the input stream and check it
|
// another character from the input stream and check it
|
||||||
@@ -197,14 +199,14 @@ public class Mustache
|
|||||||
text.replace(0, 1, "&");
|
text.replace(0, 1, "&");
|
||||||
}
|
}
|
||||||
// process the tag between the mustaches
|
// process the tag between the mustaches
|
||||||
sanityCheckTag(text, line, start1, start2);
|
sanityCheckTag(text, line, delims.start1, delims.start2);
|
||||||
accum = accum.addTagSegment(text, line);
|
accum = accum.addTagSegment(text, line);
|
||||||
skipNewline = accum.skipNewline();
|
skipNewline = accum.skipNewline();
|
||||||
}
|
}
|
||||||
state = TEXT;
|
state = TEXT;
|
||||||
} else {
|
} else {
|
||||||
text.append(end1);
|
text.append(delims.end1);
|
||||||
if (c != end1) {
|
if (c != delims.end1) {
|
||||||
text.append(c);
|
text.append(c);
|
||||||
state = TAG;
|
state = TAG;
|
||||||
}
|
}
|
||||||
@@ -219,11 +221,11 @@ public class Mustache
|
|||||||
accum.addTextSegment(text);
|
accum.addTextSegment(text);
|
||||||
break;
|
break;
|
||||||
case MATCHING_START:
|
case MATCHING_START:
|
||||||
text.append(start1);
|
text.append(delims.start1);
|
||||||
accum.addTextSegment(text);
|
accum.addTextSegment(text);
|
||||||
break;
|
break;
|
||||||
case MATCHING_END:
|
case MATCHING_END:
|
||||||
text.append(end1);
|
text.append(delims.end1);
|
||||||
accum.addTextSegment(text);
|
accum.addTextSegment(text);
|
||||||
break;
|
break;
|
||||||
case TAG:
|
case TAG:
|
||||||
@@ -239,7 +241,7 @@ public class Mustache
|
|||||||
{
|
{
|
||||||
for (int ii = 0, ll = accum.length(); ii < ll; ii++) {
|
for (int ii = 0, ll = accum.length(); ii < ll; ii++) {
|
||||||
if (accum.charAt(ii) == start1) {
|
if (accum.charAt(ii) == start1) {
|
||||||
if (start2 == -1 || (ii < ll-1 && accum.charAt(ii+1) == start2)) {
|
if (start2 == NO_CHAR || (ii < ll-1 && accum.charAt(ii+1) == start2)) {
|
||||||
throw new MustacheParseException("Tag contains start tag delimiter, probably " +
|
throw new MustacheParseException("Tag contains start tag delimiter, probably " +
|
||||||
"missing close delimiter '" + accum + "'", line);
|
"missing close delimiter '" + accum + "'", line);
|
||||||
}
|
}
|
||||||
@@ -260,6 +262,51 @@ public class Mustache
|
|||||||
protected static final int MATCHING_END = 2;
|
protected static final int MATCHING_END = 2;
|
||||||
protected static final int TAG = 3;
|
protected static final int TAG = 3;
|
||||||
|
|
||||||
|
protected static class Delims {
|
||||||
|
public char start1 = '{';
|
||||||
|
public char start2 = '{';
|
||||||
|
public char end1 = '}';
|
||||||
|
public char end2 = '}';
|
||||||
|
|
||||||
|
public boolean isDefault () {
|
||||||
|
return start1 == '{' && start2 == '{' && end1 == '}' && end2 == '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateDelims (String dtext) {
|
||||||
|
String errmsg = "Invalid delimiter configuration '" + dtext + "'. Must be of the " +
|
||||||
|
"form {{=1 2=}} or {{=12 34=}} where 1, 2, 3 and 4 are delimiter chars.";
|
||||||
|
|
||||||
|
String[] delims = dtext.split(" ");
|
||||||
|
if (delims.length != 2) throw new MustacheException(errmsg);
|
||||||
|
|
||||||
|
switch (delims[0].length()) {
|
||||||
|
case 1:
|
||||||
|
start1 = delims[0].charAt(0);
|
||||||
|
start2 = NO_CHAR;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
start1 = delims[0].charAt(0);
|
||||||
|
start2 = delims[0].charAt(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new MustacheException(errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (delims[1].length()) {
|
||||||
|
case 1:
|
||||||
|
end1 = delims[1].charAt(0);
|
||||||
|
end2 = NO_CHAR;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
end1 = delims[1].charAt(0);
|
||||||
|
end2 = delims[1].charAt(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new MustacheException(errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static class Accumulator {
|
protected static class Accumulator {
|
||||||
public Accumulator (Compiler compiler) {
|
public Accumulator (Compiler compiler) {
|
||||||
_compiler = compiler;
|
_compiler = compiler;
|
||||||
@@ -526,6 +573,9 @@ public class Mustache
|
|||||||
{ ">", ">" },
|
{ ">", ">" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Used when we have only a single character delimiter. */
|
||||||
|
protected static final char NO_CHAR = Character.MIN_VALUE;
|
||||||
|
|
||||||
protected static final TemplateLoader FAILING_LOADER = new TemplateLoader() {
|
protected static final TemplateLoader FAILING_LOADER = new TemplateLoader() {
|
||||||
public Reader getTemplate (String name) {
|
public Reader getTemplate (String name) {
|
||||||
throw new UnsupportedOperationException("Template loading not configured");
|
throw new UnsupportedOperationException("Template loading not configured");
|
||||||
|
|||||||
@@ -134,6 +134,13 @@ public class MustacheTest
|
|||||||
}), "foo inside:foo nonfoo foo", "{{bar}} {{>foo}} {{>baz}} {{bar}}", context("bar", "foo"));
|
}), "foo inside:foo nonfoo foo", "{{bar}} {{>foo}} {{>baz}} {{bar}}", context("bar", "foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void testDelimiterChange () {
|
||||||
|
test("foo bar baz", "{{one}} {{=<% %>=}}<%two%><%={{ }}=%> {{three}}",
|
||||||
|
context("one", "foo", "two", "bar", "three", "baz"));
|
||||||
|
test("baz bar foo", "{{three}} {{=% %=}}%two%%={{ }}=% {{one}}",
|
||||||
|
context("one", "foo", "two", "bar", "three", "baz"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void testUnescapeHTML () {
|
@Test public void testUnescapeHTML () {
|
||||||
assertEquals("<b>", Mustache.compiler().escapeHTML(true).compile("{{&a}}").
|
assertEquals("<b>", Mustache.compiler().escapeHTML(true).compile("{{&a}}").
|
||||||
execute(context("a", "<b>")));
|
execute(context("a", "<b>")));
|
||||||
|
|||||||
Reference in New Issue
Block a user