testing commit notification
[castle.git] / Tools / Castle.DynamicProxy2 / Castle.DynamicProxy / Generators / Emitters / CustomAttributeUtil.cs
blob9275682215990e6a781b0ec8cdfbad311610e0ff
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.
16 namespace Castle.DynamicProxy.Generators.Emitters
18 using System;
19 using System.Collections;
20 using System.Diagnostics;
21 using System.Reflection;
22 using System.Reflection.Emit;
24 internal class CustomAttributeUtil
26 public static CustomAttributeBuilder CreateCustomAttribute(Attribute attribute)
28 Type attType = attribute.GetType();
30 ConstructorInfo ci;
32 object[] ctorArgs = GetConstructorAndArgs(attType, attribute, out ci);
34 PropertyInfo[] properties;
36 object[] propertyValues = GetPropertyValues(attType, out properties, attribute);
38 FieldInfo[] fields;
40 object[] fieldValues = GetFieldValues(attType, out fields, attribute);
42 // here we are going to try to initialize the attribute with the collected arguments
43 // if we are good (created successfuly) we return it, otherwise, it is ignored.
44 try
46 Activator.CreateInstance(attType, ctorArgs);
47 return new CustomAttributeBuilder(ci, ctorArgs, properties, propertyValues, fields, fieldValues);
49 catch
51 // there is no real way to log a warning here...
52 Trace.WriteLine(@"Dynamic Proxy 2: Unable to find matching parameters for replicating attribute " + attType.FullName +
53 ".");
54 return null;
58 private static object[] GetConstructorAndArgs(Type attType, Attribute attribute, out ConstructorInfo ci)
60 object[] ctorArgs = new object[0];
62 ci = attType.GetConstructors()[0];
64 ParameterInfo[] constructorParams = ci.GetParameters();
66 if (constructorParams.Length != 0)
68 ctorArgs = new object[constructorParams.Length];
70 InitializeConstructorArgs(attType, attribute, ctorArgs, constructorParams);
73 return ctorArgs;
76 private static object[] GetPropertyValues(Type attType, out PropertyInfo[] properties, Attribute attribute)
78 ArrayList selectedProps = new ArrayList();
80 foreach(PropertyInfo pi in attType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
82 if (pi.CanRead && pi.CanWrite)
84 selectedProps.Add(pi);
88 properties = (PropertyInfo[]) selectedProps.ToArray(typeof(PropertyInfo));
90 object[] propertyValues = new object[properties.Length];
92 for(int i = 0; i < properties.Length; i++)
94 PropertyInfo propInfo = properties[i];
95 propertyValues[i] = propInfo.GetValue(attribute, null);
98 return propertyValues;
101 private static object[] GetFieldValues(Type attType, out FieldInfo[] fields, Attribute attribute)
103 fields = attType.GetFields(BindingFlags.Public | BindingFlags.Instance);
105 object[] values = new object[fields.Length];
107 for(int i = 0; i < fields.Length; i++)
109 FieldInfo fieldInfo = fields[i];
110 values[i] = fieldInfo.GetValue(attribute);
113 return values;
116 /// <summary>
117 /// Here we try to match a constructor argument to its value.
118 /// Since we can't get the values from the assembly, we use some heuristics to get it.
119 /// a/ we first try to match all the properties on the attributes by name (case insensitive) to the argument
120 /// b/ if we fail we try to match them by property type, with some smarts about convertions (i,e: can use Guid for string).
121 /// </summary>
122 private static void InitializeConstructorArgs(Type attType, Attribute attribute, object[] args,
123 ParameterInfo[] parameterInfos)
125 for(int i = 0; i < args.Length; i++)
127 args[i] = GetArgValue(attType, attribute, parameterInfos[i]);
131 private static object GetArgValue(Type attType, Attribute attribute, ParameterInfo parameterInfo)
133 Type paramType = parameterInfo.ParameterType;
135 PropertyInfo[] propertyInfos = attType.GetProperties();
136 //first try to find a property with
137 foreach(PropertyInfo propertyInfo in propertyInfos)
139 if (propertyInfo.CanRead == false && propertyInfo.GetIndexParameters().Length != 0)
141 continue;
144 if (string.Compare(propertyInfo.Name, parameterInfo.Name, true) == 0)
146 return ConvertValue(propertyInfo.GetValue(attribute, null), paramType);
151 PropertyInfo bestMatch = null;
152 //now we try to find it by type
153 foreach(PropertyInfo propertyInfo in propertyInfos)
155 if (propertyInfo.CanRead == false && propertyInfo.GetIndexParameters().Length != 0)
156 continue;
157 bestMatch = ReplaceIfBetterMatch(parameterInfo, propertyInfo, bestMatch);
159 if (bestMatch != null)
161 return ConvertValue(bestMatch.GetValue(attribute, null), paramType);
163 return GetDefaultValueFor(paramType);
166 /// <summary>
167 /// We have the following rules here.
168 /// Try to find a matching type, failing that, if the parameter is string, get the first property (under the assumption that
169 /// we can convert it.
170 /// </summary>
171 private static PropertyInfo ReplaceIfBetterMatch(ParameterInfo parameterInfo, PropertyInfo propertyInfo,
172 PropertyInfo bestMatch)
174 bool notBestMatch = bestMatch == null || bestMatch.PropertyType != parameterInfo.ParameterType;
175 if (propertyInfo.PropertyType == parameterInfo.ParameterType && notBestMatch)
176 return propertyInfo;
177 if (parameterInfo.ParameterType == typeof(string) && notBestMatch)
178 return propertyInfo;
179 return bestMatch;
182 /// <summary>
183 /// Attributes can only accept simple types, so we return null for null,
184 /// if the value is passed as string we call to string (should help with converting),
185 /// otherwise, we use the value as is (enums, integer, etc).
186 /// </summary>
187 private static object ConvertValue(object obj, Type paramType)
189 if (obj == null)
190 return null;
191 if (paramType == typeof(String))
192 return obj.ToString();
193 return obj;
196 private static object GetDefaultValueFor(Type type)
198 if (type == typeof(bool))
200 return false;
202 else if (type.IsEnum)
204 return Enum.GetValues(type).GetValue(0);
206 else if (type == typeof(char))
208 return char.MinValue;
210 else if (type.IsPrimitive)
212 return 0;
215 return null;