-
Notifications
You must be signed in to change notification settings - Fork 242
Expand file tree
/
Copy pathBindingContext.cs
More file actions
209 lines (165 loc) · 9.38 KB
/
Copy pathBindingContext.cs
File metadata and controls
209 lines (165 loc) · 9.38 KB
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
205
206
207
208
209
using System;
using System.Runtime.CompilerServices;
using HandlebarsDotNet.Collections;
using HandlebarsDotNet.Compiler;
using HandlebarsDotNet.EqualityComparers;
using HandlebarsDotNet.Helpers;
using HandlebarsDotNet.ObjectDescriptors;
using HandlebarsDotNet.PathStructure;
using HandlebarsDotNet.Runtime;
using HandlebarsDotNet.ValueProviders;
namespace HandlebarsDotNet
{
public sealed partial class BindingContext : IDisposable, IHelpersRegistry
{
internal readonly EntryIndex<ChainSegment>[] WellKnownVariables = new EntryIndex<ChainSegment>[8];
internal readonly DeferredValue<BindingContext, ObjectDescriptor> Descriptor;
private BindingContext()
{
InlinePartialTemplates = new CascadeIndex<string, Action<EncodedTextWriter, BindingContext>, StringEqualityComparer>(new StringEqualityComparer(StringComparison.OrdinalIgnoreCase));
Helpers = new CascadeIndex<string, IHelperDescriptor<HelperOptions>, StringEqualityComparer>(new StringEqualityComparer());
BlockHelpers = new CascadeIndex<string, IHelperDescriptor<BlockHelperOptions>, StringEqualityComparer>(new StringEqualityComparer());
Bag = new CascadeIndex<string, object, StringEqualityComparer>(new StringEqualityComparer(StringComparison.OrdinalIgnoreCase));
ContextDataObject = new FixedSizeDictionary<ChainSegment, object, ChainSegment.ChainSegmentEqualityComparer>(16, 7, ChainSegment.EqualityComparer);
BlockParamsObject = new FixedSizeDictionary<ChainSegment, object, ChainSegment.ChainSegmentEqualityComparer>(16, 7, ChainSegment.EqualityComparer);
Descriptor = new DeferredValue<BindingContext, ObjectDescriptor>(this, context =>
{
return ObjectDescriptor.Create(context.Value);
});
}
internal CascadeIndex<string, object, StringEqualityComparer> Bag { get; }
internal FixedSizeDictionary<ChainSegment, object, ChainSegment.ChainSegmentEqualityComparer> ContextDataObject { get; }
internal FixedSizeDictionary<ChainSegment, object, ChainSegment.ChainSegmentEqualityComparer> BlockParamsObject { get; }
internal void SetDataObject(object data)
{
if(data == null) return;
if (!ObjectDescriptor.TryCreate(data.GetType(), out var objectDescriptor))
{
throw new HandlebarsRuntimeException($"Cannot resolve object descriptor for type `{data.GetType()}`");
}
var objectAccessor = new ObjectAccessor(data, objectDescriptor);
foreach (var property in objectAccessor.Properties)
{
var value = objectAccessor[property];
if (property.WellKnownVariable == WellKnownVariable.None)
{
ContextDataObject.AddOrReplace(property, value, out _);
}
else
{
ContextDataObject.AddOrReplace(property, value, out WellKnownVariables[(int) property.WellKnownVariable]);
}
}
}
private void Initialize()
{
Root = ParentContext?.Root ?? this;
ContextDataObject.AddOrReplace(ChainSegment.Root, Root.Value, out WellKnownVariables[(int) WellKnownVariable.Root]);
if (ParentContext == null)
{
ContextDataObject.AddOrReplace(
ChainSegment.Parent,
UndefinedBindingResult.Create(ChainSegment.Parent),
out WellKnownVariables[(int) WellKnownVariable.Parent]
);
return;
}
ParentContext.ContextDataObject.CopyTo(ContextDataObject);
ParentContext.ContextDataObject.AdjustIndexes(ParentContext.WellKnownVariables, ContextDataObject, WellKnownVariables);
ContextDataObject.AddOrReplace(
ChainSegment.Parent,
ParentContext.Value,
out WellKnownVariables[(int) WellKnownVariable.Parent]
);
Bag.Outer = ParentContext.Bag;
ParentContext.BlockParamsObject.CopyTo(BlockParamsObject);
//Inline partials cannot use the Handlebars.RegisteredTemplate method
//because it pollutes the static dictionary and creates collisions
//where the same partial name might exist in multiple templates.
//To avoid collisions, pass around a dictionary of compiled partials
//in the context
InlinePartialTemplates.Outer = ParentContext.InlinePartialTemplates;
Helpers.Outer = ParentContext.Helpers;
BlockHelpers.Outer = ParentContext.BlockHelpers;
if (!(Value is HashParameterDictionary dictionary) || ParentContext.Value == null || ReferenceEquals(Value, ParentContext.Value)) return;
// Populate value with parent context
PopulateHash(dictionary, ParentContext.Value);
}
internal ICompiledHandlebarsConfiguration Configuration { get; private set; }
internal CascadeIndex<string, Action<EncodedTextWriter, BindingContext>, StringEqualityComparer> InlinePartialTemplates { get; }
internal CascadeIndex<string, IHelperDescriptor<HelperOptions>, StringEqualityComparer> Helpers { get; }
internal CascadeIndex<string, IHelperDescriptor<BlockHelperOptions>, StringEqualityComparer> BlockHelpers { get; }
internal TemplateDelegate PartialBlockTemplate { get; set; }
internal short PartialDepth { get; set; }
public object Value { get; set; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BlockParamsValues BlockParams(ChainSegment[] blockParamsVariables) => new BlockParamsValues(this, blockParamsVariables);
public DataValues Data => new DataValues(this);
/// <summary>
/// Used to cary additional data
/// </summary>
public IIndexed<string, object> Extensions => Bag;
internal BindingContext ParentContext { get; private set; }
internal BindingContext Root { get; private set; }
internal bool TryGetVariable(ChainSegment segment, out object value)
{
if (segment.WellKnownVariable != WellKnownVariable.None)
{
var wellKnownVariable = WellKnownVariables[(int) segment.WellKnownVariable];
return BlockParamsObject.TryGetValue(wellKnownVariable, out value)
|| (Descriptor.Value.MemberAccessor?.TryGetValue(Value, segment, out value) ?? false);
}
return BlockParamsObject.TryGetValue(segment, out value)
|| (Descriptor.Value.MemberAccessor?.TryGetValue(Value, segment, out value) ?? false);
}
internal bool TryGetContextVariable(ChainSegment segment, out object value)
{
if (segment.WellKnownVariable != WellKnownVariable.None)
{
var wellKnownVariable = WellKnownVariables[(int) segment.WellKnownVariable];
return BlockParamsObject.TryGetValue(segment, out value)
|| ContextDataObject.TryGetValue(wellKnownVariable, out value);
}
// Special handling for @partial-block: return the PartialBlockTemplate delegate
// so that {{#if @partial-block}} is truthy when a block partial is provided.
if (segment.LowerInvariant == "partial-block")
{
if (PartialBlockTemplate != null)
{
value = PartialBlockTemplate;
return true;
}
value = UndefinedBindingResult.Create(segment.TrimmedValue);
return false;
}
return BlockParamsObject.TryGetValue(segment, out value)
|| ContextDataObject.TryGetValue(segment, out value);
}
internal BindingContext CreateChildContext(object value, TemplateDelegate partialBlockTemplate = null)
{
return Create(Configuration, value, this, partialBlockTemplate ?? PartialBlockTemplate);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BindingContext CreateFrame(object value = null)
{
return Create(Configuration, value, this, PartialBlockTemplate);
}
private static void PopulateHash(HashParameterDictionary hash, object from)
{
var descriptor = ObjectDescriptor.Create(from);
if (descriptor == ObjectDescriptor.Empty) return;
var accessor = descriptor.MemberAccessor;
var properties = descriptor.GetProperties(descriptor, from);
var enumerator = properties.GetEnumerator();
while (enumerator.MoveNext())
{
var segment = ChainSegment.Create(enumerator.Current);
if (hash.ContainsKey(segment)) continue;
if (!accessor.TryGetValue(@from, segment, out var value)) continue;
hash[segment] = value;
}
}
IIndexed<string, IHelperDescriptor<HelperOptions>> IHelpersRegistry.GetHelpers() => Helpers;
IIndexed<string, IHelperDescriptor<BlockHelperOptions>> IHelpersRegistry.GetBlockHelpers() => BlockHelpers;
}
}