2013-05-03 12:44:51 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
2014-03-02 05:43:08 +00:00
|
|
|
|
using System.Linq;
|
2013-05-03 12:44:51 +00:00
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
|
|
|
|
namespace Mustache
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides methods for creating instances of PropertyDictionary.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal sealed class PropertyDictionary : IDictionary<string, object>
|
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
private static readonly Dictionary<Type, Dictionary<string, Func<object, object>>> _cache = new Dictionary<Type, Dictionary<string, Func<object, object>>>();
|
2013-05-03 12:44:51 +00:00
|
|
|
|
|
|
|
|
|
private readonly object _instance;
|
2014-03-02 04:11:52 +00:00
|
|
|
|
private readonly Dictionary<string, Func<object, object>> _typeCache;
|
2013-05-03 12:44:51 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of a PropertyDictionary.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="instance">The instance to wrap in the PropertyDictionary.</param>
|
|
|
|
|
public PropertyDictionary(object instance)
|
|
|
|
|
{
|
|
|
|
|
_instance = instance;
|
|
|
|
|
if (instance == null)
|
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
_typeCache = new Dictionary<string, Func<object, object>>();
|
2013-05-03 12:44:51 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_typeCache = getCacheType(_instance);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-02 04:11:52 +00:00
|
|
|
|
private static Dictionary<string, Func<object, object>> getCacheType(object instance)
|
2013-05-03 12:44:51 +00:00
|
|
|
|
{
|
|
|
|
|
Type type = instance.GetType();
|
2014-03-02 04:11:52 +00:00
|
|
|
|
Dictionary<string, Func<object, object>> typeCache;
|
2013-05-03 12:44:51 +00:00
|
|
|
|
if (!_cache.TryGetValue(type, out typeCache))
|
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
typeCache = new Dictionary<string, Func<object, object>>();
|
2014-03-02 05:43:08 +00:00
|
|
|
|
|
2013-05-03 12:44:51 +00:00
|
|
|
|
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy;
|
2014-03-02 05:43:08 +00:00
|
|
|
|
|
|
|
|
|
var properties = getMembers(type, type.GetProperties(flags).Where(p => !p.IsSpecialName));
|
|
|
|
|
foreach (PropertyInfo propertyInfo in properties)
|
2013-05-03 12:44:51 +00:00
|
|
|
|
{
|
2014-03-02 05:43:08 +00:00
|
|
|
|
typeCache.Add(propertyInfo.Name, i => propertyInfo.GetValue(i, null));
|
2013-05-03 12:44:51 +00:00
|
|
|
|
}
|
2014-03-02 05:43:08 +00:00
|
|
|
|
|
|
|
|
|
var fields = getMembers(type, type.GetFields(flags).Where(f => !f.IsSpecialName));
|
|
|
|
|
foreach (FieldInfo fieldInfo in fields)
|
2014-03-02 04:11:52 +00:00
|
|
|
|
{
|
|
|
|
|
typeCache.Add(fieldInfo.Name, i => fieldInfo.GetValue(i));
|
|
|
|
|
}
|
2014-03-02 05:43:08 +00:00
|
|
|
|
|
2013-05-03 12:44:51 +00:00
|
|
|
|
_cache.Add(type, typeCache);
|
|
|
|
|
}
|
|
|
|
|
return typeCache;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-02 05:43:08 +00:00
|
|
|
|
private static IEnumerable<TMember> getMembers<TMember>(Type type, IEnumerable<TMember> members)
|
|
|
|
|
where TMember : MemberInfo
|
|
|
|
|
{
|
|
|
|
|
var singles = from member in members
|
|
|
|
|
group member by member.Name into nameGroup
|
|
|
|
|
where nameGroup.Count() == 1
|
|
|
|
|
from property in nameGroup
|
|
|
|
|
select property;
|
|
|
|
|
var multiples = from member in members
|
|
|
|
|
group member by member.Name into nameGroup
|
|
|
|
|
where nameGroup.Count() > 1
|
|
|
|
|
select
|
|
|
|
|
(
|
|
|
|
|
from member in nameGroup
|
|
|
|
|
orderby getDistance(type, member)
|
|
|
|
|
select member
|
|
|
|
|
).First();
|
|
|
|
|
var combined = singles.Concat(multiples);
|
|
|
|
|
return combined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int getDistance(Type type, MemberInfo memberInfo)
|
|
|
|
|
{
|
|
|
|
|
int distance = 0;
|
|
|
|
|
for (; type != null && type != memberInfo.DeclaringType; type = type.BaseType)
|
|
|
|
|
{
|
|
|
|
|
++distance;
|
|
|
|
|
}
|
|
|
|
|
return distance;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-03 12:44:51 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the underlying instance.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public object Instance
|
|
|
|
|
{
|
|
|
|
|
get { return _instance; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
|
void IDictionary<string, object>.Add(string key, object value)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determines whether a property with the given name exists.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key">The name of the property.</param>
|
|
|
|
|
/// <returns>True if the property exists; otherwise, false.</returns>
|
|
|
|
|
public bool ContainsKey(string key)
|
|
|
|
|
{
|
|
|
|
|
return _typeCache.ContainsKey(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the name of the properties in the type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ICollection<string> Keys
|
|
|
|
|
{
|
|
|
|
|
get { return _typeCache.Keys; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
|
bool IDictionary<string, object>.Remove(string key)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Tries to get the value for the given property name.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key">The name of the property to get the value for.</param>
|
|
|
|
|
/// <param name="value">The variable to store the value of the property or the default value if the property is not found.</param>
|
|
|
|
|
/// <returns>True if a property with the given name is found; otherwise, false.</returns>
|
|
|
|
|
/// <exception cref="System.ArgumentNullException">The name of the property was null.</exception>
|
|
|
|
|
public bool TryGetValue(string key, out object value)
|
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
Func<object, object> getter;
|
|
|
|
|
if (!_typeCache.TryGetValue(key, out getter))
|
2013-05-03 12:44:51 +00:00
|
|
|
|
{
|
|
|
|
|
value = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-03-02 04:11:52 +00:00
|
|
|
|
value = getter(_instance);
|
2013-05-03 12:44:51 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the values of all of the properties in the object.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ICollection<object> Values
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
ICollection<Func<object, object>> getters = _typeCache.Values;
|
2013-05-03 12:44:51 +00:00
|
|
|
|
List<object> values = new List<object>();
|
2014-03-02 04:11:52 +00:00
|
|
|
|
foreach (Func<object, object> getter in getters)
|
2013-05-03 12:44:51 +00:00
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
object value = getter(_instance);
|
2013-05-03 12:44:51 +00:00
|
|
|
|
values.Add(value);
|
|
|
|
|
}
|
|
|
|
|
return values.AsReadOnly();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the value of the property with the given name.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key">The name of the property to get or set.</param>
|
|
|
|
|
/// <returns>The value of the property with the given name.</returns>
|
|
|
|
|
/// <exception cref="System.ArgumentNullException">The property name was null.</exception>
|
|
|
|
|
/// <exception cref="System.Collections.Generic.KeyNotFoundException">The type does not have a property with the given name.</exception>
|
|
|
|
|
/// <exception cref="System.ArgumentException">The property did not support getting or setting.</exception>
|
|
|
|
|
/// <exception cref="System.ArgumentException">
|
|
|
|
|
/// The object does not match the target type, or a property is a value type but the value is null.
|
|
|
|
|
/// </exception>
|
|
|
|
|
public object this[string key]
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
Func<object, object> getter = _typeCache[key];
|
|
|
|
|
return getter(_instance);
|
2013-05-03 12:44:51 +00:00
|
|
|
|
}
|
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
|
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
|
void ICollection<KeyValuePair<string, object>>.Clear()
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
|
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
Func<object, object> getter;
|
|
|
|
|
if (!_typeCache.TryGetValue(item.Key, out getter))
|
2013-05-03 12:44:51 +00:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-03-02 04:11:52 +00:00
|
|
|
|
object value = getter(_instance);
|
2013-05-03 12:44:51 +00:00
|
|
|
|
return Equals(item.Value, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
|
|
|
|
|
{
|
|
|
|
|
List<KeyValuePair<string, object>> pairs = new List<KeyValuePair<string, object>>();
|
2014-03-02 04:11:52 +00:00
|
|
|
|
ICollection<KeyValuePair<string, Func<object, object>>> collection = _typeCache;
|
|
|
|
|
foreach (KeyValuePair<string, Func<object, object>> pair in collection)
|
2013-05-03 12:44:51 +00:00
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
Func<object, object> getter = pair.Value;
|
|
|
|
|
object value = getter(_instance);
|
2013-05-03 12:44:51 +00:00
|
|
|
|
pairs.Add(new KeyValuePair<string, object>(pair.Key, value));
|
|
|
|
|
}
|
|
|
|
|
pairs.CopyTo(array, arrayIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the number of properties in the type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int Count
|
|
|
|
|
{
|
|
|
|
|
get { return _typeCache.Count; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets whether updates will be ignored.
|
|
|
|
|
/// </summary>
|
|
|
|
|
bool ICollection<KeyValuePair<string, object>>.IsReadOnly
|
|
|
|
|
{
|
|
|
|
|
get { return true; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
|
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the propety name/value pairs in the object.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
foreach (KeyValuePair<string, Func<object, object>> pair in _typeCache)
|
2013-05-03 12:44:51 +00:00
|
|
|
|
{
|
2014-03-02 04:11:52 +00:00
|
|
|
|
Func<object, object> getter = pair.Value;
|
|
|
|
|
object value = getter(_instance);
|
2013-05-03 12:44:51 +00:00
|
|
|
|
yield return new KeyValuePair<string, object>(pair.Key, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
|
|
|
{
|
|
|
|
|
return GetEnumerator();
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-09 02:33:53 +00:00
|
|
|
|
}
|