From f605067636f993283a855f00b3530ee49bb7a2ce Mon Sep 17 00:00:00 2001 From: sbohlen Date: Fri, 13 May 2011 00:15:26 +0000 Subject: [PATCH] SPRNET-1242 added support for collection property merging of Set and List --- .../TypeConversion/TypeConversionUtils.cs | 106 ++++++++++++++++-- .../Spring.Core/Spring.Core.2008.csproj | 8 ++ .../Factory/Xml/collectionMergingGeneric.xml | 4 +- .../Xml/CollectionMergingGenericTests.cs | 2 +- 4 files changed, 108 insertions(+), 12 deletions(-) diff --git a/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs b/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs index ff8e7cc2..fd38c7a8 100644 --- a/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs +++ b/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs @@ -22,8 +22,11 @@ using System; using System.Collections; -using System.ComponentModel; +#if NET_2_0 +using System.Collections.Generic; +#endif +using System.ComponentModel; using Spring.Util; #endregion @@ -65,18 +68,18 @@ namespace Spring.Core.TypeConversion Type componentType = requiredType.GetElementType(); if (newValue is ICollection) { - ICollection elements = (ICollection) newValue; + ICollection elements = (ICollection)newValue; return ToArrayWithTypeConversion(componentType, elements, propertyName); } else if (newValue is string) { if (requiredType.Equals(typeof(char[]))) { - return ((string) newValue).ToCharArray(); + return ((string)newValue).ToCharArray(); } else { - string[] elements = StringUtils.CommaDelimitedListToStringArray((string) newValue); + string[] elements = StringUtils.CommaDelimitedListToStringArray((string)newValue); return ToArrayWithTypeConversion(componentType, elements, propertyName); } } @@ -89,6 +92,31 @@ namespace Spring.Core.TypeConversion return result; } } +#if NET_2_0 + // if required type is some ISet, convert all the elements + if (requiredType != null && requiredType.IsGenericType && TypeImplementsGenericInterface(requiredType, typeof(Spring.Collections.Generic.ISet<>))) + { + // convert individual elements to array elements + Type componentType = requiredType.GetGenericArguments()[0]; + if (newValue is ICollection) + { + ICollection elements = (ICollection)newValue; + return ToTypedCollectionWithTypeConversion(typeof(Spring.Collections.Generic.Set<>), componentType, elements, propertyName); + } + } + + // if required type is some IList, convert all the elements + if (requiredType != null && requiredType.IsGenericType && TypeImplementsGenericInterface(requiredType, typeof(IList<>))) + { + // convert individual elements to array elements + Type componentType = requiredType.GetGenericArguments()[0]; + if (newValue is ICollection) + { + ICollection elements = (ICollection)newValue; + return ToTypedCollectionWithTypeConversion(typeof(List<>), componentType, elements, propertyName); + } + } +#endif // try to convert using type converter try @@ -118,7 +146,7 @@ namespace Spring.Core.TypeConversion else { // look if it's an enum - if (requiredType != null + if (requiredType != null && requiredType.IsEnum && (!(newValue is float) && (!(newValue is double)))) @@ -145,7 +173,7 @@ namespace Spring.Core.TypeConversion else { throw new TypeMismatchException( - CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType); + CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType); } } } @@ -156,8 +184,8 @@ namespace Spring.Core.TypeConversion CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType, ex); } #if NET_2_0 - if (newValue == null - && (requiredType == null + if (newValue == null + && (requiredType == null || !Type.GetType("System.Nullable`1").Equals(requiredType.GetGenericTypeDefinition()))) #else if (newValue == null) @@ -183,10 +211,34 @@ namespace Spring.Core.TypeConversion return destination; } +#if NET_2_0 + private static object ToTypedCollectionWithTypeConversion(Type targetCollectionType, Type componentType, ICollection elements, string propertyName) + { + if (!TypeImplementsGenericInterface(targetCollectionType, typeof(ICollection<>))) + { + throw new ArgumentException("argument must be a type that derives from ICollection", "targetCollectionType"); + } + + + Type collectionType = targetCollectionType.MakeGenericType(new Type[] { componentType }); + + object typedCollection = Activator.CreateInstance(collectionType); + + int i = 0; + foreach (object element in elements) + { + object value = ConvertValueIfNecessary(componentType, element, BuildIndexedPropertyName(propertyName, i)); + collectionType.GetMethod("Add").Invoke(typedCollection, new object[] { value }); + i++; + } + return typedCollection; + } + +#endif private static string BuildIndexedPropertyName(string propertyName, int index) { return (propertyName != null ? - propertyName + "[" + index + "]": + propertyName + "[" + index + "]" : null); } @@ -222,5 +274,41 @@ namespace Spring.Core.TypeConversion return new PropertyChangeEventArgs(fullPropertyName, oldValue, newValue); } + +#if NET_2_0 + /// + /// Determines if a Type implements a specific generic interface. + /// + /// Candidate to evaluate. + /// The to test for in the Candidate . + /// if a match, else + private static bool TypeImplementsGenericInterface(Type candidateType, Type matchingInterface) + { + if (!matchingInterface.IsInterface) + { + throw new ArgumentException("matchingInterface Type must be an Interface Type", "matchingInterface"); + } + + bool match = false; + + Type[] implementedInterfaces = candidateType.GetInterfaces(); + foreach (Type interfaceType in implementedInterfaces) + { + if (false == interfaceType.IsGenericType) + { + continue; + } + + Type genericType = interfaceType.GetGenericTypeDefinition(); + if (genericType == matchingInterface) + { + match = true; + break; + } + } + + return match; + } +#endif } } \ No newline at end of file diff --git a/src/Spring/Spring.Core/Spring.Core.2008.csproj b/src/Spring/Spring.Core/Spring.Core.2008.csproj index d5fd05f7..d19a4189 100644 --- a/src/Spring/Spring.Core/Spring.Core.2008.csproj +++ b/src/Spring/Spring.Core/Spring.Core.2008.csproj @@ -120,7 +120,15 @@ Code + + + + + + + + Code diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collectionMergingGeneric.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collectionMergingGeneric.xml index b838df6b..852018dc 100644 --- a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collectionMergingGeneric.xml +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collectionMergingGeneric.xml @@ -6,7 +6,7 @@ - + Rob Harrop Rod Johnson @@ -15,7 +15,7 @@ - + Juergen Hoeller diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/CollectionMergingGenericTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/CollectionMergingGenericTests.cs index 9b1c47b7..9aed790f 100644 --- a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/CollectionMergingGenericTests.cs +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/CollectionMergingGenericTests.cs @@ -39,7 +39,7 @@ namespace Spring.Objects.Factory.Xml /// Rick Evans /// Mark Pollack (.NET) [TestFixture] - [Ignore("SPRNET-1242 Support for collection merging with generic collections")] + [Description("SPRNET-1242 Support for collection merging with generic collections")] public class CollectionMergingGenericTests { private DefaultListableObjectFactory objectFactory;