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
});
}