1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle
.Components
.DictionaryAdapter
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
20 using System
.Collections
.Specialized
;
21 using System
.Reflection
;
22 using System
.Reflection
.Emit
;
23 using System
.Threading
;
26 /// Uses Reflection.Emit to expose the properties of a dictionary
27 /// through a dynamic implementation of a typed interface.
29 public class DictionaryAdapterFactory
: IDictionaryAdapterFactory
31 #region IDictionaryAdapterFactory
34 /// Gets a typed adapter bound to the <see cref="IDictionary"/>.
36 /// <typeparam name="T">The typed interface.</typeparam>
37 /// <param name="dictionary">The underlying source of properties.</param>
38 /// <returns>An implementation of the typed interface bound to the dictionary.</returns>
40 /// The type represented by T must be an interface with properties.
42 public T GetAdapter
<T
>(IDictionary dictionary
)
44 return (T
) GetAdapter(typeof(T
), dictionary
);
48 /// Gets a typed adapter bound to the <see cref="IDictionary"/>.
50 /// <param name="type">The typed interface.</param>
51 /// <param name="dictionary">The underlying source of properties.</param>
52 /// <returns>An implementation of the typed interface bound to the dictionary.</returns>
54 /// The type represented by T must be an interface with properties.
56 public object GetAdapter(Type type
, IDictionary dictionary
)
58 return InternalGetAdapter(type
, dictionary
, null);
62 /// Gets a typed adapter bound to the <see cref="IDictionary"/>.
64 /// <param name="type">The typed interface.</param>
65 /// <param name="dictionary">The underlying source of properties.</param>
66 /// <param name="descriptor">The property descriptor.</param>
67 /// <returns>An implementation of the typed interface bound to the dictionary.</returns>
69 /// The type represented by T must be an interface with properties.
71 public object GetAdapter(Type type
, IDictionary dictionary
,
72 PropertyDescriptor descriptor
)
74 return InternalGetAdapter(type
, dictionary
, descriptor
);
78 /// Gets a typed adapter bound to the <see cref="NameValueCollection"/>.
80 /// <typeparam name="T">The typed interface.</typeparam>
81 /// <param name="nameValues">The underlying source of properties.</param>
82 /// <returns>An implementation of the typed interface bound to the namedValues.</returns>
84 /// The type represented by T must be an interface with properties.
86 public T GetAdapter
<T
>(NameValueCollection nameValues
)
88 return GetAdapter
<T
>(new NameValueCollectionAdapter(nameValues
));
92 /// Gets a typed adapter bound to the <see cref="NameValueCollection"/>.
94 /// <param name="type">The typed interface.</param>
95 /// <param name="nameValues">The underlying source of properties.</param>
96 /// <returns>An implementation of the typed interface bound to the namedValues.</returns>
98 /// The type represented by T must be an interface with properties.
100 public object GetAdapter(Type type
, NameValueCollection nameValues
)
102 return GetAdapter(type
, new NameValueCollectionAdapter(nameValues
));
107 #region Dynamic Type Generation
109 private object InternalGetAdapter(Type type
, IDictionary dictionary
,
110 PropertyDescriptor descriptor
)
112 if (!type
.IsInterface
)
114 throw new ArgumentException("Only interfaces can be adapted");
117 AppDomain appDomain
= Thread
.GetDomain();
118 String adapterAssemblyName
= GetAdapterAssemblyName(type
);
119 Assembly adapterAssembly
= GetExistingAdapterAssembly(appDomain
, adapterAssemblyName
);
121 if (adapterAssembly
== null)
123 TypeBuilder typeBuilder
= CreateTypeBuilder(type
, appDomain
, adapterAssemblyName
);
124 adapterAssembly
= CreateAdapterAssembly(type
, typeBuilder
);
127 return GetExistingAdapter(type
, adapterAssembly
, dictionary
, descriptor
);
130 private static TypeBuilder
CreateTypeBuilder(Type type
, AppDomain appDomain
,
131 String adapterAssemblyName
)
133 AssemblyName assemblyName
= new AssemblyName(adapterAssemblyName
);
134 AssemblyBuilder assemblyBuilder
= appDomain
.DefineDynamicAssembly(assemblyName
, AssemblyBuilderAccess
.Run
);
135 ModuleBuilder moduleBuilder
= assemblyBuilder
.DefineDynamicModule(adapterAssemblyName
);
137 return CreateAdapterType(type
, moduleBuilder
);
140 private static TypeBuilder
CreateAdapterType(Type type
, ModuleBuilder moduleBuilder
)
142 TypeBuilder typeBuilder
=
143 moduleBuilder
.DefineType(GetAdapterFullTypeName(type
),
144 TypeAttributes
.Public
| TypeAttributes
.Class
| TypeAttributes
.BeforeFieldInit
);
145 typeBuilder
.AddInterfaceImplementation(type
);
150 private static Assembly
CreateAdapterAssembly(Type type
, TypeBuilder typeBuilder
)
152 FieldBuilder factoryField
= typeBuilder
.DefineField(
153 "factory", typeof(DictionaryAdapterFactory
), FieldAttributes
.Private
);
154 FieldBuilder dictionaryField
= typeBuilder
.DefineField(
155 "dictionary", typeof(IDictionary
), FieldAttributes
.Private
);
156 FieldBuilder descriptorField
= typeBuilder
.DefineField(
157 "descriptor", typeof(PropertyDescriptor
), FieldAttributes
.Private
);
158 FieldBuilder propertyMapField
= typeBuilder
.DefineField(
159 "propertyMap", typeof(Dictionary
<String
, PropertyDescriptor
>),
160 FieldAttributes
.Private
| FieldAttributes
.Static
);
162 CreateAdapterConstructor(typeBuilder
, factoryField
, dictionaryField
,
165 Dictionary
<String
, PropertyDescriptor
> propertyMap
= GetPropertyDescriptors(type
);
167 foreach(KeyValuePair
<String
, PropertyDescriptor
> descriptor
in propertyMap
)
169 CreateAdapterProperty(typeBuilder
, factoryField
, dictionaryField
,
170 descriptorField
, propertyMapField
, descriptor
.Value
);
173 Type adapterType
= typeBuilder
.CreateType();
174 adapterType
.InvokeMember("propertyMap",
175 BindingFlags
.NonPublic
| BindingFlags
.Static
|
176 BindingFlags
.SetField
,
177 null, null, new object[] {propertyMap}
);
179 return typeBuilder
.Assembly
;
184 #region CreateAdapterConstructor
186 private static void CreateAdapterConstructor(
187 TypeBuilder typeBuilder
, FieldInfo factoryField
,
188 FieldInfo dictionaryField
, FieldInfo descriptorField
)
190 ConstructorBuilder constructorBuilder
= typeBuilder
.DefineConstructor(
191 MethodAttributes
.Public
| MethodAttributes
.HideBySig
, CallingConventions
.Standard
,
194 typeof(DictionaryAdapterFactory
), typeof(IDictionary
),
195 typeof(IDictionaryKeyBuilder
)
197 constructorBuilder
.DefineParameter(1, ParameterAttributes
.None
, "factory");
198 constructorBuilder
.DefineParameter(2, ParameterAttributes
.None
, "dictionary");
199 constructorBuilder
.DefineParameter(3, ParameterAttributes
.None
, "descriptor");
201 ILGenerator ilGenerator
= constructorBuilder
.GetILGenerator();
203 Type objType
= Type
.GetType("System.Object");
204 ConstructorInfo objectConstructorInfo
= objType
.GetConstructor(new Type
[0]);
206 ilGenerator
.Emit(OpCodes
.Ldarg_0
);
207 ilGenerator
.Emit(OpCodes
.Call
, objectConstructorInfo
);
208 ilGenerator
.Emit(OpCodes
.Ldarg_0
);
209 ilGenerator
.Emit(OpCodes
.Ldarg_1
);
210 ilGenerator
.Emit(OpCodes
.Stfld
, factoryField
);
211 ilGenerator
.Emit(OpCodes
.Ldarg_0
);
212 ilGenerator
.Emit(OpCodes
.Ldarg_2
);
213 ilGenerator
.Emit(OpCodes
.Stfld
, dictionaryField
);
214 ilGenerator
.Emit(OpCodes
.Ldarg_0
);
215 ilGenerator
.Emit(OpCodes
.Ldarg_3
);
216 ilGenerator
.Emit(OpCodes
.Stfld
, descriptorField
);
217 ilGenerator
.Emit(OpCodes
.Ret
);
222 #region CreateAdapterProperty
224 private static void CreateAdapterProperty(
225 TypeBuilder typeBuilder
, FieldInfo factoryField
,
226 FieldInfo dictionaryField
, FieldInfo descriptorField
,
227 FieldInfo propertyMapField
, PropertyDescriptor descriptor
)
229 PropertyInfo property
= descriptor
.Property
;
230 PropertyBuilder propertyBuilder
=
231 typeBuilder
.DefineProperty(property
.Name
, property
.Attributes
,
232 property
.PropertyType
, null);
234 MethodAttributes propAttribs
=
235 MethodAttributes
.Public
| MethodAttributes
.SpecialName
|
236 MethodAttributes
.HideBySig
| MethodAttributes
.Virtual
;
238 if (property
.CanRead
)
240 CreatePropertyGetMethod(
241 typeBuilder
, factoryField
, dictionaryField
,
242 descriptorField
, propertyMapField
, propertyBuilder
,
243 descriptor
, propAttribs
);
246 if (property
.CanWrite
)
248 CreatePropertySetMethod(
249 typeBuilder
, factoryField
, dictionaryField
,
250 descriptorField
, propertyMapField
, propertyBuilder
,
251 descriptor
, propAttribs
);
255 private static void PreparePropertyMethod(
256 PropertyDescriptor descriptor
, FieldInfo dictionaryField
,
257 FieldInfo propertyMapField
, FieldInfo descriptorField
,
258 ILGenerator propILGenerator
, out LocalBuilder descriptorLocal
)
260 propILGenerator
.DeclareLocal(typeof(String
));
261 propILGenerator
.DeclareLocal(typeof(object));
262 descriptorLocal
= propILGenerator
.DeclareLocal(typeof(PropertyDescriptor
));
264 // key = propertyInfo.Name
265 propILGenerator
.Emit(OpCodes
.Ldstr
, descriptor
.PropertyName
);
266 propILGenerator
.Emit(OpCodes
.Stloc_0
);
268 // descriptor = propertyMap[key]
269 propILGenerator
.Emit(OpCodes
.Ldsfld
, propertyMapField
);
270 propILGenerator
.Emit(OpCodes
.Ldstr
, descriptor
.PropertyName
);
271 propILGenerator
.Emit(OpCodes
.Callvirt
, PropertyMapGetItem
);
272 propILGenerator
.Emit(OpCodes
.Stloc_S
, descriptorLocal
);
274 // key = descriptor.GetKey(dictionary, key, descriptor)
275 propILGenerator
.Emit(OpCodes
.Ldloc_S
, descriptorLocal
);
276 propILGenerator
.Emit(OpCodes
.Ldarg_0
);
277 propILGenerator
.Emit(OpCodes
.Ldfld
, dictionaryField
);
278 propILGenerator
.Emit(OpCodes
.Ldloc_0
);
279 propILGenerator
.Emit(OpCodes
.Ldarg_0
);
280 propILGenerator
.Emit(OpCodes
.Ldfld
, descriptorField
);
281 propILGenerator
.Emit(OpCodes
.Callvirt
, DescriptorGetKey
);
282 propILGenerator
.Emit(OpCodes
.Stloc_0
);
287 #region CreatePropertyGetMethod
289 private static void CreatePropertyGetMethod(
290 TypeBuilder typeBuilder
, FieldInfo factoryField
,
291 FieldInfo dictionaryField
, FieldInfo descriptorField
,
292 FieldInfo propertyMapField
, PropertyBuilder propertyBuilder
,
293 PropertyDescriptor descriptor
, MethodAttributes propAttribs
)
295 MethodBuilder getMethodBuilder
= typeBuilder
.DefineMethod(
296 "get_" + descriptor
.PropertyName
, propAttribs
,
297 descriptor
.PropertyType
, null);
299 ILGenerator getILGenerator
= getMethodBuilder
.GetILGenerator();
301 Label returnDefault
= getILGenerator
.DefineLabel();
302 Label storeResult
= getILGenerator
.DefineLabel();
303 Label loadResult
= getILGenerator
.DefineLabel();
304 LocalBuilder descriptorLocal
;
306 PreparePropertyMethod(
307 descriptor
, dictionaryField
, propertyMapField
,
308 descriptorField
, getILGenerator
, out descriptorLocal
);
309 LocalBuilder result
= getILGenerator
.DeclareLocal(
310 descriptor
.PropertyType
);
312 // value = dictionary[key]
313 getILGenerator
.Emit(OpCodes
.Ldarg_0
);
314 getILGenerator
.Emit(OpCodes
.Ldfld
, dictionaryField
);
315 getILGenerator
.Emit(OpCodes
.Ldloc_0
);
316 getILGenerator
.Emit(OpCodes
.Callvirt
, DictionaryGetItem
);
317 getILGenerator
.Emit(OpCodes
.Stloc_1
);
319 // value = descriptor.GetPropertyValue(factory, dictionary, key, value, descriptor)
320 getILGenerator
.Emit(OpCodes
.Ldloc_S
, descriptorLocal
);
321 getILGenerator
.Emit(OpCodes
.Ldarg_0
);
322 getILGenerator
.Emit(OpCodes
.Ldfld
, factoryField
);
323 getILGenerator
.Emit(OpCodes
.Ldarg_0
);
324 getILGenerator
.Emit(OpCodes
.Ldfld
, dictionaryField
);
325 getILGenerator
.Emit(OpCodes
.Ldloc_0
);
326 getILGenerator
.Emit(OpCodes
.Ldloc_1
);
327 getILGenerator
.Emit(OpCodes
.Ldarg_0
);
328 getILGenerator
.Emit(OpCodes
.Ldfld
, descriptorField
);
329 getILGenerator
.Emit(OpCodes
.Callvirt
, DescriptorGetValue
);
330 getILGenerator
.Emit(OpCodes
.Stloc_1
);
332 // if (value == null) return null
333 getILGenerator
.Emit(OpCodes
.Ldloc_1
);
334 getILGenerator
.Emit(OpCodes
.Brfalse_S
, returnDefault
);
336 // return (propertyInfo.PropertyType) value
337 getILGenerator
.Emit(OpCodes
.Ldloc_1
);
338 getILGenerator
.Emit(OpCodes
.Unbox_Any
, descriptor
.PropertyType
);
339 getILGenerator
.Emit(OpCodes
.Br_S
, storeResult
);
341 getILGenerator
.MarkLabel(returnDefault
);
342 getILGenerator
.Emit(OpCodes
.Ldloca_S
, result
);
343 getILGenerator
.Emit(OpCodes
.Initobj
, descriptor
.PropertyType
);
344 getILGenerator
.Emit(OpCodes
.Br_S
, loadResult
);
346 getILGenerator
.MarkLabel(storeResult
);
347 getILGenerator
.Emit(OpCodes
.Stloc_S
, result
);
348 getILGenerator
.Emit(OpCodes
.Br_S
, loadResult
);
350 getILGenerator
.MarkLabel(loadResult
);
351 getILGenerator
.Emit(OpCodes
.Ldloc_S
, result
);
353 getILGenerator
.Emit(OpCodes
.Ret
);
355 propertyBuilder
.SetGetMethod(getMethodBuilder
);
360 #region CreatePropertySetMethod
362 private static void CreatePropertySetMethod(
363 TypeBuilder typeBuilder
, FieldInfo factoryField
,
364 FieldInfo dictionaryField
, FieldInfo descriptorField
,
365 FieldInfo propertyMapField
, PropertyBuilder propertyBuilder
,
366 PropertyDescriptor descriptor
, MethodAttributes propAttribs
)
368 MethodBuilder setMethodBuilder
= typeBuilder
.DefineMethod(
369 "set_" + descriptor
.PropertyName
, propAttribs
, null,
370 new Type
[] {descriptor.PropertyType}
);
372 ILGenerator setILGenerator
= setMethodBuilder
.GetILGenerator();
373 Label skipSetter
= setILGenerator
.DefineLabel();
374 LocalBuilder descriptorLocal
;
376 PreparePropertyMethod(
377 descriptor
, dictionaryField
, propertyMapField
,
378 descriptorField
, setILGenerator
, out descriptorLocal
);
380 setILGenerator
.Emit(OpCodes
.Ldarg_1
);
381 if (descriptor
.PropertyType
.IsValueType
)
383 setILGenerator
.Emit(OpCodes
.Box
, descriptor
.PropertyType
);
385 setILGenerator
.Emit(OpCodes
.Stloc_1
);
387 // value = descriptor.SetPropertyValue(factory, dictionary, key, value, descriptor)
388 setILGenerator
.Emit(OpCodes
.Ldloc_S
, descriptorLocal
);
389 setILGenerator
.Emit(OpCodes
.Ldarg_0
);
390 setILGenerator
.Emit(OpCodes
.Ldfld
, factoryField
);
391 setILGenerator
.Emit(OpCodes
.Ldarg_0
);
392 setILGenerator
.Emit(OpCodes
.Ldfld
, dictionaryField
);
393 setILGenerator
.Emit(OpCodes
.Ldloc_0
);
394 setILGenerator
.Emit(OpCodes
.Ldloca_S
, 1);
395 setILGenerator
.Emit(OpCodes
.Ldarg_0
);
396 setILGenerator
.Emit(OpCodes
.Ldfld
, descriptorField
);
397 setILGenerator
.Emit(OpCodes
.Callvirt
, DescriptorSetValue
);
398 setILGenerator
.Emit(OpCodes
.Brfalse_S
, skipSetter
);
400 // dictionary[key] = value
401 setILGenerator
.Emit(OpCodes
.Ldarg_0
);
402 setILGenerator
.Emit(OpCodes
.Ldfld
, dictionaryField
);
403 setILGenerator
.Emit(OpCodes
.Ldloc_0
);
404 setILGenerator
.Emit(OpCodes
.Ldloc_1
);
405 setILGenerator
.Emit(OpCodes
.Callvirt
, DictionarySetItem
);
407 setILGenerator
.MarkLabel(skipSetter
);
408 setILGenerator
.Emit(OpCodes
.Ret
);
410 propertyBuilder
.SetSetMethod(setMethodBuilder
);
415 #region Property Descriptors
417 private static Dictionary
<String
, PropertyDescriptor
> GetPropertyDescriptors(Type type
)
419 Dictionary
<String
, PropertyDescriptor
> propertyMap
=
420 new Dictionary
<String
, PropertyDescriptor
>();
421 ICollection
<IDictionaryPropertyGetter
> typeGetters
=
422 GetOrderedBehaviors
<IDictionaryPropertyGetter
>(type
);
423 ICollection
<IDictionaryPropertySetter
> typeSetters
=
424 GetOrderedBehaviors
<IDictionaryPropertySetter
>(type
);
426 RecursivelyDiscoverProperties(
427 type
, delegate(PropertyInfo property
)
429 PropertyDescriptor descriptor
= new PropertyDescriptor(property
);
431 descriptor
.AddKeyBuilders(GetOrderedBehaviors
<IDictionaryKeyBuilder
>(property
));
432 descriptor
.AddKeyBuilders(GetOrderedBehaviors
<IDictionaryKeyBuilder
>(property
.ReflectedType
));
434 descriptor
.AddGetters(GetOrderedBehaviors
<IDictionaryPropertyGetter
>(property
));
435 descriptor
.AddGetters(typeGetters
);
436 AddDefaultGetter(descriptor
);
438 descriptor
.AddSetters(GetOrderedBehaviors
<IDictionaryPropertySetter
>(property
));
439 descriptor
.AddSetters(typeSetters
);
441 propertyMap
.Add(property
.Name
, descriptor
);
447 private static List
<T
> GetOrderedBehaviors
<T
>(Type type
) where T
: IDictionaryBehavior
449 List
<T
> behaviors
= AttributesUtil
.GetTypeAttributes
<T
>(type
);
450 if (behaviors
!= null)
452 behaviors
.Sort(DictionaryBehaviorComparer
<T
>.Instance
);
457 private static List
<T
> GetOrderedBehaviors
<T
>(MemberInfo member
) where T
: IDictionaryBehavior
459 List
<T
> behaviors
= AttributesUtil
.GetAttributes
<T
>(member
);
460 if (behaviors
!= null)
462 behaviors
.Sort(DictionaryBehaviorComparer
<T
>.Instance
);
467 private static void RecursivelyDiscoverProperties(
468 Type currentType
, Action
<PropertyInfo
> onProperty
)
470 List
<Type
> types
= new List
<Type
>();
471 types
.Add(currentType
);
472 types
.AddRange(currentType
.GetInterfaces());
474 foreach(Type type
in types
)
476 foreach(PropertyInfo property
in type
.GetProperties(
477 BindingFlags
.Public
| BindingFlags
.Instance
))
479 onProperty(property
);
484 private static void AddDefaultGetter(PropertyDescriptor descriptor
)
486 if (descriptor
.TypeConverter
!= null)
488 descriptor
.AddGetter(
489 new DefaultPropertyGetter(descriptor
.TypeConverter
));
495 #region Assembly Support
497 private static String
GetAdapterAssemblyName(Type type
)
499 return type
.Assembly
.GetName().Name
+ "." + type
.FullName
+ ".DictionaryAdapter";
502 private static String
GetAdapterFullTypeName(Type type
)
504 return type
.Namespace
+ "." + GetAdapterTypeName(type
);
507 private static String
GetAdapterTypeName(Type type
)
509 return type
.Name
.Substring(1) + "DictionaryAdapter";
512 private object GetExistingAdapter(Type type
, Assembly assembly
,
513 IDictionary dictionary
,
514 PropertyDescriptor descriptor
)
516 String adapterFullTypeName
= GetAdapterFullTypeName(type
);
517 return Activator
.CreateInstance(assembly
.GetType(adapterFullTypeName
, true),
518 this, dictionary
, descriptor
);
521 private static Assembly
GetExistingAdapterAssembly(AppDomain appDomain
, String assemblyName
)
523 return Array
.Find(appDomain
.GetAssemblies(),
524 delegate(Assembly assembly
) { return assembly.GetName().Name == assemblyName; }
);
529 #region MethodInfo Cache
531 private static readonly MethodInfo DictionaryGetItem
=
532 typeof(IDictionary
).GetMethod("get_Item", new Type
[] {typeof(Object)}
);
534 private static readonly MethodInfo DictionarySetItem
=
535 typeof(IDictionary
).GetMethod("set_Item", new Type
[] {typeof(Object), typeof(Object)}
);
537 private static readonly MethodInfo PropertyMapGetItem
=
538 typeof(Dictionary
<String
, object[]>).GetMethod("get_Item", new Type
[] {typeof(String)}
);
540 private static readonly MethodInfo DescriptorGetKey
=
541 typeof(PropertyDescriptor
).GetMethod("GetKey");
543 private static readonly MethodInfo DescriptorGetValue
=
544 typeof(PropertyDescriptor
).GetMethod("GetPropertyValue");
546 private static readonly MethodInfo DescriptorSetValue
=
547 typeof(PropertyDescriptor
).GetMethod("SetPropertyValue");