| 0 | 1 using System; | 
|  | 2 using System.Collections; | 
|  | 3 using System.Collections.Generic; | 
|  | 4 | 
|  | 5 using BLToolkit.Common; | 
|  | 6 using BLToolkit.Properties; | 
|  | 7 using BLToolkit.Reflection; | 
|  | 8 using BLToolkit.TypeBuilder; | 
|  | 9 using BLToolkit.TypeBuilder.Builders; | 
|  | 10 | 
|  | 11 namespace BLToolkit.Patterns | 
|  | 12 { | 
|  | 13 	/// <summary> | 
|  | 14 	/// Duck typing implementation. | 
|  | 15 	/// In computer science, duck typing is a term for dynamic typing typical | 
|  | 16 	/// of some programming languages, such as Smalltalk, Python or ColdFusion, | 
|  | 17 	/// where a variable's value itself determines what the variable can do. | 
|  | 18 	/// Thus an object or set of objects having all the methods described in | 
|  | 19 	/// an interface can be made to implement that interface dynamically | 
|  | 20 	/// at runtime, even if the objects class does not include the interface | 
|  | 21 	/// in its implements clause. | 
|  | 22 	/// </summary> | 
|  | 23 	public static class DuckTyping | 
|  | 24 	{ | 
|  | 25 		#region Single Duck | 
|  | 26 | 
|  | 27 		static readonly Dictionary<Type,Dictionary<object,Type>> _duckTypes = new Dictionary<Type,Dictionary<object,Type>>(); | 
|  | 28 | 
|  | 29 		/// <summary> | 
|  | 30 		/// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type. | 
|  | 31 		/// </summary> | 
|  | 32 		/// <param name="interfaceType">An interface type to implement.</param> | 
|  | 33 		/// <param name="objectType">Any type which expected to have all members of the given interface.</param> | 
|  | 34 		/// <returns>The duck object type.</returns> | 
|  | 35 		public static Type GetDuckType(Type interfaceType, Type objectType) | 
|  | 36 		{ | 
|  | 37 			if (interfaceType == null)      throw new ArgumentNullException("interfaceType"); | 
|  | 38 			if (!interfaceType.IsInterface) throw new ArgumentException(Resources.DuckTyping_InterfaceTypeMustBeAnInterface, "interfaceType"); | 
|  | 39 			if (!interfaceType.IsPublic && !interfaceType.IsNestedPublic) | 
|  | 40 				throw new ArgumentException(Resources.DuckTyping_InterfaceMustBePublic, "interfaceType"); | 
|  | 41 | 
|  | 42 			Dictionary<object,Type> types; | 
|  | 43 | 
|  | 44 			lock(_duckTypes) | 
|  | 45 				if (!_duckTypes.TryGetValue(interfaceType, out types)) | 
|  | 46 					_duckTypes.Add(interfaceType, types = new Dictionary<object,Type>()); | 
|  | 47 | 
|  | 48 			Type type; | 
|  | 49 | 
|  | 50 			lock (types) if (!types.TryGetValue(objectType, out type)) | 
|  | 51 			{ | 
|  | 52 				type = TypeFactory.GetType( | 
|  | 53 					new CompoundValue(interfaceType, objectType), | 
|  | 54 					interfaceType, //objectType, | 
|  | 55 					new DuckTypeBuilder(MustImplementAttribute.Default, interfaceType, new[] { objectType })); | 
|  | 56 | 
|  | 57 				types.Add(objectType, type); | 
|  | 58 			} | 
|  | 59 | 
|  | 60 			return type; | 
|  | 61 		} | 
|  | 62 | 
|  | 63 		/// <summary> | 
|  | 64 		/// Implements the requested interface for supplied object. | 
|  | 65 		/// If the supplied object implements the interface, the object itself will be returned. | 
|  | 66 		/// Otherwise a convenient duck object will be created. | 
|  | 67 		/// </summary> | 
|  | 68 		/// <param name="interfaceType">An interface type to implement.</param> | 
|  | 69 		/// <param name="baseObjectType">Any type which has all members of the given interface. | 
|  | 70 		/// When this parameter is set to null, the object type will be used.</param> | 
|  | 71 		/// <param name="obj">An object which type expected to have all members of the given interface.</param> | 
|  | 72 		/// <returns>An object which implements the interface.</returns> | 
|  | 73 		public static object Implement(Type interfaceType, Type baseObjectType, object obj) | 
|  | 74 		{ | 
|  | 75 			if (obj == null) throw new ArgumentNullException("obj"); | 
|  | 76 | 
|  | 77 			var objType = obj.GetType(); | 
|  | 78 | 
|  | 79 			if (TypeHelper.IsSameOrParent(interfaceType, objType)) | 
|  | 80 				return obj; | 
|  | 81 | 
|  | 82 			if (obj is DuckType) | 
|  | 83 			{ | 
|  | 84 				var duckObject = (DuckType)obj; | 
|  | 85 | 
|  | 86 				if (duckObject.Objects.Length == 1) | 
|  | 87 				{ | 
|  | 88 					// Switch to underlying objects when a duck object was passed. | 
|  | 89 					// | 
|  | 90 					return Implement(interfaceType, baseObjectType, duckObject.Objects[0]); | 
|  | 91 				} | 
|  | 92 | 
|  | 93 				// Re-aggregate underlying objects to expose new interface. | 
|  | 94 				// | 
|  | 95 				return Aggregate(interfaceType, duckObject.Objects); | 
|  | 96 			} | 
|  | 97 | 
|  | 98 			if (baseObjectType == null) | 
|  | 99 				baseObjectType = objType; | 
|  | 100 			else if (!TypeHelper.IsSameOrParent(baseObjectType, objType)) | 
|  | 101 				throw new ArgumentException(string.Format(Resources.DuckTyping_NotASubtypeOf, objType.FullName, baseObjectType.FullName), "obj"); | 
|  | 102 | 
|  | 103 			var duckType = GetDuckType(interfaceType, baseObjectType); | 
|  | 104 | 
|  | 105 			if (duckType == null) | 
|  | 106 				return null; | 
|  | 107 | 
|  | 108 			var duck = TypeAccessor.CreateInstanceEx(duckType); | 
|  | 109 | 
|  | 110 			((DuckType)duck).SetObjects(obj); | 
|  | 111 | 
|  | 112 			return duck; | 
|  | 113 		} | 
|  | 114 | 
|  | 115 		/// <summary> | 
|  | 116 		/// Implements the requested interface. | 
|  | 117 		/// If the supplied object implements the interface, the object itself will be returned. | 
|  | 118 		/// Otherwise a convenient duck object will be created. | 
|  | 119 		/// </summary> | 
|  | 120 		/// <param name="interfaceType">An interface type to implement.</param> | 
|  | 121 		/// <param name="obj">An object which type expected to have all members of the given interface.</param> | 
|  | 122 		/// <returns>An object which implements the interface.</returns> | 
|  | 123 		public static object Implement(Type interfaceType, object obj) | 
|  | 124 		{ | 
|  | 125 			return Implement(interfaceType, null, obj); | 
|  | 126 		} | 
|  | 127 | 
|  | 128 		/// <summary> | 
|  | 129 		/// Implements the requested interface for all supplied objects. | 
|  | 130 		/// If any of supplied object implements the interface, the object itself will be returned. | 
|  | 131 		/// Otherwise a convenient duck object will be created. | 
|  | 132 		/// </summary> | 
|  | 133 		/// <param name="interfaceType">An interface type to implement.</param> | 
|  | 134 		/// <param name="baseObjectType">Any type which has all members of the given interface. | 
|  | 135 		/// When this parameter is set to null, the object type will be used.</param> | 
|  | 136 		/// <param name="objects">An object array which types expected to have all members of the given interface. | 
|  | 137 		/// All objects may have different types.</param> | 
|  | 138 		/// <returns>An array of object which implements the interface.</returns> | 
|  | 139 		public static object[] Implement(Type interfaceType, Type baseObjectType, params object[] objects) | 
|  | 140 		{ | 
|  | 141 			if (objects == null) throw new ArgumentNullException("objects"); | 
|  | 142 | 
|  | 143 			object[] result = new object[objects.Length]; | 
|  | 144 | 
|  | 145 			for (int i = 0; i < objects.Length; i++) | 
|  | 146 				result[i] = Implement(interfaceType, baseObjectType, objects[i]); | 
|  | 147 | 
|  | 148 			return result; | 
|  | 149 		} | 
|  | 150 | 
|  | 151 		/// <summary> | 
|  | 152 		/// Implements the requested interface for all supplied objects. | 
|  | 153 		/// If any of supplied object implements the interface, the object itself will be returned. | 
|  | 154 		/// Otherwise a convenient duck object will be created. | 
|  | 155 		/// </summary> | 
|  | 156 		/// <param name="interfaceType">An interface type to implement.</param> | 
|  | 157 		/// <param name="objects">An object array which types expected to have all members of the given interface. | 
|  | 158 		/// All objects may have different types.</param> | 
|  | 159 		/// <returns>An array of object which implements the interface.</returns> | 
|  | 160 		public static object[] Implement(Type interfaceType, params object[] objects) | 
|  | 161 		{ | 
|  | 162 			return Implement(interfaceType, (Type)null, objects); | 
|  | 163 		} | 
|  | 164 | 
|  | 165 		/// <summary> | 
|  | 166 		/// Implements the requested interface for supplied object. | 
|  | 167 		/// If the supplied object implements the interface, the object itself will be returned. | 
|  | 168 		/// Otherwise a convenient duck object will be created. | 
|  | 169 		/// </summary> | 
|  | 170 		/// <typeparam name="I">An interface type to implement.</typeparam> | 
|  | 171 		/// <param name="obj">An object which type expected to have all members of the given interface.</param> | 
|  | 172 		/// <returns>An object which implements the interface.</returns> | 
|  | 173 		public static I Implement<I>(object obj) | 
|  | 174 			where I : class | 
|  | 175 		{ | 
|  | 176 			return (I)Implement(typeof(I), null, obj); | 
|  | 177 		} | 
|  | 178 | 
|  | 179 		/// <summary> | 
|  | 180 		/// Implements the requested interface for supplied object. | 
|  | 181 		/// If the supplied object implements the interface, the object itself will be returned. | 
|  | 182 		/// Otherwise a convenient duck object will be created. | 
|  | 183 		/// </summary> | 
|  | 184 		/// <typeparam name="I">An interface type to implement.</typeparam> | 
|  | 185 		/// <typeparam name="T">Any type which has all members of the given interface.</typeparam> | 
|  | 186 		/// <param name="obj">An object which type expected to have all members of the given interface.</param> | 
|  | 187 		/// <returns>An object which implements the interface.</returns> | 
|  | 188 		public static I Implement<I,T>(T obj) | 
|  | 189 			where I : class | 
|  | 190 		{ | 
|  | 191 			return (I)Implement(typeof(I), typeof(T), obj); | 
|  | 192 		} | 
|  | 193 | 
|  | 194 		/// <summary> | 
|  | 195 		/// Implements the requested interface for all supplied objects. | 
|  | 196 		/// If any of supplied object implements the interface, the object itself will be returned. | 
|  | 197 		/// Otherwise a convenient duck object will be created. | 
|  | 198 		/// </summary> | 
|  | 199 		/// <typeparam name="I">An interface type to implement.</typeparam> | 
|  | 200 		/// <param name="objects">An object array which types expected to have all members of the given interface. | 
|  | 201 		/// All objects may have different types.</param> | 
|  | 202 		/// <returns>An array of object which implements the interface.</returns> | 
|  | 203 		public static I[] Implement<I>(params object[] objects) | 
|  | 204 			where I : class | 
|  | 205 		{ | 
|  | 206 			if (objects == null) throw new ArgumentNullException("objects"); | 
|  | 207 | 
|  | 208 			I[] result = new I[objects.Length]; | 
|  | 209 | 
|  | 210 			for (int i = 0; i < objects.Length; i++) | 
|  | 211 				result[i] = Implement<I>(objects[i]); | 
|  | 212 | 
|  | 213 			return result; | 
|  | 214 		} | 
|  | 215 | 
|  | 216 		/// <summary> | 
|  | 217 		/// Implements the requested interface for all supplied objects. | 
|  | 218 		/// If any of supplied object implements the interface, the object itself will be returned. | 
|  | 219 		/// Otherwise a convenient duck object will be created. | 
|  | 220 		/// </summary> | 
|  | 221 		/// <typeparam name="I">An interface type to implement.</typeparam> | 
|  | 222 		/// <typeparam name="T">Any type which has all members of the given interface.</typeparam> | 
|  | 223 		/// <param name="objects">An object array which types expected to have all members of the given interface. | 
|  | 224 		/// All objects may have different types.</param> | 
|  | 225 		/// <returns>An array of object which implements the interface.</returns> | 
|  | 226 		public static I[] Implement<I,T>(params T[] objects) | 
|  | 227 			where I : class | 
|  | 228 		{ | 
|  | 229 			if (objects == null) throw new ArgumentNullException("objects"); | 
|  | 230 | 
|  | 231 			I[] result = new I[objects.Length]; | 
|  | 232 | 
|  | 233 			for (int i = 0; i < objects.Length; i++) | 
|  | 234 				result[i] = Implement<I,T>(objects[i]); | 
|  | 235 | 
|  | 236 			return result; | 
|  | 237 		} | 
|  | 238 | 
|  | 239 		private static bool _allowStaticMembers; | 
|  | 240 		public  static bool  AllowStaticMembers | 
|  | 241 		{ | 
|  | 242 			get { return _allowStaticMembers;  } | 
|  | 243 			set { _allowStaticMembers = value; } | 
|  | 244 		} | 
|  | 245 | 
|  | 246 		#endregion | 
|  | 247 | 
|  | 248 		#region Multiple Duck | 
|  | 249 | 
|  | 250 		/// <summary> | 
|  | 251 		/// Build a proxy type which implements the requested interface by redirecting all calls to the supplied object type. | 
|  | 252 		/// </summary> | 
|  | 253 		/// <param name="interfaceType">An interface type to implement.</param> | 
|  | 254 		/// <param name="objectTypes">Array of types which expected to have all members of the given interface.</param> | 
|  | 255 		/// <returns>The duck object type.</returns> | 
|  | 256 		public static Type GetDuckType(Type interfaceType, Type[] objectTypes) | 
|  | 257 		{ | 
|  | 258 			if (interfaceType == null)      throw new ArgumentNullException("interfaceType"); | 
|  | 259 			if (!interfaceType.IsInterface) throw new ArgumentException(Resources.DuckTyping_InterfaceTypeMustBeAnInterface, "interfaceType"); | 
|  | 260 			if (!interfaceType.IsPublic && !interfaceType.IsNestedPublic) | 
|  | 261 				throw new ArgumentException(Resources.DuckTyping_InterfaceMustBePublic, "interfaceType"); | 
|  | 262 | 
|  | 263 			Dictionary<object,Type> types; | 
|  | 264 | 
|  | 265 			lock (_duckTypes) | 
|  | 266 				if (!_duckTypes.TryGetValue(interfaceType, out types)) | 
|  | 267 					_duckTypes.Add(interfaceType, types = new Dictionary<object,Type>()); | 
|  | 268 | 
|  | 269 			object key = new CompoundValue(objectTypes); | 
|  | 270 			Type   type; | 
|  | 271 | 
|  | 272 			lock (types) if (!types.TryGetValue(key, out type)) | 
|  | 273 			{ | 
|  | 274 				type = TypeFactory.GetType( | 
|  | 275 					new CompoundValue(interfaceType, key), | 
|  | 276 					interfaceType, | 
|  | 277 					new DuckTypeBuilder(MustImplementAttribute.Aggregate, interfaceType, objectTypes)); | 
|  | 278 | 
|  | 279 				types.Add(key, type); | 
|  | 280 			} | 
|  | 281 | 
|  | 282 			return type; | 
|  | 283 		} | 
|  | 284 | 
|  | 285 		/// <summary> | 
|  | 286 		/// Implements the requested interface from supplied set of objects. | 
|  | 287 		/// </summary> | 
|  | 288 		/// <param name="interfaceType">An interface type to implement.</param> | 
|  | 289 		/// <param name="baseObjectTypes">Array of types which have all members of the given interface. | 
|  | 290 		/// When this parameter is set to null, the object type will be used.</param> | 
|  | 291 		/// <param name="objs">Array of objects which types expected to have all members of the given interface.</param> | 
|  | 292 		/// <returns>An object which implements the interface.</returns> | 
|  | 293 		public static object Aggregate(Type interfaceType, Type[] baseObjectTypes,params object[] objs) | 
|  | 294 		{ | 
|  | 295 			if (objs == null) throw new ArgumentNullException("objs"); | 
|  | 296 | 
|  | 297 			if (baseObjectTypes == null) | 
|  | 298 			{ | 
|  | 299 				baseObjectTypes = new Type[objs.Length]; | 
|  | 300 | 
|  | 301 				for (int i = 0; i < objs.Length; i++) | 
|  | 302 					if (objs[i] != null) | 
|  | 303 						baseObjectTypes[i] = objs[i].GetType(); | 
|  | 304 			} | 
|  | 305 			else | 
|  | 306 			{ | 
|  | 307 				if (baseObjectTypes.Length != objs.Length) | 
|  | 308 					throw new ArgumentException(Resources.DuckTyping_InvalidNumberOfObjs, "baseObjectTypes"); | 
|  | 309 | 
|  | 310 				for (int i = 0; i < objs.Length; i++) | 
|  | 311 				{ | 
|  | 312 					Type objType = objs[i].GetType(); | 
|  | 313 | 
|  | 314 					if (!TypeHelper.IsSameOrParent(baseObjectTypes[i], objType)) | 
|  | 315 						throw new ArgumentException( | 
|  | 316 							string.Format(Resources.DuckTyping_NotASubtypeOf, objType.FullName, baseObjectTypes[i].FullName), "objs"); | 
|  | 317 				} | 
|  | 318 			} | 
|  | 319 | 
|  | 320 			Type duckType = GetDuckType(interfaceType, baseObjectTypes); | 
|  | 321 | 
|  | 322 			if (duckType == null) | 
|  | 323 				return null; | 
|  | 324 | 
|  | 325 			object duck = TypeAccessor.CreateInstanceEx(duckType); | 
|  | 326 | 
|  | 327 			((DuckType)duck).SetObjects(objs); | 
|  | 328 | 
|  | 329 			return duck; | 
|  | 330 		} | 
|  | 331 | 
|  | 332 		/// <summary> | 
|  | 333 		/// Implements the requested interface from supplied set of objects. | 
|  | 334 		/// </summary> | 
|  | 335 		/// <param name="interfaceType">An interface type to implement.</param> | 
|  | 336 		/// <param name="objs">Array of object which types expected to have of the given interface.</param> | 
|  | 337 		/// <returns>An object which implements the interface.</returns> | 
|  | 338 		public static object Aggregate(Type interfaceType,params object[] objs) | 
|  | 339 		{ | 
|  | 340 			return Aggregate(interfaceType, (Type[])null, objs); | 
|  | 341 		} | 
|  | 342 | 
|  | 343 		/// <summary> | 
|  | 344 		/// Implements the requested interface from supplied set of objects. | 
|  | 345 		/// </summary> | 
|  | 346 		/// <typeparam name="I">An interface type to implement.</typeparam> | 
|  | 347 		/// <param name="objs">Array of object which type expected to have all members of the given interface.</param> | 
|  | 348 		/// <returns>An object which implements the interface.</returns> | 
|  | 349 		public static I Aggregate<I>(params object[] objs) | 
|  | 350 			where I : class | 
|  | 351 		{ | 
|  | 352 			return (I)Aggregate(typeof(I), null, objs); | 
|  | 353 		} | 
|  | 354 | 
|  | 355 		#endregion | 
|  | 356 	} | 
|  | 357 } |