Provide context when placeholders found.
It could be useful to know the context when discovering a placeholder. I also renamed MissingKeyEventArgs to KeyNotFoundEventArgs. I created a new event KeyFoundEventArgs.
This commit is contained in:
parent
20dcfc059d
commit
42463a888f
|
@ -332,7 +332,7 @@ Content";
|
|||
/// registering with the PlaceholderFound event.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestCompile_FindsKeys_RecordsKeys()
|
||||
public void TestCompile_FindsPlaceholders_RecordsPlaceholders()
|
||||
{
|
||||
FormatCompiler compiler = new FormatCompiler();
|
||||
HashSet<string> keys = new HashSet<string>();
|
||||
|
@ -346,6 +346,28 @@ Content";
|
|||
CollectionAssert.AreEqual(expected, actual, "Not all placeholders were found.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We can determine the context in which a placeholder is found by looking at the provided context array.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestCompile_FindsPlaceholders_ProvidesContext()
|
||||
{
|
||||
FormatCompiler compiler = new FormatCompiler();
|
||||
Context[] context = null;
|
||||
compiler.PlaceholderFound += (o, e) =>
|
||||
{
|
||||
context = e.Context;
|
||||
};
|
||||
compiler.Compile(@"{{#with Address}}{{ZipCode}}{{/with}}");
|
||||
|
||||
Assert.IsNotNull(context, "The context was not set.");
|
||||
Assert.AreEqual(2, context.Length, "The context did not contain the right number of items.");
|
||||
Assert.AreEqual(String.Empty, context[0].Tag.Name, "The top-most context had the wrong tag type.");
|
||||
Assert.AreEqual("this", context[0].Argument, "The top-level argument should always be 'this'.");
|
||||
Assert.AreEqual("with", context[1].Tag.Name, "The inner context should have been a 'with' tag.");
|
||||
Assert.AreEqual("Address", context[1].Argument, "The inner context argument was wrong.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comment
|
||||
|
|
|
@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("0.0.6.0")]
|
||||
[assembly: AssemblyFileVersion("0.0.6.0")]
|
||||
[assembly: AssemblyVersion("0.0.7.0")]
|
||||
[assembly: AssemblyFileVersion("0.0.7.0")]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mustache
|
||||
{
|
||||
|
@ -29,6 +30,23 @@ namespace mustache
|
|||
_argumentLookup.Add(parameter, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key that will be used to find the substitute value.
|
||||
/// </summary>
|
||||
/// <param name="parameterName">The name of the parameter.</param>
|
||||
public string GetKey(TagParameter parameter)
|
||||
{
|
||||
string key;
|
||||
if (_argumentLookup.TryGetValue(parameter, out key))
|
||||
{
|
||||
return key;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Substitutes the key placeholders with their respective values.
|
||||
/// </summary>
|
||||
|
|
|
@ -26,6 +26,20 @@ namespace mustache
|
|||
_primaryGenerators = new LinkedList<IGenerator>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the argument that will act as the context for the content.
|
||||
/// </summary>
|
||||
/// <returns>The argument that will act as the context for the content.</returns>
|
||||
public string GetContextArgument()
|
||||
{
|
||||
TagParameter parameter = _definition.GetChildContextParameter();
|
||||
if (parameter == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _arguments.GetKey(parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given generator.
|
||||
/// </summary>
|
||||
|
|
|
@ -88,5 +88,14 @@ namespace mustache
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter that is used to create a new child context.
|
||||
/// </summary>
|
||||
/// <returns>The parameter that is used to create a new child context.</returns>
|
||||
public override TagParameter GetChildContextParameter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
|
||||
namespace mustache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a context within a template.
|
||||
/// </summary>
|
||||
public sealed class Context
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of a Context.
|
||||
/// </summary>
|
||||
/// <param name="definition">The definition of tag that created the context.</param>
|
||||
/// <param name="argument">The argument used to create the context.</param>
|
||||
internal Context(TagDefinition definition, string argument)
|
||||
{
|
||||
Tag = definition;
|
||||
Argument = argument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag that created the context.
|
||||
/// </summary>
|
||||
public TagDefinition Tag { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the argument used to create the context.
|
||||
/// </summary>
|
||||
public string Argument { get; private set; }
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ namespace mustache
|
|||
internal sealed class EachTagDefinition : ContentTagDefinition
|
||||
{
|
||||
private const string collectionParameter = "collection";
|
||||
private static readonly TagParameter collection = new TagParameter(collectionParameter) { IsRequired = true };
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of an EachTagDefinition.
|
||||
|
@ -35,7 +36,7 @@ namespace mustache
|
|||
/// <returns>The parameters.</returns>
|
||||
protected override IEnumerable<TagParameter> GetParameters()
|
||||
{
|
||||
return new TagParameter[] { new TagParameter(collectionParameter) { IsRequired = true } };
|
||||
return new TagParameter[] { collection };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -67,5 +68,14 @@ namespace mustache
|
|||
{
|
||||
return new string[] { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter that is used to create a new child context.
|
||||
/// </summary>
|
||||
/// <returns>The parameter that is used to create a new child context.</returns>
|
||||
public override TagParameter GetChildContextParameter()
|
||||
{
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,5 +31,14 @@ namespace mustache
|
|||
{
|
||||
return new string[] { "if" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter that is used to create a new child context.
|
||||
/// </summary>
|
||||
/// <returns>The parameter that is used to create a new child context.</returns>
|
||||
public override TagParameter GetChildContextParameter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ namespace mustache
|
|||
}
|
||||
CompoundGenerator generator = new CompoundGenerator(_masterDefinition, new ArgumentCollection());
|
||||
Trimmer trimmer = new Trimmer();
|
||||
int formatIndex = buildCompoundGenerator(_masterDefinition, generator, trimmer, format, 0);
|
||||
List<Context> context = new List<Context>() { new Context(_masterDefinition, "this") };
|
||||
int formatIndex = buildCompoundGenerator(_masterDefinition, context, generator, trimmer, format, 0);
|
||||
string trailing = format.Substring(formatIndex);
|
||||
generator.AddStaticGenerators(trimmer.RecordText(trailing, false, false));
|
||||
trimmer.Trim();
|
||||
|
@ -166,6 +167,7 @@ namespace mustache
|
|||
|
||||
private int buildCompoundGenerator(
|
||||
TagDefinition tagDefinition,
|
||||
List<Context> context,
|
||||
CompoundGenerator generator,
|
||||
Trimmer trimmer,
|
||||
string format, int formatIndex)
|
||||
|
@ -193,7 +195,7 @@ namespace mustache
|
|||
string key = match.Groups["key"].Value;
|
||||
string alignment = match.Groups["alignment"].Value;
|
||||
string formatting = match.Groups["format"].Value;
|
||||
PlaceholderFoundEventArgs args = new PlaceholderFoundEventArgs(key, alignment, formatting);
|
||||
PlaceholderFoundEventArgs args = new PlaceholderFoundEventArgs(key, alignment, formatting, context.ToArray());
|
||||
if (PlaceholderFound != null)
|
||||
{
|
||||
PlaceholderFound(this, args);
|
||||
|
@ -216,7 +218,12 @@ namespace mustache
|
|||
generator.AddStaticGenerators(trimmer.RecordText(leading, true, false));
|
||||
ArgumentCollection arguments = getArguments(nextDefinition, match);
|
||||
CompoundGenerator compoundGenerator = new CompoundGenerator(nextDefinition, arguments);
|
||||
formatIndex = buildCompoundGenerator(nextDefinition, compoundGenerator, trimmer, format, formatIndex);
|
||||
string newContext = compoundGenerator.GetContextArgument();
|
||||
if (newContext != null)
|
||||
{
|
||||
context.Add(new Context(nextDefinition, newContext));
|
||||
}
|
||||
formatIndex = buildCompoundGenerator(nextDefinition, context, compoundGenerator, trimmer, format, formatIndex);
|
||||
generator.AddGenerator(nextDefinition, compoundGenerator);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -11,7 +11,8 @@ namespace mustache
|
|||
public sealed class Generator
|
||||
{
|
||||
private readonly IGenerator _generator;
|
||||
private readonly List<EventHandler<MissingKeyEventArgs>> _handlers;
|
||||
private readonly List<EventHandler<KeyFoundEventArgs>> _foundHandlers;
|
||||
private readonly List<EventHandler<KeyNotFoundEventArgs>> _notFoundHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of a Generator.
|
||||
|
@ -20,16 +21,26 @@ namespace mustache
|
|||
internal Generator(IGenerator generator)
|
||||
{
|
||||
_generator = generator;
|
||||
_handlers = new List<EventHandler<MissingKeyEventArgs>>();
|
||||
_foundHandlers = new List<EventHandler<KeyFoundEventArgs>>();
|
||||
_notFoundHandlers = new List<EventHandler<KeyNotFoundEventArgs>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a key/property is found.
|
||||
/// </summary>
|
||||
public event EventHandler<KeyFoundEventArgs> KeyFound
|
||||
{
|
||||
add { _foundHandlers.Add(value); }
|
||||
remove { _foundHandlers.Remove(value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a key/property is not found in the object graph.
|
||||
/// </summary>
|
||||
public event EventHandler<MissingKeyEventArgs> KeyNotFound
|
||||
public event EventHandler<KeyNotFoundEventArgs> KeyNotFound
|
||||
{
|
||||
add { _handlers.Add(value); }
|
||||
remove { _handlers.Remove(value); }
|
||||
add { _notFoundHandlers.Add(value); }
|
||||
remove { _notFoundHandlers.Remove(value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -60,7 +71,11 @@ namespace mustache
|
|||
private string render(IFormatProvider provider, object source)
|
||||
{
|
||||
KeyScope scope = new KeyScope(source);
|
||||
foreach (EventHandler<MissingKeyEventArgs> handler in _handlers)
|
||||
foreach (EventHandler<KeyFoundEventArgs> handler in _foundHandlers)
|
||||
{
|
||||
scope.KeyFound += handler;
|
||||
}
|
||||
foreach (EventHandler<KeyNotFoundEventArgs> handler in _notFoundHandlers)
|
||||
{
|
||||
scope.KeyNotFound += handler;
|
||||
}
|
||||
|
|
|
@ -36,5 +36,14 @@ namespace mustache
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter that is used to create a child context.
|
||||
/// </summary>
|
||||
/// <returns>The parameter that is used to create a child context.</returns>
|
||||
public override TagParameter GetChildContextParameter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
|
||||
namespace mustache
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the information about a key that was found.
|
||||
/// </summary>
|
||||
public class KeyFoundEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of a KeyFoundEventArgs.
|
||||
/// </summary>
|
||||
/// <param name="key">The fully-qualified key.</param>
|
||||
internal KeyFoundEventArgs(string key, object value)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fully-qualified key.
|
||||
/// </summary>
|
||||
public string Key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object to use as the substitute.
|
||||
/// </summary>
|
||||
public object Substitute { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,14 +5,14 @@ namespace mustache
|
|||
/// <summary>
|
||||
/// Holds the information needed to handle a missing key.
|
||||
/// </summary>
|
||||
public class MissingKeyEventArgs : EventArgs
|
||||
public class KeyNotFoundEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of a MissingKeyEventArgs.
|
||||
/// Initializes a new instance of a KeyNotFoundEventArgs.
|
||||
/// </summary>
|
||||
/// <param name="key">The fully-qualified key.</param>
|
||||
/// <param name="missingMember">The part of the key that could not be found.</param>
|
||||
internal MissingKeyEventArgs(string key, string missingMember)
|
||||
internal KeyNotFoundEventArgs(string key, string missingMember)
|
||||
{
|
||||
Key = key;
|
||||
MissingMember = missingMember;
|
|
@ -34,10 +34,15 @@ namespace mustache
|
|||
_source = source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a key/property is found in the object graph.
|
||||
/// </summary>
|
||||
public event EventHandler<KeyFoundEventArgs> KeyFound;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a key/property is not found in the object graph.
|
||||
/// </summary>
|
||||
public event EventHandler<MissingKeyEventArgs> KeyNotFound;
|
||||
public event EventHandler<KeyNotFoundEventArgs> KeyNotFound;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a child scope that searches for keys in the given object.
|
||||
|
@ -47,6 +52,7 @@ namespace mustache
|
|||
public KeyScope CreateChildScope(object source)
|
||||
{
|
||||
KeyScope scope = new KeyScope(source, this);
|
||||
scope.KeyFound = KeyFound;
|
||||
scope.KeyNotFound = KeyNotFound;
|
||||
return scope;
|
||||
}
|
||||
|
@ -75,6 +81,12 @@ namespace mustache
|
|||
nextLevel = handleKeyNotFound(name, member);
|
||||
}
|
||||
}
|
||||
if (KeyFound != null)
|
||||
{
|
||||
KeyFoundEventArgs args = new KeyFoundEventArgs(name, nextLevel);
|
||||
KeyFound(this, args);
|
||||
nextLevel = args.Substitute;
|
||||
}
|
||||
return nextLevel;
|
||||
}
|
||||
|
||||
|
@ -94,7 +106,7 @@ namespace mustache
|
|||
|
||||
private object handleKeyNotFound(string fullName, string memberName)
|
||||
{
|
||||
MissingKeyEventArgs args = new MissingKeyEventArgs(fullName, memberName);
|
||||
KeyNotFoundEventArgs args = new KeyNotFoundEventArgs(fullName, memberName);
|
||||
if (KeyNotFound != null)
|
||||
{
|
||||
KeyNotFound(this, args);
|
||||
|
|
|
@ -32,5 +32,14 @@ namespace mustache
|
|||
{
|
||||
return new string[] { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter that is used to create a new child context.
|
||||
/// </summary>
|
||||
/// <returns>The parameter that is used to create a new child context.</returns>
|
||||
public override TagParameter GetChildContextParameter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,13 @@ namespace mustache
|
|||
/// <param name="key">The key that was found.</param>
|
||||
/// <param name="alignment">The alignment that will be applied to the substitute value.</param>
|
||||
/// <param name="formatting">The formatting that will be applied to the substitute value.</param>
|
||||
internal PlaceholderFoundEventArgs(string key, string alignment, string formatting)
|
||||
/// <param name="context">The context where the placeholder was found.</param>
|
||||
internal PlaceholderFoundEventArgs(string key, string alignment, string formatting, Context[] context)
|
||||
{
|
||||
Key = key;
|
||||
Alignment = alignment;
|
||||
Formatting = formatting;
|
||||
Context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -35,5 +37,10 @@ namespace mustache
|
|||
/// Gets or sets the formatting that will be applied to the substitute value.
|
||||
/// </summary>
|
||||
public string Formatting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the context where the placeholder was found.
|
||||
/// </summary>
|
||||
public Context[] Context { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,6 @@ using System.Runtime.CompilerServices;
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.0.6.0")]
|
||||
[assembly: AssemblyFileVersion("0.0.6.0")]
|
||||
[assembly: AssemblyVersion("0.0.7.0")]
|
||||
[assembly: AssemblyFileVersion("0.0.7.0")]
|
||||
[assembly: InternalsVisibleTo("mustache-sharp.test")]
|
|
@ -129,6 +129,12 @@ namespace mustache
|
|||
return new string[] { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter that will be used to create a new child scope.
|
||||
/// </summary>
|
||||
/// <returns>The parameter that will be used to create a new child scope -or- null if no new scope is created.</returns>
|
||||
public abstract TagParameter GetChildContextParameter();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the context to use when building the inner text of the tag.
|
||||
/// </summary>
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace mustache
|
|||
internal sealed class WithTagDefinition : ContentTagDefinition
|
||||
{
|
||||
private const string contextParameter = "context";
|
||||
private static readonly TagParameter context = new TagParameter(contextParameter) { IsRequired = true };
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of a WithTagDefinition.
|
||||
|
@ -33,7 +34,16 @@ namespace mustache
|
|||
/// <returns>The parameters.</returns>
|
||||
protected override IEnumerable<TagParameter> GetParameters()
|
||||
{
|
||||
return new TagParameter[] { new TagParameter(contextParameter) { IsRequired = true } };
|
||||
return new TagParameter[] { context };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter that is used to create a new child context.
|
||||
/// </summary>
|
||||
/// <returns>The parameter that is used to create a new child context.</returns>
|
||||
public override TagParameter GetChildContextParameter()
|
||||
{
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
<Compile Include="CompoundGenerator.cs" />
|
||||
<Compile Include="ConditionTagDefinition.cs" />
|
||||
<Compile Include="ContentTagDefinition.cs" />
|
||||
<Compile Include="Context.cs" />
|
||||
<Compile Include="KeyFoundEventArgs.cs" />
|
||||
<Compile Include="InlineTagDefinition.cs" />
|
||||
<Compile Include="EachTagDefinition.cs" />
|
||||
<Compile Include="ElifTagDefinition.cs" />
|
||||
|
@ -50,7 +52,7 @@
|
|||
<Compile Include="PlaceholderFoundEventArgs.cs" />
|
||||
<Compile Include="KeyGenerator.cs" />
|
||||
<Compile Include="MasterTagDefinition.cs" />
|
||||
<Compile Include="MissingKeyEventArgs.cs" />
|
||||
<Compile Include="KeyNotFoundEventArgs.cs" />
|
||||
<Compile Include="NestedContext.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
|
|
Loading…
Reference in New Issue