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

This commit is contained in:
markpollack
2009-07-29 05:34:17 +00:00
parent edf9b867a7
commit f92a021425
25 changed files with 2364 additions and 710 deletions

View File

@@ -57,9 +57,18 @@ namespace Spring.Collections
public HybridSet()
{
InternalDictionary = new HybridDictionary();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="HybridSet"/> class with a given capacity
/// </summary>
/// <param name="size">The size.</param>
public HybridSet(int size)
{
InternalDictionary = new HybridDictionary(size);
}
/// <summary>
/// <summary>
/// Creates a new set instance based on either a list or a hash table,
/// depending on which will be more efficient based on the data-set
/// size, and initializes it based on a collection of elements.

View File

@@ -1,171 +1,238 @@
#region License
/*
* Copyright <20> 2002-2005 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 System;
using System.Collections;
#if NET_2_0
using System.Collections.Generic;
#endif
using System.Collections.Specialized;
using System.Globalization;
using Spring.Core;
using Spring.Core.TypeConversion;
using Spring.Core.TypeResolution;
#region License
/*
* Copyright <20> 2002-2005 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 System;
using System.Collections;
#if NET_2_0
using System.Collections.Generic;
#endif
using System.Collections.Specialized;
using System.Globalization;
using Spring.Core;
using Spring.Core.TypeConversion;
using Spring.Core.TypeResolution;
using Spring.Objects.Factory.Config;
using Spring.Util;
#endregion
namespace Spring.Objects.Factory.Support
{
/// <summary>
/// Tag subclass used to hold a dictionary of managed elements.
/// </summary>
/// <author>Juergen Hoeller</author>
/// <author>Rick Evans (.NET)</author>
[Serializable]
public class ManagedDictionary : Hashtable, IManagedCollection
{
private string keyTypeName;
private string valueTypeName;
/// <summary>
/// Gets or sets the unresolved name for the <see cref="System.Type"/>
/// of the keys of this managed dictionary.
/// </summary>
/// <value>The unresolved name for the type of the keys of this managed dictionary.</value>
public string KeyTypeName
{
get { return this.keyTypeName; }
set { this.keyTypeName = value; }
}
/// <summary>
/// Gets or sets the unresolved name for the <see cref="System.Type"/>
/// of the values of this managed dictionary.
/// </summary>
/// <value>The unresolved name for the type of the values of this managed dictionary.</value>
public string ValueTypeName
{
get { return this.valueTypeName; }
set { this.valueTypeName = value; }
}
/// <summary>
/// Resolves this managed collection at runtime.
/// </summary>
/// <param name="objectName">
/// The name of the top level object that is having the value of one of it's
/// collection properties resolved.
/// </param>
/// <param name="definition">
/// The definition of the named top level object.
/// </param>
/// <param name="propertyName">
/// The name of the property the value of which is being resolved.
/// </param>
/// <param name="resolver">
/// The callback that will actually do the donkey work of resolving
/// this managed collection.
/// </param>
/// <returns>A fully resolved collection.</returns>
public ICollection Resolve(
string objectName, IObjectDefinition definition,
string propertyName, ManagedCollectionElementResolver resolver)
{
IDictionary dictionary;
Type keyType = null;
if (StringUtils.HasText(this.keyTypeName))
{
keyType = TypeResolutionUtils.ResolveType(this.keyTypeName);
}
Type valueType = null;
if (StringUtils.HasText(this.valueTypeName))
{
valueType = TypeResolutionUtils.ResolveType(this.valueTypeName);
}
#if NET_2_0
if ((keyType == null) && (valueType == null))
{
dictionary = new HybridDictionary();
}
else
{
Type type = typeof(Dictionary<,>);
Type[] genericArgs = new Type[2] {
(keyType == null) ? typeof(object) : keyType,
(valueType == null) ? typeof(object) : valueType };
type = type.MakeGenericType(genericArgs);
dictionary = (IDictionary)ObjectUtils.InstantiateType(type);
}
#else
dictionary = new HybridDictionary();
#endif
foreach (object key in this.Keys)
{
string elementName = string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", propertyName, key);
object resolvedKey = resolver(objectName, definition, elementName, key);
object resolvedValue = resolver(objectName, definition, elementName, this[key]);
if (keyType != null)
{
try
{
resolvedKey = TypeConversionUtils.ConvertValueIfNecessary(keyType, resolvedKey, propertyName);
}
catch (TypeMismatchException)
{
throw new TypeMismatchException(
String.Format(
"Unable to convert managed dictionary key '{0}' from [{1}] into [{2}] during initialization"
+ " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?",
resolvedKey, resolvedKey.GetType(), keyType, propertyName, objectName));
}
}
if (valueType != null)
{
try
{
resolvedValue = TypeConversionUtils.ConvertValueIfNecessary(valueType, resolvedValue, propertyName + "[" + resolvedKey + "]");
}
catch (TypeMismatchException)
{
throw new TypeMismatchException(
String.Format(
"Unable to convert managed dictionary value '{0}' from [{1}] into [{2}] during initialization"
+ " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?",
resolvedValue, resolvedValue.GetType(), valueType, propertyName, objectName));
}
}
dictionary.Add(resolvedKey, resolvedValue);
}
return dictionary;
}
}
using Spring.Util;
#endregion
namespace Spring.Objects.Factory.Support
{
/// <summary>
/// Tag subclass used to hold a dictionary of managed elements.
/// </summary>
/// <author>Juergen Hoeller</author>
/// <author>Rick Evans (.NET)</author>
[Serializable]
public class ManagedDictionary : Hashtable, IManagedCollection, IMergable
{
private string keyTypeName;
private string valueTypeName;
private bool mergeEnabled;
/// <summary>
/// Initializes a new, empty instance of the <see cref="T:System.Collections.Hashtable"/> class using the default initial capacity, load factor, hash code provider, and comparer.
/// </summary>
public ManagedDictionary()
{
}
/// <summary>
/// Initializes a new, empty instance of the <see cref="T:System.Collections.Hashtable"/> class using the specified initial capacity, and the default load factor, hash code provider, and comparer.
/// </summary>
/// <param name="capacity">The approximate number of elements that the <see cref="T:System.Collections.Hashtable"/> object can initially contain. </param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="capacity"/> is less than zero. </exception>
public ManagedDictionary(int capacity) : base(capacity)
{
}
/// <summary>
/// Gets or sets the unresolved name for the <see cref="System.Type"/>
/// of the keys of this managed dictionary.
/// </summary>
/// <value>The unresolved name for the type of the keys of this managed dictionary.</value>
public string KeyTypeName
{
get { return this.keyTypeName; }
set { this.keyTypeName = value; }
}
/// <summary>
/// Gets or sets the unresolved name for the <see cref="System.Type"/>
/// of the values of this managed dictionary.
/// </summary>
/// <value>The unresolved name for the type of the values of this managed dictionary.</value>
public string ValueTypeName
{
get { return this.valueTypeName; }
set { this.valueTypeName = value; }
}
/// <summary>
/// Resolves this managed collection at runtime.
/// </summary>
/// <param name="objectName">
/// The name of the top level object that is having the value of one of it's
/// collection properties resolved.
/// </param>
/// <param name="definition">
/// The definition of the named top level object.
/// </param>
/// <param name="propertyName">
/// The name of the property the value of which is being resolved.
/// </param>
/// <param name="resolver">
/// The callback that will actually do the donkey work of resolving
/// this managed collection.
/// </param>
/// <returns>A fully resolved collection.</returns>
public ICollection Resolve(
string objectName, IObjectDefinition definition,
string propertyName, ManagedCollectionElementResolver resolver)
{
IDictionary dictionary;
Type keyType = null;
if (StringUtils.HasText(this.keyTypeName))
{
keyType = TypeResolutionUtils.ResolveType(this.keyTypeName);
}
Type valueType = null;
if (StringUtils.HasText(this.valueTypeName))
{
valueType = TypeResolutionUtils.ResolveType(this.valueTypeName);
}
#if NET_2_0
if ((keyType == null) && (valueType == null))
{
dictionary = new HybridDictionary();
}
else
{
Type type = typeof(Dictionary<,>);
Type[] genericArgs = new Type[2] {
(keyType == null) ? typeof(object) : keyType,
(valueType == null) ? typeof(object) : valueType };
type = type.MakeGenericType(genericArgs);
dictionary = (IDictionary)ObjectUtils.InstantiateType(type);
}
#else
dictionary = new HybridDictionary();
#endif
foreach (object key in this.Keys)
{
string elementName = string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", propertyName, key);
object resolvedKey = resolver(objectName, definition, elementName, key);
object resolvedValue = resolver(objectName, definition, elementName, this[key]);
if (keyType != null)
{
try
{
resolvedKey = TypeConversionUtils.ConvertValueIfNecessary(keyType, resolvedKey, propertyName);
}
catch (TypeMismatchException)
{
throw new TypeMismatchException(
String.Format(
"Unable to convert managed dictionary key '{0}' from [{1}] into [{2}] during initialization"
+ " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?",
resolvedKey, resolvedKey.GetType(), keyType, propertyName, objectName));
}
}
if (valueType != null)
{
try
{
resolvedValue = TypeConversionUtils.ConvertValueIfNecessary(valueType, resolvedValue, propertyName + "[" + resolvedKey + "]");
}
catch (TypeMismatchException)
{
throw new TypeMismatchException(
String.Format(
"Unable to convert managed dictionary value '{0}' from [{1}] into [{2}] during initialization"
+ " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?",
resolvedValue, resolvedValue.GetType(), valueType, propertyName, objectName));
}
}
dictionary.Add(resolvedKey, resolvedValue);
}
return dictionary;
}
/// <summary>
/// Gets a value indicating whether this instance is merge enabled for this instance
/// </summary>
/// <value>
/// <c>true</c> if this instance is merge enabled; otherwise, <c>false</c>.
/// </value>
public bool MergeEnabled
{
get { return this.mergeEnabled; }
set { this.mergeEnabled = value; }
}
/// <summary>
/// Merges the current value set with that of the supplied object.
/// </summary>
/// <remarks>The supplied object is considered the parent, and values in the
/// callee's value set must override those of the supplied object.
/// </remarks>
/// <param name="parent">The parent object to merge with</param>
/// <returns>The result of the merge operation</returns>
/// <exception cref="ArgumentNullException">If the supplied parent is <code>null</code></exception>
/// <exception cref="InvalidOperationException">If merging is not enabled for this instance,
/// (i.e. <code>MergeEnabled</code> equals <code>false</code>.</exception>
public object Merge(object parent)
{
if (!this.mergeEnabled)
{
throw new InvalidOperationException(
"Not allowed to merge when the 'MergeEnabled' property is set to 'false'");
}
if (parent == null)
{
return this;
}
IDictionary pDict = parent as IDictionary;
if (pDict == null)
{
throw new InvalidOperationException("Cannot merge with object of type [" + parent.GetType() + "]");
}
IDictionary merged = new ManagedDictionary();
foreach (DictionaryEntry dictionaryEntry in pDict)
{
merged[dictionaryEntry.Key] = dictionaryEntry.Value;
}
foreach (DictionaryEntry entry in this)
{
merged[entry.Key] = entry.Value;
}
return merged;
}
}
}

View File

@@ -1,134 +1,203 @@
#region License
/*
* Copyright <20> 2002-2005 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 System;
using System.Collections;
#if NET_2_0
using System.Collections.Generic;
#endif
using System.Globalization;
using Spring.Core;
using Spring.Core.TypeConversion;
using Spring.Core.TypeResolution;
#region License
/*
* Copyright <20> 2002-2005 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 System;
using System.Collections;
#if NET_2_0
using System.Collections.Generic;
#endif
using System.Globalization;
using Spring.Core;
using Spring.Core.TypeConversion;
using Spring.Core.TypeResolution;
using Spring.Objects.Factory.Config;
using Spring.Util;
#endregion
namespace Spring.Objects.Factory.Support
{
/// <summary>
/// Tag subclass used to hold a list of managed elements.
/// </summary>
/// <author>Rod Johnson</author>
/// <author>Rick Evans (.NET)</author>
[Serializable]
public class ManagedList : ArrayList, IManagedCollection
{
private string elementTypeName;
/// <summary>
/// Gets or sets the unresolved name for the <see cref="System.Type"/>
/// of the elements of this managed list.
/// </summary>
/// <value>The unresolved name for the type of the elements of this managed list.</value>
public string ElementTypeName
{
get { return this.elementTypeName; }
set { this.elementTypeName = value; }
}
/// <summary>
/// Resolves this managed collection at runtime.
/// </summary>
/// <param name="objectName">
/// The name of the top level object that is having the value of one of it's
/// collection properties resolved.
/// </param>
/// <param name="definition">
/// The definition of the named top level object.
/// </param>
/// <param name="propertyName">
/// The name of the property the value of which is being resolved.
/// </param>
/// <param name="resolver">
/// The callback that will actually do the donkey work of resolving
/// this managed collection.
/// </param>
/// <returns>A fully resolved collection.</returns>
public ICollection Resolve(string objectName, IObjectDefinition definition, string propertyName, ManagedCollectionElementResolver resolver)
{
IList list;
Type elementType = null;
if (StringUtils.HasText(this.elementTypeName))
{
elementType = TypeResolutionUtils.ResolveType(this.elementTypeName);
}
#if NET_2_0
if (elementType == null)
{
list = new ArrayList();
}
else
{
// CLOVER:ON
Type type = typeof(List<>);
Type[] genericArgs = new Type[1] { elementType };
type = type.MakeGenericType(genericArgs);
list = (IList)ObjectUtils.InstantiateType(type);
// CLOVER:OFF
}
#else
list = new ArrayList();
#endif
for (int i = 0; i < Count; ++i)
{
object element = this[i];
object resolvedElement =
resolver(objectName, definition, String.Format(CultureInfo.InvariantCulture, "{0}[{1}]", propertyName, i), element);
if (elementType != null)
{
try
{
resolvedElement = TypeConversionUtils.ConvertValueIfNecessary(elementType, resolvedElement, propertyName + "[" + i + "]");
}
catch (TypeMismatchException)
{
throw new TypeMismatchException(
String.Format(
"Unable to convert managed list element '{0}' from [{1}] into [{2}] during initialization"
+ " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?",
resolvedElement, resolvedElement.GetType(), elementType, propertyName, objectName));
}
}
list.Add(resolvedElement);
}
return list;
}
}
using Spring.Util;
#endregion
namespace Spring.Objects.Factory.Support
{
/// <summary>
/// Tag subclass used to hold a list of managed elements.
/// </summary>
/// <author>Rod Johnson</author>
/// <author>Rick Evans (.NET)</author>
[Serializable]
public class ManagedList : ArrayList, IManagedCollection, IMergable
{
private string elementTypeName;
private bool mergeEnabled;
/// <summary>
/// Initializes a new instance of the ManagedList class that is empty and has the default initial capacity.
/// </summary>
public ManagedList()
{
}
/// <summary>
/// Initializes a new instance of the ManagedList class that is empty and has the specified initial capacity.
/// </summary>
/// <param name="capacity">The number of elements that the new list can initially store. </param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="capacity"/> is less than zero. </exception>
public ManagedList(int capacity)
: base(capacity)
{
}
/// <summary>
/// Gets or sets the unresolved name for the <see cref="System.Type"/>
/// of the elements of this managed list.
/// </summary>
/// <value>The unresolved name for the type of the elements of this managed list.</value>
public string ElementTypeName
{
get { return this.elementTypeName; }
set { this.elementTypeName = value; }
}
/// <summary>
/// Resolves this managed collection at runtime.
/// </summary>
/// <param name="objectName">
/// The name of the top level object that is having the value of one of it's
/// collection properties resolved.
/// </param>
/// <param name="definition">
/// The definition of the named top level object.
/// </param>
/// <param name="propertyName">
/// The name of the property the value of which is being resolved.
/// </param>
/// <param name="resolver">
/// The callback that will actually do the donkey work of resolving
/// this managed collection.
/// </param>
/// <returns>A fully resolved collection.</returns>
public ICollection Resolve(string objectName, IObjectDefinition definition, string propertyName, ManagedCollectionElementResolver resolver)
{
IList list;
Type elementType = null;
if (StringUtils.HasText(this.elementTypeName))
{
elementType = TypeResolutionUtils.ResolveType(this.elementTypeName);
}
#if NET_2_0
if (elementType == null)
{
list = new ArrayList();
}
else
{
// CLOVER:ON
Type type = typeof(List<>);
Type[] genericArgs = new Type[1] { elementType };
type = type.MakeGenericType(genericArgs);
list = (IList)ObjectUtils.InstantiateType(type);
// CLOVER:OFF
}
#else
list = new ArrayList();
#endif
for (int i = 0; i < Count; ++i)
{
object element = this[i];
object resolvedElement =
resolver(objectName, definition, String.Format(CultureInfo.InvariantCulture, "{0}[{1}]", propertyName, i), element);
if (elementType != null)
{
try
{
resolvedElement = TypeConversionUtils.ConvertValueIfNecessary(elementType, resolvedElement, propertyName + "[" + i + "]");
}
catch (TypeMismatchException)
{
throw new TypeMismatchException(
String.Format(
"Unable to convert managed list element '{0}' from [{1}] into [{2}] during initialization"
+ " of property '{3}' for object '{4}'. Do you have an appropriate type converter registered?",
resolvedElement, resolvedElement.GetType(), elementType, propertyName, objectName));
}
}
list.Add(resolvedElement);
}
return list;
}
/// <summary>
/// Gets a value indicating whether this instance is merge enabled for this instance
/// </summary>
/// <value>
/// <c>true</c> if this instance is merge enabled; otherwise, <c>false</c>.
/// </value>
public bool MergeEnabled
{
get { return this.mergeEnabled; }
set { this.mergeEnabled = value; }
}
/// <summary>
/// Merges the current value set with that of the supplied object.
/// </summary>
/// <remarks>The supplied object is considered the parent, and values in the
/// callee's value set must override those of the supplied object.
/// </remarks>
/// <param name="parent">The parent object to merge with</param>
/// <returns>The result of the merge operation</returns>
/// <exception cref="ArgumentNullException">If the supplied parent is <code>null</code></exception>
/// <exception cref="InvalidOperationException">If merging is not enabled for this instance,
/// (i.e. <code>MergeEnabled</code> equals <code>false</code>.</exception>
public object Merge(object parent)
{
if (!this.mergeEnabled)
{
throw new InvalidOperationException(
"Not allowed to merge when the 'MergeEnabled' property is set to 'false'");
}
if (parent == null)
{
return this;
}
IList plist = parent as IList;
if (plist == null)
{
throw new InvalidOperationException("Cannot merge with object of type [" + parent.GetType() + "]");
}
IList merged = new ManagedList();
foreach (object element in plist)
{
merged.Add(element);
}
foreach (object o in this)
{
merged.Add(o);
}
return merged;
}
}
}

View File

@@ -0,0 +1,101 @@
#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
using System;
using System.Collections;
using System.Collections.Specialized;
namespace Spring.Objects.Factory.Support
{
/// <summary>
/// Tag class which represent a Spring-managed <see cref="NameValueCollection"/> instance that
/// supports merging of parent/child definitions.
/// </summary>
public class ManagedNameValueCollection: NameValueCollection, IMergable
{
private bool mergeEnabled;
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Collections.Specialized.NameValueCollection"/> class that is empty, has the default initial capacity and uses the default case-insensitive hash code provider and the default case-insensitive comparer.
/// </summary>
public ManagedNameValueCollection()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Collections.Specialized.NameValueCollection"/> class that is empty, has the specified initial capacity and uses the default case-insensitive hash code provider and the default case-insensitive comparer.
/// </summary>
/// <param name="capacity">The initial number of entries that the <see cref="T:System.Collections.Specialized.NameValueCollection"/> can contain.</param><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="capacity"/> is less than zero.</exception>
public ManagedNameValueCollection(int capacity) : base(capacity)
{
}
/// <summary>
/// Gets a value indicating whether this instance is merge enabled for this instance
/// </summary>
/// <value>
/// <c>true</c> if this instance is merge enabled; otherwise, <c>false</c>.
/// </value>
public bool MergeEnabled
{
get { return this.mergeEnabled; }
set { this.mergeEnabled = value; }
}
/// <summary>
/// Merges the current value set with that of the supplied object.
/// </summary>
/// <remarks>The supplied object is considered the parent, and values in the
/// callee's value set must override those of the supplied object.
/// </remarks>
/// <param name="parent">The parent object to merge with</param>
/// <returns>The result of the merge operation</returns>
/// <exception cref="ArgumentNullException">If the supplied parent is <code>null</code></exception>
/// <exception cref="InvalidOperationException">If merging is not enabled for this instance,
/// (i.e. <code>MergeEnabled</code> equals <code>false</code>.</exception>
public object Merge(object parent)
{
if (!this.mergeEnabled)
{
throw new InvalidOperationException(
"Not allowed to merge when the 'MergeEnabled' property is set to 'false'");
}
if (parent == null)
{
return this;
}
NameValueCollection pDict = parent as NameValueCollection;
if (pDict == null)
{
throw new InvalidOperationException("Cannot merge with object of type [" + parent.GetType() + "]");
}
NameValueCollection merged = new ManagedNameValueCollection();
foreach (string s in pDict.AllKeys)
{
merged[s] = pDict.Get(s);
}
foreach (string s in this.AllKeys)
{
merged[s] = this.Get(s);
}
return merged;
}
}
}

View File

@@ -40,10 +40,30 @@ namespace Spring.Objects.Factory.Support
/// <author>Juergen Hoeller</author>
/// <author>Rick Evans (.NET)</author>
[Serializable]
public class ManagedSet : HybridSet, IManagedCollection
public class ManagedSet : HybridSet, IManagedCollection, IMergable
{
private string elementTypeName;
private string elementTypeName;
private bool mergeEnabled;
/// <summary>
/// Creates a new set instance based on either a list or a hash table,
/// depending on which will be more efficient based on the data-set
/// size.
/// </summary>
public ManagedSet()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HybridSet"/> class with a given capacity
/// </summary>
/// <param name="size">The size.</param>
public ManagedSet(int size) : base(size)
{
}
/// <summary>
/// Gets or sets the unresolved name for the <see cref="System.Type"/>
/// of the elements of this managed set.
@@ -108,6 +128,57 @@ namespace Spring.Objects.Factory.Support
}
return set;
}
}
/// <summary>
/// Gets a value indicating whether this instance is merge enabled for this instance
/// </summary>
/// <value>
/// <c>true</c> if this instance is merge enabled; otherwise, <c>false</c>.
/// </value>
public bool MergeEnabled
{
get { return this.mergeEnabled; }
set { this.mergeEnabled = value; }
}
/// <summary>
/// Merges the current value set with that of the supplied object.
/// </summary>
/// <remarks>The supplied object is considered the parent, and values in the
/// callee's value set must override those of the supplied object.
/// </remarks>
/// <param name="parent">The parent object to merge with</param>
/// <returns>The result of the merge operation</returns>
/// <exception cref="ArgumentNullException">If the supplied parent is <code>null</code></exception>
/// <exception cref="InvalidOperationException">If merging is not enabled for this instance,
/// (i.e. <code>MergeEnabled</code> equals <code>false</code>.</exception>
public object Merge(object parent)
{
if (!this.mergeEnabled)
{
throw new InvalidOperationException(
"Not allowed to merge when the 'MergeEnabled' property is set to 'false'");
}
if (parent == null)
{
return this;
}
ISet pSet = parent as ISet;
if (pSet == null)
{
throw new InvalidOperationException("Cannot merge with object of type [" + parent.GetType() + "]");
}
ISet merged = new ManagedSet();
foreach (object element in pSet)
{
merged.Add(element);
}
foreach (object o in this)
{
merged.Add(o);
}
return merged;
}
}
}

View File

@@ -31,7 +31,8 @@ namespace Spring.Objects.Factory.Xml
{
private string autowire;
private string dependencyCheck;
private string lazyInit;
private string lazyInit;
private string merge;
/// <summary>
/// Gets or sets the autowire setting for the document that's currently parsed.
@@ -61,6 +62,16 @@ namespace Spring.Objects.Factory.Xml
{
get { return lazyInit; }
set { lazyInit = value; }
}
/// <summary>
/// Gets or sets the merge setting for the document that's currently parsed.
/// </summary>
/// <value>The merge.</value>
public string Merge
{
get { return merge; }
set { merge = value; }
}
}
}

View File

@@ -88,7 +88,13 @@ namespace Spring.Objects.Factory.Xml
/// <summary>
/// Specifies the default autowire mode.
/// </summary>
public const string DefaultAutowireAttribute = "default-autowire";
public const string DefaultAutowireAttribute = "default-autowire";
/// <summary>
/// Specifies the default collection merge mode.
/// </summary>
public const string DefaultMergeAttribute = "default-merge";
/// <summary>
/// Defines a single named object.
@@ -585,7 +591,12 @@ namespace Spring.Objects.Factory.Xml
/// Shortcut alternative to specifying a value element in a
/// dictionary entry element with <c>&lt;ref object="..."/&gt;</c>.
/// </summary>
public const string DictionaryValueRefShortcutAttribute = "value-ref";
public const string DictionaryValueRefShortcutAttribute = "value-ref";
/// <summary>
/// Specify if the collection values should be merged with the parent.
/// </summary>
public const string MergeAttribute = "merge";
/// <summary>
/// The string of characters that delimit object names.

View File

@@ -161,6 +161,20 @@ namespace Spring.Objects.Factory.Xml
#endregion
ddd.Merge = GetAttributeValue(root, ObjectDefinitionConstants.DefaultMergeAttribute);
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(
string.Format(
"Default merge '{0}'.",
ddd.Merge));
}
#endregion
defaults = ddd;
}

View File

@@ -62,7 +62,7 @@ namespace Spring.Objects.Factory.Xml
NamespaceParser(
Namespace = "http://www.springframework.net",
SchemaLocationAssemblyHint = typeof(ObjectsNamespaceParser),
SchemaLocation = "/Spring.Objects.Factory.Xml/spring-objects-1.1.xsd"
SchemaLocation = "/Spring.Objects.Factory.Xml/spring-objects-1.3.xsd"
)
]
// [Obsolete("ObjectsNamespaceParser will be dropped with 2.x, use ObjectDefinitionParserHelper instead", false)]
@@ -93,20 +93,20 @@ namespace Spring.Objects.Factory.Xml
#endregion
/// <summary>
/// Parse the specified XmlElement and register the resulting
/// ObjectDefinitions with the <see cref="ParserContext.Registry"/> IObjectDefinitionRegistry
/// embedded in the supplied <see cref="ParserContext"/>
/// </summary>
/// <param name="element">The element to be parsed.</param>
/// <param name="parserContext">TThe object encapsulating the current state of the parsing process.
/// Provides access to a IObjectDefinitionRegistry</param>
/// <returns>The primary object definition.</returns>
/// <remarks>
/// <p>
/// This method is never invoked if the parser is namespace aware
/// and was called to process the root node.
/// </p>
/// <summary>
/// Parse the specified XmlElement and register the resulting
/// ObjectDefinitions with the <see cref="ParserContext.Registry"/> IObjectDefinitionRegistry
/// embedded in the supplied <see cref="ParserContext"/>
/// </summary>
/// <param name="element">The element to be parsed.</param>
/// <param name="parserContext">TThe object encapsulating the current state of the parsing process.
/// Provides access to a IObjectDefinitionRegistry</param>
/// <returns>The primary object definition.</returns>
/// <remarks>
/// <p>
/// This method is never invoked if the parser is namespace aware
/// and was called to process the root node.
/// </p>
/// </remarks>
// [Obsolete("not used anymore - ObjectsNamespaceParser will be dropped with 2.x, use ObjectDefinitionParserHelper instead", false)]
public override IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext)
@@ -895,7 +895,7 @@ namespace Spring.Objects.Factory.Xml
}
case ObjectDefinitionConstants.NameValuesElement:
{
return ParseNameValueCollectionElement(element, name);
return ParseNameValueCollectionElement(element, name, parserContext);
}
case ObjectDefinitionConstants.ValueElement:
{
@@ -1026,7 +1026,7 @@ namespace Spring.Objects.Factory.Xml
/// <summary>
/// Gets a list definition.
/// </summary>
/// <param name="element">
/// <param name="collectionEle">
/// The element describing the list definition.
/// </param>
/// <param name="name">
@@ -1036,31 +1036,43 @@ namespace Spring.Objects.Factory.Xml
/// The namespace-aware parser.
/// </param>
/// <returns>The list definition.</returns>
protected virtual IList ParseListElement(XmlElement element, string name, ParserContext parserContext)
protected virtual IList ParseListElement(XmlElement collectionEle, string name, ParserContext parserContext)
{
ManagedList list = new ManagedList();
string elementTypeName = GetAttributeValue(collectionEle, "element-type");
XmlNodeList nl = collectionEle.ChildNodes;
ManagedList target = new ManagedList(nl.Count);
string elementTypeName = GetAttributeValue(element, "element-type");
if (StringUtils.HasText(elementTypeName))
{
list.ElementTypeName = elementTypeName;
target.ElementTypeName = elementTypeName;
}
target.MergeEnabled = ParseMergeAttribute(collectionEle, parserContext.ParserHelper);
foreach (XmlNode node in element.ChildNodes)
foreach (XmlNode node in collectionEle.ChildNodes)
{
XmlElement ele = node as XmlElement;
if (ele != null)
{
list.Add(ParsePropertySubElement(ele, name, parserContext));
target.Add(ParsePropertySubElement(ele, name, parserContext));
}
}
return list;
return target;
}
private bool ParseMergeAttribute(XmlElement collectionElement, ObjectDefinitionParserHelper helper)
{
string val = collectionElement.GetAttribute(ObjectDefinitionConstants.MergeAttribute);
if (ObjectDefinitionConstants.DefaultValue.Equals(val))
{
val = helper.Defaults.Merge;
}
return ObjectDefinitionConstants.TrueValue.Equals(val);
}
/// <summary>
/// Gets a set definition.
/// </summary>
/// <param name="element">
/// <param name="collectionEle">
/// The element describing the set definition.
/// </param>
/// <param name="name">
@@ -1070,44 +1082,42 @@ namespace Spring.Objects.Factory.Xml
/// The namespace-aware parser.
/// </param>
/// <returns>The set definition.</returns>
protected Set ParseSetElement(XmlElement element, string name, ParserContext parserContext)
{
ManagedSet theSet = new ManagedSet();
string elementTypeName = GetAttributeValue(element, "element-type");
protected Set ParseSetElement(XmlElement collectionEle, string name, ParserContext parserContext)
{
string elementTypeName = GetAttributeValue(collectionEle, "element-type");
XmlNodeList nl = collectionEle.ChildNodes;
ManagedSet target = new ManagedSet(nl.Count);
if (StringUtils.HasText(elementTypeName))
{
theSet.ElementTypeName = elementTypeName;
target.ElementTypeName = elementTypeName;
}
foreach (XmlNode node in element.ChildNodes)
target.MergeEnabled = ParseMergeAttribute(collectionEle, parserContext.ParserHelper);
foreach (XmlNode node in collectionEle.ChildNodes)
{
XmlElement ele = node as XmlElement;
if (ele != null)
{
object sub = ParsePropertySubElement(ele, name, parserContext);
theSet.Add(sub);
target.Add(sub);
}
}
return theSet;
return target;
}
/// <summary>
/// Gets a dictionary definition.
/// </summary>
/// <param name="element">
/// The element describing the dictionary definition.
/// </param>
/// <param name="name">
/// The name of the object (definition) associated with the dictionary definition.
/// </param>
/// <param name="parserContext">
/// The namespace-aware parser.
/// </param>
/// <param name="mapEle">The element describing the dictionary definition.</param>
/// <param name="name">The name of the object (definition) associated with the dictionary definition.</param>
/// <param name="parserContext">The namespace-aware parser.</param>
/// <returns>The dictionary definition.</returns>
protected IDictionary ParseDictionaryElement(XmlElement element, string name, ParserContext parserContext)
protected IDictionary ParseDictionaryElement(XmlElement mapEle, string name, ParserContext parserContext)
{
ManagedDictionary dictionary = new ManagedDictionary();
string keyTypeName = GetAttributeValue(element, "key-type");
string valueTypeName = GetAttributeValue(element, "value-type");
string keyTypeName = GetAttributeValue(mapEle, "key-type");
string valueTypeName = GetAttributeValue(mapEle, "value-type");
if (StringUtils.HasText(keyTypeName))
{
dictionary.KeyTypeName = keyTypeName;
@@ -1116,8 +1126,9 @@ namespace Spring.Objects.Factory.Xml
{
dictionary.ValueTypeName = valueTypeName;
}
dictionary.MergeEnabled = ParseMergeAttribute(mapEle, parserContext.ParserHelper);
XmlNodeList entryElements = SelectNodes(element, ObjectDefinitionConstants.EntryElement);
XmlNodeList entryElements = SelectNodes(mapEle, ObjectDefinitionConstants.EntryElement);
foreach (XmlElement entryEle in entryElements)
{
#region Key
@@ -1274,7 +1285,7 @@ namespace Spring.Objects.Factory.Xml
/// <summary>
/// Gets a name value collection mapping definition.
/// </summary>
/// <param name="element">
/// <param name="nameValueEle">
/// The element describing the name value collection mapping definition.
/// </param>
/// <param name="name">
@@ -1282,10 +1293,12 @@ namespace Spring.Objects.Factory.Xml
/// name value collection mapping definition.
/// </param>
/// <returns>The name value collection definition.</returns>
protected NameValueCollection ParseNameValueCollectionElement(XmlElement element, string name)
protected NameValueCollection ParseNameValueCollectionElement(XmlElement nameValueEle, string name, ParserContext parserContext)
{
NameValueCollection nvc = new NameValueCollection();
XmlNodeList addElements = element.GetElementsByTagName(ObjectDefinitionConstants.AddElement);
ManagedNameValueCollection nvc = new ManagedNameValueCollection();
nvc.MergeEnabled = ParseMergeAttribute(nameValueEle, parserContext.ParserHelper);
XmlNodeList addElements = nameValueEle.GetElementsByTagName(ObjectDefinitionConstants.AddElement);
foreach (XmlElement addElement in addElements)
{
string key = GetAttributeValue(addElement, ObjectDefinitionConstants.KeyAttribute);

View File

@@ -0,0 +1,550 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://www.springframework.net" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:vs="http://schemas.microsoft.com/Visual-Studio-Intellisense" targetNamespace="http://www.springframework.net" elementFormDefault="qualified" attributeFormDefault="unqualified" vs:friendlyname="Spring.NET Configuration" vs:ishtmlschema="false" vs:iscasesensitive="true" vs:requireattributequotes="true" vs:defaultnamespacequalifier="" vs:defaultnsprefix="">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Spring Objects XML Schema Definition
Based on Spring Beans DTD, authored by Rod Johnson &amp; Juergen Hoeller
Author: Griffin Caprio
This defines a simple and consistent way of creating a namespace
of managed objects configured by a Spring XmlObjectFactory.
This document type is used by most Spring functionality, including
web application contexts, which are based on object factories.
Each object element in this document defines an object.
Typically the object type (System.Type is specified, along with plain vanilla
object properties.
Object instances can be "singletons" (shared instances) or "prototypes"
(independent instances).
References among objects are supported, i.e. setting an object property
to refer to another object in the same factory or an ancestor factory.
As alternative to object references, "inner object definitions" can be used.
Singleton flags and names of such "inner object" are always ignored:
Inner object are anonymous prototypes.
There is also support for lists, dictionaries, and sets.
]]>
</xsd:documentation>
</xsd:annotation>
<!-- base types -->
<xsd:complexType name="identifiedType" abstract="true">
<xsd:annotation>
<xsd:documentation><![CDATA[The unique identifier for a bean. The scope of the identifier is the enclosing object factory.]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[The unique identifier for an object.]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:simpleType name="nonNullString">
<xsd:annotation>
<xsd:documentation>Defines a base type for any required string. Defines a string with a minimum length of 0</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:string">
<xsd:minLength value="0"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="description">
<xsd:annotation>
<xsd:documentation>
Element containing informative text describing the purpose of the enclosing
element. Always optional.
Used primarily for user documentation of XML object definition documents.
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="nonNullString"/>
</xsd:simpleType>
<xsd:complexType name="valueObject">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="type" type="nonNullString" use="optional"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="expression">
<xsd:sequence>
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="2"/>
</xsd:sequence>
<xsd:attribute name="value" type="nonNullString" use="required"/>
</xsd:complexType>
<!--
Defines a reference to another object in this factory or an external
factory (parent or included factory).
-->
<xsd:complexType name="objectReference">
<xsd:attribute name="object" type="nonNullString" use="optional"/>
<xsd:attribute name="local" type="xsd:IDREF" use="optional"/>
<xsd:attribute name="parent" type="nonNullString" use="optional"/>
<!--
References must specify a name of the target object.
The "object" attribute can reference any name from any object in the context,
to be checked at runtime.
Local references, using the "local" attribute, have to use object ids;
they can be checked by this DTD, thus should be preferred for references
within the same object factory XML file.
-->
</xsd:complexType>
<!-- Defines a reference to another object or a type. -->
<xsd:complexType name="objectOrClassReference">
<xsd:attribute name="object" type="nonNullString" use="optional"/>
<xsd:attribute name="local" type="xsd:IDREF" use="optional"/>
<xsd:attribute name="type" type="nonNullString" use="optional"/>
</xsd:complexType>
<xsd:group name="objectList">
<xsd:sequence>
<xsd:element name="description" type="description" minOccurs="0"/>
<xsd:choice>
<xsd:element name="object" type="vanillaObject"/>
<!--
Defines a reference to another object in this factory or an external
factory (parent or included factory).
-->
<xsd:element name="ref" type="objectReference"/>
<!--
Defines a string property value, which must also be the id of another
object in this factory or an external factory (parent or included factory).
While a regular 'value' element could instead be used for the same effect,
using idref in this case allows validation of local object ids by the xml
parser, and name completion by helper tools.
-->
<xsd:element name="idref" type="objectReference"/>
<!--
A objectList can contain multiple inner object, ref, collection, or value elements.
Lists are untyped, pending generics support, although references will be
strongly typed.
A objectList can also map to an array type. The necessary conversion
is automatically performed by AbstractObjectFactory.
-->
<xsd:element name="list">
<xsd:complexType>
<xsd:group ref="objectList" minOccurs="0" maxOccurs="unbounded"/>
<xsd:attribute name="element-type" type="nonNullString" use="optional"/>
<xsd:attribute name="merge" type="xsd:boolean" use="optional" default="false"/>
</xsd:complexType>
</xsd:element>
<!--
A set can contain multiple inner object, ref, collection, or value elements.
Sets are untyped, pending generics support, although references will be
strongly typed.
-->
<xsd:element name="set">
<xsd:complexType>
<xsd:group ref="objectList" minOccurs="0" maxOccurs="unbounded"/>
<xsd:attribute name="element-type" type="nonNullString" use="optional"/>
<xsd:attribute name="merge" type="xsd:boolean" use="optional" default="false"/>
</xsd:complexType>
</xsd:element>
<!--
A Spring map is a mapping from a string key to object (a .NET IDictionary).
Maps may be empty.
-->
<xsd:element name="dictionary" type="objectMap"/>
<!--
Name-values elements differ from map elements in that values must be strings.
Name-values may be empty.
-->
<xsd:element name="name-values" type="objectNameValues"/>
<!--
Contains a string representation of a property value.
The property may be a string, or may be converted to the
required type using the System.ComponentModel.TypeConverter
machinery. This makes it possible for application developers
to write custom TypeConverter implementations that can
convert strings to objects.
Note that this is recommended for simple objects only.
Configure more complex objects by setting properties to references
to other objects.
-->
<xsd:element name="value" type="valueObject"/>
<!--
Contains a string representation of an expression.
-->
<xsd:element name="expression" type="expression"/>
<!--
Denotes a .NET null value. Necessary because an empty "value" tag
will resolve to an empty String, which will not be resolved to a
null value unless a special TypeConverter does so.
-->
<xsd:element name="null" />
<xsd:any namespace="##other" processContents="strict" />
</xsd:choice>
</xsd:sequence>
</xsd:group>
<xsd:complexType name="objectNameValues">
<xsd:sequence>
<!--
The "value" attribute is the string value of the property. The "key"
attribute is the name of the property.
-->
<xsd:element name="add" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType mixed="true">
<xsd:attribute name="key" type="nonNullString" use="required"/>
<xsd:attribute name="value" use="required" type="xsd:string"/>
<xsd:attribute name="delimiters" use="optional" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="merge" type="xsd:boolean" use="optional" default="false"/>
</xsd:complexType>
<xsd:complexType name="importElement">
<xsd:annotation>
<xsd:documentation>Import an external file containing object definitions into this file.</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="resource" type="nonNullString" use="required"/>
</xsd:complexType>
<xsd:complexType name="aliasElement">
<xsd:annotation>
<xsd:documentation>Defines an additional alias name for an object definition.</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="nonNullString" use="required"/>
<xsd:attribute name="alias" type="nonNullString" use="required"/>
</xsd:complexType>
<xsd:complexType name="objectMap">
<xsd:sequence>
<xsd:element type="mapEntryElement" name="entry" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="key-type" type="nonNullString" use="optional"/>
<xsd:attribute name="value-type" type="nonNullString" use="optional"/>
<xsd:attribute name="merge" type="xsd:boolean" use="optional" default="false"/>
</xsd:complexType>
<xsd:complexType name="mapEntryElement">
<xsd:sequence>
<xsd:element type="mapKeyElement" name="key" minOccurs="0" maxOccurs="1"/>
<xsd:group ref="objectList" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="key" type="nonNullString" use="optional"/>
<xsd:attribute name="value" type="nonNullString" use="optional"/>
<xsd:attribute name="expression" type="nonNullString" use="optional"/>
<xsd:attribute name="key-ref" type="nonNullString" use="optional"/>
<xsd:attribute name="value-ref" type="nonNullString" use="optional"/>
</xsd:complexType>
<xsd:complexType name="mapKeyElement">
<xsd:group ref="objectList" minOccurs="1"/>
</xsd:complexType>
<xsd:complexType name="lookupMethod">
<xsd:attribute name="name" type="nonNullString" use="required"/>
<xsd:attribute name="object" type="nonNullString" use="required"/>
</xsd:complexType>
<xsd:complexType name="constructorArgument">
<xsd:annotation>
<xsd:documentation>Defines constructor argument.</xsd:documentation>
</xsd:annotation>
<xsd:group ref="objectList" minOccurs="0"/>
<!--
The constructor-arg tag can have an optional named parameter attribute,
to specify a named parameter in the constructor argument list.
-->
<xsd:attribute name="name" type="nonNullString" use="optional"/>
<!--
The constructor-arg tag can have an optional index attribute,
to specify the exact index in the constructor argument list. Only needed
to avoid ambiguities, e.g. in case of 2 arguments of the same type.
-->
<xsd:attribute name="index" type="nonNullString" use="optional"/>
<!--
The constructor-arg tag can have an optional type attribute,
to specify the exact type of the constructor argument. Only needed
to avoid ambiguities, e.g. in case of 2 single argument constructors
that can both be converted from a String.
-->
<xsd:attribute name="type" type="nonNullString" use="optional"/>
<xsd:attribute name="value" type="nonNullString" use="optional"/>
<xsd:attribute name="expression" type="nonNullString" use="optional"/>
<xsd:attribute name="ref" type="nonNullString" use="optional"/>
</xsd:complexType>
<xsd:complexType name="property">
<xsd:annotation>
<xsd:documentation>Defines property.</xsd:documentation>
</xsd:annotation>
<xsd:group ref="objectList" minOccurs="0"/>
<!-- The property name attribute is the name of the objects property. -->
<xsd:attribute name="name" type="nonNullString" use="required"/>
<xsd:attribute name="value" type="nonNullString" use="optional"/>
<xsd:attribute name="expression" type="nonNullString" use="optional"/>
<xsd:attribute name="ref" type="nonNullString" use="optional"/>
</xsd:complexType>
<xsd:complexType name="vanillaObject">
<xsd:annotation>
<xsd:documentation>Defines a single named object.</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="description" type="description" minOccurs="0" maxOccurs="1"/>
<!--
Object definitions can specify zero or more constructor arguments.
They correspond to either a specific index of the constructor argument list
or are supposed to be matched generically by type.
This is an alternative to "autowire constructor".
-->
<xsd:element name="constructor-arg" type="constructorArgument" minOccurs="0" maxOccurs="unbounded"/>
<!--
Object definitions can have zero or more properties.
Spring supports primitives, references to other objects in the same or
related factories, lists, dictionaries and properties.
-->
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded"/>
<!--
Object definitions can specify zero or more lookup-methods.
-->
<xsd:element name="lookup-method" type="lookupMethod" minOccurs="0" maxOccurs="unbounded"/>
<!-- Object definitions can have zero or more replaced-methods. -->
<xsd:element name="replaced-method" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="arg-type" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="match" type="nonNullString" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="nonNullString" use="required"/>
<xsd:attribute name="replacer" type="nonNullString" use="required"/>
</xsd:complexType>
</xsd:element>
<!-- Object definitions can have zero or more subscriptions. -->
<xsd:element name="listener" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ref" type="objectOrClassReference" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<!-- The event(s) the object is interested in. -->
<xsd:attribute name="event" type="nonNullString" use="optional"/>
<!-- The name or name pattern of the method that will handle the event(s). -->
<xsd:attribute name="method" type="nonNullString" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<!--
Objects can be identified by an id, to enable reference checking.
There are constraints on a valid XML id: if you want to reference your object
in .NET code using a name that's illegal as an XML id, use the optional
"name" attribute. If neither given, the object type name is used as id.
-->
<xsd:attribute name="id" type="xsd:ID" use="optional"/>
<!--
Optional. Can be used to create one or more aliases illegal in an id.
Multiple aliases can be separated by any number of spaces or commas.
-->
<xsd:attribute name="name" type="nonNullString" use="optional"/>
<!--
Each object definition must specify the full, assembly qualified of the type,
or the name of the parent object from which the type can be worked out.
Note that a child object definition that references a parent will just
add respectively override property values and be able to change the
singleton status. It will inherit all of the parent's other parameters
like lazy initialization or autowire settings.
-->
<xsd:attribute name="type" type="nonNullString" use="optional"/>
<xsd:attribute name="parent" type="nonNullString" use="optional"/>
<!--
Is this object "abstract", i.e. not meant to be instantiated itself but
rather just serving as parent for concrete child object definitions?
Default is false. Specify true to tell the object factory to not try to
instantiate that particular object in any case.
-->
<xsd:attribute name="abstract" type="xsd:boolean" use="optional" default="false"/>
<!--
Is this object a "singleton" (one shared instance, which will
be returned by all calls to GetObject() with the id),
or a "prototype" (independent instance resulting from each call to
getObject(). Default is singleton.
Singletons are most commonly used, and are ideal for multi-threaded
service objects.
-->
<xsd:attribute name="singleton" type="xsd:boolean" use="optional" default="true"/>
<!--
Optional attribute controlling the scope of singleton instances. It is
only applicable to ASP.Net web applications and it has no effect on prototype
objects. Applications other than ASP.Net web applications simply ignore this attribute.
It has 3 possible values:
1. "application"
Default object scope. Objects defined with application scope will behave like
traditional singleton objects. Same instance will be returned from every call
to IApplicationContext.GetObject()
2. "session"
Objects with this scope will be stored within user's HTTP session. Session scope
is typically used for objects such as shopping cart, user profile, etc.
3. "request"
Object with this scope will be initialized for each HTTP request, but unlike with prototype
objects, same instance will be returned from all calls to IApplicationContext.GetObject()
within the same HTTP request. For example, if one ASP page forwards request to another using
Server.Transfer method, they can easily share the state by configuring dependency to the same
request-scoped object.
-->
<xsd:attribute name="scope" use="optional" default="application">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="application"/>
<xsd:enumeration value="session"/>
<xsd:enumeration value="request"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<!--
Is this object to be lazily initialized?
If false, it will get instantiated on startup by object factories
that perform eager initialization of singletons.
-->
<xsd:attribute name="lazy-init" use="optional" default="default">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="true"/>
<xsd:enumeration value="false"/>
<xsd:enumeration value="default"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<!--
Optional attribute controlling whether to "autowire" object properties.
This is an automagical process in which object references don't need to be coded
explicitly in the XML object definition file, but Spring works out dependencies.
There are 5 modes:
1. "no"
The traditional Spring default. No automagical wiring. Object references
must be defined in the XML file via the <ref> element. We recommend this
in most cases as it makes documentation more explicit.
2. "byName"
Autowiring by property name. If a object of class Cat exposes a dog property,
Spring will try to set this to the value of the object "dog" in the current factory.
3. "byType"
Autowiring if there is exactly one object of the property type in the object factory.
If there is more than one, a fatal error is raised, and you can't use byType
autowiring for that object. If there is none, nothing special happens - use
dependency-check="objects" to raise an error in that case.
4. "constructor"
Analogous to "byType" for constructor arguments. If there isn't exactly one object
of the constructor argument type in the object factory, a fatal error is raised.
5. "autodetect"
Chooses "constructor" or "byType" through introspection of the object class.
If a default constructor is found, "byType" gets applied.
The latter two are similar to PicoContainer and make object factories simple to
configure for small namespaces, but doesn't work as well as standard Spring
behaviour for bigger applications.
Note that explicit dependencies, i.e. "property" and "constructor-arg" elements,
always override autowiring. Autowire behaviour can be combined with dependency
checking, which will be performed after all autowiring has been completed.
-->
<xsd:attribute name="autowire" use="optional" default="default">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="no"/>
<xsd:enumeration value="byName"/>
<xsd:enumeration value="byType"/>
<xsd:enumeration value="constructor"/>
<xsd:enumeration value="autodetect"/>
<xsd:enumeration value="default"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<!--
Optional attribute controlling whether to check whether all this
objects dependencies, expressed in its properties, are satisfied.
Default is no dependency checking.
"simple" type dependency checking includes primitives and String
"object" includes collaborators (other objects in the factory)
"all" includes both types of dependency checking
-->
<xsd:attribute name="dependency-check" use="optional" default="default">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="none"/>
<xsd:enumeration value="objects"/>
<xsd:enumeration value="simple"/>
<xsd:enumeration value="all"/>
<xsd:enumeration value="default"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<!--
The names of the objects that this object depends on being initialized.
The object factory will guarantee that these objects get initialized before.
Note that dependencies are normally expressed through object properties or
constructor arguments. This property should just be necessary for other kinds
of dependencies like statics (*ugh*) or database preparation on startup.
-->
<xsd:attribute name="depends-on" type="nonNullString" use="optional"/>
<!--
Optional attribute for the name of the custom initialization method
to invoke after setting object properties. The method must have no arguments,
but may throw any exception.
-->
<xsd:attribute name="init-method" type="nonNullString" use="optional"/>
<!--
Optional attribute for the name of the custom destroy method to invoke
on object factory shutdown. The method must have no arguments,
but may throw any exception. Note: Only invoked on singleton objects!
-->
<xsd:attribute name="destroy-method" type="nonNullString" use="optional"/>
<xsd:attribute name="factory-method" type="nonNullString" use="optional"/>
<xsd:attribute name="factory-object" type="nonNullString" use="optional"/>
</xsd:complexType>
<xsd:element name="objects">
<xsd:annotation>
<xsd:documentation>The document root. At least one object definition is required.</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="description" type="description" minOccurs="0" maxOccurs="1"/>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="import" type="importElement"/>
<xsd:element name="alias" type="aliasElement"/>
<xsd:element name="object" type="vanillaObject"/>
<xsd:any namespace="##other" processContents="strict"/>
</xsd:choice>
</xsd:sequence>
<!--
Default values for all object definitions. Can be overridden at
the "object" level. See those attribute definitions for details.
-->
<xsd:attribute name="default-lazy-init" type="xsd:boolean" use="optional" default="false"/>
<xsd:attribute name="default-merge" type="xsd:boolean" use="optional" default="false"/>
<xsd:attribute name="default-dependency-check" use="optional" default="none">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="none"/>
<xsd:enumeration value="objects"/>
<xsd:enumeration value="simple"/>
<xsd:enumeration value="all"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="default-autowire" use="optional" default="no">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="no"/>
<xsd:enumeration value="byName"/>
<xsd:enumeration value="byType"/>
<xsd:enumeration value="constructor"/>
<xsd:enumeration value="autodetect"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<!--This file is auto-generated by the XML Schema Designer. It holds layout information for components on the designer surface.-->
<XSDDesignerLayout Style="LeftRight" layoutVersion="2" viewPortLeft="0" viewPortTop="0" zoom="100">
<identifiedType_XmlComplexType left="1317" top="167061" width="5292" height="3757" selected="0" zOrder="8" index="0" expanded="1" />
<nonNullString_XmlSimpleType left="1317" top="1254" width="5292" height="3625" selected="0" zOrder="5" index="1" expanded="1" />
<description_XmlSimpleType left="1317" top="5387" width="5292" height="3625" selected="0" zOrder="6" index="2" expanded="1" />
<valueObject_XmlComplexType left="1317" top="9520" width="5292" height="3625" selected="0" zOrder="7" index="3" expanded="1" />
<expression_XmlComplexType left="1317" top="13653" width="5292" height="3625" selected="0" zOrder="9" index="4" expanded="1">
<property_XmlElement left="7243" top="13653" width="5292" height="3625" selected="0" zOrder="10" index="0" expanded="0" />
</expression_XmlComplexType>
<objectReference_XmlComplexType left="1317" top="17786" width="5292" height="3757" selected="0" zOrder="12" index="5" expanded="1" />
<objectOrClassReference_XmlComplexType left="1317" top="22051" width="5292" height="3757" selected="0" zOrder="13" index="6" expanded="1" />
<objectList_XmlGroup left="1317" top="43376" width="5292" height="3757" selected="0" zOrder="14" index="7" expanded="1">
<_x0028_group1_x0029__XmlChoice left="7243" top="43376" width="5292" height="3757" selected="0" zOrder="15" index="1" expanded="1">
<object_XmlElement left="13169" top="26316" width="5292" height="3757" selected="0" zOrder="17" index="0" expanded="0" />
<ref_XmlElement left="13169" top="30581" width="5292" height="3757" selected="0" zOrder="19" index="1" expanded="0" />
<idref_XmlElement left="13169" top="34846" width="5292" height="3757" selected="0" zOrder="21" index="2" expanded="0" />
<list_XmlElement left="13169" top="39111" width="5292" height="3757" selected="0" zOrder="23" index="3" expanded="1">
<_x0028_group1_x0029__XmlChoice left="19095" top="39111" width="5292" height="3757" selected="0" zOrder="25" index="1" expanded="0" />
</list_XmlElement>
<set_XmlElement left="13169" top="43376" width="5292" height="3757" selected="0" zOrder="27" index="4" expanded="1">
<_x0028_group1_x0029__XmlChoice left="19095" top="43376" width="5292" height="3757" selected="0" zOrder="29" index="1" expanded="0" />
</set_XmlElement>
<dictionary_XmlElement left="13169" top="47641" width="5292" height="3757" selected="0" zOrder="31" index="5" expanded="0" />
<name-values_XmlElement left="13169" top="51906" width="5292" height="3757" selected="0" zOrder="33" index="6" expanded="0" />
<value_XmlElement left="13169" top="56171" width="5292" height="3757" selected="0" zOrder="35" index="7" expanded="0" />
<expression_XmlElement left="13169" top="60436" width="5292" height="3757" selected="0" zOrder="37" index="8" expanded="0" />
</_x0028_group1_x0029__XmlChoice>
</objectList_XmlGroup>
<objectNameValues_XmlComplexType left="1317" top="64701" width="5292" height="3757" selected="0" zOrder="39" index="8" expanded="1">
<add_XmlElement left="7243" top="64701" width="5292" height="3757" selected="0" zOrder="40" index="0" expanded="1" />
</objectNameValues_XmlComplexType>
<importElement_XmlComplexType left="1317" top="68966" width="5292" height="3757" selected="0" zOrder="42" index="9" expanded="1" />
<aliasElement_XmlComplexType left="1317" top="73231" width="5292" height="3757" selected="0" zOrder="43" index="10" expanded="1" />
<objectMap_XmlComplexType left="1317" top="77496" width="5292" height="3757" selected="0" zOrder="44" index="11" expanded="1">
<entry_XmlElement left="7243" top="77496" width="5292" height="3757" selected="0" zOrder="45" index="0" expanded="0" />
</objectMap_XmlComplexType>
<mapEntryElement_XmlComplexType left="1317" top="83893" width="5292" height="3757" selected="0" zOrder="47" index="12" expanded="1">
<key_XmlElement left="7243" top="81761" width="5292" height="3757" selected="0" zOrder="48" index="0" expanded="0" />
<ref_x003D_objectList_XmlGroup left="7243" top="86026" width="5292" height="3757" selected="0" zOrder="50" index="1" expanded="0" />
</mapEntryElement_XmlComplexType>
<mapKeyElement_XmlComplexType left="1317" top="90291" width="5292" height="3757" selected="0" zOrder="52" index="13" expanded="1">
<_x0028_group1_x0029__XmlChoice left="7243" top="90291" width="5292" height="3757" selected="0" zOrder="53" index="1" expanded="0" />
</mapKeyElement_XmlComplexType>
<lookupMethod_XmlComplexType left="1317" top="94556" width="5292" height="3757" selected="0" zOrder="55" index="14" expanded="1" />
<constructorArgument_XmlComplexType left="1317" top="98821" width="5292" height="3757" selected="0" zOrder="56" index="15" expanded="1">
<_x0028_group1_x0029__XmlChoice left="7243" top="98821" width="5292" height="3757" selected="0" zOrder="57" index="1" expanded="0" />
</constructorArgument_XmlComplexType>
<property_XmlComplexType left="1317" top="103086" width="5292" height="3757" selected="0" zOrder="59" index="16" expanded="1">
<_x0028_group1_x0029__XmlChoice left="7243" top="103086" width="5292" height="3757" selected="0" zOrder="60" index="1" expanded="0" />
</property_XmlComplexType>
<vanillaObject_XmlComplexType left="1317" top="123716" width="5292" height="3757" selected="0" zOrder="62" index="17" expanded="1">
<constructor-arg_XmlElement left="7243" top="107351" width="5292" height="3757" selected="0" zOrder="63" index="1" expanded="0" />
<property_XmlElement left="7243" top="111616" width="5292" height="3757" selected="0" zOrder="65" index="2" expanded="0" />
<lookup-method_XmlElement left="7243" top="115881" width="5292" height="3757" selected="0" zOrder="67" index="3" expanded="0" />
<replaced-method_XmlElement left="7243" top="120146" width="5292" height="3757" selected="0" zOrder="69" index="4" expanded="1">
<arg-type_XmlElement left="13169" top="120146" width="5292" height="3757" selected="0" zOrder="71" index="0" expanded="1" />
</replaced-method_XmlElement>
<listener_XmlElement left="7243" top="124411" width="5292" height="3757" selected="0" zOrder="73" index="5" expanded="1">
<ref_XmlElement left="13169" top="124411" width="5292" height="3757" selected="0" zOrder="75" index="0" expanded="0" />
</listener_XmlElement>
<scope_XmlAttribute left="7243" top="130065" width="5292" height="979" selected="0" zOrder="77" index="12" expanded="1">
<_x0028_scope_x0029__XmlSimpleType left="13169" top="128676" width="5292" height="3757" selected="0" zOrder="79" index="0" expanded="1" />
</scope_XmlAttribute>
<lazy-init_XmlAttribute left="7243" top="134330" width="5292" height="979" selected="0" zOrder="81" index="13" expanded="1">
<_x0028_lazy-init_x0029__XmlSimpleType left="13169" top="132941" width="5292" height="3757" selected="0" zOrder="83" index="0" expanded="1" />
</lazy-init_XmlAttribute>
<autowire_XmlAttribute left="7243" top="138595" width="5292" height="979" selected="0" zOrder="85" index="14" expanded="1">
<_x0028_autowire_x0029__XmlSimpleType left="13169" top="137206" width="5292" height="3757" selected="0" zOrder="87" index="0" expanded="1" />
</autowire_XmlAttribute>
<dependency-check_XmlAttribute left="7243" top="142860" width="5292" height="979" selected="0" zOrder="89" index="15" expanded="1">
<_x0028_dependency-check_x0029__XmlSimpleType left="13169" top="141471" width="5292" height="3757" selected="0" zOrder="91" index="0" expanded="1" />
</dependency-check_XmlAttribute>
</vanillaObject_XmlComplexType>
<objects_XmlElement left="1317" top="155704" width="5292" height="3757" selected="0" zOrder="93" index="18" expanded="1">
<_x0028_group1_x0029__XmlChoice left="7243" top="150001" width="5292" height="3757" selected="0" zOrder="94" index="1" expanded="1">
<import_XmlElement left="13169" top="145736" width="5292" height="3757" selected="0" zOrder="96" index="0" expanded="0" />
<alias_XmlElement left="13169" top="150001" width="5292" height="3757" selected="0" zOrder="98" index="1" expanded="0" />
<object_XmlElement left="13169" top="154266" width="5292" height="3757" selected="0" zOrder="100" index="2" expanded="0" />
</_x0028_group1_x0029__XmlChoice>
<default-dependency-check_XmlAttribute left="7243" top="159920" width="5292" height="979" selected="0" zOrder="102" index="3" expanded="1">
<_x0028_default-dependency-check_x0029__XmlSimpleType left="13169" top="158531" width="5292" height="3757" selected="0" zOrder="104" index="0" expanded="1" />
</default-dependency-check_XmlAttribute>
<default-autowire_XmlAttribute left="7243" top="164185" width="5292" height="979" selected="0" zOrder="106" index="4" expanded="1">
<_x0028_default-autowire_x0029__XmlSimpleType left="13169" top="162796" width="5292" height="3757" selected="0" zOrder="108" index="0" expanded="1" />
</default-autowire_XmlAttribute>
</objects_XmlElement>
</XSDDesignerLayout>

View File

@@ -0,0 +1,55 @@
#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
using System;
namespace Spring.Objects
{
/// <summary>
/// Interface representing an object whose value set can be merged with that of a parent object.
/// </summary>
/// <author>Rob Harrop</author>
/// <author>Mark Pollack (.NET)</author>
public interface IMergable
{
/// <summary>
/// Gets a value indicating whether this instance is merge enabled for this instance
/// </summary>
/// <value>
/// <c>true</c> if this instance is merge enabled; otherwise, <c>false</c>.
/// </value>
bool MergeEnabled {
get;
}
/// <summary>
/// Merges the current value set with that of the supplied object.
/// </summary>
/// <remarks>The supplied object is considered the parent, and values in the
/// callee's value set must override those of the supplied object.
/// </remarks>
/// <param name="parent">The parent object to merge with</param>
/// <returns>The result of the merge operation</returns>
/// <exception cref="ArgumentNullException">If the supplied parent is <code>null</code></exception>
/// <exception cref="InvalidOperationException">If merging is not enabled for this instance,
/// (i.e. <code>MergeEnabled</code> equals <code>false</code>.</exception>
object Merge(object parent);
}
}

View File

@@ -1,347 +1,371 @@
#region License
/*
* Copyright <20> 2002-2005 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 System;
using System.Collections;
using System.Globalization;
using System.Text;
using Spring.Util;
#endregion
namespace Spring.Objects
{
/// <summary>
/// Default implementation of the <see cref="Spring.Objects.IPropertyValues"/>
/// interface.
/// </summary>
/// <remarks>
/// <p>
/// Allows simple manipulation of properties, and provides constructors to
/// support deep copy and construction from a number of collection types such as
/// <see cref="System.Collections.IDictionary"/> and
/// <see cref="System.Collections.IList"/>.
/// </p>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Mark Pollack (.NET)</author>
/// <author>Rick Evans (.NET)</author>
[Serializable]
public class MutablePropertyValues : IPropertyValues
{
#region Fields
/// <summary>
/// The list of <see cref="Spring.Objects.PropertyValue"/> objects.
/// </summary>
private IList propertyValuesList = new ArrayList();
#endregion
#region Constructor (s) / Destructor
/// <summary>
/// Creates a new instance of the <see cref="Spring.Objects.MutablePropertyValues"/>
/// class.
/// </summary>
/// <remarks>
/// <p>
/// The returned instance is initially empty...
/// <see cref="Spring.Objects.PropertyValue"/>s can be added with the various
/// overloaded <see cref="Spring.Objects.MutablePropertyValues.Add(PropertyValue)"/>,
/// <see cref="Spring.Objects.MutablePropertyValues.Add(string, object)"/>,
/// <see cref="Spring.Objects.MutablePropertyValues.AddAll(IDictionary)"/>,
/// and <see cref="Spring.Objects.MutablePropertyValues.AddAll(IList)"/>
/// methods.
/// </p>
/// </remarks>
/// <seealso cref="Spring.Objects.MutablePropertyValues.Add (PropertyValue)"/>
/// <seealso cref="Spring.Objects.MutablePropertyValues.Add (string, object)"/>
public MutablePropertyValues ()
{
}
/// <summary>
/// Creates a new instance of the <see cref="Spring.Objects.MutablePropertyValues"/>
/// class.
/// </summary>
/// <remarks>
/// <p>
/// Deep copy constructor. Guarantees <see cref="Spring.Objects.PropertyValue"/>
/// references are independent, although it can't deep copy objects currently
/// referenced by individual <see cref="Spring.Objects.PropertyValue"/> objects.
/// </p>
/// </remarks>
public MutablePropertyValues (IPropertyValues other)
{
if (other != null)
{
AddAll (other.PropertyValues);
}
}
/// <summary>
/// Creates a new instance of the <see cref="Spring.Objects.MutablePropertyValues"/>
/// class.
/// </summary>
/// <param name="map">
/// The <see cref="System.Collections.IDictionary"/> with property values
/// keyed by property name, which must be a <see cref="System.String"/>.
/// </param>
public MutablePropertyValues (IDictionary map)
{
AddAll (map);
}
#endregion
#region Properties
/// <summary>
/// Property to retrieve the array of property values.
/// </summary>
public PropertyValue[] PropertyValues
{
get { return (PropertyValue[]) ArrayList.Adapter (propertyValuesList).ToArray (typeof (PropertyValue)); }
}
#endregion
#region Methods
/// <summary>
/// Overloaded version of <c>Add</c> that takes a property name and a property value.
/// </summary>
/// <param name="propertyName">
/// The name of the property.
/// </param>
/// <param name="propertyValue">
/// The value of the property.
/// </param>
public void Add (string propertyName, object propertyValue)
{
Add (new PropertyValue (propertyName, propertyValue));
}
/// <summary>
/// Add the supplied <see cref="Spring.Objects.PropertyValue"/> object,
/// replacing any existing one for the respective property.
/// </summary>
/// <param name="pv">
/// The <see cref="Spring.Objects.PropertyValue"/> object to add.
/// </param>
public void Add (PropertyValue pv)
{
for (int i = 0; i < propertyValuesList.Count; ++i)
{
PropertyValue currentPv = (PropertyValue) propertyValuesList [i];
if (currentPv.Name.Equals (pv.Name))
{
propertyValuesList[i] = pv;
return ;
}
}
propertyValuesList.Add (pv);
}
/// <summary>
/// Add all property values from the given
/// <see cref="System.Collections.IDictionary"/>.
/// </summary>
/// <param name="map">
/// The map of property values, the keys of which must be
/// <see cref="System.String"/>s.
/// </param>
public void AddAll (IDictionary map)
{
if (map != null)
{
foreach (string key in map.Keys)
{
Add (new PropertyValue (key, map [key]));
}
}
}
/// <summary>
/// Add all property values from the given
/// <see cref="System.Collections.IList"/>.
/// </summary>
/// <param name="values">
/// The list of <see cref="Spring.Objects.PropertyValue"/>s to be added.
/// </param>
public void AddAll (IList values)
{
if (values != null)
{
foreach (PropertyValue value in values)
{
Add (value);
}
}
}
/// <summary>
/// Remove the given <see cref="Spring.Objects.PropertyValue"/>, if contained.
/// </summary>
/// <param name="pv">
/// The <see cref="Spring.Objects.PropertyValue"/> to remove.
/// </param>
public void Remove (PropertyValue pv)
{
propertyValuesList.Remove (pv);
}
/// <summary>
/// Removes the named <see cref="Spring.Objects.PropertyValue"/>, if contained.
/// </summary>
/// <param name="propertyName">
/// The name of the property.
/// </param>
public void Remove (string propertyName)
{
Remove (GetPropertyValue (propertyName));
}
/// <summary>
/// Modify a <see cref="Spring.Objects.PropertyValue"/> object held in this object. Indexed from 0.
/// </summary>
public void SetPropertyValueAt (PropertyValue pv, int i)
{
propertyValuesList [i] = pv;
}
/// <summary>
/// Return the property value given the name.
/// </summary>
/// <remarks>
/// The property name is checked in a <c>case-insensitive</c> fashion.
/// </remarks>
/// <param name="propertyName">
/// The name of the property.
/// </param>
/// <returns>
/// The property value.
/// </returns>
public PropertyValue GetPropertyValue (string propertyName)
{
string propertyNameLowered = propertyName.ToLower (CultureInfo.CurrentCulture);
foreach (PropertyValue pv in propertyValuesList)
{
if (pv.Name.ToLower(CultureInfo.CurrentCulture).Equals (propertyNameLowered))
{
return pv;
}
}
return null;
}
/// <summary>
/// Does the container of properties contain one of this name.
/// </summary>
/// <param name="propertyName">The name of the property to search for.</param>
/// <returns>
/// True if the property is contained in this collection, false otherwise.
/// </returns>
public bool Contains (string propertyName)
{
return GetPropertyValue (propertyName) != null;
}
/// <summary>
/// Return the difference (changes, additions, but not removals) of
/// property values between the supplied argument and the values
/// contained in the collection.
/// </summary>
/// <param name="old">Another property values collection.</param>
/// <returns>
/// The collection of property values that are different than the supplied one.
/// </returns>
public IPropertyValues ChangesSince (IPropertyValues old)
{
MutablePropertyValues changes = new MutablePropertyValues ();
if (old == this)
{
return changes;
}
// for each property value in this (the newer set)
foreach (PropertyValue newProperty in propertyValuesList)
{
PropertyValue oldProperty = old.GetPropertyValue (newProperty.Name);
if (oldProperty == null)
{
// if there wasn't an old one, add it
changes.Add (newProperty);
}
else if (!oldProperty.Equals (newProperty))
{
// it's changed
changes.Add (newProperty);
}
}
return changes;
}
/// <summary>
/// Returns an <see cref="System.Collections.IEnumerator"/> that can iterate
/// through a collection.
/// </summary>
/// <remarks>
/// <p>
/// The returned <see cref="System.Collections.IEnumerator"/> is the
/// <see cref="System.Collections.IEnumerator"/> exposed by the
/// <see cref="Spring.Objects.MutablePropertyValues.PropertyValues"/>
/// property.
/// </p>
/// </remarks>
/// <returns>
/// An <see cref="System.Collections.IEnumerator"/> that can iterate through a
/// collection.
/// </returns>
public IEnumerator GetEnumerator ()
{
return PropertyValues.GetEnumerator ();
}
// CLOVER:OFF
/// <summary>
/// Convert the object to a string representation.
/// </summary>
/// <returns>
/// A string representation of the object.
/// </returns>
public override string ToString ()
{
PropertyValue[] pvs = PropertyValues;
StringBuilder sb
= new StringBuilder (
"MutablePropertyValues: length=").Append (pvs.Length).Append ("; ");
sb.Append (StringUtils.ArrayToDelimitedString (pvs, ","));
return sb.ToString ();
}
// CLOVER:ON
#endregion
}
#region License
/*
* Copyright <20> 2002-2005 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 System;
using System.Collections;
using System.Globalization;
using System.Text;
using Spring.Util;
#endregion
namespace Spring.Objects
{
/// <summary>
/// Default implementation of the <see cref="Spring.Objects.IPropertyValues"/>
/// interface.
/// </summary>
/// <remarks>
/// <p>
/// Allows simple manipulation of properties, and provides constructors to
/// support deep copy and construction from a number of collection types such as
/// <see cref="System.Collections.IDictionary"/> and
/// <see cref="System.Collections.IList"/>.
/// </p>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Mark Pollack (.NET)</author>
/// <author>Rick Evans (.NET)</author>
[Serializable]
public class MutablePropertyValues : IPropertyValues
{
#region Fields
/// <summary>
/// The list of <see cref="Spring.Objects.PropertyValue"/> objects.
/// </summary>
private IList propertyValuesList = new ArrayList();
#endregion
#region Constructor (s) / Destructor
/// <summary>
/// Creates a new instance of the <see cref="Spring.Objects.MutablePropertyValues"/>
/// class.
/// </summary>
/// <remarks>
/// <p>
/// The returned instance is initially empty...
/// <see cref="Spring.Objects.PropertyValue"/>s can be added with the various
/// overloaded <see cref="Spring.Objects.MutablePropertyValues.Add(PropertyValue)"/>,
/// <see cref="Spring.Objects.MutablePropertyValues.Add(string, object)"/>,
/// <see cref="Spring.Objects.MutablePropertyValues.AddAll(IDictionary)"/>,
/// and <see cref="Spring.Objects.MutablePropertyValues.AddAll(IList)"/>
/// methods.
/// </p>
/// </remarks>
/// <seealso cref="Spring.Objects.MutablePropertyValues.Add (PropertyValue)"/>
/// <seealso cref="Spring.Objects.MutablePropertyValues.Add (string, object)"/>
public MutablePropertyValues ()
{
}
/// <summary>
/// Creates a new instance of the <see cref="Spring.Objects.MutablePropertyValues"/>
/// class.
/// </summary>
/// <remarks>
/// <p>
/// Deep copy constructor. Guarantees <see cref="Spring.Objects.PropertyValue"/>
/// references are independent, although it can't deep copy objects currently
/// referenced by individual <see cref="Spring.Objects.PropertyValue"/> objects.
/// </p>
/// </remarks>
public MutablePropertyValues (IPropertyValues other)
{
if (other != null)
{
AddAll (other.PropertyValues);
}
}
/// <summary>
/// Creates a new instance of the <see cref="Spring.Objects.MutablePropertyValues"/>
/// class.
/// </summary>
/// <param name="map">
/// The <see cref="System.Collections.IDictionary"/> with property values
/// keyed by property name, which must be a <see cref="System.String"/>.
/// </param>
public MutablePropertyValues (IDictionary map)
{
AddAll (map);
}
#endregion
#region Properties
/// <summary>
/// Property to retrieve the array of property values.
/// </summary>
public PropertyValue[] PropertyValues
{
get { return (PropertyValue[]) ArrayList.Adapter (propertyValuesList).ToArray (typeof (PropertyValue)); }
}
#endregion
#region Methods
/// <summary>
/// Overloaded version of <c>Add</c> that takes a property name and a property value.
/// </summary>
/// <param name="propertyName">
/// The name of the property.
/// </param>
/// <param name="propertyValue">
/// The value of the property.
/// </param>
public void Add (string propertyName, object propertyValue)
{
Add (new PropertyValue (propertyName, propertyValue));
}
/// <summary>
/// Add the supplied <see cref="Spring.Objects.PropertyValue"/> object,
/// replacing any existing one for the respective property.
/// </summary>
/// <param name="pv">
/// The <see cref="Spring.Objects.PropertyValue"/> object to add.
/// </param>
public void Add (PropertyValue pv)
{
for (int i = 0; i < propertyValuesList.Count; ++i)
{
PropertyValue currentPv = (PropertyValue) propertyValuesList [i];
if (currentPv.Name.Equals (pv.Name))
{
pv = MergeIfRequired(pv, currentPv);
propertyValuesList[i] = pv;
return ;
}
}
propertyValuesList.Add (pv);
}
/// <summary>
/// Merges the value of the supplied 'new' <see cref="PropertyValue"/> with that of
/// the current <see cref="PropertyValue"/> if merging is supported and enabled.
/// </summary>
/// <see cref="IMergable"/>
/// <param name="newPv">The new pv.</param>
/// <param name="currentPv">The current pv.</param>
/// <returns>The possibly merged PropertyValue</returns>
private PropertyValue MergeIfRequired(PropertyValue newPv, PropertyValue currentPv)
{
object val = newPv.Value;
IMergable mergable = val as IMergable;
if (mergable != null)
{
if (mergable.MergeEnabled)
{
object merged = mergable.Merge(currentPv.Value);
return new PropertyValue(newPv.Name, merged);
}
}
return newPv;
}
/// <summary>
/// Add all property values from the given
/// <see cref="System.Collections.IDictionary"/>.
/// </summary>
/// <param name="map">
/// The map of property values, the keys of which must be
/// <see cref="System.String"/>s.
/// </param>
public void AddAll (IDictionary map)
{
if (map != null)
{
foreach (string key in map.Keys)
{
Add (new PropertyValue (key, map [key]));
}
}
}
/// <summary>
/// Add all property values from the given
/// <see cref="System.Collections.IList"/>.
/// </summary>
/// <param name="values">
/// The list of <see cref="Spring.Objects.PropertyValue"/>s to be added.
/// </param>
public void AddAll (IList values)
{
if (values != null)
{
foreach (PropertyValue value in values)
{
Add (value);
}
}
}
/// <summary>
/// Remove the given <see cref="Spring.Objects.PropertyValue"/>, if contained.
/// </summary>
/// <param name="pv">
/// The <see cref="Spring.Objects.PropertyValue"/> to remove.
/// </param>
public void Remove (PropertyValue pv)
{
propertyValuesList.Remove (pv);
}
/// <summary>
/// Removes the named <see cref="Spring.Objects.PropertyValue"/>, if contained.
/// </summary>
/// <param name="propertyName">
/// The name of the property.
/// </param>
public void Remove (string propertyName)
{
Remove (GetPropertyValue (propertyName));
}
/// <summary>
/// Modify a <see cref="Spring.Objects.PropertyValue"/> object held in this object. Indexed from 0.
/// </summary>
public void SetPropertyValueAt (PropertyValue pv, int i)
{
propertyValuesList [i] = pv;
}
/// <summary>
/// Return the property value given the name.
/// </summary>
/// <remarks>
/// The property name is checked in a <c>case-insensitive</c> fashion.
/// </remarks>
/// <param name="propertyName">
/// The name of the property.
/// </param>
/// <returns>
/// The property value.
/// </returns>
public PropertyValue GetPropertyValue (string propertyName)
{
string propertyNameLowered = propertyName.ToLower (CultureInfo.CurrentCulture);
foreach (PropertyValue pv in propertyValuesList)
{
if (pv.Name.ToLower(CultureInfo.CurrentCulture).Equals (propertyNameLowered))
{
return pv;
}
}
return null;
}
/// <summary>
/// Does the container of properties contain one of this name.
/// </summary>
/// <param name="propertyName">The name of the property to search for.</param>
/// <returns>
/// True if the property is contained in this collection, false otherwise.
/// </returns>
public bool Contains (string propertyName)
{
return GetPropertyValue (propertyName) != null;
}
/// <summary>
/// Return the difference (changes, additions, but not removals) of
/// property values between the supplied argument and the values
/// contained in the collection.
/// </summary>
/// <param name="old">Another property values collection.</param>
/// <returns>
/// The collection of property values that are different than the supplied one.
/// </returns>
public IPropertyValues ChangesSince (IPropertyValues old)
{
MutablePropertyValues changes = new MutablePropertyValues ();
if (old == this)
{
return changes;
}
// for each property value in this (the newer set)
foreach (PropertyValue newProperty in propertyValuesList)
{
PropertyValue oldProperty = old.GetPropertyValue (newProperty.Name);
if (oldProperty == null)
{
// if there wasn't an old one, add it
changes.Add (newProperty);
}
else if (!oldProperty.Equals (newProperty))
{
// it's changed
changes.Add (newProperty);
}
}
return changes;
}
/// <summary>
/// Returns an <see cref="System.Collections.IEnumerator"/> that can iterate
/// through a collection.
/// </summary>
/// <remarks>
/// <p>
/// The returned <see cref="System.Collections.IEnumerator"/> is the
/// <see cref="System.Collections.IEnumerator"/> exposed by the
/// <see cref="Spring.Objects.MutablePropertyValues.PropertyValues"/>
/// property.
/// </p>
/// </remarks>
/// <returns>
/// An <see cref="System.Collections.IEnumerator"/> that can iterate through a
/// collection.
/// </returns>
public IEnumerator GetEnumerator ()
{
return PropertyValues.GetEnumerator ();
}
// CLOVER:OFF
/// <summary>
/// Convert the object to a string representation.
/// </summary>
/// <returns>
/// A string representation of the object.
/// </returns>
public override string ToString ()
{
PropertyValue[] pvs = PropertyValues;
StringBuilder sb
= new StringBuilder (
"MutablePropertyValues: length=").Append (pvs.Length).Append ("; ");
sb.Append (StringUtils.ArrayToDelimitedString (pvs, ","));
return sb.ToString ();
}
// CLOVER:ON
#endregion
}
}

View File

@@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>9.0.30729</ProductVersion>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -672,7 +672,6 @@
<Compile Include="Objects\Factory\Config\ObjectDefinitionHolder.cs" />
<Compile Include="Objects\Factory\Config\ObjectDefinitionVisitor.cs" />
<Compile Include="Objects\Factory\Config\ObjectRole.cs" />
<Compile Include="Objects\Factory\Config\ResolvePropertyValueHandler.cs" />
<Compile Include="Objects\Factory\Config\ResourceHandlerConfigurer.cs" />
<Compile Include="Objects\Factory\Config\SharedStateAwareProcessor.cs" />
<Compile Include="Objects\Factory\Config\SmartInstantiationAwareObjectPostProcessor.cs" />
@@ -692,6 +691,7 @@
<Compile Include="Objects\Factory\Support\DefaultObjectNameGenerator.cs" />
<Compile Include="Objects\Factory\Support\IConfigurableObjectDefinition.cs" />
<Compile Include="Objects\Factory\Support\IObjectNameGenerator.cs" />
<Compile Include="Objects\Factory\Support\ManagedNameValueCollection.cs" />
<Compile Include="Objects\Factory\Support\ObjectDefinitionBuilder.cs" />
<Compile Include="Objects\Factory\Support\ObjectDefinitionValueResolver.cs" />
<Compile Include="Objects\Factory\Support\SimpleAutowireCandidateResolver.cs" />
@@ -710,6 +710,7 @@
<Compile Include="Objects\Factory\Xml\ParserContext.cs" />
<Compile Include="Objects\Factory\Xml\XmlReaderContext.cs" />
<Compile Include="Objects\FatalObjectException.cs" />
<Compile Include="Objects\IMergable.cs" />
<Compile Include="Objects\ISharedStateAware.cs" />
<Compile Include="Objects\ISharedStateFactory.cs" />
<Compile Include="Objects\MutablePropertyValues.cs" />
@@ -958,7 +959,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="Objects\Factory\Support\PropertiesObjectDefinitionReader.cs" />
<Compile Include="Objects\Factory\Support\ReplacedMethodOverride.cs">
<Compile Include="Objects\Factory\Xml\ReplacedMethodOverride.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Objects\Factory\Support\RootObjectDefinition.cs">
@@ -1196,6 +1197,7 @@
<SubType>
</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Objects\Factory\Xml\spring-objects-1.3.xsd" />
<None Include="Spring.Core.build" />
<EmbeddedResource Include="Validation\Config\spring-validation-1.1.xsd" />
<EmbeddedResource Include="Resources\Strings.resx">

View File

@@ -0,0 +1,75 @@
<?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>
<value>Rob Harrop</value>
<value>Rod Johnson</value>
</list>
</property>
</object>
<object id="childWithList" parent="parentWithList">
<property name="SomeList">
<list merge="true">
<value>Juergen Hoeller</value>
</list>
</property>
</object>
<object id="parentWithSet" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="SomeSet">
<set>
<value>Rob Harrop</value>
</set>
</property>
</object>
<object id="childWithSet" parent="parentWithSet">
<property name="SomeSet">
<set merge="true">
<value>Sally Greenwood</value>
</set>
</property>
</object>
<object id="parentWithMap" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="SomeMap">
<dictionary>
<entry key="Rob" value="Sall"/>
<entry key="Juergen" value="Eva"/>
</dictionary>
</property>
</object>
<object id="childWithMap" parent="parentWithMap">
<property name="SomeMap">
<dictionary merge="true">
<entry key="Rod" value="Kerry"/>
<entry key="Rob" value="Sally"/>
</dictionary>
</property>
</object>
<object id="parentWithNameValues" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="SomeNameValueCollection">
<name-values>
<add key="Rob" value="Sall"/>
<add key="Rod" value="Kerry"/>
</name-values>
</property>
</object>
<object id="childWithNameValues" parent="parentWithNameValues" >
<property name="SomeNameValueCollection">
<name-values merge="true">
<add key="Juergen" value="Eva"/>
<add key="Rob" value="Sally"/>
</name-values>
</property>
</object>
</objects>

View File

@@ -30,11 +30,77 @@ using Spring.Objects.Factory.Config;
namespace Spring.Objects.Factory.Support
{
/// <summary>
/// Integration tests for ManagedDictionary
/// </summary>
/// <author>Erich Eichinger</author>
/// <author>Mark Pollack</author>
[TestFixture]
public class ManagedDictionaryTests
{
[Test]
public void MergeSunnyDay()
{
ManagedDictionary parent = new ManagedDictionary();
parent.Add("one", "one");
parent.Add("two", "two");
ManagedDictionary child = new ManagedDictionary();
child.Add("three", "three");
child.MergeEnabled = true;
IDictionary mergedList = (IDictionary)child.Merge(parent);
Assert.AreEqual(3, mergedList.Count);
}
[Test]
public void MergeWithNullParent()
{
ManagedDictionary child = new ManagedDictionary();
child.MergeEnabled = true;
Assert.AreSame(child, child.Merge(null));
}
[Test]
[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = @"Not allowed to merge when the 'MergeEnabled' property is set to 'false'")]
public void MergeNotAllowedWhenMergeNotEnabled()
{
ManagedDictionary child = new ManagedDictionary();
child.Merge(null);
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void MergeWithNonCompatibleParentType()
{
ManagedDictionary child = new ManagedDictionary();
child.MergeEnabled = true;
child.Merge("hello");
}
[Test]
public void MergeEmptyChild()
{
ManagedDictionary parent = new ManagedDictionary();
parent.Add("one", "one");
parent.Add("two", "two");
ManagedDictionary child = new ManagedDictionary();
child.MergeEnabled = true;
IDictionary mergedMap = (IDictionary)child.Merge(parent);
Assert.AreEqual(2, mergedMap.Count);
}
[Test]
public void MergeChildValueOverrideTheParents()
{
ManagedDictionary parent = new ManagedDictionary();
parent.Add("one", "one");
parent.Add("two", "two");
ManagedDictionary child = new ManagedDictionary();
child.Add("one", "fork");
child.MergeEnabled = true;
IDictionary mergedMap = (IDictionary)child.Merge(parent);
Assert.AreEqual(2, mergedMap.Count);
Assert.AreEqual("fork", mergedMap["one"]);
}
#if NET_2_0
internal class InternalType
{

View File

@@ -0,0 +1,96 @@
#region License
/*
* Copyright 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
using System;
using System.Collections;
using NUnit.Framework;
namespace Spring.Objects.Factory.Support
{
[TestFixture]
public class ManagedListTests
{
[Test]
public void MergeSunnyDay()
{
ManagedList parent = new ManagedList();
parent.Add("one");
parent.Add("two");
ManagedList child = new ManagedList();
child.Add("three");
child.MergeEnabled = true;
IList mergedList = (IList) child.Merge(parent);
Assert.AreEqual(3, mergedList.Count);
}
[Test]
public void MergeWithNullParent()
{
ManagedList child = new ManagedList();
child.Add("one");
child.MergeEnabled = true;
Assert.AreSame(child, child.Merge(null));
}
[Test]
[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = @"Not allowed to merge when the 'MergeEnabled' property is set to 'false'")]
public void MergeNotAllowedWhenMergeNotEnabled()
{
ManagedList child = new ManagedList();
child.Merge(null);
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void MergeWithNonCompatibleParentType()
{
ManagedList child = new ManagedList();
child.Add("one");
child.MergeEnabled = true;
child.Merge("hello");
}
[Test]
public void MergeEmptyChild()
{
ManagedList parent = new ManagedList();
parent.Add("one");
parent.Add("two");
ManagedList child = new ManagedList();
child.MergeEnabled = true;
IList mergedList = (IList) child.Merge(parent);
Assert.AreEqual(2, mergedList.Count);
}
[Test]
public void MergeChildValueOverrideTheParents()
{
//doesn't make much sense in the context of a list...
ManagedList parent = new ManagedList();
parent.Add("one");
parent.Add("two");
ManagedList child = new ManagedList();
child.Add("one");
child.MergeEnabled = true;
IList mergedList = (IList) child.Merge(parent);
Assert.AreEqual(3, mergedList.Count);
}
}
}

View File

@@ -0,0 +1,99 @@
#region License
/*
* Copyright 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
using System;
using System.Collections;
using System.Collections.Specialized;
using NUnit.Framework;
namespace Spring.Objects.Factory.Support
{
/// <summary>
/// Integration tests for ManagedNameValueCollectionTests
/// </summary>
/// <author>Mark Pollack</author>
[TestFixture]
public class ManagedNameValueCollectionTests
{
[Test]
public void MergeSunnyDay()
{
ManagedNameValueCollection parent = new ManagedNameValueCollection();
parent.Add("one", "one");
parent.Add("two", "two");
ManagedNameValueCollection child = new ManagedNameValueCollection();
child.Add("three", "three");
child.MergeEnabled = true;
NameValueCollection mergedList = (NameValueCollection)child.Merge(parent);
Assert.AreEqual(3, mergedList.Count);
}
[Test]
public void MergeWithNullParent()
{
ManagedNameValueCollection child = new ManagedNameValueCollection();
child.MergeEnabled = true;
Assert.AreSame(child, child.Merge(null));
}
[Test]
[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = @"Not allowed to merge when the 'MergeEnabled' property is set to 'false'")]
public void MergeNotAllowedWhenMergeNotEnabled()
{
ManagedNameValueCollection child = new ManagedNameValueCollection();
child.Merge(null);
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void MergeWithNonCompatibleParentType()
{
ManagedNameValueCollection child = new ManagedNameValueCollection();
child.MergeEnabled = true;
child.Merge("hello");
}
[Test]
public void MergeEmptyChild()
{
ManagedNameValueCollection parent = new ManagedNameValueCollection();
parent.Add("one", "one");
parent.Add("two", "two");
ManagedNameValueCollection child = new ManagedNameValueCollection();
child.MergeEnabled = true;
NameValueCollection mergedMap = (NameValueCollection)child.Merge(parent);
Assert.AreEqual(2, mergedMap.Count);
}
[Test]
public void MergeChildValueOverrideTheParents()
{
ManagedNameValueCollection parent = new ManagedNameValueCollection();
parent.Add("one", "one");
parent.Add("two", "two");
ManagedNameValueCollection child = new ManagedNameValueCollection();
child.Add("one", "fork");
child.MergeEnabled = true;
NameValueCollection mergedMap = (NameValueCollection)child.Merge(parent);
Assert.AreEqual(2, mergedMap.Count);
Assert.AreEqual("fork", mergedMap["one"]);
}
}
}

View File

@@ -0,0 +1,96 @@
#region License
/*
* Copyright 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
using System;
using System.Collections;
using NUnit.Framework;
using Spring.Collections;
namespace Spring.Objects.Factory.Support
{
[TestFixture]
public class ManagedSetTests
{
[Test]
public void MergeSunnyDay()
{
ManagedSet parent = new ManagedSet();
parent.Add("one");
parent.Add("two");
ManagedSet child = new ManagedSet();
child.Add("three");
child.MergeEnabled = true;
ISet mergedList = (ISet) child.Merge(parent);
Assert.AreEqual(3, mergedList.Count);
}
[Test]
public void MergeWithNullParent()
{
ManagedSet child = new ManagedSet();
child.Add("one");
child.MergeEnabled = true;
Assert.AreSame(child, child.Merge(null));
}
[Test]
[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = @"Not allowed to merge when the 'MergeEnabled' property is set to 'false'")]
public void MergeNotAllowedWhenMergeNotEnabled()
{
ManagedSet child = new ManagedSet();
child.Merge(null);
}
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void MergeWithNonCompatibleParentType()
{
ManagedSet child = new ManagedSet();
child.Add("one");
child.MergeEnabled = true;
child.Merge("hello");
}
[Test]
public void MergeEmptyChild()
{
ManagedSet parent = new ManagedSet();
parent.Add("one");
parent.Add("two");
ManagedSet child = new ManagedSet();
child.MergeEnabled = true;
ISet mergedSet = (ISet) child.Merge(parent);
Assert.AreEqual(2, mergedSet.Count);
}
[Test]
public void MergeChildValueOverrideTheParents()
{
ManagedSet parent = new ManagedSet();
parent.Add("one");
parent.Add("two");
ManagedSet child = new ManagedSet();
child.Add("one");
child.MergeEnabled = true;
ISet mergedList = (ISet) child.Merge(parent);
Assert.AreEqual(2, mergedList.Count);
}
}
}

View File

@@ -0,0 +1,96 @@
#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
#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]
public class CollectionMergingTests
{
private DefaultListableObjectFactory objectFactory;
[SetUp]
public void SetUp()
{
this.objectFactory = new DefaultListableObjectFactory();
IObjectDefinitionReader reader = new XmlObjectDefinitionReader(this.objectFactory);
reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("collectionMerging.xml", GetType()));
}
[Test]
public void MergeList()
{
TestObject to = (TestObject) this.objectFactory.GetObject("childWithList");
IList list = to.SomeList;
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]));
}
[Test]
public void MergeSet()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithSet");
ISet set = to.SomeSet;
Assert.AreEqual(2, set.Count);
Assert.IsTrue(set.Contains("Rob Harrop"));
Assert.IsTrue(set.Contains("Sally Greenwood"));
}
[Test]
public void MergeDictionary()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithMap");
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 MergeNameValueCollection()
{
TestObject to = (TestObject)this.objectFactory.GetObject("childWithNameValues");
NameValueCollection map = to.SomeNameValueCollection;
Assert.AreEqual(3, map.Count);
Assert.AreEqual("Sally", map["Rob"]);
Assert.AreEqual("Kerry", map["Rod"]);
Assert.AreEqual("Eva", map["Juergen"]);
}
}
}

View File

@@ -22,6 +22,7 @@
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
@@ -259,6 +260,18 @@ namespace Spring.Objects
set { this.someMap = value; }
}
public virtual IList SomeList
{
get { return someList; }
set { this.someList = value;}
}
public virtual NameValueCollection SomeNameValueCollection
{
get { return someNameValueCollection; }
set { this.someNameValueCollection = value;}
}
protected virtual string HappyPlace
{
get { return _happyPlace; }
@@ -360,6 +373,7 @@ namespace Spring.Objects
private Set computers = new HybridSet();
private Set someSet = new HybridSet();
private IDictionary someMap = new Hashtable();
private IList someList = new ArrayList();
private DateTime date = DateTime.Now;
private Single myFloat = (float) 0.0;
private CultureInfo myCulture = CultureInfo.InvariantCulture;
@@ -382,6 +396,8 @@ namespace Spring.Objects
private bool initCompleted;
private IDictionary sharedState;
private NameValueCollection someNameValueCollection;
#endregion
#region Constructor (s) / Destructor

View File

@@ -948,6 +948,26 @@
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Expressions\OpADDTests.cs"
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Expressions\OpANDTests.cs"
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Expressions\OpORTests.cs"
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Expressions\OpXORTests.cs"
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Expressions\PropertyOrFieldNodeTests.cs"
SubType = "Code"

View File

@@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>9.0.30729</ProductVersion>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{44B16BAA-6DF8-447C-9D7F-3AD3D854D904}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -331,9 +331,13 @@
<Compile Include="Objects\Factory\Config\VariablePlaceholderConfigurerTests.cs" />
<Compile Include="Objects\Factory\DefaultListableObjectFactoryPerfTests.cs" />
<Compile Include="Objects\Factory\DummyConfigurableFactory.cs" />
<Compile Include="Objects\Factory\Support\ManagedNameValueCollectionTests.cs" />
<Compile Include="Objects\Factory\Support\ManagedListTests.cs" />
<Compile Include="Objects\Factory\Support\ManagedDictionaryTests.cs" />
<Compile Include="Objects\Factory\Support\ManagedSetTests.cs" />
<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\LocaleTests.cs" />
<Compile Include="Objects\Factory\Xml\NamespaceParserRegistryTests.cs" />
<Compile Include="Objects\Factory\Xml\ObjectFactorySectionHandlerTests.cs" />
@@ -806,6 +810,7 @@
<Content Include="Data\Spring\Objects\Factory\Xml\collections.xml" />
<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\objectNameGeneration.xml" />
<Content Include="Data\Spring\Objects\Factory\Xml\simple-constructor-arg.xml" />
<Content Include="Data\Spring\Objects\Factory\Xml\expressions.xml" />

View File

@@ -111,7 +111,7 @@ namespace Spring.Validation
private XmlDocument GetValidatedXmlResource(string resourceExtension)
{
AssemblyResource validationSchema = new AssemblyResource("assembly://Spring.Core/Spring.Validation.Config/spring-validation-1.1.xsd");
AssemblyResource objectsSchema = new AssemblyResource("assembly://Spring.Core/Spring.Objects.Factory.Xml/spring-objects-1.1.xsd");
AssemblyResource objectsSchema = new AssemblyResource("assembly://Spring.Core/Spring.Objects.Factory.Xml/spring-objects-1.3.xsd");
return TestResourceLoader.GetXmlValidated(this, resourceExtension, objectsSchema, validationSchema);
}