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.
|
/// registering with the PlaceholderFound event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestCompile_FindsKeys_RecordsKeys()
|
public void TestCompile_FindsPlaceholders_RecordsPlaceholders()
|
||||||
{
|
{
|
||||||
FormatCompiler compiler = new FormatCompiler();
|
FormatCompiler compiler = new FormatCompiler();
|
||||||
HashSet<string> keys = new HashSet<string>();
|
HashSet<string> keys = new HashSet<string>();
|
||||||
|
@ -346,6 +346,28 @@ Content";
|
||||||
CollectionAssert.AreEqual(expected, actual, "Not all placeholders were found.");
|
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
|
#endregion
|
||||||
|
|
||||||
#region Comment
|
#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
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
[assembly: AssemblyVersion("0.0.6.0")]
|
[assembly: AssemblyVersion("0.0.7.0")]
|
||||||
[assembly: AssemblyFileVersion("0.0.6.0")]
|
[assembly: AssemblyFileVersion("0.0.7.0")]
|
||||||
|
|
|
@ -1,56 +1,74 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
namespace mustache
|
|
||||||
{
|
namespace mustache
|
||||||
/// <summary>
|
{
|
||||||
/// Associates parameters to their argument values.
|
/// <summary>
|
||||||
/// </summary>
|
/// Associates parameters to their argument values.
|
||||||
internal sealed class ArgumentCollection
|
/// </summary>
|
||||||
{
|
internal sealed class ArgumentCollection
|
||||||
private readonly Dictionary<TagParameter, string> _argumentLookup;
|
{
|
||||||
|
private readonly Dictionary<TagParameter, string> _argumentLookup;
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of an ArgumentCollection.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of an ArgumentCollection.
|
||||||
public ArgumentCollection()
|
/// </summary>
|
||||||
{
|
public ArgumentCollection()
|
||||||
_argumentLookup = new Dictionary<TagParameter, string>();
|
{
|
||||||
}
|
_argumentLookup = new Dictionary<TagParameter, string>();
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Associates the given parameter to the key placeholder.
|
/// <summary>
|
||||||
/// </summary>
|
/// Associates the given parameter to the key placeholder.
|
||||||
/// <param name="parameter">The parameter to associate the key with.</param>
|
/// </summary>
|
||||||
/// <param name="key">The key placeholder used as the argument.</param>
|
/// <param name="parameter">The parameter to associate the key with.</param>
|
||||||
/// <remarks>If the key is null, the default value of the parameter will be used.</remarks>
|
/// <param name="key">The key placeholder used as the argument.</param>
|
||||||
public void AddArgument(TagParameter parameter, string key)
|
/// <remarks>If the key is null, the default value of the parameter will be used.</remarks>
|
||||||
{
|
public void AddArgument(TagParameter parameter, string key)
|
||||||
_argumentLookup.Add(parameter, key);
|
{
|
||||||
}
|
_argumentLookup.Add(parameter, key);
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Substitutes the key placeholders with their respective values.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets the key that will be used to find the substitute value.
|
||||||
/// <param name="scope">The current lexical scope.</param>
|
/// </summary>
|
||||||
/// <returns>A dictionary associating the parameter name to the associated value.</returns>
|
/// <param name="parameterName">The name of the parameter.</param>
|
||||||
public Dictionary<string, object> GetArguments(KeyScope scope)
|
public string GetKey(TagParameter parameter)
|
||||||
{
|
{
|
||||||
Dictionary<string, object> arguments = new Dictionary<string,object>();
|
string key;
|
||||||
foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup)
|
if (_argumentLookup.TryGetValue(parameter, out key))
|
||||||
{
|
{
|
||||||
object value;
|
return key;
|
||||||
if (pair.Value == null)
|
}
|
||||||
{
|
else
|
||||||
value = pair.Key.DefaultValue;
|
{
|
||||||
}
|
return null;
|
||||||
else
|
}
|
||||||
{
|
}
|
||||||
value = scope.Find(pair.Value);
|
|
||||||
}
|
/// <summary>
|
||||||
arguments.Add(pair.Key.Name, value);
|
/// Substitutes the key placeholders with their respective values.
|
||||||
}
|
/// </summary>
|
||||||
return arguments;
|
/// <param name="scope">The current lexical scope.</param>
|
||||||
}
|
/// <returns>A dictionary associating the parameter name to the associated value.</returns>
|
||||||
}
|
public Dictionary<string, object> GetArguments(KeyScope scope)
|
||||||
}
|
{
|
||||||
|
Dictionary<string, object> arguments = new Dictionary<string,object>();
|
||||||
|
foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup)
|
||||||
|
{
|
||||||
|
object value;
|
||||||
|
if (pair.Value == null)
|
||||||
|
{
|
||||||
|
value = pair.Key.DefaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = scope.Find(pair.Value);
|
||||||
|
}
|
||||||
|
arguments.Add(pair.Key.Name, value);
|
||||||
|
}
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,105 +1,119 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds text by combining the output of other generators.
|
/// Builds text by combining the output of other generators.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class CompoundGenerator : IGenerator
|
internal sealed class CompoundGenerator : IGenerator
|
||||||
{
|
{
|
||||||
private readonly TagDefinition _definition;
|
private readonly TagDefinition _definition;
|
||||||
private readonly ArgumentCollection _arguments;
|
private readonly ArgumentCollection _arguments;
|
||||||
private readonly LinkedList<IGenerator> _primaryGenerators;
|
private readonly LinkedList<IGenerator> _primaryGenerators;
|
||||||
private IGenerator _subGenerator;
|
private IGenerator _subGenerator;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a CompoundGenerator.
|
/// Initializes a new instance of a CompoundGenerator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="definition">The tag that the text is being generated for.</param>
|
/// <param name="definition">The tag that the text is being generated for.</param>
|
||||||
/// <param name="arguments">The arguments that were passed to the tag.</param>
|
/// <param name="arguments">The arguments that were passed to the tag.</param>
|
||||||
public CompoundGenerator(TagDefinition definition, ArgumentCollection arguments)
|
public CompoundGenerator(TagDefinition definition, ArgumentCollection arguments)
|
||||||
{
|
{
|
||||||
_definition = definition;
|
_definition = definition;
|
||||||
_arguments = arguments;
|
_arguments = arguments;
|
||||||
_primaryGenerators = new LinkedList<IGenerator>();
|
_primaryGenerators = new LinkedList<IGenerator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the given generator.
|
/// Gets the argument that will act as the context for the content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="generator">The generator to add.</param>
|
/// <returns>The argument that will act as the context for the content.</returns>
|
||||||
public void AddGenerator(IGenerator generator)
|
public string GetContextArgument()
|
||||||
{
|
{
|
||||||
addGenerator(generator, false);
|
TagParameter parameter = _definition.GetChildContextParameter();
|
||||||
}
|
if (parameter == null)
|
||||||
|
{
|
||||||
/// <summary>
|
return null;
|
||||||
/// Adds the given generator, determining whether the generator should
|
}
|
||||||
/// be part of the primary generators or added as an secondary generator.
|
return _arguments.GetKey(parameter);
|
||||||
/// </summary>
|
}
|
||||||
/// <param name="definition">The tag that the generator is generating text for.</param>
|
|
||||||
/// <param name="generator">The generator to add.</param>
|
/// <summary>
|
||||||
public void AddGenerator(TagDefinition definition, IGenerator generator)
|
/// Adds the given generator.
|
||||||
{
|
/// </summary>
|
||||||
bool isSubGenerator = _definition.ShouldCreateSecondaryGroup(definition);
|
/// <param name="generator">The generator to add.</param>
|
||||||
addGenerator(generator, isSubGenerator);
|
public void AddGenerator(IGenerator generator)
|
||||||
}
|
{
|
||||||
|
addGenerator(generator, false);
|
||||||
/// <summary>
|
}
|
||||||
/// Creates a StaticGenerator from the given value and adds it.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="generators">The static generators to add.</param>
|
/// Adds the given generator, determining whether the generator should
|
||||||
public void AddStaticGenerators(IEnumerable<StaticGenerator> generators)
|
/// be part of the primary generators or added as an secondary generator.
|
||||||
{
|
/// </summary>
|
||||||
foreach (StaticGenerator generator in generators)
|
/// <param name="definition">The tag that the generator is generating text for.</param>
|
||||||
{
|
/// <param name="generator">The generator to add.</param>
|
||||||
LinkedListNode<IGenerator> node = _primaryGenerators.AddLast(generator);
|
public void AddGenerator(TagDefinition definition, IGenerator generator)
|
||||||
generator.Node = node;
|
{
|
||||||
}
|
bool isSubGenerator = _definition.ShouldCreateSecondaryGroup(definition);
|
||||||
}
|
addGenerator(generator, isSubGenerator);
|
||||||
|
}
|
||||||
private void addGenerator(IGenerator generator, bool isSubGenerator)
|
|
||||||
{
|
/// <summary>
|
||||||
if (isSubGenerator)
|
/// Creates a StaticGenerator from the given value and adds it.
|
||||||
{
|
/// </summary>
|
||||||
_subGenerator = generator;
|
/// <param name="generators">The static generators to add.</param>
|
||||||
}
|
public void AddStaticGenerators(IEnumerable<StaticGenerator> generators)
|
||||||
else
|
{
|
||||||
{
|
foreach (StaticGenerator generator in generators)
|
||||||
_primaryGenerators.AddLast(generator);
|
{
|
||||||
}
|
LinkedListNode<IGenerator> node = _primaryGenerators.AddLast(generator);
|
||||||
}
|
generator.Node = node;
|
||||||
|
}
|
||||||
void IGenerator.GetText(KeyScope scope, TextWriter writer)
|
}
|
||||||
{
|
|
||||||
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
|
private void addGenerator(IGenerator generator, bool isSubGenerator)
|
||||||
IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments);
|
{
|
||||||
LinkedList<IGenerator> generators;
|
if (isSubGenerator)
|
||||||
if (_definition.ShouldGeneratePrimaryGroup(arguments))
|
{
|
||||||
{
|
_subGenerator = generator;
|
||||||
generators = _primaryGenerators;
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
_primaryGenerators.AddLast(generator);
|
||||||
generators = new LinkedList<IGenerator>();
|
}
|
||||||
if (_subGenerator != null)
|
}
|
||||||
{
|
|
||||||
generators.AddLast(_subGenerator);
|
void IGenerator.GetText(KeyScope scope, TextWriter writer)
|
||||||
}
|
{
|
||||||
}
|
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
|
||||||
foreach (NestedContext context in contexts)
|
IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments);
|
||||||
{
|
LinkedList<IGenerator> generators;
|
||||||
foreach (IGenerator generator in generators)
|
if (_definition.ShouldGeneratePrimaryGroup(arguments))
|
||||||
{
|
{
|
||||||
generator.GetText(context.KeyScope ?? scope, context.Writer ?? writer);
|
generators = _primaryGenerators;
|
||||||
if (context.WriterNeedsConsidated)
|
}
|
||||||
{
|
else
|
||||||
writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments));
|
{
|
||||||
}
|
generators = new LinkedList<IGenerator>();
|
||||||
}
|
if (_subGenerator != null)
|
||||||
}
|
{
|
||||||
}
|
generators.AddLast(_subGenerator);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
foreach (NestedContext context in contexts)
|
||||||
|
{
|
||||||
|
foreach (IGenerator generator in generators)
|
||||||
|
{
|
||||||
|
generator.GetText(context.KeyScope ?? scope, context.Writer ?? writer);
|
||||||
|
if (context.WriterNeedsConsidated)
|
||||||
|
{
|
||||||
|
writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,92 +1,101 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a tag that conditionally prints its content.
|
/// Defines a tag that conditionally prints its content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal abstract class ConditionTagDefinition : ContentTagDefinition
|
internal abstract class ConditionTagDefinition : ContentTagDefinition
|
||||||
{
|
{
|
||||||
private const string conditionParameter = "condition";
|
private const string conditionParameter = "condition";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a ConditionTagDefinition.
|
/// Initializes a new instance of a ConditionTagDefinition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tagName">The name of the tag.</param>
|
/// <param name="tagName">The name of the tag.</param>
|
||||||
protected ConditionTagDefinition(string tagName)
|
protected ConditionTagDefinition(string tagName)
|
||||||
: base(tagName, true)
|
: base(tagName, true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the parameters that can be passed to the tag.
|
/// Gets the parameters that can be passed to the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The parameters.</returns>
|
/// <returns>The parameters.</returns>
|
||||||
protected override IEnumerable<TagParameter> GetParameters()
|
protected override IEnumerable<TagParameter> GetParameters()
|
||||||
{
|
{
|
||||||
return new TagParameter[] { new TagParameter(conditionParameter) { IsRequired = true } };
|
return new TagParameter[] { new TagParameter(conditionParameter) { IsRequired = true } };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the tags that come into scope within the context of the current tag.
|
/// Gets the tags that come into scope within the context of the current tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The child tag definitions.</returns>
|
/// <returns>The child tag definitions.</returns>
|
||||||
protected override IEnumerable<string> GetChildTags()
|
protected override IEnumerable<string> GetChildTags()
|
||||||
{
|
{
|
||||||
return new string[] { "elif", "else" };
|
return new string[] { "elif", "else" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the given tag's generator should be used for a secondary (or substitute) text block.
|
/// Gets whether the given tag's generator should be used for a secondary (or substitute) text block.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="definition">The tag to inspect.</param>
|
/// <param name="definition">The tag to inspect.</param>
|
||||||
/// <returns>True if the tag's generator should be used as a secondary generator.</returns>
|
/// <returns>True if the tag's generator should be used as a secondary generator.</returns>
|
||||||
public override bool ShouldCreateSecondaryGroup(TagDefinition definition)
|
public override bool ShouldCreateSecondaryGroup(TagDefinition definition)
|
||||||
{
|
{
|
||||||
return new string[] { "elif", "else" }.Contains(definition.Name);
|
return new string[] { "elif", "else" }.Contains(definition.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the primary generator group should be used to render the tag.
|
/// Gets whether the primary generator group should be used to render the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// True if the primary generator group should be used to render the tag;
|
/// True if the primary generator group should be used to render the tag;
|
||||||
/// otherwise, false to use the secondary group.
|
/// otherwise, false to use the secondary group.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool ShouldGeneratePrimaryGroup(Dictionary<string, object> arguments)
|
public override bool ShouldGeneratePrimaryGroup(Dictionary<string, object> arguments)
|
||||||
{
|
{
|
||||||
object condition = arguments[conditionParameter];
|
object condition = arguments[conditionParameter];
|
||||||
return isConditionSatisfied(condition);
|
return isConditionSatisfied(condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool isConditionSatisfied(object condition)
|
private bool isConditionSatisfied(object condition)
|
||||||
{
|
{
|
||||||
if (condition == null)
|
if (condition == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
IEnumerable enumerable = condition as IEnumerable;
|
IEnumerable enumerable = condition as IEnumerable;
|
||||||
if (enumerable != null)
|
if (enumerable != null)
|
||||||
{
|
{
|
||||||
return enumerable.Cast<object>().Any();
|
return enumerable.Cast<object>().Any();
|
||||||
}
|
}
|
||||||
if (condition is Char)
|
if (condition is Char)
|
||||||
{
|
{
|
||||||
return (Char)condition != '\0';
|
return (Char)condition != '\0';
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
decimal number = (decimal)Convert.ChangeType(condition, typeof(decimal));
|
decimal number = (decimal)Convert.ChangeType(condition, typeof(decimal));
|
||||||
return number != 0.0m;
|
return number != 0.0m;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return true;
|
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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,71 +1,81 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a tag that can iterate over a collection of items and render
|
/// Defines a tag that can iterate over a collection of items and render
|
||||||
/// the content using each item as the context.
|
/// the content using each item as the context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class EachTagDefinition : ContentTagDefinition
|
internal sealed class EachTagDefinition : ContentTagDefinition
|
||||||
{
|
{
|
||||||
private const string collectionParameter = "collection";
|
private const string collectionParameter = "collection";
|
||||||
|
private static readonly TagParameter collection = new TagParameter(collectionParameter) { IsRequired = true };
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of an EachTagDefinition.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of an EachTagDefinition.
|
||||||
public EachTagDefinition()
|
/// </summary>
|
||||||
: base("each", true)
|
public EachTagDefinition()
|
||||||
{
|
: base("each", true)
|
||||||
}
|
{
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Gets whether the tag only exists within the scope of its parent.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets whether the tag only exists within the scope of its parent.
|
||||||
protected override bool GetIsContextSensitive()
|
/// </summary>
|
||||||
{
|
protected override bool GetIsContextSensitive()
|
||||||
return false;
|
{
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Gets the parameters that can be passed to the tag.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets the parameters that can be passed to the tag.
|
||||||
/// <returns>The parameters.</returns>
|
/// </summary>
|
||||||
protected override IEnumerable<TagParameter> GetParameters()
|
/// <returns>The parameters.</returns>
|
||||||
{
|
protected override IEnumerable<TagParameter> GetParameters()
|
||||||
return new TagParameter[] { new TagParameter(collectionParameter) { IsRequired = true } };
|
{
|
||||||
}
|
return new TagParameter[] { collection };
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Gets the context to use when building the inner text of the tag.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets the context to use when building the inner text of the tag.
|
||||||
/// <param name="writer">The text writer passed</param>
|
/// </summary>
|
||||||
/// <param name="scope">The current scope.</param>
|
/// <param name="writer">The text writer passed</param>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="scope">The current scope.</param>
|
||||||
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
public override IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
/// <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)
|
||||||
object value = arguments[collectionParameter];
|
{
|
||||||
IEnumerable enumerable = value as IEnumerable;
|
object value = arguments[collectionParameter];
|
||||||
if (enumerable == null)
|
IEnumerable enumerable = value as IEnumerable;
|
||||||
{
|
if (enumerable == null)
|
||||||
yield break;
|
{
|
||||||
}
|
yield break;
|
||||||
foreach (object item in enumerable)
|
}
|
||||||
{
|
foreach (object item in enumerable)
|
||||||
yield return new NestedContext() { KeyScope = scope.CreateChildScope(item), Writer = writer };
|
{
|
||||||
}
|
yield return new NestedContext() { KeyScope = scope.CreateChildScope(item), Writer = writer };
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Gets the tags that are in scope under this tag.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets the tags that are in scope under this tag.
|
||||||
/// <returns>The name of the tags that are in scope.</returns>
|
/// </summary>
|
||||||
protected override IEnumerable<string> GetChildTags()
|
/// <returns>The name of the tags that are in scope.</returns>
|
||||||
{
|
protected override IEnumerable<string> GetChildTags()
|
||||||
return new string[] { };
|
{
|
||||||
}
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,35 +1,44 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a tag that renders its content if all preceding if and elif tags.
|
/// Defines a tag that renders its content if all preceding if and elif tags.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class ElseTagDefinition : ContentTagDefinition
|
internal sealed class ElseTagDefinition : ContentTagDefinition
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a ElseTagDefinition.
|
/// Initializes a new instance of a ElseTagDefinition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ElseTagDefinition()
|
public ElseTagDefinition()
|
||||||
: base("else", true)
|
: base("else", true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the tag only exists within the scope of its parent.
|
/// Gets whether the tag only exists within the scope of its parent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override bool GetIsContextSensitive()
|
protected override bool GetIsContextSensitive()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the tags that indicate the end of the current tag's content.
|
/// Gets the tags that indicate the end of the current tag's content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override IEnumerable<string> GetClosingTags()
|
protected override IEnumerable<string> GetClosingTags()
|
||||||
{
|
{
|
||||||
return new string[] { "if" };
|
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());
|
CompoundGenerator generator = new CompoundGenerator(_masterDefinition, new ArgumentCollection());
|
||||||
Trimmer trimmer = new Trimmer();
|
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);
|
string trailing = format.Substring(formatIndex);
|
||||||
generator.AddStaticGenerators(trimmer.RecordText(trailing, false, false));
|
generator.AddStaticGenerators(trimmer.RecordText(trailing, false, false));
|
||||||
trimmer.Trim();
|
trimmer.Trim();
|
||||||
|
@ -165,7 +166,8 @@ namespace mustache
|
||||||
}
|
}
|
||||||
|
|
||||||
private int buildCompoundGenerator(
|
private int buildCompoundGenerator(
|
||||||
TagDefinition tagDefinition,
|
TagDefinition tagDefinition,
|
||||||
|
List<Context> context,
|
||||||
CompoundGenerator generator,
|
CompoundGenerator generator,
|
||||||
Trimmer trimmer,
|
Trimmer trimmer,
|
||||||
string format, int formatIndex)
|
string format, int formatIndex)
|
||||||
|
@ -193,7 +195,7 @@ namespace mustache
|
||||||
string key = match.Groups["key"].Value;
|
string key = match.Groups["key"].Value;
|
||||||
string alignment = match.Groups["alignment"].Value;
|
string alignment = match.Groups["alignment"].Value;
|
||||||
string formatting = match.Groups["format"].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)
|
if (PlaceholderFound != null)
|
||||||
{
|
{
|
||||||
PlaceholderFound(this, args);
|
PlaceholderFound(this, args);
|
||||||
|
@ -216,7 +218,12 @@ namespace mustache
|
||||||
generator.AddStaticGenerators(trimmer.RecordText(leading, true, false));
|
generator.AddStaticGenerators(trimmer.RecordText(leading, true, false));
|
||||||
ArgumentCollection arguments = getArguments(nextDefinition, match);
|
ArgumentCollection arguments = getArguments(nextDefinition, match);
|
||||||
CompoundGenerator compoundGenerator = new CompoundGenerator(nextDefinition, arguments);
|
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);
|
generator.AddGenerator(nextDefinition, compoundGenerator);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -11,7 +11,8 @@ namespace mustache
|
||||||
public sealed class Generator
|
public sealed class Generator
|
||||||
{
|
{
|
||||||
private readonly IGenerator _generator;
|
private readonly IGenerator _generator;
|
||||||
private readonly List<EventHandler<MissingKeyEventArgs>> _handlers;
|
private readonly List<EventHandler<KeyFoundEventArgs>> _foundHandlers;
|
||||||
|
private readonly List<EventHandler<KeyNotFoundEventArgs>> _notFoundHandlers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a Generator.
|
/// Initializes a new instance of a Generator.
|
||||||
|
@ -20,16 +21,26 @@ namespace mustache
|
||||||
internal Generator(IGenerator generator)
|
internal Generator(IGenerator generator)
|
||||||
{
|
{
|
||||||
_generator = 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>
|
/// <summary>
|
||||||
/// Occurs when a key/property is not found in the object graph.
|
/// Occurs when a key/property is not found in the object graph.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<MissingKeyEventArgs> KeyNotFound
|
public event EventHandler<KeyNotFoundEventArgs> KeyNotFound
|
||||||
{
|
{
|
||||||
add { _handlers.Add(value); }
|
add { _notFoundHandlers.Add(value); }
|
||||||
remove { _handlers.Remove(value); }
|
remove { _notFoundHandlers.Remove(value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -60,7 +71,11 @@ namespace mustache
|
||||||
private string render(IFormatProvider provider, object source)
|
private string render(IFormatProvider provider, object source)
|
||||||
{
|
{
|
||||||
KeyScope scope = new KeyScope(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;
|
scope.KeyNotFound += handler;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,49 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a tag that cannot contain inner text.
|
/// Defines a tag that cannot contain inner text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class InlineTagDefinition : TagDefinition
|
public abstract class InlineTagDefinition : TagDefinition
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of an InlineTagDefinition.
|
/// Initializes a new instance of an InlineTagDefinition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tagName">The name of the tag being defined.</param>
|
/// <param name="tagName">The name of the tag being defined.</param>
|
||||||
protected InlineTagDefinition(string tagName)
|
protected InlineTagDefinition(string tagName)
|
||||||
: base(tagName)
|
: base(tagName)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of an InlineTagDefinition.
|
/// Initializes a new instance of an InlineTagDefinition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tagName">The name of the tag being defined.</param>
|
/// <param name="tagName">The name of the tag being defined.</param>
|
||||||
/// <param name="isBuiltin">Specifies whether the tag is a built-in tag.</param>
|
/// <param name="isBuiltin">Specifies whether the tag is a built-in tag.</param>
|
||||||
internal InlineTagDefinition(string tagName, bool isBuiltin)
|
internal InlineTagDefinition(string tagName, bool isBuiltin)
|
||||||
: base(tagName, isBuiltin)
|
: base(tagName, isBuiltin)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether the tag can have content.
|
/// Gets or sets whether the tag can have content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if the tag can have a body; otherwise, false.</returns>
|
/// <returns>True if the tag can have a body; otherwise, false.</returns>
|
||||||
protected override bool GetHasContent()
|
protected override bool GetHasContent()
|
||||||
{
|
{
|
||||||
return false;
|
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>
|
/// <summary>
|
||||||
/// Holds the information needed to handle a missing key.
|
/// Holds the information needed to handle a missing key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MissingKeyEventArgs : EventArgs
|
public class KeyNotFoundEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a MissingKeyEventArgs.
|
/// Initializes a new instance of a KeyNotFoundEventArgs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The fully-qualified key.</param>
|
/// <param name="key">The fully-qualified key.</param>
|
||||||
/// <param name="missingMember">The part of the key that could not be found.</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;
|
Key = key;
|
||||||
MissingMember = missingMember;
|
MissingMember = missingMember;
|
|
@ -34,10 +34,15 @@ namespace mustache
|
||||||
_source = source;
|
_source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a key/property is found in the object graph.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<KeyFoundEventArgs> KeyFound;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a key/property is not found in the object graph.
|
/// Occurs when a key/property is not found in the object graph.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<MissingKeyEventArgs> KeyNotFound;
|
public event EventHandler<KeyNotFoundEventArgs> KeyNotFound;
|
||||||
|
|
||||||
/// <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.
|
||||||
|
@ -47,6 +52,7 @@ namespace mustache
|
||||||
public KeyScope CreateChildScope(object source)
|
public KeyScope CreateChildScope(object source)
|
||||||
{
|
{
|
||||||
KeyScope scope = new KeyScope(source, this);
|
KeyScope scope = new KeyScope(source, this);
|
||||||
|
scope.KeyFound = KeyFound;
|
||||||
scope.KeyNotFound = KeyNotFound;
|
scope.KeyNotFound = KeyNotFound;
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
@ -75,6 +81,12 @@ namespace mustache
|
||||||
nextLevel = handleKeyNotFound(name, member);
|
nextLevel = handleKeyNotFound(name, member);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (KeyFound != null)
|
||||||
|
{
|
||||||
|
KeyFoundEventArgs args = new KeyFoundEventArgs(name, nextLevel);
|
||||||
|
KeyFound(this, args);
|
||||||
|
nextLevel = args.Substitute;
|
||||||
|
}
|
||||||
return nextLevel;
|
return nextLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +106,7 @@ namespace mustache
|
||||||
|
|
||||||
private object handleKeyNotFound(string fullName, string memberName)
|
private object handleKeyNotFound(string fullName, string memberName)
|
||||||
{
|
{
|
||||||
MissingKeyEventArgs args = new MissingKeyEventArgs(fullName, memberName);
|
KeyNotFoundEventArgs args = new KeyNotFoundEventArgs(fullName, memberName);
|
||||||
if (KeyNotFound != null)
|
if (KeyNotFound != null)
|
||||||
{
|
{
|
||||||
KeyNotFound(this, args);
|
KeyNotFound(this, args);
|
||||||
|
|
|
@ -1,36 +1,45 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a pseudo tag that wraps the entire content of a format string.
|
/// Defines a pseudo tag that wraps the entire content of a format string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class MasterTagDefinition : ContentTagDefinition
|
internal sealed class MasterTagDefinition : ContentTagDefinition
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a MasterTagDefinition.
|
/// Initializes a new instance of a MasterTagDefinition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MasterTagDefinition()
|
public MasterTagDefinition()
|
||||||
: base(String.Empty, true)
|
: base(String.Empty, true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the tag only exists within the scope of its parent.
|
/// Gets whether the tag only exists within the scope of its parent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override bool GetIsContextSensitive()
|
protected override bool GetIsContextSensitive()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the tags that indicate that the tag's context is closed.
|
/// Gets the name of the tags that indicate that the tag's context is closed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The tag names.</returns>
|
/// <returns>The tag names.</returns>
|
||||||
protected override IEnumerable<string> GetClosingTags()
|
protected override IEnumerable<string> GetClosingTags()
|
||||||
{
|
{
|
||||||
return new string[] { };
|
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="key">The key that was found.</param>
|
||||||
/// <param name="alignment">The alignment that will be applied to the substitute value.</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>
|
/// <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;
|
Key = key;
|
||||||
Alignment = alignment;
|
Alignment = alignment;
|
||||||
Formatting = formatting;
|
Formatting = formatting;
|
||||||
|
Context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -35,5 +37,10 @@ namespace mustache
|
||||||
/// Gets or sets the formatting that will be applied to the substitute value.
|
/// Gets or sets the formatting that will be applied to the substitute value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Formatting { get; set; }
|
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
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("0.0.6.0")]
|
[assembly: AssemblyVersion("0.0.7.0")]
|
||||||
[assembly: AssemblyFileVersion("0.0.6.0")]
|
[assembly: AssemblyFileVersion("0.0.7.0")]
|
||||||
[assembly: InternalsVisibleTo("mustache-sharp.test")]
|
[assembly: InternalsVisibleTo("mustache-sharp.test")]
|
|
@ -1,184 +1,190 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using mustache.Properties;
|
using mustache.Properties;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the attributes of a custom tag.
|
/// Defines the attributes of a custom tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class TagDefinition
|
public abstract class TagDefinition
|
||||||
{
|
{
|
||||||
private readonly string _tagName;
|
private readonly string _tagName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a TagDefinition.
|
/// Initializes a new instance of a TagDefinition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tagName">The name of the tag.</param>
|
/// <param name="tagName">The name of the tag.</param>
|
||||||
/// <exception cref="System.ArgumentException">The name of the tag is null or blank.</exception>
|
/// <exception cref="System.ArgumentException">The name of the tag is null or blank.</exception>
|
||||||
protected TagDefinition(string tagName)
|
protected TagDefinition(string tagName)
|
||||||
: this(tagName, false)
|
: this(tagName, false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a TagDefinition.
|
/// Initializes a new instance of a TagDefinition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tagName">The name of the tag.</param>
|
/// <param name="tagName">The name of the tag.</param>
|
||||||
/// <param name="isBuiltIn">Specifies whether the tag is built-in or not. Checks are not performed on the names of built-in tags.</param>
|
/// <param name="isBuiltIn">Specifies whether the tag is built-in or not. Checks are not performed on the names of built-in tags.</param>
|
||||||
internal TagDefinition(string tagName, bool isBuiltIn)
|
internal TagDefinition(string tagName, bool isBuiltIn)
|
||||||
{
|
{
|
||||||
if (!isBuiltIn && !RegexHelper.IsValidIdentifier(tagName))
|
if (!isBuiltIn && !RegexHelper.IsValidIdentifier(tagName))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(Resources.BlankTagName, "tagName");
|
throw new ArgumentException(Resources.BlankTagName, "tagName");
|
||||||
}
|
}
|
||||||
_tagName = tagName;
|
_tagName = tagName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the tag.
|
/// Gets the name of the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name
|
public string Name
|
||||||
{
|
{
|
||||||
get { return _tagName; }
|
get { return _tagName; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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>
|
||||||
internal bool IsContextSensitive
|
internal bool IsContextSensitive
|
||||||
{
|
{
|
||||||
get { return GetIsContextSensitive(); }
|
get { return GetIsContextSensitive(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether a tag is limited to the parent tag's context.
|
/// Gets whether a tag is limited to the parent tag's context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool GetIsContextSensitive()
|
protected virtual bool GetIsContextSensitive()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the parameters that are defined for the tag.
|
/// Gets the parameters that are defined for the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IEnumerable<TagParameter> Parameters
|
internal IEnumerable<TagParameter> Parameters
|
||||||
{
|
{
|
||||||
get { return GetParameters(); }
|
get { return GetParameters(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies which parameters are passed to the tag.
|
/// Specifies which parameters are passed to the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The tag parameters.</returns>
|
/// <returns>The tag parameters.</returns>
|
||||||
protected virtual IEnumerable<TagParameter> GetParameters()
|
protected virtual IEnumerable<TagParameter> GetParameters()
|
||||||
{
|
{
|
||||||
return new TagParameter[] { };
|
return new TagParameter[] { };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the tag contains content.
|
/// Gets whether the tag contains content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal bool HasContent
|
internal bool HasContent
|
||||||
{
|
{
|
||||||
get { return GetHasContent(); }
|
get { return GetHasContent(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether tag has content.
|
/// Gets whether tag has content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if the tag has content; otherwise, false.</returns>
|
/// <returns>True if the tag has content; otherwise, false.</returns>
|
||||||
protected abstract bool GetHasContent();
|
protected abstract bool GetHasContent();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the tags that can indicate that the tag has closed.
|
/// Gets the tags that can indicate that the tag has closed.
|
||||||
/// This field is only used if no closing tag is expected.
|
/// This field is only used if no closing tag is expected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IEnumerable<string> ClosingTags
|
internal IEnumerable<string> ClosingTags
|
||||||
{
|
{
|
||||||
get { return GetClosingTags(); }
|
get { return GetClosingTags(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual IEnumerable<string> GetClosingTags()
|
protected virtual IEnumerable<string> GetClosingTags()
|
||||||
{
|
{
|
||||||
if (HasContent)
|
if (HasContent)
|
||||||
{
|
{
|
||||||
return new string[] { Name };
|
return new string[] { Name };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new string[] { };
|
return new string[] { };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the tags that are in scope within the current tag.
|
/// Gets the tags that are in scope within the current tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IEnumerable<string> ChildTags
|
internal IEnumerable<string> ChildTags
|
||||||
{
|
{
|
||||||
get { return GetChildTags(); }
|
get { return GetChildTags(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies which tags are scoped under the current tag.
|
/// Specifies which tags are scoped under the current tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The child tag definitions.</returns>
|
/// <returns>The child tag definitions.</returns>
|
||||||
protected virtual IEnumerable<string> GetChildTags()
|
protected virtual IEnumerable<string> GetChildTags()
|
||||||
{
|
{
|
||||||
return new string[] { };
|
return new string[] { };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the context to use when building the inner text of the tag.
|
/// Gets the parameter that will be used to create a new child scope.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The text writer passed</param>
|
/// <returns>The parameter that will be used to create a new child scope -or- null if no new scope is created.</returns>
|
||||||
/// <param name="scope">The current scope.</param>
|
public abstract TagParameter GetChildContextParameter();
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
|
||||||
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
/// <summary>
|
||||||
public virtual IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
/// Gets the context to use when building the inner text of the tag.
|
||||||
{
|
/// </summary>
|
||||||
yield return new NestedContext() { KeyScope = scope, Writer = writer };
|
/// <param name="writer">The text writer passed</param>
|
||||||
}
|
/// <param name="scope">The current scope.</param>
|
||||||
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <summary>
|
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
||||||
/// Applies additional formatting to the inner text of the tag.
|
public virtual IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="writer">The text writer to write to.</param>
|
yield return new NestedContext() { KeyScope = scope, Writer = writer };
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
}
|
||||||
public virtual void GetText(TextWriter writer, Dictionary<string, object> arguments)
|
|
||||||
{
|
/// <summary>
|
||||||
}
|
/// Applies additional formatting to the inner text of the tag.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
/// <param name="writer">The text writer to write to.</param>
|
||||||
/// Consolidates the text in the given writer to a string, using the given arguments as necessary.
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// </summary>
|
public virtual void GetText(TextWriter writer, Dictionary<string, object> arguments)
|
||||||
/// <param name="writer">The writer containing the text to consolidate.</param>
|
{
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
}
|
||||||
/// <returns>The consolidated string.</returns>
|
|
||||||
public virtual string ConsolidateWriter(TextWriter writer, Dictionary<string, object> arguments)
|
/// <summary>
|
||||||
{
|
/// Consolidates the text in the given writer to a string, using the given arguments as necessary.
|
||||||
return writer.ToString();
|
/// </summary>
|
||||||
}
|
/// <param name="writer">The writer containing the text to consolidate.</param>
|
||||||
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <summary>
|
/// <returns>The consolidated string.</returns>
|
||||||
/// Requests which generator group to associate the given tag type.
|
public virtual string ConsolidateWriter(TextWriter writer, Dictionary<string, object> arguments)
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="definition">The child tag definition being grouped.</param>
|
return writer.ToString();
|
||||||
/// <returns>The name of the group to associate the given tag with.</returns>
|
}
|
||||||
public virtual bool ShouldCreateSecondaryGroup(TagDefinition definition)
|
|
||||||
{
|
/// <summary>
|
||||||
return false;
|
/// Requests which generator group to associate the given tag type.
|
||||||
}
|
/// </summary>
|
||||||
|
/// <param name="definition">The child tag definition being grouped.</param>
|
||||||
/// <summary>
|
/// <returns>The name of the group to associate the given tag with.</returns>
|
||||||
/// Gets whether the group with the given name should have text generated for them.
|
public virtual bool ShouldCreateSecondaryGroup(TagDefinition definition)
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
return false;
|
||||||
/// <returns>True if text should be generated for the group; otherwise, false.</returns>
|
}
|
||||||
public virtual bool ShouldGeneratePrimaryGroup(Dictionary<string, object> arguments)
|
|
||||||
{
|
/// <summary>
|
||||||
return true;
|
/// Gets whether the group with the given name should have text generated for them.
|
||||||
}
|
/// </summary>
|
||||||
}
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
}
|
/// <returns>True if text should be generated for the group; otherwise, false.</returns>
|
||||||
|
public virtual bool ShouldGeneratePrimaryGroup(Dictionary<string, object> arguments)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,52 +1,62 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a tag that changes the scope to the object passed as an argument.
|
/// Defines a tag that changes the scope to the object passed as an argument.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class WithTagDefinition : ContentTagDefinition
|
internal sealed class WithTagDefinition : ContentTagDefinition
|
||||||
{
|
{
|
||||||
private const string contextParameter = "context";
|
private const string contextParameter = "context";
|
||||||
|
private static readonly TagParameter context = new TagParameter(contextParameter) { IsRequired = true };
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of a WithTagDefinition.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of a WithTagDefinition.
|
||||||
public WithTagDefinition()
|
/// </summary>
|
||||||
: base("with", true)
|
public WithTagDefinition()
|
||||||
{
|
: base("with", true)
|
||||||
}
|
{
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Gets whether the tag only exists within the scope of its parent.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets whether the tag only exists within the scope of its parent.
|
||||||
protected override bool GetIsContextSensitive()
|
/// </summary>
|
||||||
{
|
protected override bool GetIsContextSensitive()
|
||||||
return false;
|
{
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Gets the parameters that can be passed to the tag.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets the parameters that can be passed to the tag.
|
||||||
/// <returns>The parameters.</returns>
|
/// </summary>
|
||||||
protected override IEnumerable<TagParameter> GetParameters()
|
/// <returns>The parameters.</returns>
|
||||||
{
|
protected override IEnumerable<TagParameter> GetParameters()
|
||||||
return new TagParameter[] { new TagParameter(contextParameter) { IsRequired = true } };
|
{
|
||||||
}
|
return new TagParameter[] { context };
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Gets the context to use when building the inner text of the tag.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets the parameter that is used to create a new child context.
|
||||||
/// <param name="writer">The text writer passed</param>
|
/// </summary>
|
||||||
/// <param name="scope">The current scope.</param>
|
/// <returns>The parameter that is used to create a new child context.</returns>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
public override TagParameter GetChildContextParameter()
|
||||||
/// <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)
|
return context;
|
||||||
{
|
}
|
||||||
object context = arguments[contextParameter];
|
|
||||||
yield return new NestedContext() { KeyScope = scope.CreateChildScope(context), Writer = writer };
|
/// <summary>
|
||||||
}
|
/// Gets the context to use when building the inner text of the tag.
|
||||||
}
|
/// </summary>
|
||||||
}
|
/// <param name="writer">The text writer passed</param>
|
||||||
|
/// <param name="scope">The current scope.</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>
|
||||||
|
public override IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
||||||
|
{
|
||||||
|
object context = arguments[contextParameter];
|
||||||
|
yield return new NestedContext() { KeyScope = scope.CreateChildScope(context), Writer = writer };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
<Compile Include="CompoundGenerator.cs" />
|
<Compile Include="CompoundGenerator.cs" />
|
||||||
<Compile Include="ConditionTagDefinition.cs" />
|
<Compile Include="ConditionTagDefinition.cs" />
|
||||||
<Compile Include="ContentTagDefinition.cs" />
|
<Compile Include="ContentTagDefinition.cs" />
|
||||||
|
<Compile Include="Context.cs" />
|
||||||
|
<Compile Include="KeyFoundEventArgs.cs" />
|
||||||
<Compile Include="InlineTagDefinition.cs" />
|
<Compile Include="InlineTagDefinition.cs" />
|
||||||
<Compile Include="EachTagDefinition.cs" />
|
<Compile Include="EachTagDefinition.cs" />
|
||||||
<Compile Include="ElifTagDefinition.cs" />
|
<Compile Include="ElifTagDefinition.cs" />
|
||||||
|
@ -50,7 +52,7 @@
|
||||||
<Compile Include="PlaceholderFoundEventArgs.cs" />
|
<Compile Include="PlaceholderFoundEventArgs.cs" />
|
||||||
<Compile Include="KeyGenerator.cs" />
|
<Compile Include="KeyGenerator.cs" />
|
||||||
<Compile Include="MasterTagDefinition.cs" />
|
<Compile Include="MasterTagDefinition.cs" />
|
||||||
<Compile Include="MissingKeyEventArgs.cs" />
|
<Compile Include="KeyNotFoundEventArgs.cs" />
|
||||||
<Compile Include="NestedContext.cs" />
|
<Compile Include="NestedContext.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
|
Loading…
Reference in New Issue