Moved falsey-ness detection into compiler.

Fixes #39.

This fixes a bug where falsey values were not properly triggering inverted
sections, and consolidates some logic.

Also did some renaming. Couldn't help myself.
This commit is contained in:
Michael Bayne
2013-07-03 16:47:21 -07:00
parent 6e3051c3ba
commit 9441d062a6
2 changed files with 42 additions and 26 deletions
@@ -162,6 +162,14 @@ public class Mustache
return (nullValue == null) ? null : nullValue.replace("{{name}}", name);
}
/** Returns true if the supplied value is "falsey". If {@link #emptyStringIsFalse} is true,
* then empty strings are considered falsey. If {@link #zeroIsFalse} is true, then zero
* values are considered falsey. */
public boolean isFalsey (Object value) {
return (emptyStringIsFalse && "".equals(value)) ||
(zeroIsFalse && (value instanceof Number) && ((Number)value).longValue() == 0);
}
protected Compiler (boolean escapeHTML, boolean standardsMode, String nullValue,
boolean missingIsNull, boolean emptyStringIsFalse, boolean zeroIsFalse,
TemplateLoader loader, Collector collector, Delims delims) {
@@ -494,7 +502,7 @@ public class Mustache
protected static class Accumulator {
public Accumulator (Compiler compiler) {
_compiler = compiler;
_comp = compiler;
}
public boolean justOpenedOrClosedBlock () {
@@ -518,7 +526,7 @@ public class Mustache
switch (tag.charAt(0)) {
case '#':
requireNoNewlines(tag, tagLine);
return new Accumulator(_compiler) {
return new Accumulator(_comp) {
@Override public boolean justOpenedOrClosedBlock () {
// if we just opened this section, we'll have no segments
return (_segs.isEmpty()) || super.justOpenedOrClosedBlock();
@@ -529,19 +537,18 @@ public class Mustache
}
@Override protected Accumulator addCloseSectionSegment (String itag, int line) {
requireSameName(tag1, itag, line);
outer._segs.add(
new SectionSegment(itag, super.finish(), tagLine, _compiler));
outer._segs.add(new SectionSegment(_comp, itag, super.finish(), tagLine));
return outer;
}
};
case '>':
_segs.add(new IncludedTemplateSegment(tag1, _compiler));
_segs.add(new IncludedTemplateSegment(_comp, tag1));
return this;
case '^':
requireNoNewlines(tag, tagLine);
return new Accumulator(_compiler) {
return new Accumulator(_comp) {
@Override public boolean justOpenedOrClosedBlock () {
// if we just opened this section, we'll have no segments
return (_segs.isEmpty()) || super.justOpenedOrClosedBlock();
@@ -552,7 +559,7 @@ public class Mustache
}
@Override protected Accumulator addCloseSectionSegment (String itag, int line) {
requireSameName(tag1, itag, line);
outer._segs.add(new InvertedSectionSegment(itag, super.finish(), tagLine));
outer._segs.add(new InvertedSegment(_comp, itag, super.finish(), tagLine));
return outer;
}
};
@@ -572,7 +579,7 @@ public class Mustache
default:
requireNoNewlines(tag, tagLine);
_segs.add(new VariableSegment(tag, _compiler.escapeHTML, tagLine));
_segs.add(new VariableSegment(tag, _comp.escapeHTML, tagLine));
return this;
}
}
@@ -601,7 +608,7 @@ public class Mustache
}
}
protected final Compiler _compiler;
protected final Compiler _comp;
protected final List<Template.Segment> _segs = new ArrayList<Template.Segment>();
}
@@ -617,16 +624,16 @@ public class Mustache
}
protected static class IncludedTemplateSegment extends Template.Segment {
public IncludedTemplateSegment (String name, Compiler compiler) {
public IncludedTemplateSegment (Compiler compiler, String name) {
_comp = compiler;
_name = name;
_compiler = compiler;
}
@Override public void execute (Template tmpl, Template.Context ctx, Writer out) {
// we compile our template lazily to avoid infinie recursion if a template includes
// itself (see issue #13)
if (_template == null) {
try {
_template = _compiler.compile(_compiler.loader.getTemplate(_name));
_template = _comp.compile(_comp.loader.getTemplate(_name));
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
@@ -639,8 +646,8 @@ public class Mustache
// would happen if we just called execute() with ctx.data
_template.executeSegs(ctx, out);
}
protected final Compiler _comp;
protected final String _name;
protected final Compiler _compiler;
protected Template _template;
}
@@ -688,13 +695,13 @@ public class Mustache
/** A segment that represents a section. */
protected static class SectionSegment extends BlockSegment {
public SectionSegment (String name, Template.Segment[] segs, int line, Compiler compiler) {
public SectionSegment (Compiler compiler, String name, Template.Segment[] segs, int line) {
super(name, segs, line);
_compiler = compiler;
_comp = compiler;
}
@Override public void execute (Template tmpl, Template.Context ctx, Writer out) {
Object value = tmpl.getSectionValue(ctx, _name, _line); // won't return null
Iterator<?> iter = tmpl._compiler.collector.toIterator(value);
Iterator<?> iter = _comp.collector.toIterator(value);
if (iter != null) {
int index = 0;
while (iter.hasNext()) {
@@ -712,26 +719,24 @@ public class Mustache
} catch (IOException ioe) {
throw new MustacheException(ioe);
}
} else if (_compiler.emptyStringIsFalse && "".equals(value)) {
// omit the section
} else if (_compiler.zeroIsFalse && (value instanceof Number) &&
((Number)value).longValue() == 0) {
} else if (_comp.isFalsey(value)) {
// omit the section
} else {
executeSegs(tmpl, ctx.nest(value, 0, false, false), out);
}
}
protected final Compiler _compiler;
protected final Compiler _comp;
}
/** A segment that represents an inverted section. */
protected static class InvertedSectionSegment extends BlockSegment {
public InvertedSectionSegment (String name, Template.Segment[] segs, int line) {
protected static class InvertedSegment extends BlockSegment {
public InvertedSegment (Compiler compiler, String name, Template.Segment[] segs, int line) {
super(name, segs, line);
_comp = compiler;
}
@Override public void execute (Template tmpl, Template.Context ctx, Writer out) {
Object value = tmpl.getSectionValue(ctx, _name, _line); // won't return null
Iterator<?> iter = tmpl._compiler.collector.toIterator(value);
Iterator<?> iter = _comp.collector.toIterator(value);
if (iter != null) {
if (!iter.hasNext()) {
executeSegs(tmpl, ctx, out);
@@ -740,8 +745,11 @@ public class Mustache
if (!(Boolean)value) {
executeSegs(tmpl, ctx, out);
}
} else if (_comp.isFalsey(value)) {
executeSegs(tmpl, ctx, out);
} // TODO: fail?
}
protected final Compiler _comp;
}
/** Map of strings that must be replaced inside html attributes and their replacements. (They
@@ -146,9 +146,17 @@ public class MustacheTest
}
@Test public void testSectionWithFalseyEmptyString () {
test(Mustache.compiler().emptyStringIsFalse(true), "", "{{#foo}}test{{/foo}}", new Object() {
Object ctx = new Object() {
String foo = "";
});
String bar = "nonempty";
};
// test normal sections with falsey empty string
Mustache.Compiler compiler = Mustache.compiler().emptyStringIsFalse(true);
test(compiler, "", "{{#foo}}test{{/foo}}", ctx);
test(compiler, "test", "{{#bar}}test{{/bar}}", ctx);
// test inverted sections with falsey empty string
test(compiler, "test", "{{^foo}}test{{/foo}}", ctx);
test(compiler, "", "{{^bar}}test{{/bar}}", ctx);
}
@Test public void testSectionWithNonFalseyZero () {