Changed the signature of IDictionaryAdapterSetter to return a bool to indicate if...
[castle.git] / Components / DictionaryAdapter / Castle.Components.DictionaryAdapter / DictionaryAdapterFactory.cs
blob5fd3382afa3daef6faffaf05bbb748c07ab6e2d4
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
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
17 using System;
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;
25 /// <summary>
26 /// Uses Reflection.Emit to expose the properties of a dictionary
27 /// through a dynamic implementation of a typed interface.
28 /// </summary>
29 public class DictionaryAdapterFactory : IDictionaryAdapterFactory
31 #region IDictionaryAdapterFactory
33 /// <summary>
34 /// Gets a typed adapter bound to the <see cref="IDictionary"/>.
35 /// </summary>
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>
39 /// <remarks>
40 /// The type represented by T must be an interface with properties.
41 /// </remarks>
42 public T GetAdapter<T>(IDictionary dictionary)
44 return (T) GetAdapter(typeof(T), dictionary);
47 /// <summary>
48 /// Gets a typed adapter bound to the <see cref="IDictionary"/>.
49 /// </summary>
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>
53 /// <remarks>
54 /// The type represented by T must be an interface with properties.
55 /// </remarks>
56 public object GetAdapter(Type type, IDictionary dictionary)
58 return InternalGetAdapter(type, dictionary, null);
61 /// <summary>
62 /// Gets a typed adapter bound to the <see cref="IDictionary"/>.
63 /// </summary>
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>
68 /// <remarks>
69 /// The type represented by T must be an interface with properties.
70 /// </remarks>
71 public object GetAdapter(Type type, IDictionary dictionary,
72 PropertyDescriptor descriptor)
74 return InternalGetAdapter(type, dictionary, descriptor);
77 /// <summary>
78 /// Gets a typed adapter bound to the <see cref="NameValueCollection"/>.
79 /// </summary>
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>
83 /// <remarks>
84 /// The type represented by T must be an interface with properties.
85 /// </remarks>
86 public T GetAdapter<T>(NameValueCollection nameValues)
88 return GetAdapter<T>(new NameValueCollectionAdapter(nameValues));
91 /// <summary>
92 /// Gets a typed adapter bound to the <see cref="NameValueCollection"/>.
93 /// </summary>
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>
97 /// <remarks>
98 /// The type represented by T must be an interface with properties.
99 /// </remarks>
100 public object GetAdapter(Type type, NameValueCollection nameValues)
102 return GetAdapter(type, new NameValueCollectionAdapter(nameValues));
105 #endregion
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);
147 return typeBuilder;
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,
163 descriptorField);
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;
182 #endregion
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,
192 new Type[]
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);
220 #endregion
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);
285 #endregion
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);
358 #endregion
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);
413 #endregion
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 AttributesUtil.GetTypeAttributes<IDictionaryPropertyGetter>(type);
423 ICollection<IDictionaryPropertySetter> typeSetters =
424 AttributesUtil.GetTypeAttributes<IDictionaryPropertySetter>(type);
426 RecursivelyDiscoverProperties(
427 type, delegate(PropertyInfo property)
429 PropertyDescriptor descriptor = new PropertyDescriptor(property);
431 descriptor.AddKeyBuilders(
432 AttributesUtil.GetAttributes<IDictionaryKeyBuilder>(property));
433 descriptor.AddKeyBuilders(
434 AttributesUtil.GetTypeAttributes<IDictionaryKeyBuilder>(property.ReflectedType));
436 descriptor.AddGetters(
437 AttributesUtil.GetAttributes<IDictionaryPropertyGetter>(property));
438 descriptor.AddGetters(typeGetters);
439 AddDefaultGetter(descriptor);
441 descriptor.AddSetters(
442 AttributesUtil.GetAttributes<IDictionaryPropertySetter>(property));
443 descriptor.AddSetters(typeSetters);
445 propertyMap.Add(property.Name, descriptor);
448 return propertyMap;
451 private static void RecursivelyDiscoverProperties(
452 Type currentType, Action<PropertyInfo> onProperty)
454 List<Type> types = new List<Type>();
455 types.Add(currentType);
456 types.AddRange(currentType.GetInterfaces());
458 foreach(Type type in types)
460 foreach(PropertyInfo property in type.GetProperties(
461 BindingFlags.Public | BindingFlags.Instance))
463 onProperty(property);
468 private static void AddDefaultGetter(PropertyDescriptor descriptor)
470 if (descriptor.TypeConverter != null)
472 descriptor.AddGetter(
473 new DefaultPropertyGetter(descriptor.TypeConverter));
477 #endregion
479 #region Assembly Support
481 private static String GetAdapterAssemblyName(Type type)
483 return type.Assembly.GetName().Name + "." + type.FullName + ".DictionaryAdapter";
486 private static String GetAdapterFullTypeName(Type type)
488 return type.Namespace + "." + GetAdapterTypeName(type);
491 private static String GetAdapterTypeName(Type type)
493 return type.Name.Substring(1) + "DictionaryAdapter";
496 private object GetExistingAdapter(Type type, Assembly assembly,
497 IDictionary dictionary,
498 PropertyDescriptor descriptor)
500 String adapterFullTypeName = GetAdapterFullTypeName(type);
501 return Activator.CreateInstance(assembly.GetType(adapterFullTypeName, true),
502 this, dictionary, descriptor);
505 private static Assembly GetExistingAdapterAssembly(AppDomain appDomain, String assemblyName)
507 return Array.Find(appDomain.GetAssemblies(),
508 delegate(Assembly assembly) { return assembly.GetName().Name == assemblyName; });
511 #endregion
513 #region MethodInfo Cache
515 private static readonly MethodInfo DictionaryGetItem =
516 typeof(IDictionary).GetMethod("get_Item", new Type[] {typeof(Object)});
518 private static readonly MethodInfo DictionarySetItem =
519 typeof(IDictionary).GetMethod("set_Item", new Type[] {typeof(Object), typeof(Object)});
521 private static readonly MethodInfo PropertyMapGetItem =
522 typeof(Dictionary<String, object[]>).GetMethod("get_Item", new Type[] {typeof(String)});
524 private static readonly MethodInfo DescriptorGetKey =
525 typeof(PropertyDescriptor).GetMethod("GetKey");
527 private static readonly MethodInfo DescriptorGetValue =
528 typeof(PropertyDescriptor).GetMethod("GetPropertyValue");
530 private static readonly MethodInfo DescriptorSetValue =
531 typeof(PropertyDescriptor).GetMethod("SetPropertyValue");
533 #endregion