using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Mustache.Properties;
namespace Mustache
{
///
/// Represents a scope of keys.
///
public sealed class Scope
{
private readonly object _source;
private readonly Scope _parent;
///
/// Initializes a new instance of a KeyScope.
///
/// The object to search for keys in.
internal Scope(object source)
: this(source, null)
{
}
///
/// Initializes a new instance of a KeyScope.
///
/// The object to search for keys in.
/// The parent scope to search in if the value is not found.
internal Scope(object source, Scope parent)
{
_parent = parent;
_source = source;
}
///
/// Occurs when a key/property is found in the object graph.
///
public event EventHandler KeyFound;
///
/// Occurs when a key/property is not found in the object graph.
///
public event EventHandler KeyNotFound;
///
/// Creates a child scope that searches for keys in a default dictionary of key/value pairs.
///
/// The new child scope.
public Scope CreateChildScope()
{
return CreateChildScope(new Dictionary());
}
///
/// Creates a child scope that searches for keys in the given object.
///
/// The object to search for keys in.
/// The new child scope.
public Scope CreateChildScope(object source)
{
Scope scope = new Scope(source, this);
scope.KeyFound = KeyFound;
scope.KeyNotFound = KeyNotFound;
return scope;
}
///
/// Attempts to find the value associated with the key with given name.
///
/// The name of the key.
/// The value associated with the key with the given name.
/// A key with the given name could not be found.
internal object Find(string name)
{
string member = null;
object value = null;
if (tryFind(name, ref member, ref value))
{
onKeyFound(name, ref value);
return value;
}
if (onKeyNotFound(name, member, ref value))
{
return value;
}
string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, member);
throw new KeyNotFoundException(message);
}
private void onKeyFound(string name, ref object value)
{
if (KeyFound != null)
{
KeyFoundEventArgs args = new KeyFoundEventArgs(name, value);
KeyFound(this, args);
value = args.Substitute;
}
}
private bool onKeyNotFound(string name, string member, ref object value)
{
if (KeyNotFound == null)
{
return false;
}
KeyNotFoundEventArgs args = new KeyNotFoundEventArgs(name, member);
KeyNotFound(this, args);
if (!args.Handled)
{
return false;
}
value = args.Substitute;
return true;
}
private static IDictionary toLookup(object value)
{
IDictionary lookup = value as IDictionary;
if (lookup == null)
{
lookup = new PropertyDictionary(value);
}
return lookup;
}
internal void Set(string key, object value)
{
IDictionary lookup = toLookup(_source);
lookup[key] = value;
}
public bool TryFind(string name, out object value)
{
string member = null;
value = null;
return tryFind(name, ref member, ref value);
}
private bool tryFind(string name, ref string member, ref object value)
{
string[] names = name.Split('.');
member = names[0];
value = _source;
if (member != "this")
{
if (!tryFindFirst(member, ref value))
{
return false;
}
}
for (int index = 1; index < names.Length; ++index)
{
IDictionary context = toLookup(value);
member = names[index];
if (!context.TryGetValue(member, out value))
{
value = null;
return false;
}
}
return true;
}
private bool tryFindFirst(string member, ref object value)
{
IDictionary lookup = toLookup(_source);
if (lookup.ContainsKey(member))
{
value = lookup[member];
return true;
}
if (_parent == null)
{
value = null;
return false;
}
return _parent.tryFindFirst(member, ref value);
}
}
}