User: xtoff
Date: 2009/10/28 08:11 AM

Added:
 /DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/
  AttributeDisassembler.cs

Modified:
 /DynamicProxy/trunk/src/Castle.DynamicProxy/
  AttributeUtil.cs, Castle.DynamicProxy-vs2008.csproj, 
ProxyGenerationException.cs
 /DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/Emitters/
  AbstractTypeEmitter.cs

Log:
 - Extracted AttributeDisassembler.
 - Added FallbackDisassembler property to AttributeUtil (defaults to instance 
of AttributeDisassembler)
 - Attribute disassembler by default throws when can't successfully disassemble 
an attribute. The method that decides that is virtual, so it's easy to change 
just that behavior without having to manually reimplement the whole disassembly 
engine.
 Notice that his affects only Silverlight, as for most cases in full .NET 
CustomAttributeData will be used instead of IAttributeDisassembler.

File Changes:

Directory: /DynamicProxy/trunk/src/Castle.DynamicProxy/
=======================================================

File [modified]: AttributeUtil.cs
Delta lines: +1 -0
===================================================================

--- 
DynamicProxy/trunk/src/Castle.DynamicProxy/Castle.DynamicProxy-vs2008.csproj    
    2009-10-28 03:22:48 UTC (rev 6280)
+++ 
DynamicProxy/trunk/src/Castle.DynamicProxy/Castle.DynamicProxy-vs2008.csproj    
    2009-10-28 15:11:12 UTC (rev 6281)
@@ -99,6 +99,7 @@
     <Compile Include="AssemblyInfo.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="Generators\AttributeDisassembler.cs" />
     <Compile Include="CacheMappingsAttribute.cs" />
     <Compile Include="Contributors\ClassMembersCollector.cs" />

File [modified]: Castle.DynamicProxy-vs2008.csproj
Delta lines: +291 -0
===================================================================

--- 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/AttributeDisassembler.cs  
                            (rev 0)
+++ 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/AttributeDisassembler.cs  
    2009-10-28 15:11:12 UTC (rev 6281)
@@ -0,0 +1,291 @@
+namespace Castle.DynamicProxy.Generators
+{
+       using System;
+       using System.Collections.Generic;
+       using System.Diagnostics;
+       using System.Reflection;
+       using System.Reflection.Emit;
+
+#if !SILVERLIGHT
+       [Serializable]
+#endif
+       public class AttributeDisassembler : IAttributeDisassembler
+       {
+               public CustomAttributeBuilder Disassemble(Attribute attribute)
+               {
+                       Type type = attribute.GetType();
+
+                       ConstructorInfo ctor;
+                       object[] ctorArgs;
+
+                       PropertyInfo[] properties;
+                       object[] propertyValues;
+
+                       FieldInfo[] fields;
+                       object[] fieldValues;
+
+                       try
+                       {
+                               ctorArgs = GetConstructorAndArgs(type, 
attribute, out ctor);
+                               var replicated = (Attribute) 
Activator.CreateInstance(type, ctorArgs);
+                               propertyValues = GetPropertyValues(type, out 
properties, attribute, replicated);
+                               fieldValues = GetFieldValues(type, out fields, 
attribute, replicated);
+                               return new CustomAttributeBuilder(ctor, 
ctorArgs, properties, propertyValues, fields, fieldValues);
+                       }
+                       catch (Exception ex)
+                       {
+
+                               // there is no real way to log a warning here...
+                               return HandleError(type,ex);
+                       }
+               }
+
+               /// <summary>
+               /// Handles error during disassembly process
+               /// </summary>
+               /// <param name="attributeType">Type of the attribute being 
disassembled</param>
+               /// <param name="exception">Exception thrown during the 
process</param>
+               /// <returns>usually null, or (re)throws the exception</returns>
+               protected virtual CustomAttributeBuilder HandleError(Type 
attributeType, Exception exception)
+               {
+                       // ouch...
+                       string message = "Dynamic Proxy was unable to 
disassemble attribute " + attributeType.Name +
+                                        " using default AttributeDisassembler. 
" +
+                                        "To handle the disassembly process 
properly implement the IAttributeDisassembler interface, " +
+                                        "and register your disassembler to 
handle this type of attributes using " +
+                                        typeof (AttributeUtil).Name + 
".AddDisassembler<" + attributeType.Name + ">(yourDisassembler) method";
+                       throw new ProxyGenerationException(message, exception);
+               }
+
+               private static object[] GetConstructorAndArgs(Type attType, 
Attribute attribute, out ConstructorInfo ci)
+               {
+                       var ctorArgs = new object[0];
+
+                       ci = attType.GetConstructors()[0];
+
+                       ParameterInfo[] constructorParams = ci.GetParameters();
+
+                       if (constructorParams.Length != 0)
+                       {
+                               ctorArgs = new object[constructorParams.Length];
+
+                               InitializeConstructorArgs(attType, attribute, 
ctorArgs, constructorParams);
+                       }
+
+                       return ctorArgs;
+               }
+
+               private static object[] GetPropertyValues(Type attType, out 
PropertyInfo[] properties, Attribute original,
+                                                         Attribute replicated)
+               {
+                       List<PropertyInfo> propertyCandidates = 
GetPropertyCandidates(attType);
+
+                       var selectedValues = new 
List<object>(propertyCandidates.Count);
+                       var selectedProperties = new 
List<PropertyInfo>(propertyCandidates.Count);
+                       foreach (PropertyInfo property in propertyCandidates)
+                       {
+                               object originalValue = 
property.GetValue(original, null);
+                               object replicatedValue = 
property.GetValue(replicated, null);
+                               if (AreAttributeElementsEqual(originalValue, 
replicatedValue))
+                               {
+                                       //this property has default value so we 
skip it
+                                       continue;
+                               }
+
+                               selectedProperties.Add(property);
+                               selectedValues.Add(originalValue);
+                       }
+
+                       properties = selectedProperties.ToArray();
+                       return selectedValues.ToArray();
+               }
+
+               private static object[] GetFieldValues(Type attType, out 
FieldInfo[] fields, Attribute original, Attribute replicated)
+               {
+                       FieldInfo[] fieldsCandidates = 
attType.GetFields(BindingFlags.Public | BindingFlags.Instance);
+
+                       var selectedValues = new 
List<object>(fieldsCandidates.Length);
+                       var selectedFields = new 
List<FieldInfo>(fieldsCandidates.Length);
+                       foreach (FieldInfo field in fieldsCandidates)
+                       {
+                               object originalValue = field.GetValue(original);
+                               object replicatedValue = 
field.GetValue(replicated);
+                               if (AreAttributeElementsEqual(originalValue, 
replicatedValue))
+                               {
+                                       //this field has default value so we 
skip it
+                                       continue;
+                               }
+
+                               selectedFields.Add(field);
+                               selectedValues.Add(originalValue);
+                       }
+
+                       fields = selectedFields.ToArray();
+                       return selectedValues.ToArray();
+               }
+
+               /// <summary>
+               /// Here we try to match a constructor argument to its value.
+               /// Since we can't get the values from the assembly, we use 
some heuristics to get it.
+               /// a/ we first try to match all the properties on the 
attributes by name (case insensitive) to the argument
+               /// b/ if we fail we try to match them by property type, with 
some smarts about convertions (i,e: can use Guid for string).
+               /// </summary>
+               private static void InitializeConstructorArgs(Type attType, 
Attribute attribute, object[] args,
+                                                             ParameterInfo[] 
parameterInfos)
+               {
+                       for (int i = 0; i < args.Length; i++)
+                       {
+                               args[i] = GetArgValue(attType, attribute, 
parameterInfos[i]);
+                       }
+               }
+
+               private static object GetArgValue(Type attType, Attribute 
attribute, ParameterInfo parameterInfo)
+               {
+                       Type paramType = parameterInfo.ParameterType;
+
+                       PropertyInfo[] propertyInfos = attType.GetProperties();
+                       //first try to find a property with 
+                       foreach (PropertyInfo propertyInfo in propertyInfos)
+                       {
+                               if (propertyInfo.CanRead == false && 
propertyInfo.GetIndexParameters().Length != 0)
+                               {
+                                       continue;
+                               }
+
+                               if (String.Compare(propertyInfo.Name, 
parameterInfo.Name, StringComparison.CurrentCultureIgnoreCase) == 0)
+                               {
+                                       return 
ConvertValue(propertyInfo.GetValue(attribute, null), paramType);
+                               }
+                       }
+
+
+                       PropertyInfo bestMatch = null;
+                       //now we try to find it by type
+                       foreach (PropertyInfo propertyInfo in propertyInfos)
+                       {
+                               if (propertyInfo.CanRead == false && 
propertyInfo.GetIndexParameters().Length != 0)
+                                       continue;
+                               bestMatch = ReplaceIfBetterMatch(parameterInfo, 
propertyInfo, bestMatch);
+                       }
+                       if (bestMatch != null)
+                       {
+                               return 
ConvertValue(bestMatch.GetValue(attribute, null), paramType);
+                       }
+                       return GetDefaultValueFor(paramType);
+               }
+
+               /// <summary>
+               /// We have the following rules here.
+               /// Try to find a matching type, failing that, if the parameter 
is string, get the first property (under the assumption that
+               /// we can convert it.
+               /// </summary>
+               private static PropertyInfo ReplaceIfBetterMatch(ParameterInfo 
parameterInfo, PropertyInfo propertyInfo,
+                                                                PropertyInfo 
bestMatch)
+               {
+                       bool notBestMatch = bestMatch == null || 
bestMatch.PropertyType != parameterInfo.ParameterType;
+                       if (propertyInfo.PropertyType == 
parameterInfo.ParameterType && notBestMatch)
+                               return propertyInfo;
+                       if (parameterInfo.ParameterType == typeof (string) && 
notBestMatch)
+                               return propertyInfo;
+                       return bestMatch;
+               }
+
+               /// <summary>
+               /// Attributes can only accept simple types, so we return null 
for null,
+               /// if the value is passed as string we call to string (should 
help with converting), 
+               /// otherwise, we use the value as is (enums, integer, etc).
+               /// </summary>
+               private static object ConvertValue(object obj, Type paramType)
+               {
+                       if (obj == null)
+                               return null;
+                       if (paramType == typeof (String))
+                               return obj.ToString();
+                       return obj;
+               }
+
+               private static object GetDefaultValueFor(Type type)
+               {
+                       if (type == typeof (bool))
+                       {
+                               return false;
+                       }
+                       if (type.IsEnum)
+                       {
+#if SILVERLIGHT
+                               return 
Castle.DynamicProxy.SilverlightExtensions.EnumHelper.GetValues(type).GetValue(0);
+#else
+                               return Enum.GetValues(type).GetValue(0);
+#endif
+                       }
+                       if (type == typeof (char))
+                       {
+                               return Char.MinValue;
+                       }
+                       if (type.IsPrimitive)
+                       {
+                               return 0;
+                       }
+
+                       return null;
+               }
+
+               private static List<PropertyInfo> GetPropertyCandidates(Type 
attributeType)
+               {
+                       var propertyCandidates = new List<PropertyInfo>();
+
+                       foreach (PropertyInfo pi in 
attributeType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
+                       {
+                               if (pi.CanRead && pi.CanWrite)
+                               {
+                                       propertyCandidates.Add(pi);
+                               }
+                       }
+
+                       return propertyCandidates;
+               }
+
+               private static bool AreAttributeElementsEqual(object first, 
object second)
+               {
+                       //we can have either System.Type, string or numeric type
+                       if (first == null)
+                       {
+                               return second == null;
+                       }
+
+                       //let's try string
+                       var firstString = first as string;
+                       if (firstString != null)
+                       {
+                               return AreStringsEqual(firstString, second as 
string);
+                       }
+
+                       //by now we should only be left with numeric types
+                       return first.Equals(second);
+               }
+
+               private static bool AreStringsEqual(string first, string second)
+               {
+                       Debug.Assert(first != null, "first != null");
+                       return first.Equals(second, StringComparison.Ordinal);
+               }
+
+               public bool Equals(AttributeDisassembler other)
+               {
+                       return !ReferenceEquals(null, other);
+               }
+
+               public override bool Equals(object obj)
+               {
+                       if (ReferenceEquals(null, obj)) return false;
+                       if (ReferenceEquals(this, obj)) return true;
+                       if (obj.GetType() != typeof (AttributeDisassembler)) 
return false;
+                       return Equals((AttributeDisassembler) obj);
+               }
+
+               public override int GetHashCode()
+               {
+                       return GetType().GetHashCode();
+               }
+       }
+}

File [modified]: ProxyGenerationException.cs
Delta lines: +0 -0
===================================================================

Directory: /DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/Emitters/
===========================================================================

File [modified]: AbstractTypeEmitter.cs
Delta lines: +4 -0
===================================================================

--- DynamicProxy/trunk/src/Castle.DynamicProxy/ProxyGenerationException.cs      
2009-10-28 03:22:48 UTC (rev 6280)
+++ DynamicProxy/trunk/src/Castle.DynamicProxy/ProxyGenerationException.cs      
2009-10-28 15:11:12 UTC (rev 6281)
@@ -6,5 +6,9 @@
        {
                public ProxyGenerationException(string message) : base(message)
                {}
+
+               public ProxyGenerationException(string message, Exception 
innerException):base(message,innerException)
+               {
+               }
        }

Directory: /DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/
==================================================================

File [added]: AttributeDisassembler.cs
Delta lines: +4 -1
===================================================================

--- 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/Emitters/AbstractTypeEmitter.cs
       2009-10-28 03:22:48 UTC (rev 6280)
+++ 
DynamicProxy/trunk/src/Castle.DynamicProxy/Generators/Emitters/AbstractTypeEmitter.cs
       2009-10-28 15:11:12 UTC (rev 6281)
@@ -87,7 +87,10 @@
                        foreach (Attribute attr in 
proxyGenerationOptions.attributesToAddToGeneratedTypes)
                        {
                                var customAttributeBuilder = 
AttributeUtil.CreateBuilder(attr);
-                               
typebuilder.SetCustomAttribute(customAttributeBuilder);
+                               if (customAttributeBuilder != null)
+                               {
+                                       
typebuilder.SetCustomAttribute(customAttributeBuilder);
+                               }
                        }
 


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Castle Project Commits" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/castle-project-commits?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to