From 43516e6dbefc7049b26f35ec04b2c9a0bd65c051 Mon Sep 17 00:00:00 2001 From: Steve Bohlen Date: Tue, 15 Nov 2011 11:11:09 -0500 Subject: [PATCH] merge mods to pull 6 for SPRNET-1470, SPRNET-1471 --- .../TypeConversion/TypeConversionUtils.cs | 59 ++++++-- .../Xml/collectionConversionGeneric.xml | 33 +++++ .../Xml/CollectionConversionGenericTests.cs | 131 ++++++++++++++++++ .../Spring.Core.Tests/Objects/TestObject.cs | 21 +++ .../Spring.Core.Tests.2008.csproj | 2 + .../Spring.Core.Tests.2010.csproj | 2 + 6 files changed, 240 insertions(+), 8 deletions(-) create mode 100644 test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collectionConversionGeneric.xml create mode 100644 test/Spring/Spring.Core.Tests/Objects/Factory/Xml/CollectionConversionGenericTests.cs diff --git a/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs b/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs index fd38c7a8..bac5e3cc 100644 --- a/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs +++ b/src/Spring/Spring.Core/Core/TypeConversion/TypeConversionUtils.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; using System.ComponentModel; using Spring.Util; +using System.Reflection; #endregion @@ -116,6 +117,45 @@ namespace Spring.Core.TypeConversion return ToTypedCollectionWithTypeConversion(typeof(List<>), componentType, elements, propertyName); } } + + // if required type is some IDictionary, convert all the elements + if (requiredType != null && requiredType.IsGenericType && TypeImplementsGenericInterface(requiredType, typeof(IDictionary<,>))) + { + Type[] typeParameters = requiredType.GetGenericArguments(); + Type keyType = typeParameters[0]; + Type valueType = typeParameters[1]; + if (newValue is IDictionary) + { + IDictionary elements = (IDictionary)newValue; + Type targetCollectionType = typeof(Dictionary<,>); + Type collectionType = targetCollectionType.MakeGenericType(new Type[] { keyType, valueType }); + object typedCollection = Activator.CreateInstance(collectionType); + + MethodInfo addMethod = collectionType.GetMethod("Add", new Type[] { keyType, valueType }); + int i = 0; + foreach (DictionaryEntry entry in elements) + { + string propertyExpr = BuildIndexedPropertyName(propertyName, i); + object key = ConvertValueIfNecessary(keyType, entry.Key, propertyExpr + ".Key"); + object value = ConvertValueIfNecessary(valueType, entry.Value, propertyExpr + ".Value"); + addMethod.Invoke(typedCollection, new object[] { key, value }); + i++; + } + return typedCollection; + } + } + + // if required type is some IEnumerable, convert all the elements + if (requiredType != null && requiredType.IsGenericType && TypeImplementsGenericInterface(requiredType, typeof(IEnumerable<>))) + { + // 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 @@ -289,18 +329,16 @@ namespace Spring.Core.TypeConversion throw new ArgumentException("matchingInterface Type must be an Interface Type", "matchingInterface"); } - bool match = false; + if (candidateType.IsInterface && IsMatchingGenericInterface(candidateType, matchingInterface)) + { + return true; + } + bool match = false; Type[] implementedInterfaces = candidateType.GetInterfaces(); foreach (Type interfaceType in implementedInterfaces) { - if (false == interfaceType.IsGenericType) - { - continue; - } - - Type genericType = interfaceType.GetGenericTypeDefinition(); - if (genericType == matchingInterface) + if (IsMatchingGenericInterface(interfaceType, matchingInterface)) { match = true; break; @@ -309,6 +347,11 @@ namespace Spring.Core.TypeConversion return match; } + + private static bool IsMatchingGenericInterface(Type candidateInterfaceType, Type matchingGenericInterface) + { + return candidateInterfaceType.IsGenericType && candidateInterfaceType.GetGenericTypeDefinition() == matchingGenericInterface; + } #endif } } \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collectionConversionGeneric.xml b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collectionConversionGeneric.xml new file mode 100644 index 00000000..02fd9c04 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Data/Spring/Objects/Factory/Xml/collectionConversionGeneric.xml @@ -0,0 +1,33 @@ + + + + + + + + 123 + 234 + 345 + + + + + + + + + + + + + + + + + + 123 + + + + + diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/CollectionConversionGenericTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/CollectionConversionGenericTests.cs new file mode 100644 index 00000000..503e81ca --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/CollectionConversionGenericTests.cs @@ -0,0 +1,131 @@ +#region License + +/* + * Copyright © 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Objects.Factory.Support; +using System.Collections; +using Spring.Core.TypeConversion; +using System.Collections.Generic; +using System.Collections.Specialized; + +#endregion + +namespace Spring.Objects.Factory.Xml +{ + /// + /// Unit and integration tests for collection conversion support + /// + /// Choy Rim + [TestFixture] + [Description("SPRNET-1470 Setting property of type IList using without the @element-type specified fails.")] + public class CollectionConversionGenericTests + { + private DefaultListableObjectFactory objectFactory; + + [SetUp] + public void SetUp() + { + this.objectFactory = new DefaultListableObjectFactory(); + IObjectDefinitionReader reader = new XmlObjectDefinitionReader(this.objectFactory); + reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("collectionConversionGeneric.xml", GetType())); + } + + [Test] + public void ShouldConvertListToGenericIList() + { + TestObject to = (TestObject)this.objectFactory.GetObject("HasGenericIListProperty"); + IList list = to.SomeGenericIListInt32; + Assert.That(list.Count, Is.EqualTo(3)); + Assert.That(list[0], Is.EqualTo(123)); + Assert.That(list[1], Is.EqualTo(234)); + Assert.That(list[2], Is.EqualTo(345)); + } + + [Test] + public void ShouldConvertDictionaryToGenericIDictionary() + { + TestObject to = (TestObject)this.objectFactory.GetObject("HasGenericIDictionaryProperty"); + IDictionary dict = to.SomeGenericIDictionaryStringInt32; + Assert.That(dict.Count, Is.EqualTo(3)); + Assert.That(dict["aaa"], Is.EqualTo(111)); + Assert.That(dict["bbb"], Is.EqualTo(222)); + Assert.That(dict["ccc"], Is.EqualTo(333)); + } + + [Test] + public void ShouldConvertListToGenericIEnumerable() + { + TestObject to = (TestObject)this.objectFactory.GetObject("HasGenericIEnumerableProperty"); + IEnumerable enumerable = to.SomeGenericIEnumerableInt32; + int enumerableLength = 0; + int first = 0; + + foreach (int i in enumerable) + { + enumerableLength += 1; + if (enumerableLength == 1) + { + first = i; + } + } + + Assert.That(enumerableLength, Is.EqualTo(1)); + Assert.That(first, Is.EqualTo(123)); + } + + [Test] + public void ConvertArrayListToGenericIList() + { + ArrayList xs = new ArrayList(); + xs.Add("Mark Pollack"); + object ys = TypeConversionUtils.ConvertValueIfNecessary(typeof(IList), xs, "ignored"); + Assert.That(ys as IList, Is.Not.Null); + IList zs = (IList)ys; + Assert.That(zs[0], Is.EqualTo("Mark Pollack")); + } + + [Test] + public void ConvertHybridDictionaryToGenericIDictionary() + { + HybridDictionary xs = new HybridDictionary(); + xs.Add("first", 1); + object ys = TypeConversionUtils.ConvertValueIfNecessary(typeof(IDictionary), xs, "ignored"); + Assert.That(ys as IDictionary, Is.Not.Null); + IDictionary zs = (IDictionary)ys; + Assert.That(zs["first"], Is.EqualTo(1)); + } + + [Test] + public void ConvertArrayListToGenericIEnumerable() + { + ArrayList xs = new ArrayList(); + xs.Add("Mark Pollack"); + object ys = TypeConversionUtils.ConvertValueIfNecessary(typeof(IEnumerable), xs, "ignored"); + Assert.That(ys as IEnumerable, Is.Not.Null); + IEnumerable zs = (IEnumerable)ys; + IEnumerator zse = zs.GetEnumerator(); + Assert.That(zse.MoveNext(), Is.True); + Assert.That(zse.Current, Is.EqualTo("Mark Pollack")); + } + + } +} diff --git a/test/Spring/Spring.Core.Tests/Objects/TestObject.cs b/test/Spring/Spring.Core.Tests/Objects/TestObject.cs index 1f9ed460..b34b43c1 100644 --- a/test/Spring/Spring.Core.Tests/Objects/TestObject.cs +++ b/test/Spring/Spring.Core.Tests/Objects/TestObject.cs @@ -273,6 +273,27 @@ namespace Spring.Objects set { this.someGenericStringList = value; } } + private IList someGenericIListInt32; + public virtual IList SomeGenericIListInt32 + { + get { return someGenericIListInt32; } + set { someGenericIListInt32 = value; } + } + + private IDictionary someGenericIDictionaryStringInt32; + public virtual IDictionary SomeGenericIDictionaryStringInt32 + { + get { return someGenericIDictionaryStringInt32; } + set { someGenericIDictionaryStringInt32 = value; } + } + + private IEnumerable someGenericIEnumerableInt32; + public virtual IEnumerable SomeGenericIEnumerableInt32 + { + get { return someGenericIEnumerableInt32; } + set { someGenericIEnumerableInt32 = value; } + } + public virtual NameValueCollection SomeNameValueCollection { get { return someNameValueCollection; } diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2008.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2008.csproj index 1a9aadd6..ad8eee70 100644 --- a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2008.csproj +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2008.csproj @@ -343,6 +343,7 @@ + @@ -818,6 +819,7 @@ + diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2010.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2010.csproj index c3e07eac..0529c137 100644 --- a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2010.csproj +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2010.csproj @@ -352,6 +352,7 @@ + @@ -827,6 +828,7 @@ +