| part of mustache; |
| |
| 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, _Template 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 pushValue(value) => _stack.add(value); |
| |
| Object popValue() => _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); |
| } |