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 KeyScope { private readonly object _source; private readonly KeyScope _parent; /// /// Initializes a new instance of a KeyScope. /// /// The object to search for keys in. internal KeyScope(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 KeyScope(object source, KeyScope 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 the given object. /// /// The object to search for keys in. /// The new child scope. public KeyScope CreateChildScope(object source) { KeyScope scope = new KeyScope(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[] names = name.Split('.'); string member = names[0]; object nextLevel = _source; if (member != "this") { nextLevel = find(name, member); } for (int index = 1; index < names.Length; ++index) { IDictionary context = toLookup(nextLevel); member = names[index]; if (!context.TryGetValue(member, out nextLevel)) { nextLevel = handleKeyNotFound(name, member); } } if (KeyFound != null) { KeyFoundEventArgs args = new KeyFoundEventArgs(name, nextLevel); KeyFound(this, args); nextLevel = args.Substitute; } return nextLevel; } private object find(string fullName, string memberName) { IDictionary lookup = toLookup(_source); if (lookup.ContainsKey(memberName)) { return lookup[memberName]; } if (_parent == null) { return handleKeyNotFound(fullName, memberName); } return _parent.find(fullName, memberName); } private object handleKeyNotFound(string fullName, string memberName) { KeyNotFoundEventArgs args = new KeyNotFoundEventArgs(fullName, memberName); if (KeyNotFound != null) { KeyNotFound(this, args); } if (args.Handled) { return args.Substitute; } string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, memberName); throw new KeyNotFoundException(message); } private static IDictionary toLookup(object value) { IDictionary lookup = value as IDictionary; if (lookup == null) { lookup = new PropertyDictionary(value); } return lookup; } } }