Begin implementation of context scopes.
Instead of context data being a single object, it now supports the same scoping rules as keys. Now the #index tag is always available.
This commit is contained in:
parent
eb7e909f95
commit
f136dd61a5
|
@ -1093,7 +1093,7 @@ Item Number: foo<br />
|
||||||
return new TagParameter[] { new TagParameter("param") { IsRequired = false, DefaultValue = 123 } };
|
return new TagParameter[] { new TagParameter("param") { IsRequired = false, DefaultValue = 123 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData)
|
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope contextScope)
|
||||||
{
|
{
|
||||||
writer.Write(arguments["param"]);
|
writer.Write(arguments["param"]);
|
||||||
}
|
}
|
||||||
|
@ -1168,5 +1168,23 @@ Your order total was: $7.50";
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Context Variables
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// We will use the index variable to determine whether or not to print out a line.
|
||||||
|
/// </summary>
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCompile_CanUseContextVariablesToMakeDecisions()
|
||||||
|
{
|
||||||
|
FormatCompiler compiler = new FormatCompiler();
|
||||||
|
const string format = @"{{#each this}}{{#if @index}}{{#index}}{{/if}}{{/each}}";
|
||||||
|
Generator generator = compiler.Compile(format);
|
||||||
|
string actual = generator.Render(new int[] { 1, 1, 1, 1, });
|
||||||
|
string expected = "123";
|
||||||
|
Assert.AreEqual(expected, actual, "The numbers were not valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,10 @@ namespace Mustache
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Substitutes the key placeholders with their respective values.
|
/// Substitutes the key placeholders with their respective values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scope">The current lexical scope.</param>
|
/// <param name="keyScope">The key/value pairs in the current lexical scope.</param>
|
||||||
|
/// <param name="contextScope">The key/value pairs in current context.</param>
|
||||||
/// <returns>A dictionary associating the parameter name to the associated value.</returns>
|
/// <returns>A dictionary associating the parameter name to the associated value.</returns>
|
||||||
public Dictionary<string, object> GetArguments(KeyScope scope)
|
public Dictionary<string, object> GetArguments(Scope keyScope, Scope contextScope)
|
||||||
{
|
{
|
||||||
Dictionary<string, object> arguments = new Dictionary<string,object>();
|
Dictionary<string, object> arguments = new Dictionary<string,object>();
|
||||||
foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup)
|
foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup)
|
||||||
|
@ -62,13 +63,27 @@ namespace Mustache
|
||||||
{
|
{
|
||||||
value = pair.Key.DefaultValue;
|
value = pair.Key.DefaultValue;
|
||||||
}
|
}
|
||||||
|
else if (pair.Value.StartsWith("@"))
|
||||||
|
{
|
||||||
|
value = contextScope.Find(pair.Value.Substring(1));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
value = scope.Find(pair.Value);
|
value = keyScope.Find(pair.Value);
|
||||||
}
|
}
|
||||||
arguments.Add(pair.Key.Name, value);
|
arguments.Add(pair.Key.Name, value);
|
||||||
}
|
}
|
||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, object> GetArguments()
|
||||||
|
{
|
||||||
|
Dictionary<string, object> arguments = new Dictionary<string, object>();
|
||||||
|
foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup)
|
||||||
|
{
|
||||||
|
arguments.Add(pair.Key.Name, pair.Value);
|
||||||
|
}
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,10 +59,10 @@ namespace Mustache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
|
void IGenerator.GetText(Scope keyScope, TextWriter writer, Scope contextScope)
|
||||||
{
|
{
|
||||||
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
|
Dictionary<string, object> arguments = _arguments.GetArguments(keyScope, contextScope);
|
||||||
IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments);
|
IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, keyScope, arguments, contextScope);
|
||||||
List<IGenerator> generators;
|
List<IGenerator> generators;
|
||||||
if (_definition.ShouldGeneratePrimaryGroup(arguments))
|
if (_definition.ShouldGeneratePrimaryGroup(arguments))
|
||||||
{
|
{
|
||||||
|
@ -80,7 +80,7 @@ namespace Mustache
|
||||||
{
|
{
|
||||||
foreach (IGenerator generator in generators)
|
foreach (IGenerator generator in generators)
|
||||||
{
|
{
|
||||||
generator.GetText(context.KeyScope ?? scope, context.Writer ?? writer, context.Data);
|
generator.GetText(context.KeyScope ?? keyScope, context.Writer ?? writer, context.ContextScope);
|
||||||
if (context.WriterNeedsConsidated)
|
if (context.WriterNeedsConsidated)
|
||||||
{
|
{
|
||||||
writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments));
|
writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments));
|
||||||
|
|
|
@ -43,10 +43,14 @@ namespace Mustache
|
||||||
/// Gets the context to use when building the inner text of the tag.
|
/// Gets the context to use when building the inner text of the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The text writer passed</param>
|
/// <param name="writer">The text writer passed</param>
|
||||||
/// <param name="scope">The current scope.</param>
|
/// <param name="keyScope">The current scope.</param>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
||||||
public override IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
public override IEnumerable<NestedContext> GetChildContext(
|
||||||
|
TextWriter writer,
|
||||||
|
Scope keyScope,
|
||||||
|
Dictionary<string, object> arguments,
|
||||||
|
Scope contextScope)
|
||||||
{
|
{
|
||||||
object value = arguments[collectionParameter];
|
object value = arguments[collectionParameter];
|
||||||
IEnumerable enumerable = value as IEnumerable;
|
IEnumerable enumerable = value as IEnumerable;
|
||||||
|
@ -57,7 +61,14 @@ namespace Mustache
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (object item in enumerable)
|
foreach (object item in enumerable)
|
||||||
{
|
{
|
||||||
yield return new NestedContext() { KeyScope = scope.CreateChildScope(item), Writer = writer, Data = index };
|
NestedContext childContext = new NestedContext()
|
||||||
|
{
|
||||||
|
KeyScope = keyScope.CreateChildScope(item),
|
||||||
|
Writer = writer,
|
||||||
|
ContextScope = contextScope.CreateChildScope(),
|
||||||
|
};
|
||||||
|
childContext.ContextScope.Set("index", index);
|
||||||
|
yield return childContext;
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ namespace Mustache
|
||||||
_tagLookup.Add(withDefinition.Name, withDefinition);
|
_tagLookup.Add(withDefinition.Name, withDefinition);
|
||||||
NewlineTagDefinition newlineDefinition = new NewlineTagDefinition();
|
NewlineTagDefinition newlineDefinition = new NewlineTagDefinition();
|
||||||
_tagLookup.Add(newlineDefinition.Name, newlineDefinition);
|
_tagLookup.Add(newlineDefinition.Name, newlineDefinition);
|
||||||
|
SetTagDefinition setDefinition = new SetTagDefinition();
|
||||||
|
_tagLookup.Add(setDefinition.Name, setDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -150,9 +152,11 @@ namespace Mustache
|
||||||
foreach (TagParameter parameter in definition.Parameters)
|
foreach (TagParameter parameter in definition.Parameters)
|
||||||
{
|
{
|
||||||
regexBuilder.Append(@"(\s+?");
|
regexBuilder.Append(@"(\s+?");
|
||||||
regexBuilder.Append(@"(?<argument>");
|
regexBuilder.Append(@"(?<argument>(");
|
||||||
regexBuilder.Append(RegexHelper.CompoundKey);
|
regexBuilder.Append(RegexHelper.CompoundKey);
|
||||||
regexBuilder.Append(@"))");
|
regexBuilder.Append("|@");
|
||||||
|
regexBuilder.Append(RegexHelper.Key);
|
||||||
|
regexBuilder.Append(@")))");
|
||||||
if (!parameter.IsRequired)
|
if (!parameter.IsRequired)
|
||||||
{
|
{
|
||||||
regexBuilder.Append("?");
|
regexBuilder.Append("?");
|
||||||
|
|
|
@ -70,7 +70,7 @@ namespace Mustache
|
||||||
|
|
||||||
private string render(IFormatProvider provider, object source)
|
private string render(IFormatProvider provider, object source)
|
||||||
{
|
{
|
||||||
KeyScope scope = new KeyScope(source);
|
Scope scope = new Scope(source);
|
||||||
foreach (EventHandler<KeyFoundEventArgs> handler in _foundHandlers)
|
foreach (EventHandler<KeyFoundEventArgs> handler in _foundHandlers)
|
||||||
{
|
{
|
||||||
scope.KeyFound += handler;
|
scope.KeyFound += handler;
|
||||||
|
@ -80,7 +80,8 @@ namespace Mustache
|
||||||
scope.KeyNotFound += handler;
|
scope.KeyNotFound += handler;
|
||||||
}
|
}
|
||||||
StringWriter writer = new StringWriter(provider);
|
StringWriter writer = new StringWriter(provider);
|
||||||
_generator.GetText(scope, writer, null);
|
Scope contextScope = new Scope(new Dictionary<string, object>());
|
||||||
|
_generator.GetText(scope, writer, contextScope);
|
||||||
return writer.ToString();
|
return writer.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,10 @@ namespace Mustache
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates the text when applying the format plan.
|
/// Generates the text when applying the format plan.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scope">The current lexical scope of the keys.</param>
|
/// <param name="keyScope">The current lexical scope of the keys.</param>
|
||||||
/// <param name="writer">The text writer to send all text to.</param>
|
/// <param name="writer">The text writer to send all text to.</param>
|
||||||
/// <param name="contextData">The data associated to the context.</param>
|
/// <param name="contextScope">The data associated to the context.</param>
|
||||||
/// <returns>The generated text.</returns>
|
/// <returns>The generated text.</returns>
|
||||||
void GetText(KeyScope scope, TextWriter writer, object contextData);
|
void GetText(Scope keyScope, TextWriter writer, Scope contextScope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,28 +13,23 @@ namespace Mustache
|
||||||
/// Initializes a new instance of an IndexTagDefinition.
|
/// Initializes a new instance of an IndexTagDefinition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IndexTagDefinition()
|
public IndexTagDefinition()
|
||||||
: base("index")
|
: base("index", true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets whether the tag only exists within the scope of its parent.
|
|
||||||
/// </summary>
|
|
||||||
protected override bool GetIsContextSensitive()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the text to output.
|
/// Gets the text to output.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The writer to write the output to.</param>
|
/// <param name="writer">The writer to write the output to.</param>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <param name="contextData">Extra data passed along with the context.</param>
|
/// <param name="contextScope">Extra data passed along with the context.</param>
|
||||||
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData)
|
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope contextScope)
|
||||||
|
{
|
||||||
|
object index;
|
||||||
|
if (contextScope.TryFind("index", out index))
|
||||||
{
|
{
|
||||||
int index = (int)contextData;
|
|
||||||
writer.Write(index);
|
writer.Write(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ using System.IO;
|
||||||
namespace Mustache
|
namespace Mustache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates the text for a tag that only exists on a single line.
|
/// Generates the text for a tag that is replaced with its generated text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class InlineGenerator : IGenerator
|
internal sealed class InlineGenerator : IGenerator
|
||||||
{
|
{
|
||||||
|
@ -23,10 +23,18 @@ namespace Mustache
|
||||||
_arguments = arguments;
|
_arguments = arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
|
void IGenerator.GetText(Scope scope, TextWriter writer, Scope context)
|
||||||
{
|
{
|
||||||
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
|
Dictionary<string, object> arguments;
|
||||||
_definition.GetText(writer, arguments, contextData);
|
if (_definition.IsSetter)
|
||||||
|
{
|
||||||
|
arguments = _arguments.GetArguments();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arguments = _arguments.GetArguments(scope, context);
|
||||||
|
}
|
||||||
|
_definition.GetText(writer, arguments, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ namespace Mustache
|
||||||
return formatBuilder.ToString();
|
return formatBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
|
void IGenerator.GetText(Scope scope, TextWriter writer, Scope context)
|
||||||
{
|
{
|
||||||
object value = scope.Find(_key);
|
object value = scope.Find(_key);
|
||||||
writer.Write(_format, value);
|
writer.Write(_format, value);
|
||||||
|
|
|
@ -19,36 +19,24 @@ namespace Mustache
|
||||||
/// Gets or sets the writer to use when generating the child context.
|
/// Gets or sets the writer to use when generating the child context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Setting the writer to null will indicate that the tag's writer should be used.</remarks>
|
/// <remarks>Setting the writer to null will indicate that the tag's writer should be used.</remarks>
|
||||||
public TextWriter Writer
|
public TextWriter Writer { get; set; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether the text sent to the returned writer needs to be added
|
/// Gets or sets whether the text sent to the returned writer needs to be added
|
||||||
/// to the parent tag's writer. This should be false if the parent writer is
|
/// to the parent tag's writer. This should be false if the parent writer is
|
||||||
/// being returned or is being wrapped.
|
/// being returned or is being wrapped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool WriterNeedsConsidated
|
public bool WriterNeedsConsidated { get; set; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the scope to use when generating the child context.
|
/// Gets or sets the key scope to use when generating the child context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Setting the scope to null will indicate that the current scope should be used.</remarks>
|
/// <remarks>Setting the scope to null will indicate that the current scope should be used.</remarks>
|
||||||
public KeyScope KeyScope
|
public Scope KeyScope { get; set; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets data associated with the context.
|
/// Gets or sets data associated with the context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object Data { get; set; }
|
public Scope ContextScope { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ namespace Mustache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The writer to write the output to.</param>
|
/// <param name="writer">The writer to write the output to.</param>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <param name="contextData">Extra data passed along with the context.</param>
|
/// <param name="context">Extra data passed along with the context.</param>
|
||||||
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData)
|
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope context)
|
||||||
{
|
{
|
||||||
writer.Write(Environment.NewLine);
|
writer.Write(Environment.NewLine);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace Mustache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class RegexHelper
|
public static class RegexHelper
|
||||||
{
|
{
|
||||||
private const string Key = @"[_\w][_\w\d]*";
|
internal const string Key = @"[_\w][_\w\d]*";
|
||||||
internal const string CompoundKey = Key + @"(\." + Key + ")*";
|
internal const string CompoundKey = Key + @"(\." + Key + ")*";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -9,16 +9,16 @@ namespace Mustache
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a scope of keys.
|
/// Represents a scope of keys.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class KeyScope
|
public sealed class Scope
|
||||||
{
|
{
|
||||||
private readonly object _source;
|
private readonly object _source;
|
||||||
private readonly KeyScope _parent;
|
private readonly Scope _parent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a KeyScope.
|
/// Initializes a new instance of a KeyScope.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The object to search for keys in.</param>
|
/// <param name="source">The object to search for keys in.</param>
|
||||||
internal KeyScope(object source)
|
internal Scope(object source)
|
||||||
: this(source, null)
|
: this(source, null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ namespace Mustache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The object to search for keys in.</param>
|
/// <param name="source">The object to search for keys in.</param>
|
||||||
/// <param name="parent">The parent scope to search in if the value is not found.</param>
|
/// <param name="parent">The parent scope to search in if the value is not found.</param>
|
||||||
internal KeyScope(object source, KeyScope parent)
|
internal Scope(object source, Scope parent)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
_source = source;
|
_source = source;
|
||||||
|
@ -44,14 +44,23 @@ namespace Mustache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<KeyNotFoundEventArgs> KeyNotFound;
|
public event EventHandler<KeyNotFoundEventArgs> KeyNotFound;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a child scope that searches for keys in a default dictionary of key/value pairs.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The new child scope.</returns>
|
||||||
|
public Scope CreateChildScope()
|
||||||
|
{
|
||||||
|
return CreateChildScope(new Dictionary<string, object>());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a child scope that searches for keys in the given object.
|
/// Creates a child scope that searches for keys in the given object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The object to search for keys in.</param>
|
/// <param name="source">The object to search for keys in.</param>
|
||||||
/// <returns>The new child scope.</returns>
|
/// <returns>The new child scope.</returns>
|
||||||
public KeyScope CreateChildScope(object source)
|
public Scope CreateChildScope(object source)
|
||||||
{
|
{
|
||||||
KeyScope scope = new KeyScope(source, this);
|
Scope scope = new Scope(source, this);
|
||||||
scope.KeyFound = KeyFound;
|
scope.KeyFound = KeyFound;
|
||||||
scope.KeyNotFound = KeyNotFound;
|
scope.KeyNotFound = KeyNotFound;
|
||||||
return scope;
|
return scope;
|
||||||
|
@ -65,58 +74,45 @@ namespace Mustache
|
||||||
/// <exception cref="System.Collections.Generic.KeyNotFoundException">A key with the given name could not be found.</exception>
|
/// <exception cref="System.Collections.Generic.KeyNotFoundException">A key with the given name could not be found.</exception>
|
||||||
internal object Find(string name)
|
internal object Find(string name)
|
||||||
{
|
{
|
||||||
string[] names = name.Split('.');
|
string member = null;
|
||||||
string member = names[0];
|
object value = null;
|
||||||
object nextLevel = _source;
|
if (tryFind(name, ref member, ref value))
|
||||||
if (member != "this")
|
|
||||||
{
|
{
|
||||||
nextLevel = find(name, member);
|
onKeyFound(name, ref value);
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
for (int index = 1; index < names.Length; ++index)
|
if (onKeyNotFound(name, member, ref value))
|
||||||
{
|
{
|
||||||
IDictionary<string, object> context = toLookup(nextLevel);
|
return value;
|
||||||
member = names[index];
|
}
|
||||||
if (!context.TryGetValue(member, out nextLevel))
|
string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, member);
|
||||||
|
throw new KeyNotFoundException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onKeyFound(string name, ref object value)
|
||||||
{
|
{
|
||||||
nextLevel = handleKeyNotFound(name, member);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (KeyFound != null)
|
if (KeyFound != null)
|
||||||
{
|
{
|
||||||
KeyFoundEventArgs args = new KeyFoundEventArgs(name, nextLevel);
|
KeyFoundEventArgs args = new KeyFoundEventArgs(name, value);
|
||||||
KeyFound(this, args);
|
KeyFound(this, args);
|
||||||
nextLevel = args.Substitute;
|
value = args.Substitute;
|
||||||
}
|
}
|
||||||
return nextLevel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private object find(string fullName, string memberName)
|
private bool onKeyNotFound(string name, string member, ref object value)
|
||||||
{
|
{
|
||||||
IDictionary<string, object> lookup = toLookup(_source);
|
if (KeyNotFound == null)
|
||||||
if (lookup.ContainsKey(memberName))
|
|
||||||
{
|
{
|
||||||
return lookup[memberName];
|
return false;
|
||||||
}
|
}
|
||||||
if (_parent == null)
|
KeyNotFoundEventArgs args = new KeyNotFoundEventArgs(name, member);
|
||||||
{
|
|
||||||
return handleKeyNotFound(fullName, memberName);
|
|
||||||
}
|
|
||||||
return _parent.find(fullName, memberName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private object handleKeyNotFound(string fullName, string memberName)
|
|
||||||
{
|
|
||||||
KeyNotFoundEventArgs args = new KeyNotFoundEventArgs(fullName, memberName);
|
|
||||||
if (KeyNotFound != null)
|
|
||||||
{
|
|
||||||
KeyNotFound(this, args);
|
KeyNotFound(this, args);
|
||||||
}
|
if (!args.Handled)
|
||||||
if (args.Handled)
|
|
||||||
{
|
{
|
||||||
return args.Substitute;
|
return false;
|
||||||
}
|
}
|
||||||
string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, memberName);
|
value = args.Substitute;
|
||||||
throw new KeyNotFoundException(message);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IDictionary<string, object> toLookup(object value)
|
private static IDictionary<string, object> toLookup(object value)
|
||||||
|
@ -128,5 +124,59 @@ namespace Mustache
|
||||||
}
|
}
|
||||||
return lookup;
|
return lookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void Set(string key, object value)
|
||||||
|
{
|
||||||
|
IDictionary<string, object> lookup = toLookup(_source);
|
||||||
|
lookup[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryFind(string name, out object value)
|
||||||
|
{
|
||||||
|
string member = null;
|
||||||
|
value = null;
|
||||||
|
return tryFind(name, ref member, ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool tryFind(string name, ref string member, ref object value)
|
||||||
|
{
|
||||||
|
string[] names = name.Split('.');
|
||||||
|
member = names[0];
|
||||||
|
value = _source;
|
||||||
|
if (member != "this")
|
||||||
|
{
|
||||||
|
if (!tryFindFirst(member, ref value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int index = 1; index < names.Length; ++index)
|
||||||
|
{
|
||||||
|
IDictionary<string, object> context = toLookup(value);
|
||||||
|
member = names[index];
|
||||||
|
if (!context.TryGetValue(member, out value))
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool tryFindFirst(string member, ref object value)
|
||||||
|
{
|
||||||
|
IDictionary<string, object> lookup = toLookup(_source);
|
||||||
|
if (lookup.ContainsKey(member))
|
||||||
|
{
|
||||||
|
value = lookup[member];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_parent == null)
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _parent.tryFindFirst(member, ref value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Mustache
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a tag that declares a named value in the current context.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class SetTagDefinition : InlineTagDefinition
|
||||||
|
{
|
||||||
|
private const string nameParameter = "name";
|
||||||
|
private static readonly TagParameter name = new TagParameter(nameParameter) { IsRequired = true };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of an SetTagDefinition.
|
||||||
|
/// </summary>
|
||||||
|
public SetTagDefinition()
|
||||||
|
: base("set", true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool GetIsSetter()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<TagParameter> GetParameters()
|
||||||
|
{
|
||||||
|
return new TagParameter[] { name };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the text to output.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The writer to write the output to.</param>
|
||||||
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
|
/// <param name="contextScope">Extra data passed along with the context.</param>
|
||||||
|
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope contextScope)
|
||||||
|
{
|
||||||
|
string name = (string)arguments[nameParameter];
|
||||||
|
// TODO - get the value for the variable
|
||||||
|
object value = null;
|
||||||
|
contextScope.Set(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ namespace Mustache
|
||||||
get { return value; }
|
get { return value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
|
void IGenerator.GetText(Scope scope, TextWriter writer, Scope context)
|
||||||
{
|
{
|
||||||
writer.Write(Value);
|
writer.Write(Value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,16 @@ namespace Mustache
|
||||||
get { return _tagName; }
|
get { return _tagName; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal bool IsSetter
|
||||||
|
{
|
||||||
|
get { return GetIsSetter(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual bool GetIsSetter()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the tag is limited to the parent tag's context.
|
/// Gets whether the tag is limited to the parent tag's context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -139,12 +149,22 @@ namespace Mustache
|
||||||
/// Gets the context to use when building the inner text of the tag.
|
/// Gets the context to use when building the inner text of the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The text writer passed</param>
|
/// <param name="writer">The text writer passed</param>
|
||||||
/// <param name="scope">The current scope.</param>
|
/// <param name="keyScope">The current key scope.</param>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
||||||
public virtual IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
public virtual IEnumerable<NestedContext> GetChildContext(
|
||||||
|
TextWriter writer,
|
||||||
|
Scope keyScope,
|
||||||
|
Dictionary<string, object> arguments,
|
||||||
|
Scope contextScope)
|
||||||
{
|
{
|
||||||
yield return new NestedContext() { KeyScope = scope, Writer = writer };
|
NestedContext context = new NestedContext()
|
||||||
|
{
|
||||||
|
KeyScope = keyScope,
|
||||||
|
Writer = writer,
|
||||||
|
ContextScope = contextScope.CreateChildScope()
|
||||||
|
};
|
||||||
|
yield return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -152,8 +172,8 @@ namespace Mustache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The text writer to write to.</param>
|
/// <param name="writer">The text writer to write to.</param>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <param name="contextData">The data associated to the context.</param>
|
/// <param name="context">The data associated to the context.</param>
|
||||||
public virtual void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData)
|
public virtual void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,13 +50,23 @@ namespace Mustache
|
||||||
/// Gets the context to use when building the inner text of the tag.
|
/// Gets the context to use when building the inner text of the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The text writer passed</param>
|
/// <param name="writer">The text writer passed</param>
|
||||||
/// <param name="scope">The current scope.</param>
|
/// <param name="keyScope">The current key scope.</param>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
||||||
public override IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
public override IEnumerable<NestedContext> GetChildContext(
|
||||||
|
TextWriter writer,
|
||||||
|
Scope keyScope,
|
||||||
|
Dictionary<string, object> arguments,
|
||||||
|
Scope contextScope)
|
||||||
{
|
{
|
||||||
object context = arguments[contextParameter];
|
object contextSource = arguments[contextParameter];
|
||||||
yield return new NestedContext() { KeyScope = scope.CreateChildScope(context), Writer = writer };
|
NestedContext context = new NestedContext()
|
||||||
|
{
|
||||||
|
KeyScope = keyScope.CreateChildScope(contextSource),
|
||||||
|
Writer = writer,
|
||||||
|
ContextScope = contextScope.CreateChildScope()
|
||||||
|
};
|
||||||
|
yield return context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -40,6 +40,7 @@
|
||||||
<Compile Include="ContentTagDefinition.cs" />
|
<Compile Include="ContentTagDefinition.cs" />
|
||||||
<Compile Include="Context.cs" />
|
<Compile Include="Context.cs" />
|
||||||
<Compile Include="ContextParameter.cs" />
|
<Compile Include="ContextParameter.cs" />
|
||||||
|
<Compile Include="SetTagDefinition.cs" />
|
||||||
<Compile Include="NewlineTagDefinition.cs" />
|
<Compile Include="NewlineTagDefinition.cs" />
|
||||||
<Compile Include="IndexTagDefinition.cs" />
|
<Compile Include="IndexTagDefinition.cs" />
|
||||||
<Compile Include="KeyFoundEventArgs.cs" />
|
<Compile Include="KeyFoundEventArgs.cs" />
|
||||||
|
@ -68,8 +69,8 @@
|
||||||
<Compile Include="StaticGenerator.cs" />
|
<Compile Include="StaticGenerator.cs" />
|
||||||
<Compile Include="TagDefinition.cs" />
|
<Compile Include="TagDefinition.cs" />
|
||||||
<Compile Include="TagParameter.cs" />
|
<Compile Include="TagParameter.cs" />
|
||||||
<Compile Include="KeyScope.cs" />
|
<Compile Include="Scope.cs" />
|
||||||
<Compile Include="WithGenerator.cs" />
|
<Compile Include="WithTagDefinition.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Properties\Resources.resx">
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
|
Loading…
Reference in New Issue