using System;
using System.Collections.Generic;
using Pfz.Extensions.TypeExtensions;
namespace Pfz.Collections
{
/// <summary>
/// This class is a special dictionary for types, which can also
/// search values that are set for base types or interfaces.
/// </summary>
public sealed class TypeDictionary<TValue>
{
private Dictionary<Type, TValue> fSealedTypes = new Dictionary<Type, TValue>();
private Dictionary<Type, TValue> fInheritedTypes = new Dictionary<Type, TValue>();
private Dictionary<Type, TValue> fInterfaceTypes = new Dictionary<Type, TValue>();
private Dictionary<Type, TValue> fSealedGenericTypes = new Dictionary<Type, TValue>();
private Dictionary<Type, TValue> fInheritedGenericTypes = new Dictionary<Type, TValue>();
private Dictionary<Type, TValue> fGenericInterfaceTypes = new Dictionary<Type, TValue>();
/// <summary>
/// Gets or sets a value for a type.
/// </summary>
public TValue this[Type type]
{
get
{
if (type == null)
throw new ArgumentNullException("type");
TValue result;
if (type.IsGenericTypeDefinition)
{
if (fSealedGenericTypes.TryGetValue(type, out result))
return result;
if (type.IsInterface)
return fGenericInterfaceTypes[type];
return fInheritedGenericTypes[type];
}
if (fSealedTypes.TryGetValue(type, out result))
return result;
if (type.IsInterface)
return fInterfaceTypes[type];
return fInheritedTypes[type];
}
set
{
if (type == null)
throw new ArgumentNullException("type");
if (type.IsGenericTypeDefinition)
{
if (type.IsSealed)
fSealedGenericTypes[type] = value;
else
if (type.IsInterface)
fGenericInterfaceTypes[type] = value;
else
fInheritedGenericTypes[type] = value;
}
else
{
if (type.IsSealed)
fSealedTypes[type] = value;
else
if (type.IsInterface)
fInterfaceTypes[type] = value;
else
fInheritedTypes[type] = value;
}
}
}
/// <summary>
/// Clears all items in this dictionary.
/// </summary>
public void Clear()
{
fSealedTypes.Clear();
fInheritedTypes.Clear();
fInterfaceTypes.Clear();
fSealedGenericTypes.Clear();
fInheritedGenericTypes.Clear();
fGenericInterfaceTypes.Clear();
}
/// <summary>
/// Removes a value for a type.
/// </summary>
public bool Remove(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (type.IsGenericTypeDefinition)
{
if (fSealedGenericTypes.Remove(type))
return true;
if (type.IsInterface)
return fGenericInterfaceTypes.Remove(type);
return fInheritedGenericTypes.Remove(type);
}
if (fSealedTypes.Remove(type))
return true;
if (type.IsInterface)
return fInterfaceTypes.Remove(type);
return fInheritedTypes.Remove(type);
}
/// <summary>
/// Set the value for a given type, but consider the type sealed, even
/// if it is not.
/// A "sealed" type will only be found in the FindUp if it's exact type is
/// passed as parameter, as any child types will simple be ignored.
/// </summary>
public void SetAsSealed(Type type, TValue value)
{
if (type == null)
throw new ArgumentNullException("type");
if (type.IsGenericTypeDefinition)
fSealedGenericTypes[type] = value;
else
fSealedTypes[type] = value;
}
/// <summary>
/// Finds a value for the actual type or for a parent type.
/// Returns the default value if nothing is found.
/// </summary>
public TValue FindUpOrDefault(Type type)
{
TValue result;
TryFindUp(type, out result);
return result;
}
/// <summary>
/// Tries to find the value for the actual type or for a parent type.
/// Returns true if the value is found, false otherwise.
/// </summary>
public bool TryFindUp(Type type, out TValue value)
{
if (type == null)
throw new ArgumentNullException("type");
if (type.IsGenericTypeDefinition)
{
if (fSealedGenericTypes.TryGetValue(type, out value))
return true;
}
else
{
if (fSealedTypes.TryGetValue(type, out value))
return true;
if (type.IsGenericType)
{
// For example, here we can have the List<string> type.
// But, before searching for a registered type for List<>
// We need to check if we don't have such type registered in
// the inheritance dictionary.
if (type.IsInterface)
{
if (fInterfaceTypes.TryGetValue(type, out value))
return true;
}
else
{
if (fInheritedTypes.TryGetValue(type, out value))
return true;
}
Type typeDefinition = type.GetGenericTypeDefinition();
if (fSealedGenericTypes.TryGetValue(typeDefinition, out value))
return true;
}
}
if (type.IsInterface)
{
// In one of the following blocks we will get the interfaces of the actual type.
// But, if the actual type is an interface also, getting it's interfaces
// will not get the type itself, so we must check for it.
if (type.IsGenericTypeDefinition)
{
if (fGenericInterfaceTypes.TryGetValue(type, out value))
return true;
}
else
{
if (fInterfaceTypes.TryGetValue(type, out value))
return true;
if (type.IsGenericType)
{
Type typeDefinition = type.GetGenericTypeDefinition();
if (fGenericInterfaceTypes.TryGetValue(typeDefinition, out value))
return true;
}
}
}
else
{
Type baseType = type;
// we already checked for the first item it the type is a generic one,
// so we don't do it again.
if (type.IsGenericType && !type.IsGenericTypeDefinition)
baseType = type.BaseType;
while(baseType != null)
{
if (baseType.IsGenericTypeDefinition)
{
if (fInheritedGenericTypes.TryGetValue(baseType, out value))
return true;
}
else
{
if (fInheritedTypes.TryGetValue(baseType, out value))
return true;
if (baseType.IsGenericType)
{
Type typeDefinition = baseType.GetGenericTypeDefinition();
if (fInheritedGenericTypes.TryGetValue(typeDefinition, out value))
return true;
}
}
baseType = baseType.BaseType;
}
}
var orderedInterfaces = type.GetOrderedInterfaces();
foreach(Type interfaceType in orderedInterfaces)
{
if (fInterfaceTypes.TryGetValue(interfaceType, out value))
return true;
if (interfaceType.IsGenericType)
{
Type genericTypeDefinition = interfaceType.GetGenericTypeDefinition();
if (fGenericInterfaceTypes.TryGetValue(genericTypeDefinition, out value))
return true;
}
}
return false;
}
}
}