@Override protected SoyData visitDataRefNode(DataRefNode node) {
// First resolve the first key, which may reference a variable, data, or injected data.
SoyData value0 = resolveDataRefFirstKey(node);
// Case 1: There is only one key. We already have the final value of the data reference.
if (node.numChildren() == 0) {
return value0;
}
// Case 2: There are more keys.
SoyData value = value0;
for (ExprNode child : node.getChildren()) {
DataRefAccessNode accessNode = (DataRefAccessNode) child;
// We expect 'value' to be a CollectionData during every iteration.
if (! (value instanceof CollectionData)) {
if (accessNode.isNullSafe()) {
if (value == null || value instanceof UndefinedData || value instanceof NullData) {
return NullData.INSTANCE;
} else {
throw new RenderException(
"While evaluating \"" + node.toSourceString() + "\", encountered non-collection" +
" just before accessing \"" + accessNode.toSourceString() + "\".");
}
} else {
// This behavior is not ideal, but needed for compatibility with existing code.
return UndefinedData.INSTANCE;
// TODO: If feasible, find and fix existing instances, then enable this exception.
//if (value == null || value instanceof UndefinedData) {
// throw new RenderException(
// "While evaluating \"" + node.toSourceString() + "\", encountered undefined LHS" +
// " just before accessing \"" + accessNode.toSourceString() + "\".");
//}
//value = UndefinedData.INSTANCE;
//continue;
}
}
// Extract either a string key or integer index from the child access node.
String key = null;
int index = -1;
switch (accessNode.getKind()) {
case DATA_REF_ACCESS_KEY_NODE:
key = ((DataRefAccessKeyNode) accessNode).getKey();
break;
case DATA_REF_ACCESS_INDEX_NODE:
index = ((DataRefAccessIndexNode) accessNode).getIndex();
break;
case DATA_REF_ACCESS_EXPR_NODE:
SoyData keyData = visit(accessNode.getChild(0));
if (keyData instanceof IntegerData) {
index = ((IntegerData) keyData).getValue();
} else {
key = keyData.toString();
}
break;
default:
throw new AssertionError();
}