using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
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
{
lock (_cache)
{
_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;
var properties = getMembers(type, type.GetProperties(flags).Where(p => !p.IsSpecialName));
foreach (PropertyInfo propertyInfo in properties)
{
typeCache.Add(propertyInfo.Name, i => propertyInfo.GetValue(i, null));
}
var fields = getMembers(type, type.GetFields(flags).Where(f => !f.IsSpecialName));
foreach (FieldInfo fieldInfo in fields)
{
typeCache.Add(fieldInfo.Name, i => fieldInfo.GetValue(i));
}
_cache.Add(type, typeCache);
}
return typeCache;
}
private static IEnumerable getMembers(Type type, IEnumerable 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;
}
///
/// 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