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, propertyInfo); } } _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) { PropertyInfo propertyInfo; if (!_typeCache.TryGetValue(key, out propertyInfo)) { value = null; return false; } value = getValue(propertyInfo); return true; } /// /// Gets the values of all of the properties in the object. /// public ICollection Values { get { ICollection propertyInfos = _typeCache.Values; List values = new List(); foreach (PropertyInfo propertyInfo in propertyInfos) { object value = getValue(propertyInfo); 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 { PropertyInfo propertyInfo = _typeCache[key]; return getValue(propertyInfo); } [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) { PropertyInfo propertyInfo; if (!_typeCache.TryGetValue(item.Key, out propertyInfo)) { return false; } object value = getValue(propertyInfo); return Equals(item.Value, value); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { List> pairs = new List>(); ICollection> collection = _typeCache; foreach (KeyValuePair pair in collection) { PropertyInfo propertyInfo = pair.Value; object value = getValue(propertyInfo); 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) { object value = getValue(pair.Value); yield return new KeyValuePair(pair.Key, value); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private object getValue(PropertyInfo propertyInfo) { return propertyInfo.GetValue(_instance, null); } } }