2013-04-18 23:26:58 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Globalization;
|
2013-04-19 12:56:21 +00:00
|
|
|
|
using System.Linq;
|
2013-05-03 12:44:51 +00:00
|
|
|
|
using Mustache.Properties;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
|
2013-05-03 12:44:51 +00:00
|
|
|
|
namespace Mustache
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Represents a scope of keys.
|
|
|
|
|
/// </summary>
|
2013-08-17 03:35:46 +00:00
|
|
|
|
public sealed class Scope
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
|
|
|
|
private readonly object _source;
|
2013-08-17 03:35:46 +00:00
|
|
|
|
private readonly Scope _parent;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of a KeyScope.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="source">The object to search for keys in.</param>
|
2013-08-17 03:35:46 +00:00
|
|
|
|
internal Scope(object source)
|
2013-04-18 23:26:58 +00:00
|
|
|
|
: this(source, null)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of a KeyScope.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <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>
|
2013-08-17 03:35:46 +00:00
|
|
|
|
internal Scope(object source, Scope parent)
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
|
|
|
|
_parent = parent;
|
|
|
|
|
_source = source;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-25 01:21:00 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Occurs when a key/property is found in the object graph.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public event EventHandler<KeyFoundEventArgs> KeyFound;
|
|
|
|
|
|
2013-04-18 23:26:58 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Occurs when a key/property is not found in the object graph.
|
|
|
|
|
/// </summary>
|
2013-04-25 01:21:00 +00:00
|
|
|
|
public event EventHandler<KeyNotFoundEventArgs> KeyNotFound;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
|
2013-10-28 19:58:50 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Occurs when a setter is encountered and requires a value to be provided.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public event EventHandler<ValueRequestEventArgs> ValueRequested;
|
|
|
|
|
|
2013-08-17 03:35:46 +00:00
|
|
|
|
/// <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>());
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-18 23:26:58 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a child scope that searches for keys in the given object.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="source">The object to search for keys in.</param>
|
|
|
|
|
/// <returns>The new child scope.</returns>
|
2013-08-17 03:35:46 +00:00
|
|
|
|
public Scope CreateChildScope(object source)
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
2013-08-17 03:35:46 +00:00
|
|
|
|
Scope scope = new Scope(source, this);
|
2013-04-25 01:21:00 +00:00
|
|
|
|
scope.KeyFound = KeyFound;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
scope.KeyNotFound = KeyNotFound;
|
2013-10-28 19:58:50 +00:00
|
|
|
|
scope.ValueRequested = ValueRequested;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
return scope;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempts to find the value associated with the key with given name.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The name of the key.</param>
|
|
|
|
|
/// <returns>The value associated with the key with the given name.</returns>
|
|
|
|
|
/// <exception cref="System.Collections.Generic.KeyNotFoundException">A key with the given name could not be found.</exception>
|
|
|
|
|
internal object Find(string name)
|
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
SearchResults results = tryFind(name);
|
|
|
|
|
if (results.Found)
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
return onKeyFound(name, results.Value);
|
2013-04-18 23:26:58 +00:00
|
|
|
|
}
|
2013-10-28 19:58:50 +00:00
|
|
|
|
object value;
|
|
|
|
|
if (onKeyNotFound(name, results.Member, out value))
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
2013-08-17 03:35:46 +00:00
|
|
|
|
return value;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
}
|
2013-10-28 19:58:50 +00:00
|
|
|
|
string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, results.Member);
|
2013-08-17 03:35:46 +00:00
|
|
|
|
throw new KeyNotFoundException(message);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-28 19:58:50 +00:00
|
|
|
|
private object onKeyFound(string name, object value)
|
2013-08-17 03:35:46 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
if (KeyFound == null)
|
2013-04-25 01:21:00 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
return value;
|
2013-04-25 01:21:00 +00:00
|
|
|
|
}
|
2013-10-28 19:58:50 +00:00
|
|
|
|
KeyFoundEventArgs args = new KeyFoundEventArgs(name, value);
|
|
|
|
|
KeyFound(this, args);
|
|
|
|
|
return args.Substitute;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-28 19:58:50 +00:00
|
|
|
|
private bool onKeyNotFound(string name, string member, out object value)
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
2013-08-17 03:35:46 +00:00
|
|
|
|
if (KeyNotFound == null)
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
value = null;
|
2013-08-17 03:35:46 +00:00
|
|
|
|
return false;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
}
|
2013-08-17 03:35:46 +00:00
|
|
|
|
KeyNotFoundEventArgs args = new KeyNotFoundEventArgs(name, member);
|
|
|
|
|
KeyNotFound(this, args);
|
|
|
|
|
if (!args.Handled)
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
value = null;
|
2013-08-17 03:35:46 +00:00
|
|
|
|
return false;
|
2013-04-19 12:56:21 +00:00
|
|
|
|
}
|
2013-08-17 03:35:46 +00:00
|
|
|
|
value = args.Substitute;
|
|
|
|
|
return true;
|
2013-04-19 12:56:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-17 03:35:46 +00:00
|
|
|
|
private static IDictionary<string, object> toLookup(object value)
|
2013-04-19 12:56:21 +00:00
|
|
|
|
{
|
2014-05-20 23:34:54 +00:00
|
|
|
|
IDictionary<string, object> lookup = UpcastDictionary.Create(value);
|
2013-08-17 03:35:46 +00:00
|
|
|
|
if (lookup == null)
|
2013-04-19 12:56:21 +00:00
|
|
|
|
{
|
2013-08-17 03:35:46 +00:00
|
|
|
|
lookup = new PropertyDictionary(value);
|
2013-04-19 12:56:21 +00:00
|
|
|
|
}
|
2013-08-17 03:35:46 +00:00
|
|
|
|
return lookup;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-28 19:58:50 +00:00
|
|
|
|
internal void Set(string key)
|
|
|
|
|
{
|
|
|
|
|
SearchResults results = tryFind(key);
|
|
|
|
|
if (ValueRequested == null)
|
|
|
|
|
{
|
|
|
|
|
set(results, results.Value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ValueRequestEventArgs e = new ValueRequestEventArgs();
|
|
|
|
|
if (results.Found)
|
|
|
|
|
{
|
|
|
|
|
e.Value = results.Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ValueRequested(this, e);
|
|
|
|
|
set(results, e.Value);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-17 03:35:46 +00:00
|
|
|
|
internal void Set(string key, object value)
|
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
SearchResults results = tryFind(key);
|
|
|
|
|
set(results, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void set(SearchResults results, object value)
|
|
|
|
|
{
|
|
|
|
|
// handle setting value in child scope
|
|
|
|
|
while (results.MemberIndex < results.Members.Length - 1)
|
|
|
|
|
{
|
|
|
|
|
Dictionary<string, object> context = new Dictionary<string, object>();
|
|
|
|
|
results.Value = context;
|
|
|
|
|
results.Lookup[results.Member] = results.Value;
|
|
|
|
|
results.Lookup = context;
|
|
|
|
|
++results.MemberIndex;
|
|
|
|
|
}
|
|
|
|
|
results.Lookup[results.Member] = value;
|
2013-08-17 03:35:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool TryFind(string name, out object value)
|
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
SearchResults result = tryFind(name);
|
|
|
|
|
value = result.Value;
|
|
|
|
|
return result.Found;
|
2013-08-17 03:35:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-28 19:58:50 +00:00
|
|
|
|
private SearchResults tryFind(string name)
|
2013-08-17 03:35:46 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
SearchResults results = new SearchResults();
|
|
|
|
|
results.Members = name.Split('.');
|
|
|
|
|
results.MemberIndex = 0;
|
|
|
|
|
if (results.Member == "this")
|
2013-04-19 12:56:21 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
results.Found = true;
|
|
|
|
|
results.Lookup = toLookup(_source);
|
|
|
|
|
results.Value = _source;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
}
|
2013-10-28 19:58:50 +00:00
|
|
|
|
else
|
2013-08-17 03:35:46 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
tryFindFirst(results);
|
2013-08-17 03:35:46 +00:00
|
|
|
|
}
|
2013-10-28 19:58:50 +00:00
|
|
|
|
for (int index = 1; results.Found && index < results.Members.Length; ++index)
|
|
|
|
|
{
|
|
|
|
|
results.Lookup = toLookup(results.Value);
|
|
|
|
|
results.MemberIndex = index;
|
|
|
|
|
object value;
|
|
|
|
|
results.Found = results.Lookup.TryGetValue(results.Member, out value);
|
|
|
|
|
results.Value = value;
|
|
|
|
|
}
|
|
|
|
|
return results;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-28 19:58:50 +00:00
|
|
|
|
private void tryFindFirst(SearchResults results)
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
results.Lookup = toLookup(_source);
|
|
|
|
|
object value;
|
|
|
|
|
if (results.Lookup.TryGetValue(results.Member, out value))
|
2013-04-18 23:26:58 +00:00
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
results.Found = true;
|
|
|
|
|
results.Value = value;
|
|
|
|
|
return;
|
2013-04-18 23:26:58 +00:00
|
|
|
|
}
|
2013-08-17 03:35:46 +00:00
|
|
|
|
if (_parent == null)
|
|
|
|
|
{
|
2013-10-28 19:58:50 +00:00
|
|
|
|
results.Found = false;
|
|
|
|
|
results.Value = null;
|
|
|
|
|
return;
|
2013-08-17 03:35:46 +00:00
|
|
|
|
}
|
2013-10-28 19:58:50 +00:00
|
|
|
|
_parent.tryFindFirst(results);
|
2013-04-18 23:26:58 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-10-28 19:58:50 +00:00
|
|
|
|
|
|
|
|
|
internal class SearchResults
|
|
|
|
|
{
|
|
|
|
|
public IDictionary<string, object> Lookup { get; set; }
|
|
|
|
|
|
|
|
|
|
public string[] Members { get; set; }
|
|
|
|
|
|
|
|
|
|
public int MemberIndex { get; set; }
|
|
|
|
|
|
|
|
|
|
public string Member { get { return Members[MemberIndex]; } }
|
|
|
|
|
|
|
|
|
|
public bool Found { get; set; }
|
|
|
|
|
|
|
|
|
|
public object Value { get; set; }
|
|
|
|
|
}
|
2013-04-18 23:26:58 +00:00
|
|
|
|
}
|