Added a "standards mode" based on patches from mrdon. In standards mode, we do

not search parent contexts for name bindings, nor do we support compound keys.
Also added support for {{.}} which has the same semantics as {{this}}. I must
have missed this when reading the Mustache spec.
This commit is contained in:
Michael Bayne
2011-02-28 11:52:40 -08:00
parent c271e7df12
commit f4cfa40c2b
3 changed files with 85 additions and 34 deletions
@@ -60,9 +60,10 @@ public class Template
return out.toString();
}
protected Template (Segment[] segs)
protected Template (Segment[] segs, Mustache.Compiler compiler)
{
_segs = segs;
_compiler = compiler;
}
/**
@@ -71,29 +72,33 @@ public class Template
*
* @param ctx the context in which to look up the variable.
* @param name the name of the variable to be resolved, which must be an interned string.
*
* @return the value associated with the supplied name or null if no value could be resolved.
*/
protected Object getValue (Context ctx, String name, int line)
{
// if we're dealing with a compound key, resolve each component and use the result to
// resolve the subsequent component and so forth
if (name.indexOf(".") != -1) {
String[] comps = name.split("\\.");
// we want to allow the first component of a compound key to be located in a parent
// context, but once we're selecting sub-components, they must only be resolved in the
// object that represents that component
Object data = getValue(ctx, comps[0].intern(), line);
for (int ii = 1; ii < comps.length; ii++) {
// generate more helpful error message
if (data == null) {
throw new NullPointerException(
"Null context for compound variable '" + name + "' on line " + line +
". '" + comps[ii - 1] + "' resolved to null.");
if (!_compiler.standardsMode) {
// if we're dealing with a compound key, resolve each component and use the result to
// resolve the subsequent component and so forth
if (name.indexOf(".") != -1) {
String[] comps = name.split("\\.");
// we want to allow the first component of a compound key to be located in a parent
// context, but once we're selecting sub-components, they must only be resolved in the
// object that represents that component
Object data = getValue(ctx, comps[0].intern(), line);
for (int ii = 1; ii < comps.length; ii++) {
// generate more helpful error message
if (data == null) {
throw new NullPointerException(
"Null context for compound variable '" + name + "' on line " + line +
". '" + comps[ii - 1] + "' resolved to null.");
}
// once we step into a composite key, we drop the ability to query our parent
// contexts; that would be weird and confusing
data = getValueIn(data, comps[ii].intern(), line);
}
// once we step into a composite key, we drop the ability to query our parent
// contexts; that would be weird and confusing
data = getValueIn(data, comps[ii].intern(), line);
return data;
}
return data;
}
// handle our special variables
@@ -105,6 +110,11 @@ public class Template
return ctx.index;
}
// if we're in standards mode, we don't search our parent contexts
if (_compiler.standardsMode) {
return getValueIn(ctx.data, name, line);
}
while (ctx != null) {
Object value = getValueIn(ctx.data, name, line);
if (value != null) {
@@ -112,9 +122,8 @@ public class Template
}
ctx = ctx.parent;
}
// we've popped all the way off the top of our stack of contexts, so fail
throw new MustacheException(
"No key, method or field with name '" + name + "' on line " + line);
// we've popped off the top of our stack of contexts, so return null
return null;
}
protected Object getValueIn (Object data, String name, int line)
@@ -154,12 +163,14 @@ public class Template
}
protected final Segment[] _segs;
protected final Mustache.Compiler _compiler;
protected final Map<Key, VariableFetcher> _fcache =
new ConcurrentHashMap<Key, VariableFetcher>();
protected static VariableFetcher createFetcher (Key key)
{
if (key.name == THIS_NAME) {
// support both .name and this.name to fetch members
if (key.name == DOT_NAME || key.name == THIS_NAME) {
return THIS_FETCHER;
}
@@ -314,6 +325,7 @@ public class Template
}
};
protected static final String DOT_NAME = ".".intern();
protected static final String THIS_NAME = "this".intern();
protected static final String FIRST_NAME = "-first".intern();
protected static final String LAST_NAME = "-last".intern();