blob: ffc4d73eb6968249f67e30665a8c5b62ce832f00 [file] [log] [blame]
part of mustache.impl;
final RegExp _validTag = new RegExp(r'^[0-9a-zA-Z\_\-\.]+$');
final RegExp _integerTag = new RegExp(r'^[0-9]+$');
const Object _noSuchProperty = const Object();
class _RenderContext {
_RenderContext(this._sink,
List stack,
this.lenient,
this.htmlEscapeValues,
this.partialResolver,
this.templateName,
this.indent,
this.source)
: _stack = new List.from(stack);
_RenderContext.partial(_RenderContext ctx, TemplateImpl partial, String indent)
: this(ctx._sink,
ctx._stack,
ctx.lenient,
ctx.htmlEscapeValues,
ctx.partialResolver,
ctx.templateName,
ctx.indent + indent,
partial.source);
_RenderContext.subtree(_RenderContext ctx, StringSink sink)
: this(sink,
ctx._stack,
ctx.lenient,
ctx.htmlEscapeValues,
ctx.partialResolver,
ctx.templateName,
ctx.indent,
ctx.source);
_RenderContext.lambda(
_RenderContext ctx,
String source,
String indent,
StringSink sink,
String delimiters)
: this(sink,
ctx._stack,
ctx.lenient,
ctx.htmlEscapeValues,
ctx.partialResolver,
ctx.templateName,
ctx.indent + indent,
source);
final StringSink _sink;
final List _stack;
final bool lenient;
final bool htmlEscapeValues;
final PartialResolver partialResolver;
final String templateName;
final String indent;
final String source;
void push(value) => _stack.add(value);
Object pop() => _stack.removeLast();
write(Object output) => _sink.write(output.toString());
// Walks up the stack looking for the variable.
// Handles dotted names of the form "a.b.c".
Object resolveValue(String name) {
if (name == '.') {
return _stack.last;
}
var parts = name.split('.');
var object = _noSuchProperty;
for (var o in _stack.reversed) {
object = _getNamedProperty(o, parts[0]);
if (object != _noSuchProperty) {
break;
}
}
for (int i = 1; i < parts.length; i++) {
if (object == null || object == _noSuchProperty) {
return _noSuchProperty;
}
object = _getNamedProperty(object, parts[i]);
}
return object;
}
// Returns the property of the given object by name. For a map,
// which contains the key name, this is object[name]. For other
// objects, this is object.name or object.name(). If no property
// by the given name exists, this method returns noSuchProperty.
_getNamedProperty(object, name) {
if (object is Map && object.containsKey(name))
return object[name];
if (object is List && _integerTag.hasMatch(name))
return object[int.parse(name)];
if (lenient && !_validTag.hasMatch(name))
return _noSuchProperty;
var instance = reflect(object);
var field = instance.type.instanceMembers[new Symbol(name)];
if (field == null) return _noSuchProperty;
var invocation = null;
if ((field is VariableMirror) || ((field is MethodMirror) && (field.isGetter))) {
invocation = instance.getField(field.simpleName);
} else if ((field is MethodMirror) && (field.parameters.length == 0)) {
invocation = instance.invoke(field.simpleName, []);
}
if (invocation == null) {
return _noSuchProperty;
}
return invocation.reflectee;
}
TemplateException error(String message, _Node node)
=> new _TemplateException(message, templateName, source, node.start);
}