-
Notifications
You must be signed in to change notification settings - Fork 15
/
mustache_context.dart
204 lines (163 loc) · 5.28 KB
/
mustache_context.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
library mustache_context;
import 'dart:collection';
import 'package:mustache4dart/src/reflect.dart';
const String DOT = '\.';
typedef NoParamLambda();
typedef OptionalParamLambda({nestedContext});
typedef TwoParamLambda(String s, {nestedContext});
abstract class MustacheContext {
factory MustacheContext(ctx,
{MustacheContext parent, assumeNullNonExistingProperty: true}) {
return _createMustacheContext(ctx,
parent: parent,
assumeNullNonExistingProperty: assumeNullNonExistingProperty);
}
get ctx;
value([arg]);
bool get isFalsey;
bool get isLambda;
MustacheContext field(String key);
MustacheContext _getMustacheContext(String key);
}
_createMustacheContext(obj,
{MustacheContext parent, bool assumeNullNonExistingProperty}) {
if (obj is Iterable) {
return new _IterableMustacheContextDecorator(obj,
parent: parent,
assumeNullNonExistingProperty: assumeNullNonExistingProperty);
}
if (obj == false) {
return falseyContext;
}
return new _MustacheContext(obj,
parent: parent,
assumeNullNonExistingProperty: assumeNullNonExistingProperty);
}
final falseyContext = new _MustacheContext(false);
class _MustacheContext implements MustacheContext {
final ctx;
final _MustacheContext parent;
final bool assumeNullNonExistingProperty;
Reflection _ctxReflection;
_MustacheContext(this.ctx,
{_MustacheContext this.parent, this.assumeNullNonExistingProperty});
bool get isLambda => ctx is Function;
bool get isFalsey => ctx == null || ctx == false;
value([arg]) => isLambda ? callLambda(arg) : ctx.toString();
callLambda(arg) {
if (ctx is NoParamLambda) {
return ctx is OptionalParamLambda ? ctx(nestedContext: this) : ctx();
}
if (ctx is TwoParamLambda) {
return ctx(arg, nestedContext: this);
}
return ctx(arg);
}
MustacheContext field(String key) {
if (ctx == null) return null;
return _getInThisOrParent(key);
}
MustacheContext _getInThisOrParent(String key) {
var result = _getContextForKey(key);
//if the result is null, try the parent context
if (result == null) {
final hasSlot = ctxReflector.field(key).exists;
if (!assumeNullNonExistingProperty && !hasSlot && parent == null) {
throw new StateError('Could not find "$key" in given context');
}
//if the result is null, try the parent context
if (!hasSlot && parent != null) {
result = parent.field(key);
if (result != null) {
return _createChildMustacheContext(result.ctx);
}
}
}
return result;
}
MustacheContext _getContextForKey(String key) {
if (key == DOT) {
return this;
}
if (key.contains(DOT)) {
final Iterator<String> i = key.split(DOT).iterator;
MustacheContext val = this;
while (i.moveNext()) {
val = val._getMustacheContext(i.current);
if (val == null) {
return null;
}
}
return val;
}
//else
return _getMustacheContext(key);
}
MustacheContext _getMustacheContext(String fieldName) {
final v = ctxReflector.field(fieldName).val();
return _createChildMustacheContext(v);
}
_createChildMustacheContext(obj) {
if (obj == null) {
return null;
}
return _createMustacheContext(obj,
parent: this,
assumeNullNonExistingProperty: this.assumeNullNonExistingProperty);
}
Reflection get ctxReflector {
if (_ctxReflection == null) {
_ctxReflection = reflect(ctx);
}
return _ctxReflection;
}
}
class _IterableMustacheContextDecorator extends IterableBase<MustacheContext>
implements MustacheContext {
final Iterable ctx;
final _MustacheContext parent;
final bool assumeNullNonExistingProperty;
_IterableMustacheContextDecorator(this.ctx,
{this.parent, this.assumeNullNonExistingProperty});
value([arg]) =>
throw new Exception('Iterable can not be called as a function');
Iterator<MustacheContext> get iterator =>
new _MustacheContextIteratorDecorator(ctx.iterator,
parent: parent,
assumeNullNonExistingProperty: assumeNullNonExistingProperty);
int get length => ctx.length;
bool get isEmpty => ctx.isEmpty;
bool get isFalsey => isEmpty;
bool get isLambda => false;
field(String key) {
assert(key ==
DOT); // 'Iterable can only be iterated. No [] implementation is available'
return this;
}
_getMustacheContext(String key) {
// 'Iterable can only be asked for empty or isEmpty keys or be iterated'
assert(key == 'empty' || key == 'isEmpty');
return new _MustacheContext(isEmpty,
parent: parent,
assumeNullNonExistingProperty: assumeNullNonExistingProperty);
}
}
class _MustacheContextIteratorDecorator extends Iterator<MustacheContext> {
final Iterator delegate;
final _MustacheContext parent;
final bool assumeNullNonExistingProperty;
MustacheContext current;
_MustacheContextIteratorDecorator(this.delegate,
{this.parent, this.assumeNullNonExistingProperty});
bool moveNext() {
if (delegate.moveNext()) {
current = _createMustacheContext(delegate.current,
parent: parent,
assumeNullNonExistingProperty: assumeNullNonExistingProperty);
return true;
} else {
current = null;
return false;
}
}
}