SPRNET-620 - Support for Collection Merging in Parent/Child Object Definitions

This commit is contained in:
markpollack
2009-07-29 21:10:31 +00:00
parent 269cba2b74
commit efa1d5b7cc
9 changed files with 525 additions and 39 deletions

View File

@@ -199,7 +199,7 @@
<!-- additional collaborators and configuration for this object go here -->
</object>
<!-- more bean definitions for services go here -->
<!-- more object definitions for services go here -->
&lt;/objects&gt;</programlisting>The following example shows the data access
objects <literal>(daos.xml)</literal> configuration file:</para>
@@ -207,14 +207,14 @@
<programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object id="AccountDao" type="Petstore.Dao.HibernateAccountDao, PetStore"&gt;
&lt;!-- additional collaborators and configuration for this bean go here --&gt;
&lt;!-- additional collaborators and configuration for this object go here --&gt;
&lt;/object&gt;
&lt;object id="ItemDao" type="Petstore.Dao.HibernateItemDao, PetStore"&gt;
&lt;!-- additional collaborators and configuration for this bean go here --&gt;
&lt;!-- additional collaborators and configuration for this object go here --&gt;
&lt;/object&gt;
&lt;!-- more bean definitions for data access objects go here --&gt;
&lt;!-- more object definitions for data access objects go here --&gt;
&lt;/objects&gt;</programlisting>
<para>In the preceeding example, the service layer consists of the class
@@ -436,7 +436,7 @@
that these paths are relative, it is better form not to use the slash
at all. The contents of the files being imported, including the top
level <literal>&lt;objects/&gt;</literal> element, must be valid XML
bean definitions according to the Spring Schema.</para>
object definitions according to the Spring Schema.</para>
</sect3>
</sect2>
@@ -453,7 +453,7 @@
<para>The <literal>IApplicationContext</literal> enables you to read
object definitions and access them as follows:</para>
<programlisting language="csharp">// create and configure beans
<programlisting language="csharp">// create and configure objects
IApplicationContext context = new XmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
@@ -1037,8 +1037,8 @@ public class TestGenericObjectFactory
instance after it is constructed. (Factory methods may be considered a
special case of providing constructor arguments for the purposes of this
description). The container injects these dependencies when it creates
the bean. This process is fundamentally the inverse to the case when the
bean itself is controlling the instantiation or location of its
the object. This process is fundamentally the inverse to the case when
the object itself is controlling the instantiation or location of its
dependencies by using direct construction of classes, or the Service
Locator pattern. The inverting of this responsibility is why the name
Inversion of Control (IoC) is used to describe the container's
@@ -1725,9 +1725,9 @@ public class MixedIocObject
no validation is performed on the value that is passed to the
<literal>targetName</literal> property of the client object. Typos
are only discovered (with ost mikely fatal results) when the
'client' object is actually instantiated. If the 'client' bean is a
prototype bean, this typo and the resulting exception may only be
discovered long after the container is deployed.</para>
'client' object is actually instantiated. If the 'client' object is
a prototype object, this typo and the resulting exception may only
be discovered long after the container is deployed.</para>
<para>Additionally, if the reference object is in the same XML unit,
and the object name is the object <emphasis>id</emphasis>, you can
@@ -1770,7 +1770,7 @@ public class MixedIocObject
collaborator is a singleton object it may be initialized already by
the container.) All references are ultimately just a reference to
another object. Scoping and validation depend on whether you specify
the id/name of the object through the <literal>bean</literal>,
the id/name of the object through the <literal>object</literal>,
<literal>local</literal>, or <literal>parent</literal>
attributes.</para>
@@ -1802,16 +1802,16 @@ public class MixedIocObject
the values in the '<literal>name'</literal> attribute of the target
object, and the target object must be in a parent container to the
current one. You us ethis object reference variant mainly when you
have a hierarchy of containers and you want to wrap an existing bean
have a hierarchy of containers and you want to wrap an existing object
in a parent container with some sort of proxy which will have the same
name as the parent bean.<programlisting language="myxml">&lt;!-- in the parent context --&gt;
name as the parent object.<programlisting language="myxml">&lt;!-- in the parent context --&gt;
&lt;object id="AccountService" type="MyApp.SimpleAccountService, MyApp"&gt;
&lt;!-- insert dependencies as required as here --&gt;
&lt;!-- insert dependencies as required as here --&gt;
&lt;/object&gt;</programlisting><programlisting>&lt;!-- in the child (descendant) context --&gt;
&lt;object id="AccountService" &lt;-- notice that the name of this bean is the same as the name of the 'parent' bean
type="Spring.Aop.Framework.ProxyFactoryBean, Spring.Aop"&gt;
&lt;object id="AccountService" &lt;-- notice that the name of this object is the same as the name of the 'parent' object
type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="target"&gt;
&lt;ref parent="AccountService"/&gt; &lt;-- notice how we refer to the parent bean --&gt;
&lt;ref parent="AccountService"/&gt; &lt;-- notice how we refer to the parent object --&gt;
&lt;/property&gt;
&lt;!-- insert other configuration and dependencies as required as here --&gt;
&lt;/object&gt;</programlisting></para>
@@ -1841,7 +1841,7 @@ public class MixedIocObject
<literal>scope</literal> flag. Inner object are
<emphasis>always</emphasis> anonymous and they are
<emphasis>always</emphasis> scoped as prototypes. It is not possible
to inject inner objects into collaborating beans other than into the
to inject inner objects into collaborating objects other than into the
enclosing object.</para>
</sect3>
@@ -2032,6 +2032,85 @@ public class MixedIocObject
&lt;/object&gt;</programlisting></para>
</sect3>
<sect3>
<title>Collection Merging</title>
<para>As of Spring 1.3, the container supports the merging of
collections. An application developer can define a parent-style
<literal>&lt;list/&gt;</literal>,
<literal>&lt;dictionary/&gt;</literal>,
<literal>&lt;set/&gt;</literal> or
<literal>&lt;name-value/&gt;</literal> element, and have child-style
<literal>&lt;list/&gt;</literal>,
<literal>&lt;dictionary/&gt;</literal>,
<literal>&lt;set/&gt;</literal> or
<literal>&lt;name-value/&gt;</literal> elements inherit and override
values from the parent collection. That is, the child collection's
values are the result of merging the elements of the parent and child
collections, with the child's collection elements overriding values
specified in the parent collection.</para>
<para><emphasis>This section on merging discusses the parent-child
object mechanism. Readers unfamiliar with parent and child object
definitions may wish to read the <link
linkend="objects-childobjects">relevant section</link> before
continuing.</emphasis></para>
<para>The following example demonstrates collection merging:</para>
<programlisting> &lt;object id="parent" abstract="true" type="Example.ComplexObject, Examples"&gt;
&lt;property name="AdminEmails"&gt;
&lt;name-values&gt;
&lt;add key="administrator" value="administrator@example.com"/&gt;
&lt;add key="support" value="support@example.com"/&gt;
&lt;/name-values&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object id="child" parent="parent" &gt;
&lt;property name="AdminEmails"&gt;
&lt;!-- the merge is specified on the *child* collection definition --&gt;
&lt;name-values merge="true"&gt;
&lt;add key="sales" value="sales@example.com"/&gt;
&lt;add key="support" value="support@example.co.uk"/&gt;
&lt;/name-values&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>Notice the use of the <literal>merge=true</literal> attribute on
the <literal>&lt;name-values/&gt;</literal> element of the
<literal>AdminEmails</literal> property of the child object
definition. When the child object is resolved and instantiated by the
container, the resulting instance has an
<literal>AdminEmails</literal> Properties collection that contains the
result of the merging of the child's <literal>AdminEmails</literal>
collection with the parent's <literal>AdminEmails</literal>
collection.</para>
<programlisting>administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk</programlisting>
<para>The child Properties collection's value set inherits all
property elements from the parent
<literal>&lt;name-values/&gt;</literal>, and the child's value for the
support value overrides the value in the parent collection. This
merging behavior applies similarly to the
<literal>&lt;list/&gt;</literal>,
<literal>&lt;dictionary/&gt;</literal>, and
<literal>&lt;set/&gt;</literal> collection types. In the specific case
of the <literal>&lt;list/&gt;</literal> element, the semantics
associated with the <literal>IList</literal> collection type, that is,
the notion of an ordered collection of values, is maintained; the
parent's values precede all of the child list's values. In the case of
the <literal>IDictionary</literal>, <literal>ISet</literal>, and
<literal>NameValue</literal> collection types, no ordering exists.
Hence no ordering semantics are in effect for the collection types
that underlie the associated <literal>IDictionary</literal>,
<literal>ISet</literal>, and <literal>NameValueCollection</literal>
implementation types that the container uses internally.</para>
</sect3>
<sect3 xml:id="objects-null-values">
<title>Null and empty values</title>
@@ -2388,11 +2467,11 @@ source.OnClick(); // First eventListener1.HandleEvent is invoked, then eventList
<para>If an object is a dependency of another that usually means that
one object is set as a property of another. Typically you accomplish
this with the <literal>&lt;ref/&gt;</literal> element in XML-based
configuration metadata. However, sometimes dependencies between beans
configuration metadata. However, sometimes dependencies between objects
are less direct; for example, a static initializer in a class needs to
be triggered, such as device driver registration. The
<literal>depends-on</literal> attribute can explicitly force one or more
beans to be initialized before the bean using this element is
objects to be initialized before the object using this element is
initialized. The following example uses the
<literal>depends-on</literal> attribute to express a dependency on a
single object: <programlisting language="myxml">&lt;object id="objectOne" type="Examples.ExampleObject, ExamplesLibrary" depends-on="manager"&gt;
@@ -2452,13 +2531,13 @@ source.OnClick(); // First eventListener1.HandleEvent is invoked, then eventList
<literal>IApplicationContext</literal> is starting up, whereas the
<literal>not.lazy</literal> object is eagerly pre-instantiated.</para>
<para>However, when a lazy-initialized bean is a dependency of a
<para>However, when a lazy-initialized object is a dependency of a
singleton object that is <emphasis>not</emphasis>
<literal>lazy-initialized</literal>, the
<classname>IApplicationContext</classname> creates the lazy-initialized
bean at startup, because it must satisfy the singleton's dependencies.
The lazy-initialized bean is injected into a singleton object elsewhere
that is not lazy-initialized.</para>
object at startup, because it must satisfy the singleton's dependencies.
The lazy-initialized object is injected into a singleton object
elsewhere that is not lazy-initialized.</para>
<para>You can also control lazy-initialization at the container level by
using the <literal>default-lazy-init </literal>attribute on the
@@ -3460,7 +3539,7 @@ public class MyClassFactory
prototype objects, be aware that <emphasis>dependencies are resolved at
instantiation time</emphasis>. Thus if you dependency-inject a
prototype-scoped objects into a singleton-scoped object, a new prototype
bean is instantiated and then dependency-injected into the singleton
object is instantiated and then dependency-injected into the singleton
object. The prototype instance is the sole instance that is ever
supplied to the singleton-scoped object.</para>

View File

@@ -180,17 +180,35 @@ namespace Spring.Objects.Factory.Config
GenericArgumentValues.Add(o);
}
foreach (DictionaryEntry entry in other.IndexedArgumentValues)
{
IndexedArgumentValues.Add(entry.Key, entry.Value);
{
ValueHolder vh = entry.Value as ValueHolder;
if (vh != null)
{
AddOrMergeIndexedArgumentValues( (int) entry.Key, vh.Copy());
}
}
foreach (DictionaryEntry entry in other.NamedArgumentValues)
{
NamedArgumentValues.Add(entry.Key, entry.Value);
}
}
}
}
private void AddOrMergeIndexedArgumentValues(int key, ValueHolder newValue)
{
ValueHolder currentValue = _indexedArgumentValues[key] as ValueHolder;
IMergable mergable = newValue.Value as IMergable;
if (currentValue != null && mergable != null )
{
if (mergable.MergeEnabled)
{
newValue.Value = mergable.Merge(currentValue.Value);
}
}
_indexedArgumentValues[key] = newValue;
}
/// <summary>
/// <summary>
/// Add argument value for the given index in the constructor argument list.
/// </summary>
/// <param name="index">
@@ -599,7 +617,13 @@ namespace Spring.Objects.Factory.Config
#endregion
#region Methods
#region Methods
public ValueHolder Copy()
{
ValueHolder copy = new ValueHolder(this._ctorValue, this.typeName);
return copy;
}
/// <summary>
/// A <see cref="System.String"/> that represents the current
@@ -650,7 +674,7 @@ namespace Spring.Objects.Factory.Config
private object _ctorValue;
private string typeName;
#endregion
#endregion
}
#endregion

View File

@@ -3,7 +3,7 @@
<objects xmlns="http://www.springframework.net">
<object id="parentWithList" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="SomeList">
<property name="SomeGenericStringList">
<list>
<value>Rob Harrop</value>
<value>Rod Johnson</value>
@@ -12,13 +12,22 @@
</object>
<object id="childWithList" parent="parentWithList">
<property name="SomeList">
<property name="SomeGenericStringList">
<list merge="true">
<value>Juergen Hoeller</value>
</list>
</property>
</object>
<object id="childWithListOfRefs" parent="parentWithList">
<property name="SomeList">
<list merge="true">
<object type="Spring.Objects.TestObject, Spring.Core.Tests"/>
</list>
</property>
</object>
<object id="parentWithSet" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="SomeSet">
@@ -36,6 +45,17 @@
</property>
</object>
<object id="childWithSetOfRefs" parent="parentWithSet">
<property name="SomeSet">
<set merge="true">
<object type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="Name" value="Sally"/>
</object>
</set>
</property>
</object>
<object id="parentWithMap" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="SomeMap">
<dictionary>
@@ -55,6 +75,19 @@
</property>
</object>
<object id="childWithMapOfRefs" parent="parentWithMap">
<property name="SomeMap">
<dictionary merge="true">
<entry key="Rob">
<object type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="Name" value="Sally"/>
</object>
</entry>
</dictionary>
</property>
</object>
<object id="parentWithNameValues" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="SomeNameValueCollection">
<name-values>
@@ -72,4 +105,106 @@
</name-values>
</property>
</object>
<object name="parentWithListInConstructor" type="Spring.Objects.TestObject, Spring.Core.Tests">
<constructor-arg index="0">
<list>
<value>Rob Harrop</value>
<value>Rod Johnson</value>
</list>
</constructor-arg>
</object>
<object name="childWithListInConstructor" parent="parentWithListInConstructor">
<constructor-arg index="0">
<list merge="true">
<value>Juergen Hoeller</value>
</list>
</constructor-arg>
</object>
<object name="childWithListOfRefsInConstructor" parent="parentWithListInConstructor">
<constructor-arg index="0">
<list merge="true">
<object type="Spring.Objects.TestObject, Spring.Core.Tests"/>
</list>
</constructor-arg>
</object>
<object name="parentWithSetInConstructor" type="Spring.Objects.TestObject, Spring.Core.Tests">
<constructor-arg index="0">
<set>
<value>Rob Harrop</value>
</set>
</constructor-arg>
</object>
<object name="childWithSetInConstructor" parent="parentWithSetInConstructor">
<constructor-arg index="0">
<set merge="true">
<value>Sally Greenwood</value>
</set>
</constructor-arg>
</object>
<object name="childWithSetOfRefsInConstructor" parent="parentWithSetInConstructor">
<constructor-arg index="0">
<set merge="true">
<object type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="Name" value="Sally"/>
</object>
</set>
</constructor-arg>
</object>
<object name="parentWithMapInConstructor" type="Spring.Objects.TestObject, Spring.Core.Tests">
<constructor-arg index="0">
<dictionary>
<entry key="Rob" value="Sall"/>
<entry key="Juergen" value="Eva"/>
</dictionary>
</constructor-arg>
</object>
<object name="childWithMapInConstructor" parent="parentWithMapInConstructor">
<constructor-arg index="0">
<dictionary merge="true">
<entry key="Rod" value="Kerry"/>
<entry key="Rob" value="Sally"/>
</dictionary>
</constructor-arg>
</object>
<object name="childWithMapOfRefsInConstructor" parent="parentWithMapInConstructor">
<constructor-arg index="0">
<dictionary merge="true">
<entry key="Rob">
<object type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="Name" value="Sally"/>
</object>
</entry>
</dictionary>
</constructor-arg>
</object>
<object name="parentWithPropsInConstructor" type="Spring.Objects.TestObject, Spring.Core.Tests">
<constructor-arg index="0">
<name-values>
<add key="Rob" value="Sall"/>
<add key="Rod" value="Kerry"/>
</name-values>
</constructor-arg>
</object>
<object name="childWithPropsInConstructor" parent="parentWithPropsInConstructor">
<constructor-arg index="0">
<name-values merge="true">
<add key="Juergen" value="Eva"/>
<add key="Rob" value="Sally"/>
</name-values>
</constructor-arg>
</object>
</objects>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.net">
<object id="parentWithList" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="SomeList">
<list element-type="string">
<value>Rob Harrop</value>
<value>Rod Johnson</value>
</list>
</property>
</object>
<object id="childWithList" parent="parentWithList">
<property name="SomeList">
<list merge="true" element-type="string">
<value>Juergen Hoeller</value>
</list>
</property>
</object>
</objects>

View File

@@ -0,0 +1,68 @@
#region License
/*
* Copyright <20> 2002-2009 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
#if !NET_1_1
#region Imports
using System;
using System.Collections;
using System.Collections.Specialized;
using NUnit.Framework;
using Spring.Collections;
using Spring.Objects.Factory.Support;
#endregion
namespace Spring.Objects.Factory.Xml
{
/// <summary>
/// Unit and integration tests for the collection merging support
/// </summary>
/// <author>Rod Johnson</author>
/// <author>Rick Evans</author>
/// <author>Mark Pollack (.NET)</author>
[TestFixture]
[Ignore("SPRNET-1242 Support for collection merging with generic collections")]
public class CollectionMergingGenericTests
{
private DefaultListableObjectFactory objectFactory;
[SetUp]
public void SetUp()
{
this.objectFactory = new DefaultListableObjectFactory();
IObjectDefinitionReader reader = new XmlObjectDefinitionReader(this.objectFactory);
reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("collectionMergingGeneric.xml", GetType()));
}
[Test]
public void MergeList()
{
TestObject to = (TestObject) this.objectFactory.GetObject("childWithList");
System.Collections.Generic.List<string> list = to.SomeGenericStringList;
Assert.That(3, Is.EqualTo(list.Count));
Assert.That("Rob Harrop", Is.EqualTo(list[0]));
Assert.That("Rod Johnson", Is.EqualTo(list[1]));
Assert.That("Juergen Hoeller", Is.EqualTo(list[2]));
}
}
}
#endif

View File

@@ -61,6 +61,17 @@ namespace Spring.Objects.Factory.Xml
Assert.That("Juergen Hoeller", Is.EqualTo(list[2]));
}
[Test]
public void MergListWithInnerObjectAsListElement()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithListOfRefs");
IList list = to.SomeList;
Assert.IsNotNull(list);
Assert.AreEqual(3, list.Count);
Assert.IsNotNull(list[2]);
Assert.That(list[2], Is.InstanceOf(typeof (TestObject)));
}
[Test]
public void MergeSet()
{
@@ -71,6 +82,22 @@ namespace Spring.Objects.Factory.Xml
Assert.IsTrue(set.Contains("Sally Greenwood"));
}
[Test]
public void MergeSetWithInnerObjectAsSetElement()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithSetOfRefs");
ISet set = to.SomeSet;
Assert.IsNotNull(set);
Assert.AreEqual(2, set.Count);
IEnumerator enumerator = set.GetEnumerator();
enumerator.MoveNext();
enumerator.MoveNext();
object o = enumerator.Current;
Assert.IsNotNull(o);
Assert.That(o, Is.InstanceOf(typeof(TestObject)));
Assert.AreEqual("Sally", ((TestObject) o).Name);
}
[Test]
public void MergeDictionary()
{
@@ -82,6 +109,18 @@ namespace Spring.Objects.Factory.Xml
Assert.AreEqual("Eva", map["Juergen"]);
}
[Test]
public void MergeMapWithInnerObjectAsMapEntryValue()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithMapOfRefs");
IDictionary map = to.SomeMap;
Assert.NotNull(map);
Assert.AreEqual(2, map.Count);
Assert.NotNull(map["Rob"]);
Assert.That(map["Rob"], Is.InstanceOf(typeof(TestObject)));
Assert.AreEqual("Sally", ((TestObject) map["Rob"]).Name);
}
[Test]
public void MergeNameValueCollection()
{
@@ -92,5 +131,89 @@ namespace Spring.Objects.Factory.Xml
Assert.AreEqual("Kerry", map["Rod"]);
Assert.AreEqual("Eva", map["Juergen"]);
}
[Test]
public void MergeListInConstructor()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithListInConstructor");
IList list = to.SomeList;
Assert.AreEqual(3, list.Count);
Assert.AreEqual("Rob Harrop", list[0]);
Assert.AreEqual("Rod Johnson", list[1]);
Assert.AreEqual("Juergen Hoeller", list[2]);
}
[Test]
public void MergeListWithInnerObjectAsListElementInConstructor()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithListOfRefsInConstructor");
IList list = to.SomeList;
Assert.IsNotNull(list);
Assert.AreEqual(3, list.Count);
Assert.IsNotNull(list[2]);
Assert.That(list[2], Is.InstanceOf(typeof(TestObject)));
}
[Test]
public void MergeSetInConstructor()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithSetInConstructor");
ISet set = to.SomeSet;
Assert.AreEqual(2, set.Count);
Assert.IsTrue(set.Contains("Rob Harrop"));
Assert.IsTrue(set.Contains("Sally Greenwood"));
}
[Test]
public void MergeSetWithInnerObjectAsSetElementInConstructor()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithSetOfRefsInConstructor");
ISet set = to.SomeSet;
Assert.IsNotNull(set);
Assert.AreEqual(2, set.Count);
IEnumerator enumerator = set.GetEnumerator();
enumerator.MoveNext();
enumerator.MoveNext();
object o = enumerator.Current;
Assert.IsNotNull(o);
Assert.That(o, Is.InstanceOf(typeof(TestObject)));
Assert.AreEqual("Sally", ((TestObject)o).Name);
}
[Test]
public void MergeMapInConstructor()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithMapInConstructor");
IDictionary map = to.SomeMap;
Assert.AreEqual(3, map.Count);
Assert.AreEqual("Sally", map["Rob"]);
Assert.AreEqual("Kerry", map["Rod"]);
Assert.AreEqual("Eva", map["Juergen"]);
}
[Test]
public void MergeMapWithInnerObjectgAsMapEntryValueInConstructor()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithMapOfRefsInConstructor");
IDictionary map = to.SomeMap;
Assert.IsNotNull(map);
Assert.AreEqual(2, map.Count);
Assert.IsNotNull(map["Rob"]);
Assert.That(map["Rob"], Is.InstanceOf(typeof(TestObject)));
Assert.AreEqual("Sally", ((TestObject) map["Rob"]).Name);
}
[Test]
public void MergeNameValueCollectionInConstructor()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithPropsInConstructor");
NameValueCollection props = to.SomeNameValueCollection;
Assert.AreEqual(3, props.Count);
Assert.AreEqual("Sally", props["Rob"]);
Assert.AreEqual("Kerry", props["Rod"]);
Assert.AreEqual("Eva", props["Juergen"]);
}
}
}

View File

@@ -22,7 +22,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
#if !NET_1_1
using System.Collections.Specialized;
#endif
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
@@ -242,7 +245,7 @@ namespace Spring.Objects
get { return computers; }
}
public virtual Set SomeSet
public virtual ISet SomeSet
{
get { return someSet; }
set { this.someSet = value; }
@@ -266,6 +269,14 @@ namespace Spring.Objects
set { this.someList = value;}
}
#if !NET_1_1
public virtual List<string> SomeGenericStringList
{
get { return someGenericStringList; }
set { this.someGenericStringList = value; }
}
#endif
public virtual NameValueCollection SomeNameValueCollection
{
get { return someNameValueCollection; }
@@ -370,8 +381,8 @@ namespace Spring.Objects
};
private Type classProperty;
private Set computers = new HybridSet();
private Set someSet = new HybridSet();
private ISet computers = new HybridSet();
private ISet someSet = new HybridSet();
private IDictionary someMap = new Hashtable();
private IList someList = new ArrayList();
private DateTime date = DateTime.Now;
@@ -397,6 +408,7 @@ namespace Spring.Objects
private IDictionary sharedState;
private NameValueCollection someNameValueCollection;
private List<string> someGenericStringList;
#endregion
@@ -432,6 +444,26 @@ namespace Spring.Objects
this.spouse = spouse;
}
public TestObject(IList someList)
{
this.someList = someList;
}
public TestObject(ISet someSet)
{
this.someSet = someSet;
}
public TestObject(IDictionary someMap)
{
this.someMap = someMap;
}
public TestObject(NameValueCollection someProps)
{
this.someNameValueCollection = someProps;
}
#endregion
#region Static Methods

View File

@@ -338,6 +338,7 @@
<Compile Include="Objects\Factory\Support\ObjectDefinitionBuilderTests.cs" />
<Compile Include="Objects\Factory\Xml\ArrayCtorDependencyObject.cs" />
<Compile Include="Objects\Factory\Xml\CollectionMergingTests.cs" />
<Compile Include="Objects\Factory\Xml\CollectionMergingGenericTests.cs" />
<Compile Include="Objects\Factory\Xml\LocaleTests.cs" />
<Compile Include="Objects\Factory\Xml\NamespaceParserRegistryTests.cs" />
<Compile Include="Objects\Factory\Xml\ObjectFactorySectionHandlerTests.cs" />
@@ -811,6 +812,7 @@
<Content Include="Data\Spring\Objects\Factory\Xml\constructor-arg.xml" />
<Content Include="Data\Spring\Objects\Factory\Xml\array-autowire.xml" />
<Content Include="Data\Spring\Objects\Factory\Xml\collectionMerging.xml" />
<Content Include="Data\Spring\Objects\Factory\Xml\collectionMergingGeneric.xml" />
<Content Include="Data\Spring\Objects\Factory\Xml\objectNameGeneration.xml" />
<Content Include="Data\Spring\Objects\Factory\Xml\simple-constructor-arg.xml" />
<Content Include="Data\Spring\Objects\Factory\Xml\expressions.xml" />

View File

@@ -202,7 +202,7 @@
<File
RelPath = "Data\Spring\Objects\Factory\Support\TestForm.aspx.cs"
DependentUpon = "TestForm.aspx"
SubType = "ASPXCodeBehind"
SubType = "Code"
BuildAction = "Compile"
/>
<File
@@ -321,7 +321,7 @@
/>
<File
RelPath = "Web\Support\ControlInterceptionTests.cs"
SubType = "ASPXCodeBehind"
SubType = "Code"
BuildAction = "Compile"
/>
<File