using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; namespace Mustache { /// /// Provides methods for creating instances of PropertyDictionary. /// internal sealed class PropertyDictionary : IDictionary { private static readonly Dictionary>> _cache = new Dictionary>>(); private readonly object _instance; private readonly Dictionary> _typeCache; /// /// Initializes a new instance of a PropertyDictionary. /// /// The instance to wrap in the PropertyDictionary. public PropertyDictionary(object instance) { _instance = instance; if (instance == null) { _typeCache = new Dictionary>(); } else { _typeCache = getCacheType(_instance); } } private static Dictionary> getCacheType(object instance) { Type type = instance.GetType(); Dictionary> typeCache; if (!_cache.TryGetValue(type, out typeCache)) { typeCache = new Dictionary>(); BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy; foreach (PropertyInfo propertyInfo in type.GetProperties(flags)) { if (!propertyInfo.IsSpecialName) { typeCache.Add(propertyInfo.Name, i => propertyInfo.GetValue(i, null)); } } foreach (FieldInfo fieldInfo in type.GetFields(flags)) { typeCache.Add(fieldInfo.Name, i => fieldInfo.GetValue(i)); } _cache.Add(type, typeCache); } return typeCache; } /// /// Gets the underlying instance. /// public object Instance { get { return _instance; } } [EditorBrowsable(EditorBrowsableState.Never)] void IDictionary.Add(string key, object value) { throw new NotSupportedException(); } /// /// Determines whether a property with the given name exists. /// /// The name of the property. /// True if the property exists; otherwise, false. public bool ContainsKey(string key) { return _typeCache.ContainsKey(key); } /// /// Gets the name of the properties in the type. /// public ICollection Keys { get { return _typeCache.Keys; } } [EditorBrowsable(EditorBrowsableState.Never)] bool IDictionary.Remove(string key) { throw new NotSupportedException(); } /// /// Tries to get the value for the given property name. /// /// The name of the property to get the value for. /// The variable to store the value of the property or the default value if the property is not found. /// True if a property with the given name is found; otherwise, false. /// The name of the property was null. public bool TryGetValue(string key, out object value) { Func getter; if (!_typeCache.TryGetValue(key, out getter)) { value = null; return false; } value = getter(_instance); return true; } /// /// Gets the values of all of the properties in the object. /// public ICollection Values { get { ICollection> getters = _typeCache.Values; List values = new List(); foreach (Func getter in getters) { object value = getter(_instance); values.Add(value); } return values.AsReadOnly(); } } /// /// Gets or sets the value of the property with the given name. /// /// The name of the property to get or set. /// The value of the property with the given name. /// The property name was null. /// The type does not have a property with the given name. /// The property did not support getting or setting. /// /// The object does not match the target type, or a property is a value type but the value is null. /// public object this[string key] { get { Func getter = _typeCache[key]; return getter(_instance); } [EditorBrowsable(EditorBrowsableState.Never)] set { throw new NotSupportedException(); } } [EditorBrowsable(EditorBrowsableState.Never)] void ICollection>.Add(KeyValuePair item) { throw new NotSupportedException(); } [EditorBrowsable(EditorBrowsableState.Never)] void ICollection>.Clear() { throw new NotSupportedException(); } bool ICollection>.Contains(KeyValuePair item) { Func getter; if (!_typeCache.TryGetValue(item.Key, out getter)) { return false; } object value = getter(_instance); return Equals(item.Value, value); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { List> pairs = new List>(); ICollection>> collection = _typeCache; foreach (KeyValuePair> pair in collection) { Func getter = pair.Value; object value = getter(_instance); pairs.Add(new KeyValuePair(pair.Key, value)); } pairs.CopyTo(array, arrayIndex); } /// /// Gets the number of properties in the type. /// public int Count { get { return _typeCache.Count; } } /// /// Gets or sets whether updates will be ignored. /// bool ICollection>.IsReadOnly { get { return true; } } [EditorBrowsable(EditorBrowsableState.Never)] bool ICollection>.Remove(KeyValuePair item) { throw new NotSupportedException(); } /// /// Gets the propety name/value pairs in the object. /// /// public IEnumerator> GetEnumerator() { foreach (KeyValuePair> pair in _typeCache) { Func getter = pair.Value; object value = getter(_instance); yield return new KeyValuePair(pair.Key, value); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }