Some file names did not match class name.
[castle.git] / Components / DictionaryAdapter / Castle.Components.DictionaryAdapter / DictionaryAdapterFactory.cs
blob8384c15d6afd56ad93a0eb08f09e2e8ca23d39de
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.Reflection;
21 using System.Reflection.Emit;
22 using System.Threading;
23 using System.Collections.Specialized;
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 LocalBuilder descriptorLocal;
375 PreparePropertyMethod(
376 descriptor, dictionaryField, propertyMapField,
377 descriptorField, setILGenerator, out descriptorLocal);
379 setILGenerator.Emit(OpCodes.Ldarg_1);
380 if (descriptor.PropertyType.IsValueType)
382 setILGenerator.Emit(OpCodes.Box, descriptor.PropertyType);
384 setILGenerator.Emit(OpCodes.Stloc_1);
386 // value = descriptor.SetPropertyValue(factory, dictionary, key, value, descriptor)
387 setILGenerator.Emit(OpCodes.Ldloc_S, descriptorLocal);
388 setILGenerator.Emit(OpCodes.Ldarg_0);
389 setILGenerator.Emit(OpCodes.Ldfld, factoryField);
390 setILGenerator.Emit(OpCodes.Ldarg_0);
391 setILGenerator.Emit(OpCodes.Ldfld, dictionaryField);
392 setILGenerator.Emit(OpCodes.Ldloc_0);
393 setILGenerator.Emit(OpCodes.Ldloc_1);
394 setILGenerator.Emit(OpCodes.Ldarg_0);
395 setILGenerator.Emit(OpCodes.Ldfld, descriptorField);
396 setILGenerator.Emit(OpCodes.Callvirt, DescriptorSetValue);
397 setILGenerator.Emit(OpCodes.Stloc_1);
399 // dictionary[key] = value
400 setILGenerator.Emit(OpCodes.Ldarg_0);
401 setILGenerator.Emit(OpCodes.Ldfld, dictionaryField);
402 setILGenerator.Emit(OpCodes.Ldloc_0);
403 setILGenerator.Emit(OpCodes.Ldloc_1);
404 setILGenerator.Emit(OpCodes.Callvirt, DictionarySetItem);
406 setILGenerator.Emit(OpCodes.Ret);
408 propertyBuilder.SetSetMethod(setMethodBuilder);
411 #endregion
413 #region Property Descriptors
415 private static Dictionary<String, PropertyDescriptor>GetPropertyDescriptors(Type type)
417 IDictionaryPropertyGetter typeGetter =
418 AttributesUtil.GetTypeAttribute<IDictionaryPropertyGetter>(type);
419 IDictionaryPropertySetter typeSetter =
420 AttributesUtil.GetTypeAttribute<IDictionaryPropertySetter>(type);
421 Dictionary<String, PropertyDescriptor> propertyMap =
422 new Dictionary<String, PropertyDescriptor>();
424 RecursivelyDiscoverProperties(
425 type, delegate(PropertyInfo property)
427 PropertyDescriptor descriptor = new PropertyDescriptor(property);
429 descriptor.Getter = AttributesUtil.
430 GetAttribute<IDictionaryPropertyGetter>(property)
431 ?? typeGetter ?? DefaultPropertyGetter.Instance;
432 descriptor.Setter = AttributesUtil.
433 GetAttribute<IDictionaryPropertySetter>(property)
434 ?? typeSetter;
436 descriptor.AddKeyBuilders(
437 AttributesUtil.GetAttributes<IDictionaryKeyBuilder>(property));
438 descriptor.AddKeyBuilders(
439 AttributesUtil.GetTypeAttributes<IDictionaryKeyBuilder>(property.ReflectedType));
441 propertyMap.Add(property.Name, descriptor);
444 return propertyMap;
447 private static void RecursivelyDiscoverProperties(
448 Type currentType, Action<PropertyInfo> onProperty)
450 List<Type> types = new List<Type>();
451 types.Add(currentType);
452 types.AddRange(currentType.GetInterfaces());
454 foreach (Type type in types)
456 foreach (PropertyInfo property in type.GetProperties(
457 BindingFlags.Public | BindingFlags.Instance))
459 onProperty(property);
464 #endregion
466 #region Assembly Support
468 private static String GetAdapterAssemblyName(Type type)
470 return type.Assembly.GetName().Name + "." + type.FullName + ".DictionaryAdapter";
473 private static String GetAdapterFullTypeName(Type type)
475 return type.Namespace + "." + GetAdapterTypeName(type);
478 private static String GetAdapterTypeName(Type type)
480 return type.Name.Substring(1) + "DictionaryAdapter";
483 private object GetExistingAdapter(Type type, Assembly assembly,
484 IDictionary dictionary,
485 PropertyDescriptor descriptor)
487 String adapterFullTypeName = GetAdapterFullTypeName(type);
488 return Activator.CreateInstance(assembly.GetType(adapterFullTypeName, true),
489 this, dictionary, descriptor);
492 private static Assembly GetExistingAdapterAssembly(AppDomain appDomain, String assemblyName)
494 return Array.Find(appDomain.GetAssemblies(),
495 delegate(Assembly assembly) { return assembly.GetName().Name == assemblyName; });
498 #endregion
500 #region MethodInfo Cache
502 private static readonly MethodInfo DictionaryGetItem =
503 typeof(IDictionary).GetMethod("get_Item", new Type[] {typeof(Object)});
505 private static readonly MethodInfo DictionarySetItem =
506 typeof(IDictionary).GetMethod("set_Item", new Type[] {typeof(Object), typeof(Object)});
508 private static readonly MethodInfo PropertyMapGetItem =
509 typeof(Dictionary<String, object[]>).GetMethod("get_Item", new Type[] { typeof(String) });
511 private static readonly MethodInfo DescriptorGetKey =
512 typeof(PropertyDescriptor).GetMethod(
513 "GetKey", new Type[]
515 typeof(IDictionary), typeof(String),
516 typeof(PropertyDescriptor)
519 private static readonly MethodInfo DescriptorGetValue =
520 typeof(PropertyDescriptor).GetMethod(
521 "GetPropertyValue", new Type[]
523 typeof(DictionaryAdapterFactory),
524 typeof(IDictionary), typeof(String), typeof(object),
525 typeof(PropertyDescriptor)
528 private static readonly MethodInfo DescriptorSetValue =
529 typeof(PropertyDescriptor).GetMethod(
530 "SetPropertyValue", new Type[]
532 typeof(DictionaryAdapterFactory),
533 typeof(IDictionary), typeof(String), typeof(object),
534 typeof(PropertyDescriptor)
537 #endregion