1 // Copyright 2004-2007 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.
16 namespace Castle
.DynamicProxy
.Generators
.Emitters
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();
32 object[] ctorArgs
= GetConstructorAndArgs(attType
, attribute
, out ci
);
34 PropertyInfo
[] properties
;
36 object[] propertyValues
= GetPropertyValues(attType
, out properties
, attribute
);
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.
46 Activator
.CreateInstance(attType
, ctorArgs
);
47 return new CustomAttributeBuilder(ci
, ctorArgs
, properties
, propertyValues
, fields
, fieldValues
);
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
+
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
);
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
);
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).
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)
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)
157 bestMatch
= ReplaceIfBetterMatch(parameterInfo
, propertyInfo
, bestMatch
);
159 if (bestMatch
!= null)
161 return ConvertValue(bestMatch
.GetValue(attribute
, null), paramType
);
163 return GetDefaultValueFor(paramType
);
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.
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
)
177 if (parameterInfo
.ParameterType
== typeof(string) && notBestMatch
)
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).
187 private static object ConvertValue(object obj
, Type paramType
)
191 if (paramType
== typeof(String
))
192 return obj
.ToString();
196 private static object GetDefaultValueFor(Type type
)
198 if (type
== typeof(bool))
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
)