Actually implemented delimiter change. Tidied things upalong the way.

This commit is contained in:
Michael Bayne
2011-04-13 15:42:28 -07:00
parent f34539850a
commit 6b1551a1c9
2 changed files with 76 additions and 19 deletions
@@ -98,7 +98,7 @@ public class Mustache
{
// a hand-rolled parser; whee!
Accumulator accum = new Accumulator(compiler);
char start1 = '{', start2 = '{', end1 = '}', end2 = '}';
Delims delims = new Delims();
int state = TEXT;
StringBuilder text = new StringBuilder();
int line = 1;
@@ -130,8 +130,8 @@ public class Mustache
switch (state) {
case TEXT:
if (c == start1) {
if (start2 == -1) {
if (c == delims.start1) {
if (delims.start2 == NO_CHAR) {
accum.addTextSegment(text);
state = TAG;
} else {
@@ -143,12 +143,12 @@ public class Mustache
break;
case MATCHING_START:
if (c == start2) {
if (c == delims.start2) {
accum.addTextSegment(text);
state = TAG;
} else {
text.append(start1);
if (c != start1) {
text.append(delims.start1);
if (c != delims.start1) {
text.append(c);
state = TEXT;
}
@@ -156,12 +156,13 @@ public class Mustache
break;
case TAG:
if (c == end1) {
if (end2 == -1) {
if (c == delims.end1) {
if (delims.end2 == NO_CHAR) {
if (text.charAt(0) == '=') {
// TODO: change delimiters
delims.updateDelims(text.substring(1, text.length()-1));
text.setLength(0);
} else {
sanityCheckTag(text, line, start1, start2);
sanityCheckTag(text, line, delims.start1, delims.start2);
accum = accum.addTagSegment(text, line);
skipNewline = accum.skipNewline();
}
@@ -175,13 +176,14 @@ public class Mustache
break;
case MATCHING_END:
if (c == end2) {
if (c == delims.end2) {
if (text.charAt(0) == '=') {
// TODO: change delimiters
delims.updateDelims(text.substring(1, text.length()-1));
text.setLength(0);
} else {
// if we haven't remapped the delimiters, and the tag starts with {{{ then
// require that it end with }}} and disable HTML escaping
if (start1 == '{' && start2 == '{' && text.charAt(0) == '{') {
if (delims.isDefault() && text.charAt(0) == delims.start1) {
try {
// we've only parsed }} at this point, so we have to slurp in
// another character from the input stream and check it
@@ -197,14 +199,14 @@ public class Mustache
text.replace(0, 1, "&");
}
// process the tag between the mustaches
sanityCheckTag(text, line, start1, start2);
sanityCheckTag(text, line, delims.start1, delims.start2);
accum = accum.addTagSegment(text, line);
skipNewline = accum.skipNewline();
}
state = TEXT;
} else {
text.append(end1);
if (c != end1) {
text.append(delims.end1);
if (c != delims.end1) {
text.append(c);
state = TAG;
}
@@ -219,11 +221,11 @@ public class Mustache
accum.addTextSegment(text);
break;
case MATCHING_START:
text.append(start1);
text.append(delims.start1);
accum.addTextSegment(text);
break;
case MATCHING_END:
text.append(end1);
text.append(delims.end1);
accum.addTextSegment(text);
break;
case TAG:
@@ -239,7 +241,7 @@ public class Mustache
{
for (int ii = 0, ll = accum.length(); ii < ll; ii++) {
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 " +
"missing close delimiter '" + accum + "'", line);
}
@@ -260,6 +262,51 @@ public class Mustache
protected static final int MATCHING_END = 2;
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 {
public Accumulator (Compiler compiler) {
_compiler = compiler;
@@ -526,6 +573,9 @@ public class Mustache
{ ">", "&gt;" },
};
/** 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() {
public Reader getTemplate (String name) {
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"));
}
@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 () {
assertEquals("<b>", Mustache.compiler().escapeHTML(true).compile("{{&a}}").
execute(context("a", "<b>")));