diff --git a/README.md b/README.md index 8f95258..3c9ef8d 100644 --- a/README.md +++ b/README.md @@ -280,18 +280,9 @@ sections. The above examples could also be represented as: Hello {{#class}}{{name}}{{/class}}! Note also that one semantic difference exists between nested singleton sections -and compound variables: compound variables do not make use of the default value -and null value configuration. If a null or missing value is encountered while -resolving a compound variable, an exception is always raised. - -If you desire for a default value to be used when null or missing values are -encountered, use the somewhat more verbose singleton sections. The use of -sections clearly communicates to someone reading the template that the section -will not be rendered if the object it references is missing or null and allows -for a site-specific default value to be used, as in: - - {{#foo}}{{name}}{{/foo}} - {{^foo}}Missing foo!{{/foo}} +and compound variables: after resolving the object for the first component of +the compound variable, parent contexts will not be searched when resolving +subcomponents. Newline trimming ---------------- diff --git a/src/main/java/com/samskivert/mustache/Mustache.java b/src/main/java/com/samskivert/mustache/Mustache.java index 2e239e1..8867fd8 100644 --- a/src/main/java/com/samskivert/mustache/Mustache.java +++ b/src/main/java/com/samskivert/mustache/Mustache.java @@ -85,9 +85,7 @@ public class Mustache /** Returns a compiler that will use the given value for any variable that is missing, or * otherwise resolves to null. This is like {@link #nullValue} except that it returns the - * supplied default for missing keys and existing keys that return null values. Note that - * regardless of whether a null or default value is configured, if the resolution of part - * of a compound key results in a missing or null value, an exception will be raised. */ + * supplied default for missing keys and existing keys that return null values. */ public Compiler defaultValue (String defaultValue) { return new Compiler(this.escapeHTML, this.standardsMode, defaultValue, true, this.loader, this.collector); @@ -103,10 +101,7 @@ public class Mustache *
  • In the case of a {@link Map} being used as a context, all possible accessors are * assumed to exist (but potentially return null), and no exception will ever be * raised.
  • - * - * Note that regardless of whether a null or default value is configured, if the resolution - * of part of a compound key results in a missing or null value, an exception will be - * raised. */ + * */ public Compiler nullValue (String nullValue) { return new Compiler(this.escapeHTML, this.standardsMode, nullValue, false, this.loader, this.collector); diff --git a/src/main/java/com/samskivert/mustache/Template.java b/src/main/java/com/samskivert/mustache/Template.java index bc31afb..4ca8e5b 100644 --- a/src/main/java/com/samskivert/mustache/Template.java +++ b/src/main/java/com/samskivert/mustache/Template.java @@ -93,13 +93,12 @@ public class Template Object data = getValue(ctx, comps[0].intern(), line, missingIsNull); for (int ii = 1; ii < comps.length; ii++) { if (data == NO_FETCHER_FOUND) { - throw new NullPointerException( + if (!missingIsNull) throw new MustacheException( "Missing context for compound variable '" + name + "' on line " + line + ". '" + comps[ii - 1] + "' was not found."); + return null; } else if (data == null) { - throw new NullPointerException( - "Null context for compound variable '" + name + "' on line " + line + - ". '" + comps[ii - 1] + "' resolved to null."); + return null; } // once we step into a composite key, we drop the ability to query our parent // contexts; that would be weird and confusing @@ -214,12 +213,9 @@ public class Template protected Object checkForMissing (String name, int line, boolean missingIsNull, Object value) { if (value == NO_FETCHER_FOUND) { - if (missingIsNull) { - return null; - } else { - throw new MustacheException( - "No method or field with name '" + name + "' on line " + line); - } + if (missingIsNull) return null; + throw new MustacheException( + "No method or field with name '" + name + "' on line " + line); } else { return value; } diff --git a/src/test/java/com/samskivert/mustache/MustacheTest.java b/src/test/java/com/samskivert/mustache/MustacheTest.java index 3a55f33..eed5800 100644 --- a/src/test/java/com/samskivert/mustache/MustacheTest.java +++ b/src/test/java/com/samskivert/mustache/MustacheTest.java @@ -259,7 +259,7 @@ public class MustacheTest execute(context("things", Arrays.asList("bar", "baz", "bif")))); } - @Test public void testStructuredVariable () { + @Test public void testCompoundVariable () { test("hello", "{{foo.bar.baz}}", new Object() { Object foo () { return new Object() { @@ -271,25 +271,39 @@ public class MustacheTest }); } - @Test(expected=NullPointerException.class) + @Test(expected=MustacheException.class) public void testNullComponentInCompoundVariable () { - // make sure that even if we have a null value configured, we freak out - test(Mustache.compiler().nullValue("foo"), "unused", "{{foo.bar.baz}}", new Object() { - Object foo () { - return new Object() { - Object bar = null; - }; - } + test(Mustache.compiler(), "unused", "{{foo.bar.baz}}", new Object() { + Object foo = new Object() { + Object bar = null; + }; }); } - @Test(expected=NullPointerException.class) + @Test(expected=MustacheException.class) public void testMissingComponentInCompoundVariable () { - // make sure that even if we have a default value configured, we freak out - test(Mustache.compiler().defaultValue("foo"), "unused", "{{foo.bar.baz}}", new Object() { - Object foo () { - return new Object(); // no bar - } + test(Mustache.compiler(), "unused", "{{foo.bar.baz}}", new Object() { + Object foo = new Object(); // no bar + }); + } + + public void testNullComponentInCompoundVariableWithDefault () { + test(Mustache.compiler().nullValue("null"), "null", "{{foo.bar.baz}}", new Object() { + Object foo = null; + }); + test(Mustache.compiler().nullValue("null"), "null", "{{foo.bar.baz}}", new Object() { + Object foo = new Object() { + Object bar = null; + }; + }); + } + + public void testMissingComponentInCompoundVariableWithDefault () { + test(Mustache.compiler().defaultValue("?"), "?", "{{foo.bar.baz}}", new Object() { + // no foo, no bar + }); + test(Mustache.compiler().defaultValue("?"), "?", "{{foo.bar.baz}}", new Object() { + Object foo = new Object(); // no bar }); }