using System;
using System.Collections;
using BLToolkit.Common;
using BLToolkit.Properties;
using BLToolkit.Reflection;
using BLToolkit.TypeBuilder;
using BLToolkit.TypeBuilder.Builders;
namespace BLToolkit.Patterns
{
///
/// Duck typing implementation.
/// In computer science, duck typing is a term for dynamic typing typical
/// of some programming languages, such as Smalltalk, Python or ColdFusion,
/// where a variable's value itself determines what the variable can do.
/// Thus an object or set of objects having all the methods described in
/// an interface can be made to implement that interface dynamically
/// at runtime, even if the object’s class does not include the interface
/// in its implements clause.
///
public static class DuckTyping
{
#region Single Duck
private static readonly Hashtable _duckTypes = new Hashtable();
///
/// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type.
///
/// An interface type to implement.
/// Any type which expected to have all members of the given interface.
/// The duck object type.
public static Type GetDuckType(Type interfaceType, Type objectType)
{
if (interfaceType == null) throw new ArgumentNullException("interfaceType");
if (!interfaceType.IsInterface) throw new ArgumentException(Resources.DuckTyping_InterfaceTypeMustBeAnInterface, "interfaceType");
if (!interfaceType.IsPublic && !interfaceType.IsNestedPublic)
throw new ArgumentException(Resources.DuckTyping_InterfaceMustBePublic, "interfaceType");
Hashtable types = (Hashtable)_duckTypes[interfaceType];
if (types == null)
{
lock (_duckTypes.SyncRoot)
{
types = (Hashtable)_duckTypes[interfaceType];
if (types == null)
_duckTypes.Add(interfaceType, types = new Hashtable());
}
}
Type type = (Type)types[objectType];
if (type == null)
{
lock (types.SyncRoot)
{
type = (Type)types[objectType];
if (type != null || types.ContainsKey(objectType))
return type;
type = TypeFactory.GetType(
new CompoundValue(interfaceType, objectType),
interfaceType, //objectType,
new DuckTypeBuilder(MustImplementAttribute.Default, interfaceType, new Type[] { objectType }));
types.Add(objectType, type);
}
}
return type;
}
///
/// Implements the requested interface for supplied object.
/// If the supplied object implements the interface, the object itself will be returned.
/// Otherwise a convenient duck object will be created.
///
/// An interface type to implement.
/// Any type which has all members of the given interface.
/// When this parameter is set to null, the object type will be used.
/// An object which type expected to have all members of the given interface.
/// An object which implements the interface.
public static object Implement(Type interfaceType, Type baseObjectType, object obj)
{
if (obj == null) throw new ArgumentNullException("obj");
Type objType = obj.GetType();
if (TypeHelper.IsSameOrParent(interfaceType, objType))
return obj;
if (obj is DuckType)
{
DuckType duckObject = (DuckType)obj;
if (duckObject.Objects.Length == 1)
{
// Switch to underlying objects when a duck object was passed.
//
return Implement(interfaceType, baseObjectType, duckObject.Objects[0]);
}
// Re-aggregate underlying objects to expose new interface.
//
return Aggregate(interfaceType, duckObject.Objects);
}
if (baseObjectType == null)
baseObjectType = objType;
else if (!TypeHelper.IsSameOrParent(baseObjectType, objType))
throw new ArgumentException(
string.Format(Resources.DuckTyping_NotASubtypeOf, objType.FullName, baseObjectType.FullName), "obj");
Type duckType = GetDuckType(interfaceType, baseObjectType);
if (duckType == null)
return null;
object duck = TypeAccessor.CreateInstanceEx(duckType);
((DuckType)duck).SetObjects(obj);
return duck;
}
///
/// Implements the requested interface.
/// If the supplied object implements the interface, the object itself will be returned.
/// Otherwise a convenient duck object will be created.
///
/// An interface type to implement.
/// An object which type expected to have all members of the given interface.
/// An object which implements the interface.
public static object Implement(Type interfaceType, object obj)
{
return Implement(interfaceType, null, obj);
}
///
/// Implements the requested interface for all supplied objects.
/// If any of supplied object implements the interface, the object itself will be returned.
/// Otherwise a convenient duck object will be created.
///
/// An interface type to implement.
/// Any type which has all members of the given interface.
/// When this parameter is set to null, the object type will be used.
/// An object array which types expected to have all members of the given interface.
/// All objects may have different types.
/// An array of object which implements the interface.
public static object[] Implement(Type interfaceType, Type baseObjectType, params object[] objects)
{
if (objects == null) throw new ArgumentNullException("objects");
object[] result = new object[objects.Length];
for (int i = 0; i < objects.Length; i++)
result[i] = Implement(interfaceType, baseObjectType, objects[i]);
return result;
}
///
/// Implements the requested interface for all supplied objects.
/// If any of supplied object implements the interface, the object itself will be returned.
/// Otherwise a convenient duck object will be created.
///
/// An interface type to implement.
/// An object array which types expected to have all members of the given interface.
/// All objects may have different types.
/// An array of object which implements the interface.
public static object[] Implement(Type interfaceType, params object[] objects)
{
return Implement(interfaceType, (Type)null, objects);
}
///
/// Implements the requested interface for supplied object.
/// If the supplied object implements the interface, the object itself will be returned.
/// Otherwise a convenient duck object will be created.
///
/// An interface type to implement.
/// An object which type expected to have all members of the given interface.
/// An object which implements the interface.
public static I Implement(object obj)
where I : class
{
return (I)Implement(typeof(I), null, obj);
}
///
/// Implements the requested interface for supplied object.
/// If the supplied object implements the interface, the object itself will be returned.
/// Otherwise a convenient duck object will be created.
///
/// An interface type to implement.
/// Any type which has all members of the given interface.
/// An object which type expected to have all members of the given interface.
/// An object which implements the interface.
public static I Implement(T obj)
where I : class
{
return (I)Implement(typeof(I), typeof(T), obj);
}
///
/// Implements the requested interface for all supplied objects.
/// If any of supplied object implements the interface, the object itself will be returned.
/// Otherwise a convenient duck object will be created.
///
/// An interface type to implement.
/// An object array which types expected to have all members of the given interface.
/// All objects may have different types.
/// An array of object which implements the interface.
public static I[] Implement(params object[] objects)
where I : class
{
if (objects == null) throw new ArgumentNullException("objects");
I[] result = new I[objects.Length];
for (int i = 0; i < objects.Length; i++)
result[i] = Implement(objects[i]);
return result;
}
///
/// Implements the requested interface for all supplied objects.
/// If any of supplied object implements the interface, the object itself will be returned.
/// Otherwise a convenient duck object will be created.
///
/// An interface type to implement.
/// Any type which has all members of the given interface.
/// An object array which types expected to have all members of the given interface.
/// All objects may have different types.
/// An array of object which implements the interface.
public static I[] Implement(params T[] objects)
where I : class
{
if (objects == null) throw new ArgumentNullException("objects");
I[] result = new I[objects.Length];
for (int i = 0; i < objects.Length; i++)
result[i] = Implement(objects[i]);
return result;
}
private static bool _allowStaticMembers;
public static bool AllowStaticMembers
{
get { return _allowStaticMembers; }
set { _allowStaticMembers = value; }
}
#endregion
#region Multiple Duck
///
/// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type.
///
/// An interface type to implement.
/// Array of types which expected to have all members of the given interface.
/// The duck object type.
public static Type GetDuckType(Type interfaceType, Type[] objectTypes)
{
if (interfaceType == null) throw new ArgumentNullException("interfaceType");
if (!interfaceType.IsInterface) throw new ArgumentException(Resources.DuckTyping_InterfaceTypeMustBeAnInterface, "interfaceType");
if (!interfaceType.IsPublic && !interfaceType.IsNestedPublic)
throw new ArgumentException(Resources.DuckTyping_InterfaceMustBePublic, "interfaceType");
Hashtable types = (Hashtable)_duckTypes[interfaceType];
if (types == null)
{
lock (_duckTypes.SyncRoot)
{
types = (Hashtable)_duckTypes[interfaceType];
if (types == null)
_duckTypes.Add(interfaceType, types = new Hashtable());
}
}
object key = new CompoundValue(objectTypes);
Type type = (Type)types[key];
if (type == null)
{
lock (types.SyncRoot)
{
type = (Type)types[key];
if (type != null || types.ContainsKey(key))
return type;
type = TypeFactory.GetType(
new CompoundValue(interfaceType, key),
interfaceType,
new DuckTypeBuilder(MustImplementAttribute.Aggregate, interfaceType, objectTypes));
types.Add(key, type);
}
}
return type;
}
///
/// Implements the requested interface from supplied set of objects.
///
/// An interface type to implement.
/// Array of types which have all members of the given interface.
/// When this parameter is set to null, the object type will be used.
/// Array of objects which types expected to have all members of the given interface.
/// An object which implements the interface.
public static object Aggregate(Type interfaceType, Type[] baseObjectTypes,params object[] objs)
{
if (objs == null) throw new ArgumentNullException("objs");
if (baseObjectTypes == null)
{
baseObjectTypes = new Type[objs.Length];
for (int i = 0; i < objs.Length; i++)
if (objs[i] != null)
baseObjectTypes[i] = objs[i].GetType();
}
else
{
if (baseObjectTypes.Length != objs.Length)
throw new ArgumentException(Resources.DuckTyping_InvalidNumberOfObjs, "baseObjectTypes");
for (int i = 0; i < objs.Length; i++)
{
Type objType = objs[i].GetType();
if (!TypeHelper.IsSameOrParent(baseObjectTypes[i], objType))
throw new ArgumentException(
string.Format(Resources.DuckTyping_NotASubtypeOf, objType.FullName, baseObjectTypes[i].FullName), "objs");
}
}
Type duckType = GetDuckType(interfaceType, baseObjectTypes);
if (duckType == null)
return null;
object duck = TypeAccessor.CreateInstanceEx(duckType);
((DuckType)duck).SetObjects(objs);
return duck;
}
///
/// Implements the requested interface from supplied set of objects.
///
/// An interface type to implement.
/// Array of object which types expected to have of the given interface.
/// An object which implements the interface.
public static object Aggregate(Type interfaceType,params object[] objs)
{
return Aggregate(interfaceType, (Type[])null, objs);
}
///
/// Implements the requested interface from supplied set of objects.
///
/// An interface type to implement.
/// Array of object which type expected to have all members of the given interface.
/// An object which implements the interface.
public static I Aggregate(params object[] objs)
where I : class
{
return (I)Aggregate(typeof(I), null, objs);
}
#endregion
}
}