initial port of lower-level classes

This commit is contained in:
sbohlen
2010-11-10 01:16:59 +00:00
parent 84e6411dec
commit ff4dac6ea9
30 changed files with 3055 additions and 5 deletions

View File

@@ -0,0 +1,249 @@
/* Copyright <20> 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */
using System;
using System.Collections;
using System.Collections.Generic;
namespace Spring.Collections.Generic
{
/// <summary>
/// <p><c>DictionarySet</c> is an abstract class that supports the creation of new <c>Set</c>
/// types where the underlying data store is an <c>IDictionary</c> instance.</p>
///
/// <p>You can use any object that implements the <c>IDictionary</c> interface to hold set data.
/// You can define your own, or you can use one of the objects provided in the Framework.
/// The type of <c>IDictionary</c> you choose will affect both the performance and the behavior
/// of the <c>Set</c> using it. </p>
///
/// <p>To make a <c>Set</c> typed based on your own <c>IDictionary</c>, simply derive a
/// new class with a constructor that takes no parameters. Some <c>Set</c> implmentations
/// cannot be defined with a default constructor. If this is the case for your class,
/// you will need to override <c>Clone()</c> as well.</p>
///
/// <p>It is also standard practice that at least one of your constructors takes an <c>ICollection</c> or
/// an <c>ISet</c> as an argument.</p>
/// </summary>
[Serializable]
public abstract class DictionarySet<T> : Set<T>
{
/// <summary>
/// Provides the storage for elements in the <c>Set</c>, stored as the key-set
/// of the <c>IDictionary</c> object. Set this object in the constructor
/// if you create your own <c>Set</c> class.
/// </summary>
protected IDictionary<T, object> InternalDictionary = null;
private static readonly object PlaceholderObject = new object();
/// <summary>
/// The placeholder object used as the value for the <c>IDictionary</c> instance.
/// </summary>
/// <remarks>
/// There is a single instance of this object globally, used for all <c>Sets</c>.
/// </remarks>
protected object Placeholder
{
get { return PlaceholderObject; }
}
/// <summary>
/// Adds the specified element to this set if it is not already present.
/// </summary>
/// <param name="o">The <typeparamref name="T"/> to add to the set.</param>
/// <returns><see langword="true" /> is the object was added, <see langword="false" /> if it was already present.</returns>
public override bool Add(T o)
{
if (InternalDictionary.ContainsKey(o))
{
return false;
}
else
{
//The object we are adding is just a placeholder. The thing we are
//really concerned with is 'o', the key.
InternalDictionary.Add(o, PlaceholderObject);
return true;
}
}
/// <summary>
/// Adds all the elements in the specified collection to the set if they are not already present.
/// </summary>
/// <param name="c">A collection of objects to add to the set.</param>
/// <returns><see langword="true" /> is the set changed as a result of this operation, <see langword="false" /> if not.</returns>
public override bool AddAll(ICollection<T> c)
{
bool changed = false;
foreach (T o in c)
{
changed |= this.Add(o);
}
return changed;
}
/// <summary>
/// Removes all objects from the set.
/// </summary>
public override void Clear()
{
InternalDictionary.Clear();
}
/// <summary>
/// Returns <see langword="true" /> if this set contains the specified element.
/// </summary>
/// <param name="o">The element to look for.</param>
/// <returns><see langword="true" /> if this set contains the specified element, <see langword="false" /> otherwise.</returns>
public override bool Contains(T o)
{
return InternalDictionary.ContainsKey(o);
}
/// <summary>
/// Returns <see langword="true" /> if the set contains all the elements in the specified collection.
/// </summary>
/// <param name="c">A collection of objects.</param>
/// <returns><see langword="true" /> if the set contains all the elements in the specified collection, <see langword="false" /> otherwise.</returns>
public override bool ContainsAll(ICollection<T> c)
{
foreach (T o in c)
{
if (!this.Contains(o))
{
return false;
}
}
return true;
}
/// <summary>
/// Returns <see langword="true" /> if this set contains no elements.
/// </summary>
public override bool IsEmpty
{
get { return InternalDictionary.Count == 0; }
}
/// <summary>
/// Removes the specified element from the set.
/// </summary>
/// <param name="o">The element to be removed.</param>
/// <returns><see langword="true" /> if the set contained the specified element, <see langword="false" /> otherwise.</returns>
public override bool Remove(T o)
{
bool contained = this.Contains(o);
if (contained)
{
InternalDictionary.Remove(o);
}
return contained;
}
/// <summary>
/// Remove all the specified elements from this set, if they exist in this set.
/// </summary>
/// <param name="c">A collection of elements to remove.</param>
/// <returns><see langword="true" /> if the set was modified as a result of this operation.</returns>
public override bool RemoveAll(ICollection<T> c)
{
bool changed = false;
foreach (T o in c)
{
changed |= this.Remove(o);
}
return changed;
}
/// <summary>
/// Retains only the elements in this set that are contained in the specified collection.
/// </summary>
/// <param name="c">Collection that defines the set of elements to be retained.</param>
/// <returns><see langword="true" /> if this set changed as a result of this operation.</returns>
public override bool RetainAll(ICollection<T> c)
{
//Put data from C into a set so we can use the Contains() method.
Set<T> cSet = new HashedSet<T>(c);
//We are going to build a set of elements to remove.
Set<T> removeSet = new HashedSet<T>();
foreach (T o in this)
{
//If C does not contain O, then we need to remove O from our
//set. We can't do this while iterating through our set, so
//we put it into RemoveSet for later.
if (!cSet.Contains(o))
removeSet.Add(o);
}
return this.RemoveAll(removeSet);
}
/// <summary>
/// Copies the elements in the <c>Set</c> to an array of T. The type of array needs
/// to be compatible with the objects in the <c>Set</c>, obviously.
/// </summary>
/// <param name="array">An array that will be the target of the copy operation.</param>
/// <param name="index">The zero-based index where copying will start.</param>
public override void CopyTo(T[] array, int index)
{
InternalDictionary.Keys.CopyTo(array, index);
}
/// <summary>
/// The number of elements contained in this collection.
/// </summary>
public override int Count
{
get { return InternalDictionary.Count; }
}
/// <summary>
/// None of the objects based on <c>DictionarySet</c> are synchronized. Use the
/// <c>SyncRoot</c> property instead.
/// </summary>
public override bool IsSynchronized
{
get { return false; }
}
/// <summary>
/// Returns an object that can be used to synchronize the <c>Set</c> between threads.
/// </summary>
public override object SyncRoot
{
get { return ((ICollection) InternalDictionary).SyncRoot; }
}
/// <summary>
/// Gets an enumerator for the elements in the <c>Set</c>.
/// </summary>
/// <returns>An <c>IEnumerator</c> over the elements in the <c>Set</c>.</returns>
public override IEnumerator<T> GetEnumerator()
{
return InternalDictionary.Keys.GetEnumerator();
}
/// <summary>
/// Indicates wether the <c>Set</c> is read-only or not
/// </summary>
public override bool IsReadOnly
{
get { return InternalDictionary.IsReadOnly; }
}
/// <summary>
/// Copies the elements in the <c>Set</c> to an array. The type of array needs
/// to be compatible with the objects in the <c>Set</c>, obviously. Needed for
/// non-generic ISet methods implementation
/// </summary>
/// <param name="array">An array that will be the target of the copy operation.</param>
/// <param name="index">The zero-based index where copying will start.</param>
protected override void NonGenericCopyTo(Array array, int index)
{
((ICollection) InternalDictionary.Keys).CopyTo(array, index);
}
}
}

View File

@@ -0,0 +1,33 @@
/* Copyright <20> 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */
using System;
using System.Collections.Generic;
namespace Spring.Collections.Generic
{
/// <summary>
/// Implements a <c>Set</c> based on a Dictionary (which is equivalent of
/// non-genric <c>HashTable</c>) This will give the best lookup, add, and remove
/// performance for very large data-sets, but iteration will occur in no particular order.
/// </summary>
[Serializable]
public class HashedSet<T> : DictionarySet<T>
{
/// <summary>
/// Creates a new set instance based on a Dictinary.
/// </summary>
public HashedSet()
{
InternalDictionary = new Dictionary<T, object>();
}
/// <summary>
/// Creates a new set instance based on a Dictinary and
/// initializes it based on a collection of elements.
/// </summary>
/// <param name="initialValues">A collection of elements that defines the initial set contents.</param>
public HashedSet(ICollection<T> initialValues) : this()
{
this.AddAll(initialValues);
}
}
}

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Spring.Collections.Generic
{
/// <summary>
/// <p>A collection that contains no duplicate elements. This interface models the mathematical
/// <c>Set</c> abstraction.
/// The order of elements in a set is dependant on (a)the data-structure implementation, and
/// (b)the implementation of the various <c>Set</c> methods, and thus is not guaranteed.</p>
///
/// <p>None of the <c>Set</c> implementations in this library are guranteed to be thread-safe
/// in any way unless wrapped in a <c>SynchronizedSet</c>.</p>
///
/// <p>The following table summarizes the binary operators that are supported by the <c>Set</c> class.</p>
/// <list type="table">
/// <listheader>
/// <term>Operation</term>
/// <term>Description</term>
/// <term>Method</term>
/// </listheader>
/// <item>
/// <term>Union (OR)</term>
/// <term>Element included in result if it exists in either <c>A</c> OR <c>B</c>.</term>
/// <term><c>Union()</c></term>
/// </item>
/// <item>
/// <term>Intersection (AND)</term>
/// <term>Element included in result if it exists in both <c>A</c> AND <c>B</c>.</term>
/// <term><c>InterSect()</c></term>
/// </item>
/// <item>
/// <term>Exclusive Or (XOR)</term>
/// <term>Element included in result if it exists in one, but not both, of <c>A</c> and <c>B</c>.</term>
/// <term><c>ExclusiveOr()</c></term>
/// </item>
/// <item>
/// <term>Minus (n/a)</term>
/// <term>Take all the elements in <c>A</c>. Now, if any of them exist in <c>B</c>, remove
/// them. Note that unlike the other operators, <c>A - B</c> is not the same as <c>B - A</c>.</term>
/// <term><c>Minus()</c></term>
/// </item>
/// </list>
/// </summary>
public interface ISet<T> : ICollection<T>, IEnumerable<T>, IEnumerable, ICloneable
{
// Clear is declared in ICollection<T>, but not in ICollection
// void Clear();
// Remove is declared in ICollection<T>, but not in ICollection
// bool Remove(T o);
// Contains is declared in ICollection<T>, but not in ICollection
// bool Contains(T o);
/// <summary>
/// Performs a "union" of the two sets, where all the elements
/// in both sets are present. That is, the element is included if it is in either <c>a</c> or <c>b</c>.
/// Neither this set nor the input set are modified during the operation. The return value
/// is a <c>Clone()</c> of this set with the extra elements added in.
/// </summary>
/// <param name="a">A collection of elements.</param>
/// <returns>A new <c>Set</c> containing the union of this <c>Set</c> with the specified collection.
/// Neither of the input objects is modified by the union.</returns>
ISet<T> Union(ISet<T> a);
/// <summary>
/// Performs an "intersection" of the two sets, where only the elements
/// that are present in both sets remain. That is, the element is included if it exists in
/// both sets. The <c>Intersect()</c> operation does not modify the input sets. It returns
/// a <c>Clone()</c> of this set with the appropriate elements removed.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <returns>The intersection of this set with <c>a</c>.</returns>
ISet<T> Intersect(ISet<T> a);
/// <summary>
/// Performs a "minus" of set <c>b</c> from set <c>a</c>. This returns a set of all
/// the elements in set <c>a</c>, removing the elements that are also in set <c>b</c>.
/// The original sets are not modified during this operation. The result set is a <c>Clone()</c>
/// of this <c>Set</c> containing the elements from the operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <returns>A set containing the elements from this set with the elements in <c>a</c> removed.</returns>
ISet<T> Minus(ISet<T> a);
/// <summary>
/// Performs an "exclusive-or" of the two sets, keeping only the elements that
/// are in one of the sets, but not in both. The original sets are not modified
/// during this operation. The result set is a <c>Clone()</c> of this set containing
/// the elements from the exclusive-or operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <returns>A set containing the result of <c>a ^ b</c>.</returns>
ISet<T> ExclusiveOr(ISet<T> a);
/// <summary>
/// Returns <see langword="true" /> if the set contains all the elements in the specified collection.
/// </summary>
/// <param name="c">A collection of objects.</param>
/// <returns><see langword="true" /> if the set contains all the elements in the specified collection, <see langword="false" /> otherwise.</returns>
bool ContainsAll(ICollection<T> c);
/// <summary>
/// Returns <see langword="true" /> if this set contains no elements.
/// </summary>
bool IsEmpty { get; }
/// <summary>
/// Adds the specified element to this set if it is not already present.
/// </summary>
/// <param name="o">The object to add to the set.</param>
/// <returns><see langword="true" /> is the object was added, <see langword="false" /> if it was already present.</returns>
new bool Add(T o);
/// <summary>
/// Adds all the elements in the specified collection to the set if they are not already present.
/// </summary>
/// <param name="c">A collection of objects to add to the set.</param>
/// <returns><see langword="true" /> is the set changed as a result of this operation, <see langword="false" /> if not.</returns>
bool AddAll(ICollection<T> c);
/// <summary>
/// Remove all the specified elements from this set, if they exist in this set.
/// </summary>
/// <param name="c">A collection of elements to remove.</param>
/// <returns><see langword="true" /> if the set was modified as a result of this operation.</returns>
bool RemoveAll(ICollection<T> c);
/// <summary>
/// Retains only the elements in this set that are contained in the specified collection.
/// </summary>
/// <param name="c">Collection that defines the set of elements to be retained.</param>
/// <returns><see langword="true" /> if this set changed as a result of this operation.</returns>
bool RetainAll(ICollection<T> c);
}
}

View File

@@ -0,0 +1,308 @@
/* Copyright <20> 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */
using System;
using System.Collections;
using System.Collections.Generic;
namespace Spring.Collections.Generic
{
/// <summary>
/// <p>Implements an immutable (read-only) <c>Set</c> wrapper.</p>
/// <p>Although this is advertised as immutable, it really isn't. Anyone with access to the
/// <c>basisSet</c> can still change the data-set. So <c>GetHashCode()</c> is not implemented
/// for this <c>Set</c>, as is the case for all <c>Set</c> implementations in this library.
/// This design decision was based on the efficiency of not having to <c>Clone()</c> the
/// <c>basisSet</c> every time you wrap a mutable <c>Set</c>.</p>
/// </summary>
[Serializable]
public sealed class ImmutableSet<T> : Set<T>
{
private const string ERROR_MESSAGE = "Object is immutable.";
private ISet<T> mBasisSet;
internal ISet<T> BasisSet
{
get { return mBasisSet; }
}
/// <summary>
/// Constructs an immutable (read-only) <c>Set</c> wrapper.
/// </summary>
/// <param name="basisSet">The <c>Set</c> that is wrapped.</param>
public ImmutableSet(ISet<T> basisSet)
{
mBasisSet = basisSet;
}
/// <summary>
/// Adds the specified element to this set if it is not already present.
/// </summary>
/// <param name="o">The object to add to the set.</param>
/// <returns>nothing</returns>
/// <exception cref="NotSupportedException"> is always thrown</exception>
public override sealed bool Add(T o)
{
throw new NotSupportedException(ERROR_MESSAGE);
}
/// <summary>
/// Adds all the elements in the specified collection to the set if they are not already present.
/// </summary>
/// <param name="c">A collection of objects to add to the set.</param>
/// <returns>nothing</returns>
/// <exception cref="NotSupportedException"> is always thrown</exception>
public override sealed bool AddAll(ICollection<T> c)
{
throw new NotSupportedException(ERROR_MESSAGE);
}
/// <summary>
/// Removes all objects from the set.
/// </summary>
/// <exception cref="NotSupportedException"> is always thrown</exception>
public override sealed void Clear()
{
throw new NotSupportedException(ERROR_MESSAGE);
}
/// <summary>
/// Returns <see langword="true" /> if this set contains the specified element.
/// </summary>
/// <param name="o">The element to look for.</param>
/// <returns><see langword="true" /> if this set contains the specified element, <see langword="false" /> otherwise.</returns>
public override sealed bool Contains(T o)
{
return mBasisSet.Contains(o);
}
/// <summary>
/// Returns <see langword="true" /> if the set contains all the elements in the specified collection.
/// </summary>
/// <param name="c">A collection of objects.</param>
/// <returns><see langword="true" /> if the set contains all the elements in the specified collection, <see langword="false" /> otherwise.</returns>
public override sealed bool ContainsAll(ICollection<T> c)
{
return mBasisSet.ContainsAll(c);
}
/// <summary>
/// Returns <see langword="true" /> if this set contains no elements.
/// </summary>
public override sealed bool IsEmpty
{
get { return mBasisSet.IsEmpty; }
}
/// <summary>
/// Removes the specified element from the set.
/// </summary>
/// <param name="o">The element to be removed.</param>
/// <returns>nothing</returns>
/// <exception cref="NotSupportedException"> is always thrown</exception>
public override sealed bool Remove(T o)
{
throw new NotSupportedException(ERROR_MESSAGE);
}
/// <summary>
/// Remove all the specified elements from this set, if they exist in this set.
/// </summary>
/// <param name="c">A collection of elements to remove.</param>
/// <returns>nothing</returns>
/// <exception cref="NotSupportedException"> is always thrown</exception>
public override sealed bool RemoveAll(ICollection<T> c)
{
throw new NotSupportedException(ERROR_MESSAGE);
}
/// <summary>
/// Retains only the elements in this set that are contained in the specified collection.
/// </summary>
/// <param name="c">Collection that defines the set of elements to be retained.</param>
/// <returns>nothing</returns>
/// <exception cref="NotSupportedException"> is always thrown</exception>
public override sealed bool RetainAll(ICollection<T> c)
{
throw new NotSupportedException(ERROR_MESSAGE);
}
/// <summary>
/// Copies the elements in the <c>Set</c> to an array of T. The type of array needs
/// to be compatible with the objects in the <c>Set</c>, obviously.
/// </summary>
/// <param name="array">An array that will be the target of the copy operation.</param>
/// <param name="index">The zero-based index where copying will start.</param>
public override sealed void CopyTo(T[] array, int index)
{
mBasisSet.CopyTo(array, index);
}
/// <summary>
/// The number of elements contained in this collection.
/// </summary>
public override sealed int Count
{
get { return mBasisSet.Count; }
}
/// <summary>
/// Returns an object that can be used to synchronize use of the <c>Set</c> across threads.
/// </summary>
public override sealed bool IsSynchronized
{
get { return ((ICollection) mBasisSet).IsSynchronized; }
}
/// <summary>
/// Returns an object that can be used to synchronize the <c>Set</c> between threads.
/// </summary>
public override sealed object SyncRoot
{
get { return ((ICollection) mBasisSet).SyncRoot; }
}
/// <summary>
/// Gets an enumerator for the elements in the <c>Set</c>.
/// </summary>
/// <returns>An <c>IEnumerator</c> over the elements in the <c>Set</c>.</returns>
public override sealed IEnumerator<T> GetEnumerator()
{
return mBasisSet.GetEnumerator();
}
/// <summary>
/// Returns a clone of the <c>Set</c> instance.
/// </summary>
/// <returns>A clone of this object.</returns>
public override sealed object Clone()
{
return new ImmutableSet<T>(mBasisSet);
}
/// <summary>
/// Performs a "union" of the two sets, where all the elements
/// in both sets are present. That is, the element is included if it is in either <c>a</c> or <c>b</c>.
/// Neither this set nor the input set are modified during the operation. The return value
/// is a <c>Clone()</c> of this set with the extra elements added in.
/// </summary>
/// <param name="a">A collection of elements.</param>
/// <returns>A new <c>Set</c> containing the union of this <c>Set</c> with the specified collection.
/// Neither of the input objects is modified by the union.</returns>
public override sealed ISet<T> Union(ISet<T> a)
{
ISet<T> m = GetUltimateBasisSet();
return new ImmutableSet<T>(m.Union(a));
}
/// <summary>
/// Performs an "intersection" of the two sets, where only the elements
/// that are present in both sets remain. That is, the element is included if it exists in
/// both sets. The <c>Intersect()</c> operation does not modify the input sets. It returns
/// a <c>Clone()</c> of this set with the appropriate elements removed.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <returns>The intersection of this set with <c>a</c>.</returns>
public override sealed ISet<T> Intersect(ISet<T> a)
{
ISet<T> m = GetUltimateBasisSet();
return new ImmutableSet<T>(m.Intersect(a));
}
/// <summary>
/// Performs a "minus" of set <c>b</c> from set <c>a</c>. This returns a set of all
/// the elements in set <c>a</c>, removing the elements that are also in set <c>b</c>.
/// The original sets are not modified during this operation. The result set is a <c>Clone()</c>
/// of this <c>Set</c> containing the elements from the operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <returns>A set containing the elements from this set with the elements in <c>a</c> removed.</returns>
public override sealed ISet<T> Minus(ISet<T> a)
{
ISet<T> m = GetUltimateBasisSet();
return new ImmutableSet<T>(m.Minus(a));
}
/// <summary>
/// Performs an "exclusive-or" of the two sets, keeping only the elements that
/// are in one of the sets, but not in both. The original sets are not modified
/// during this operation. The result set is a <c>Clone()</c> of this set containing
/// the elements from the exclusive-or operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <returns>A set containing the result of <c>a ^ b</c>.</returns>
public override sealed ISet<T> ExclusiveOr(ISet<T> a)
{
ISet<T> m = GetUltimateBasisSet();
return new ImmutableSet<T>(m.ExclusiveOr(a));
}
/// <summary>
/// Indicates that the given instance is read-only
/// </summary>
public override sealed bool IsReadOnly
{
get { return true; }
}
/// <summary>
/// Performs CopyTo when called trhough non-generic ISet (ICollection) interface
/// </summary>
/// <param name="array"></param>
/// <param name="index"></param>
protected override void NonGenericCopyTo(Array array, int index)
{
((ICollection) this.BasisSet).CopyTo(array, index);
}
/// <summary>
/// Performs Union when called trhough non-generic ISet interface
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
protected override sealed ISet NonGenericUnion(ISet a)
{
ISet<T> m = GetUltimateBasisSet();
return new ImmutableSet(((ISet) m).Union(a));
}
/// <summary>
/// Performs Minus when called trhough non-generic ISet interface
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
protected override sealed ISet NonGenericMinus(ISet a)
{
ISet<T> m = GetUltimateBasisSet();
return new ImmutableSet(((ISet) m).Minus(a));
}
/// <summary>
/// Performs Intersect when called trhough non-generic ISet interface
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
protected override sealed ISet NonGenericIntersect(ISet a)
{
ISet<T> m = GetUltimateBasisSet();
return new ImmutableSet(((ISet) m).Intersect(a));
}
/// <summary>
/// Performs ExclusiveOr when called trhough non-generic ISet interface
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
protected override sealed ISet NonGenericExclusiveOr(ISet a)
{
ISet<T> m = GetUltimateBasisSet();
return new ImmutableSet(((ISet) m).ExclusiveOr(a));
}
private ISet<T> GetUltimateBasisSet()
{
ISet<T> m = this.mBasisSet;
while (m is ImmutableSet<T>)
m = ((ImmutableSet<T>) m).mBasisSet;
return m;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
namespace Spring.Collections.Generic
{
/// <summary>
/// Implements an ordered <c>Set</c> based on a dictionary.
/// </summary>
[Serializable]
public class OrderedSet<T> : DictionarySet<T>
{
/// <summary>
/// Initializes a new instance of the <see cref="OrderedSet{T}" /> class.
/// </summary>
public OrderedSet()
{
InternalDictionary = new Dictionary<T, object>();
}
/// <summary>
/// Initializes a new instance of the <see cref="OrderedSet{T}"/> class.
/// </summary>
/// <param name="initialValues">A collection of elements that defines the initial set contents.</param>
public OrderedSet(ICollection<T> initialValues)
: this()
{
AddAll(initialValues);
}
}
}

View File

@@ -0,0 +1,561 @@
/* Copyright <20> 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */
using System;
using System.Collections;
using System.Collections.Generic;
namespace Spring.Collections.Generic
{
/// <summary><p>A collection that contains no duplicate elements. This class models the mathematical
/// <c>Set</c> abstraction, and is the base class for all other <c>Set</c> implementations.
/// The order of elements in a set is dependant on (a)the data-structure implementation, and
/// (b)the implementation of the various <c>Set</c> methods, and thus is not guaranteed.</p>
///
/// <p>None of the <c>Set</c> implementations in this library are guranteed to be thread-safe
/// in any way unless wrapped in a <c>SynchronizedSet</c>.</p>
///
/// <p>The following table summarizes the binary operators that are supported by the <c>Set</c> class.</p>
/// <list type="table">
/// <listheader>
/// <term>Operation</term>
/// <term>Description</term>
/// <term>Method</term>
/// <term>Operator</term>
/// </listheader>
/// <item>
/// <term>Union (OR)</term>
/// <term>Element included in result if it exists in either <c>A</c> OR <c>B</c>.</term>
/// <term><c>Union()</c></term>
/// <term><c>|</c></term>
/// </item>
/// <item>
/// <term>Intersection (AND)</term>
/// <term>Element included in result if it exists in both <c>A</c> AND <c>B</c>.</term>
/// <term><c>InterSect()</c></term>
/// <term><c>&amp;</c></term>
/// </item>
/// <item>
/// <term>Exclusive Or (XOR)</term>
/// <term>Element included in result if it exists in one, but not both, of <c>A</c> and <c>B</c>.</term>
/// <term><c>ExclusiveOr()</c></term>
/// <term><c>^</c></term>
/// </item>
/// <item>
/// <term>Minus (n/a)</term>
/// <term>Take all the elements in <c>A</c>. Now, if any of them exist in <c>B</c>, remove
/// them. Note that unlike the other operators, <c>A - B</c> is not the same as <c>B - A</c>.</term>
/// <term><c>Minus()</c></term>
/// <term><c>-</c></term>
/// </item>
/// </list>
/// </summary>
[Serializable]
public abstract class Set<T> : ISet<T>, ICollection<T>, IEnumerable<T>,
ISet, ICollection, IEnumerable, ICloneable
{
/// <summary>
/// Performs a "union" of the two sets, where all the elements
/// in both sets are present. That is, the element is included if it is in either <c>a</c> or <c>b</c>.
/// Neither this set nor the input set are modified during the operation. The return value
/// is a <c>Clone()</c> of this set with the extra elements added in.
/// </summary>
/// <param name="a">A collection of elements.</param>
/// <returns>A new <c>Set</c> containing the union of this <c>Set</c> with the specified collection.
/// Neither of the input objects is modified by the union.</returns>
public virtual ISet<T> Union(ISet<T> a)
{
ISet<T> resultSet = (ISet<T>) this.Clone();
if (a != null)
{
resultSet.AddAll(a);
}
return resultSet;
}
/// <summary>
/// Performs a "union" of two sets, where all the elements
/// in both are present. That is, the element is included if it is in either <c>a</c> or <c>b</c>.
/// The return value is a <c>Clone()</c> of one of the sets (<c>a</c> if it is not <see langword="null" />) with elements of the other set
/// added in. Neither of the input sets is modified by the operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <param name="b">A set of elements.</param>
/// <returns>A set containing the union of the input sets. <see langword="null" /> if both sets are <see langword="null" />.</returns>
public static ISet<T> Union(ISet<T> a, ISet<T> b)
{
if (a == null && b == null)
return null;
else if (a == null)
return (ISet<T>) b.Clone();
else if (b == null)
return (ISet<T>) a.Clone();
else
return a.Union(b);
}
/// <summary>
/// Performs a "union" of two sets, where all the elements
/// in both are present. That is, the element is included if it is in either <c>a</c> or <c>b</c>.
/// The return value is a <c>Clone()</c> of one of the sets (<c>a</c> if it is not <see langword="null" />) with elements of the other set
/// added in. Neither of the input sets is modified by the operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <param name="b">A set of elements.</param>
/// <returns>A set containing the union of the input sets. <see langword="null" /> if both sets are <see langword="null" />.</returns>
public static Set<T> operator |(Set<T> a, Set<T> b)
{
return (Set<T>) Union(a, b);
}
/// <summary>
/// Performs an "intersection" of the two sets, where only the elements
/// that are present in both sets remain. That is, the element is included if it exists in
/// both sets. The <c>Intersect()</c> operation does not modify the input sets. It returns
/// a <c>Clone()</c> of this set with the appropriate elements removed.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <returns>The intersection of this set with <c>a</c>.</returns>
public virtual ISet<T> Intersect(ISet<T> a)
{
ISet<T> resultSet = (ISet<T>) this.Clone();
if (a != null)
resultSet.RetainAll(a);
else
resultSet.Clear();
return resultSet;
}
/// <summary>
/// Performs an "intersection" of the two sets, where only the elements
/// that are present in both sets remain. That is, the element is included only if it exists in
/// both <c>a</c> and <c>b</c>. Neither input object is modified by the operation.
/// The result object is a <c>Clone()</c> of one of the input objects (<c>a</c> if it is not <see langword="null" />) containing the
/// elements from the intersect operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <param name="b">A set of elements.</param>
/// <returns>The intersection of the two input sets. <see langword="null" /> if both sets are <see langword="null" />.</returns>
public static ISet<T> Intersect(ISet<T> a, ISet<T> b)
{
if (a == null && b == null)
return null;
else if (a == null)
{
return b.Intersect(a);
}
else
return a.Intersect(b);
}
/// <summary>
/// Performs an "intersection" of the two sets, where only the elements
/// that are present in both sets remain. That is, the element is included only if it exists in
/// both <c>a</c> and <c>b</c>. Neither input object is modified by the operation.
/// The result object is a <c>Clone()</c> of one of the input objects (<c>a</c> if it is not <see langword="null" />) containing the
/// elements from the intersect operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <param name="b">A set of elements.</param>
/// <returns>The intersection of the two input sets. <see langword="null" /> if both sets are <see langword="null" />.</returns>
public static Set<T> operator &(Set<T> a, Set<T> b)
{
return (Set<T>) Intersect(a, b);
}
/// <summary>
/// Performs a "minus" of set <c>b</c> from set <c>a</c>. This returns a set of all
/// the elements in set <c>a</c>, removing the elements that are also in set <c>b</c>.
/// The original sets are not modified during this operation. The result set is a <c>Clone()</c>
/// of this <c>Set</c> containing the elements from the operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <returns>A set containing the elements from this set with the elements in <c>a</c> removed.</returns>
public virtual ISet<T> Minus(ISet<T> a)
{
ISet<T> resultSet = (ISet<T>) this.Clone();
if (a != null)
resultSet.RemoveAll(a);
return resultSet;
}
/// <summary>
/// Performs a "minus" of set <c>b</c> from set <c>a</c>. This returns a set of all
/// the elements in set <c>a</c>, removing the elements that are also in set <c>b</c>.
/// The original sets are not modified during this operation. The result set is a <c>Clone()</c>
/// of set <c>a</c> containing the elements from the operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <param name="b">A set of elements.</param>
/// <returns>A set containing <c>A - B</c> elements. <see langword="null" /> if <c>a</c> is <see langword="null" />.</returns>
public static ISet<T> Minus(ISet<T> a, ISet<T> b)
{
if (a == null)
return null;
else
return a.Minus(b);
}
/// <summary>
/// Performs a "minus" of set <c>b</c> from set <c>a</c>. This returns a set of all
/// the elements in set <c>a</c>, removing the elements that are also in set <c>b</c>.
/// The original sets are not modified during this operation. The result set is a <c>Clone()</c>
/// of set <c>a</c> containing the elements from the operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <param name="b">A set of elements.</param>
/// <returns>A set containing <c>A - B</c> elements. <see langword="null" /> if <c>a</c> is <see langword="null" />.</returns>
public static Set<T> operator -(Set<T> a, Set<T> b)
{
return (Set<T>) Minus(a, b);
}
/// <summary>
/// Performs an "exclusive-or" of the two sets, keeping only the elements that
/// are in one of the sets, but not in both. The original sets are not modified
/// during this operation. The result set is a <c>Clone()</c> of this set containing
/// the elements from the exclusive-or operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <returns>A set containing the result of <c>a ^ b</c>.</returns>
public virtual ISet<T> ExclusiveOr(ISet<T> a)
{
ISet<T> resultSet = (ISet<T>) this.Clone();
foreach (T element in a)
{
if (resultSet.Contains(element))
resultSet.Remove(element);
else
resultSet.Add(element);
}
return resultSet;
}
/// <summary>
/// Performs an "exclusive-or" of the two sets, keeping only the elements that
/// are in one of the sets, but not in both. The original sets are not modified
/// during this operation. The result set is a <c>Clone()</c> of one of the sets
/// (<c>a</c> if it is not <see langword="null" />) containing
/// the elements from the exclusive-or operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <param name="b">A set of elements.</param>
/// <returns>A set containing the result of <c>a ^ b</c>. <see langword="null" /> if both sets are <see langword="null" />.</returns>
public static ISet<T> ExclusiveOr(ISet<T> a, ISet<T> b)
{
if (a == null && b == null)
return null;
else if (a == null)
return (ISet<T>) b.Clone();
else if (b == null)
return (ISet<T>) a.Clone();
else
return a.ExclusiveOr(b);
}
/// <summary>
/// Performs an "exclusive-or" of the two sets, keeping only the elements that
/// are in one of the sets, but not in both. The original sets are not modified
/// during this operation. The result set is a <c>Clone()</c> of one of the sets
/// (<c>a</c> if it is not <see langword="null" />) containing
/// the elements from the exclusive-or operation.
/// </summary>
/// <param name="a">A set of elements.</param>
/// <param name="b">A set of elements.</param>
/// <returns>A set containing the result of <c>a ^ b</c>. <see langword="null" /> if both sets are <see langword="null" />.</returns>
public static Set<T> operator ^(Set<T> a, Set<T> b)
{
return (Set<T>) ExclusiveOr(a, b);
}
/// <summary>
/// Adds the specified element to this set if it is not already present.
/// </summary>
/// <param name="o">The object to add to the set.</param>
/// <returns><see langword="true" /> is the object was added, <see langword="false" /> if it was already present.</returns>
public abstract bool Add(T o);
/// <summary>
/// Adds all the elements in the specified collection to the set if they are not already present.
/// </summary>
/// <param name="c">A collection of objects to add to the set.</param>
/// <returns><see langword="true" /> is the set changed as a result of this operation, <see langword="false" /> if not.</returns>
public abstract bool AddAll(ICollection<T> c);
/// <summary>
/// Removes all objects from the set.
/// </summary>
public abstract void Clear();
/// <summary>
/// Returns <see langword="true" /> if this set contains the specified element.
/// </summary>
/// <param name="o">The element to look for.</param>
/// <returns><see langword="true" /> if this set contains the specified element, <see langword="false" /> otherwise.</returns>
public abstract bool Contains(T o);
/// <summary>
/// Returns <see langword="true" /> if the set contains all the elements in the specified collection.
/// </summary>
/// <param name="c">A collection of objects.</param>
/// <returns><see langword="true" /> if the set contains all the elements in the specified collection, <see langword="false" /> otherwise.</returns>
public abstract bool ContainsAll(ICollection<T> c);
/// <summary>
/// Returns <see langword="true" /> if this set contains no elements.
/// </summary>
public abstract bool IsEmpty { get; }
/// <summary>
/// Removes the specified element from the set.
/// </summary>
/// <param name="o">The element to be removed.</param>
/// <returns><see langword="true" /> if the set contained the specified element, <see langword="false" /> otherwise.</returns>
public abstract bool Remove(T o);
/// <summary>
/// Remove all the specified elements from this set, if they exist in this set.
/// </summary>
/// <param name="c">A collection of elements to remove.</param>
/// <returns><see langword="true" /> if the set was modified as a result of this operation.</returns>
public abstract bool RemoveAll(ICollection<T> c);
/// <summary>
/// Retains only the elements in this set that are contained in the specified collection.
/// </summary>
/// <param name="c">Collection that defines the set of elements to be retained.</param>
/// <returns><see langword="true" /> if this set changed as a result of this operation.</returns>
public abstract bool RetainAll(ICollection<T> c);
/// <summary>
/// Returns a clone of the <c>Set</c> instance. This will work for derived <c>Set</c>
/// classes if the derived class implements a constructor that takes no arguments.
/// </summary>
/// <returns>A clone of this object.</returns>
public virtual object Clone()
{
Set<T> newSet = (Set<T>) Activator.CreateInstance(this.GetType());
newSet.AddAll(this);
return newSet;
}
/// <summary>
/// Copies the elements in the <c>Set</c> to an array. The type of array needs
/// to be compatible with the objects in the <c>Set</c>, obviously.
/// </summary>
/// <param name="array">An array that will be the target of the copy operation.</param>
/// <param name="index">The zero-based index where copying will start.</param>
public abstract void CopyTo(T[] array, int index);
/// <summary>
/// The number of elements currently contained in this collection.
/// </summary>
public abstract int Count { get; }
/// <summary>
/// Returns <see langword="true" /> if the <c>Set</c> is synchronized across threads. Note that
/// enumeration is inherently not thread-safe. Use the <c>SyncRoot</c> to lock the
/// object during enumeration.
/// </summary>
public abstract bool IsSynchronized { get; }
/// <summary>
/// An object that can be used to synchronize this collection to make it thread-safe.
/// When implementing this, if your object uses a base object, like an <c>IDictionary</c>,
/// or anything that has a <c>SyncRoot</c>, return that object instead of "<c>this</c>".
/// </summary>
public abstract object SyncRoot { get; }
/// <summary>
/// Gets an enumerator for the elements in the <c>Set</c>.
/// </summary>
/// <returns>An <c>IEnumerator</c> over the elements in the <c>Set</c>.</returns>
public abstract IEnumerator<T> GetEnumerator();
/// <summary>
/// Indicates whether the given instance is read-only or not
/// </summary>
/// <value>
/// <see langword="true" /> if the ISet is read-only; otherwise, <see langword="false" />.
/// In the default implementation of Set, this property always returns false.
/// </value>
public virtual bool IsReadOnly
{
get { return false; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection<T>.Add(T item)
{
this.Add(item);
}
#region Protected helpers
/// <summary>
/// Performs CopyTo when called trhough non-generic ISet (ICollection) interface
/// </summary>
/// <param name="array"></param>
/// <param name="index"></param>
protected abstract void NonGenericCopyTo(Array array, int index);
/// <summary>
/// Performs Union when called trhough non-generic ISet interface
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
protected virtual ISet NonGenericUnion(ISet a)
{
ISet resultSet = (ISet) this.Clone();
if (a != null)
resultSet.AddAll(a);
return resultSet;
}
/// <summary>
/// Performs Minus when called trhough non-generic ISet interface
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
protected virtual ISet NonGenericMinus(ISet a)
{
ISet resultSet = (ISet) this.Clone();
if (a != null)
resultSet.RemoveAll(a);
return resultSet;
}
/// <summary>
/// Performs Intersect when called trhough non-generic ISet interface
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
protected virtual ISet NonGenericIntersect(ISet a)
{
ISet resultSet = (ISet) this.Clone();
if (a != null)
resultSet.RetainAll(a);
else
resultSet.Clear();
return resultSet;
}
/// <summary>
/// Performs ExclusiveOr when called trhough non-generic ISet interface
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
protected virtual ISet NonGenericExclusiveOr(ISet a)
{
ISet resultSet = (ISet) this.Clone();
foreach (object element in a)
{
if (resultSet.Contains(element))
resultSet.Remove(element);
else
resultSet.Add(element);
}
return resultSet;
}
#endregion Protected helpers
#region ISet implementation
void ICollection.CopyTo(Array array, int index)
{
this.NonGenericCopyTo(array, index);
}
ISet ISet.Union(ISet a)
{
return this.NonGenericUnion(a);
}
ISet ISet.Intersect(ISet a)
{
return NonGenericIntersect(a);
}
ISet ISet.Minus(ISet a)
{
return NonGenericMinus(a);
}
ISet ISet.ExclusiveOr(ISet a)
{
return NonGenericExclusiveOr(a);
}
bool ISet.Contains(object o)
{
if (o is T)
return this.Contains((T) o);
else
return false;
}
bool ISet.ContainsAll(ICollection c)
{
ICollection<T> col = new List<T>(c.Count);
foreach (object o in c)
{
if (o is T)
col.Add((T) o);
else
return false;
}
return this.ContainsAll(col);
}
bool ISet.Add(object o)
{
return this.Add((T) o);
}
bool ISet.AddAll(ICollection c)
{
bool changed = false;
foreach (T obj in c)
changed |= this.Add(obj);
return changed;
}
bool ISet.Remove(object o)
{
if (o is T)
return this.Remove((T) o);
else
return false;
}
bool ISet.RemoveAll(ICollection c)
{
ICollection<T> col = new List<T>(c.Count);
foreach (object o in c)
{
if (o is T)
col.Add((T) o);
}
return this.RemoveAll(col);
}
bool ISet.RetainAll(ICollection c)
{
ICollection<T> col = new List<T>(c.Count);
foreach (object o in c)
{
if (o is T)
col.Add((T) o);
}
return this.RetainAll(col);
}
#endregion ISet implementation
}
}

View File

@@ -0,0 +1,69 @@
/* Copyright <20> 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */
using System;
using System.Collections;
using System.Collections.Generic;
namespace Spring.Collections.Generic
{
/// <summary>
/// Implements a <c>Set</c> based on a sorted tree. This gives good performance for operations on very
/// large data-sets, though not as good - asymptotically - as a <c>HashedSet</c>. However, iteration
/// occurs in order. Elements that you put into this type of collection must implement <c>IComparable</c>,
/// and they must actually be comparable. You can't mix <c>string</c> and <c>int</c> values, for example.
/// </summary>
[Serializable]
public class SortedSet<T> : DictionarySet<T>
{
/// <summary>
/// Creates a new set instance based on a sorted tree.
/// </summary>
public SortedSet()
{
InternalDictionary = new SortedDictionary<T, object>();
}
/// <summary>
/// Creates a new set instance based on a sorted tree.
/// </summary>
/// <param name="comparer">The <see cref="IComparer&lt;T&gt;"/> to use for sorting.</param>
public SortedSet(IComparer<T> comparer)
{
InternalDictionary = new SortedList<T, object>(comparer);
}
/// <summary>
/// Creates a new set instance based on a sorted tree and
/// initializes it based on a collection of elements.
/// </summary>
/// <param name="initialValues">A collection of elements that defines the initial set contents.</param>
public SortedSet(ICollection<T> initialValues)
: this()
{
this.AddAll(initialValues);
}
/// <summary>
/// Creates a new set instance based on a sorted tree and
/// initializes it based on a collection of elements.
/// </summary>
/// <param name="initialValues">A collection of elements that defines the initial set contents.</param>
public SortedSet(ICollection initialValues)
: this()
{
((ISet) this).AddAll(initialValues);
}
/// <summary>
/// Creates a new set instance based on a sorted tree and
/// initializes it based on a collection of elements.
/// </summary>
/// <param name="initialValues">A collection of elements that defines the initial set contents.</param>
/// <param name="comparer">The <see cref="IComparer&lt;T&gt;"/> to use for sorting.</param>
public SortedSet(ICollection<T> initialValues, IComparer<T> comparer)
: this(comparer)
{
this.AddAll(initialValues);
}
}
}

View File

@@ -0,0 +1,262 @@
/* Copyright <20> 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */
using System;
using System.Collections;
using System.Collections.Generic;
namespace Spring.Collections.Generic
{
/// <summary>
/// <p>Implements a thread-safe <c>Set</c> wrapper. The implementation is extremely conservative,
/// serializing critical sections to prevent possible deadlocks, and locking on everything.
/// The one exception is for enumeration, which is inherently not thread-safe. For this, you
/// have to <c>lock</c> the <c>SyncRoot</c> object for the duration of the enumeration.</p>
/// </summary>
[Serializable]
public sealed class SynchronizedSet<T> : Set<T>
{
private ISet<T> mBasisSet;
private object mSyncRoot;
/// <summary>
/// Constructs a thread-safe <c>Set</c> wrapper.
/// </summary>
/// <param name="basisSet">The <c>Set</c> object that this object will wrap.</param>
public SynchronizedSet(ISet<T> basisSet)
{
mBasisSet = basisSet;
mSyncRoot = ((ICollection) basisSet).SyncRoot;
if (mSyncRoot == null)
throw new NullReferenceException("The Set you specified returned a null SyncRoot.");
}
/// <summary>
/// Adds the specified element to this set if it is not already present.
/// </summary>
/// <param name="o">The object to add to the set.</param>
/// <returns><see langword="true" /> is the object was added, <see langword="false" /> if it was already present.</returns>
public override sealed bool Add(T o)
{
lock (mSyncRoot)
{
return mBasisSet.Add(o);
}
}
/// <summary>
/// Adds all the elements in the specified collection to the set if they are not already present.
/// </summary>
/// <param name="c">A collection of objects to add to the set.</param>
/// <returns><see langword="true" /> is the set changed as a result of this operation, <see langword="false" /> if not.</returns>
public override sealed bool AddAll(ICollection<T> c)
{
Set<T> temp;
lock (((ICollection) c).SyncRoot)
{
temp = new HashedSet<T>(c);
}
lock (mSyncRoot)
{
return mBasisSet.AddAll(temp);
}
}
/// <summary>
/// Removes all objects from the set.
/// </summary>
public override sealed void Clear()
{
lock (mSyncRoot)
{
mBasisSet.Clear();
}
}
/// <summary>
/// Returns <see langword="true" /> if this set contains the specified element.
/// </summary>
/// <param name="o">The element to look for.</param>
/// <returns><see langword="true" /> if this set contains the specified element, <see langword="false" /> otherwise.</returns>
public override sealed bool Contains(T o)
{
lock (mSyncRoot)
{
return mBasisSet.Contains(o);
}
}
/// <summary>
/// Returns <see langword="true" /> if the set contains all the elements in the specified collection.
/// </summary>
/// <param name="c">A collection of objects.</param>
/// <returns><see langword="true" /> if the set contains all the elements in the specified collection, <see langword="false" /> otherwise.</returns>
public override sealed bool ContainsAll(ICollection<T> c)
{
Set<T> temp;
lock (((ICollection) c).SyncRoot)
{
temp = new HashedSet<T>(c);
}
lock (mSyncRoot)
{
return mBasisSet.ContainsAll(temp);
}
}
/// <summary>
/// Returns <see langword="true" /> if this set contains no elements.
/// </summary>
public override sealed bool IsEmpty
{
get
{
lock (mSyncRoot)
{
return mBasisSet.IsEmpty;
}
}
}
/// <summary>
/// Removes the specified element from the set.
/// </summary>
/// <param name="o">The element to be removed.</param>
/// <returns><see langword="true" /> if the set contained the specified element, <see langword="false" /> otherwise.</returns>
public override sealed bool Remove(T o)
{
lock (mSyncRoot)
{
return mBasisSet.Remove(o);
}
}
/// <summary>
/// Remove all the specified elements from this set, if they exist in this set.
/// </summary>
/// <param name="c">A collection of elements to remove.</param>
/// <returns><see langword="true" /> if the set was modified as a result of this operation.</returns>
public override sealed bool RemoveAll(ICollection<T> c)
{
Set<T> temp;
lock (((ICollection) c).SyncRoot)
{
temp = new HashedSet<T>(c);
}
lock (mSyncRoot)
{
return mBasisSet.RemoveAll(temp);
}
}
/// <summary>
/// Retains only the elements in this set that are contained in the specified collection.
/// </summary>
/// <param name="c">Collection that defines the set of elements to be retained.</param>
/// <returns><see langword="true" /> if this set changed as a result of this operation.</returns>
public override sealed bool RetainAll(ICollection<T> c)
{
Set<T> temp;
lock (((ICollection) c).SyncRoot)
{
temp = new HashedSet<T>(c);
}
lock (mSyncRoot)
{
return mBasisSet.RetainAll(temp);
}
}
/// <summary>
/// Copies the elements in the <c>Set</c> to an array. The type of array needs
/// to be compatible with the objects in the <c>Set</c>, obviously.
/// </summary>
/// <param name="array">An array that will be the target of the copy operation.</param>
/// <param name="index">The zero-based index where copying will start.</param>
public override sealed void CopyTo(T[] array, int index)
{
lock (mSyncRoot)
{
mBasisSet.CopyTo(array, index);
}
}
/// <summary>
/// The number of elements contained in this collection.
/// </summary>
public override sealed int Count
{
get
{
lock (mSyncRoot)
{
return mBasisSet.Count;
}
}
}
/// <summary>
/// Returns <see langword="true" />, indicating that this object is thread-safe. The exception to this
/// is enumeration, which is inherently not thread-safe. Use the <c>SyncRoot</c> object to
/// lock this object for the entire duration of the enumeration.
/// </summary>
public override sealed bool IsSynchronized
{
get { return true; }
}
/// <summary>
/// Returns an object that can be used to synchronize the <c>Set</c> between threads.
/// </summary>
public override sealed object SyncRoot
{
get { return mSyncRoot; }
}
/// <summary>
/// Enumeration is, by definition, not thread-safe. Use a <c>lock</c> on the <c>SyncRoot</c>
/// to synchronize the entire enumeration process.
/// </summary>
/// <returns></returns>
public override sealed IEnumerator<T> GetEnumerator()
{
return mBasisSet.GetEnumerator();
}
/// <summary>
/// Returns a clone of the <c>Set</c> instance.
/// </summary>
/// <returns>A clone of this object.</returns>
public override object Clone()
{
return new SynchronizedSet((ISet) mBasisSet.Clone());
}
/// <summary>
/// Indicates whether given instace is read-only or not
/// </summary>
public override bool IsReadOnly
{
get
{
lock (mSyncRoot)
{
return mBasisSet.IsReadOnly;
}
}
}
/// <summary>
/// Performs CopyTo when called trhough non-generic ISet (ICollection) interface
/// </summary>
/// <param name="array"></param>
/// <param name="index"></param>
protected override void NonGenericCopyTo(Array array, int index)
{
lock (mSyncRoot)
{
((ICollection) this.mBasisSet).CopyTo(array, index);
}
}
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Spring.Context.Annotation
{
/// <summary>
/// Indicates that a class declares one or more <see cref="Definition"/> methods and may be processed
/// by the Spring container to generate object definitions and service requests for those objects
/// at runtime.
///
/// <para>Configuration is meta-annotated as a {@link Component}, therefore Configuration
/// classes are candidates for component-scanning and may also take advantage of
/// <see cref="AutoWired"/> at the field and method but not at the constructor level.
/// </para>
/// <para>May be used in conjunction with the <see cref="Lazy"/> attribute to indicate that all object
/// methods declared within this class are by default lazily initialized.
///</para>
/// <h3>Constraints</h3>
/// <ul>
/// <li>Configuration classes must be non-final</li>
/// <li>Configuration classes must be non-local (may not be declared within a method)</li>
/// <li>Configuration classes must have a default/no-arg constructor and may not use
/// {@link Autowired} constructor parameters</li>
/// </ul>
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class ConfigurationAttribute : Attribute
{
private string _name;
/// <summary>
/// Initializes a new instance of the Configuration class.
/// </summary>
/// <param name="name"></param>
public ConfigurationAttribute(string name)
{
_name = name;
}
/// <summary>
/// Explicitly specify the name of the Spring object definition associated
/// with this Configuration class. If left unspecified (the common case),
/// a object name will be automatically generated.
///
/// <para>The custom name applies only if the Configuration class is picked up via
/// component scanning or supplied directly to a <see cref="AnnotationConfigApplicationContext"/>.
/// If the Configuration class is registered as a traditional XML object definition,
/// the name/id of the object element will take precedence.
/// </para>
/// <see cref="Spring.Objects.Factory.Support.DefaultObjectNameGenerator"/>
/// </summary>
/// <value>The name.</value>
public string Name
{
get { return _name; }
set
{
_name = value;
}
}
}
}

View File

@@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spring.Core.IO;
using Spring.Collections.Generic;
using Spring.Objects.Factory.Parsing;
namespace Spring.Context.Annotation
{
public class ConfigurationClass
{
private Type _configurationClassType;
private IDictionary<string, Type> _importedResources = new Dictionary<string, Type>();
private ISet<ConfigurationClassMethod> _methods = new HashedSet<ConfigurationClassMethod>();
private string _objectName;
private IResource _resource;
/// <summary>
/// Initializes a new instance of the ConfigurationClass class.
/// </summary>
/// <param name="objectName"></param>
/// <param name="type"></param>
public ConfigurationClass(string objectName, Type type)
{
this._objectName = objectName;
this._configurationClassType = type;
}
public Type ConfigurationClassType
{
get
{
return _configurationClassType;
}
}
public IDictionary<string, Type> ImportedResources
{
get
{
return _importedResources;
}
}
public ISet<ConfigurationClassMethod> Methods
{
get
{
return _methods;
}
}
public string ObjectName
{
get
{
return _objectName;
}
set
{
_objectName = value;
}
}
public IResource Resource
{
get
{
return _resource;
}
}
public string SimpleName
{
get { return ConfigurationClassType.Name; }
}
public void AddImportedResource(string importedResource, Type readerClass)
{
_importedResources.Add(importedResource, readerClass);
}
public override bool Equals(object other)
{
return this == other || (other is ConfigurationClass && ConfigurationClassType.Name.Equals(((ConfigurationClass)other).ConfigurationClassType.Name));
}
public override int GetHashCode()
{
return ConfigurationClassType.Name.GetHashCode() * 14;
}
public void Validate(IProblemReporter problemReporter)
{
// An @Bean method may only be overloaded through inheritance. No single
// @Configuration class may declare two @Bean methods with the same name.
char hashDelim = '#';
Dictionary<String, int> methodNameCounts = new Dictionary<String, int>();
foreach (ConfigurationClassMethod method in _methods)
{
String dClassName = method.MethodMetadata.DeclaringType.FullName;
String methodName = method.MethodMetadata.Name;
String fqMethodName = dClassName + hashDelim + methodName;
if (!methodNameCounts.ContainsKey(fqMethodName))
{
methodNameCounts.Add(fqMethodName, 1);
}
else
{
int currentCount = methodNameCounts[fqMethodName];
methodNameCounts.Add(fqMethodName, currentCount++);
}
}
foreach (String methodName in methodNameCounts.Keys)
{
int count = methodNameCounts[methodName];
if (count > 1)
{
String shortMethodName = methodName.Substring(methodName.IndexOf(hashDelim) + 1);
problemReporter.Error(new ObjectMethodOverloadingProblem(shortMethodName, count, Resource, ConfigurationClassType));
}
}
if (Attribute.GetCustomAttribute(_configurationClassType, typeof(ConfigurationAttribute)) != null)
{
if (ConfigurationClassType.IsSealed)
{
problemReporter.Error(new SealedConfigurationProblem(SimpleName, Resource, ConfigurationClassType));
}
foreach (ConfigurationClassMethod method in _methods)
{
method.Validate(problemReporter);
}
}
}
private class SealedConfigurationProblem : Problem
{
public SealedConfigurationProblem(string name, IResource resource, Type configurationClassType)
: base(String.Format("[Configuration] class '{0}' may not be sealed. Remove the sealed modifier to continue.", name), new Location(resource, configurationClassType))
{ }
}
private class ObjectMethodOverloadingProblem : Problem
{
public ObjectMethodOverloadingProblem(string methodName, int count, IResource resource, Type configurationClassType)
: base(String.Format("[Configuration] class '{0}' has {1} overloaded [Definiton] methods named '{2}'. " +
"Only one [Definition] method of a given name is allowed within each [Configuration] class.", configurationClassType.Name, count, methodName), new Location(resource, configurationClassType))
{ }
}
}
}

View File

@@ -0,0 +1,122 @@
#region License
/*
* Copyright © 2002-2010 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.Collections.Generic;
using System;
using System.Text;
using Spring.Core.IO;
using System.Reflection;
using Spring.Objects.Factory.Parsing;
namespace Spring.Context.Annotation
{
/**
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.0
* @see ConfigurationClass
* @see ConfigurationClassParser
* @see ConfigurationClassBeanDefinitionReader
*/
public class ConfigurationClassMethod
{
private ConfigurationClass _configurationClass;
private MethodInfo _methodInfo;
/// <summary>
/// Initializes a new instance of the ConfigurationClassMethod class.
/// </summary>
/// <param name="methodInfo"></param>
/// <param name="configurationClass"></param>
public ConfigurationClassMethod(MethodInfo methodInfo, ConfigurationClass configurationClass)
{
_methodInfo = methodInfo;
_configurationClass = configurationClass;
}
public ConfigurationClass ConfigurationClass
{
get
{
return _configurationClass;
}
}
public MethodInfo MethodMetadata
{
get
{
return _methodInfo;
}
}
public Location ResourceLocation
{
get { return new Location(_configurationClass.Resource, _methodInfo); }
}
public override string ToString()
{
return string.Format("{0}:name={1},declaringClass={2}", this.GetType().Name, _methodInfo.Name, _methodInfo.DeclaringType.FullName);
}
public void Validate(IProblemReporter problemReporter)
{
if (Attribute.GetCustomAttribute(ConfigurationClass.GetType(), typeof(ConfigurationAttribute)) != null)
{
if (!MethodMetadata.IsVirtual)
{
problemReporter.Error(new NonVirtualMethodError(MethodMetadata.Name, ResourceLocation));
}
}
else
{
if (MethodMetadata.IsStatic)
{
problemReporter.Error(new StaticMethodError(MethodMetadata.Name, ResourceLocation));
}
}
}
private class StaticMethodError : Problem
{
public StaticMethodError(string methodName, Location location)
: base(String.Format("Method '{0}' must not be static; remove the method's static modifier to continue",
methodName), location)
{ }
}
private class NonVirtualMethodError : Problem
{
public NonVirtualMethodError(string methodName, Location location)
: base(String.Format("Method '{0}' must not be private, final or static; change the method's modifiers to continue",
methodName), location)
{ }
}
}
}

View File

@@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spring.Objects.Factory.Support;
using Spring.Objects.Factory.Parsing;
using Spring.Collections.Generic;
using System.Reflection;
using Spring.Objects.Factory.Config;
using Common.Logging;
namespace Spring.Context.Annotation
{
public class ConfigurationClassObjectDefinitionReader
{
private readonly ILog _logger = LogManager.GetLogger(typeof(ConfigurationClassObjectDefinitionReader));
private IProblemReporter _problemReporter;
private IObjectDefinitionRegistry _registry;
/// <summary>
/// Initializes a new instance of the ConfigurationClassObjectDefinitionReader class.
/// </summary>
/// <param name="registry"></param>
/// <param name="problemReporter"></param>
public ConfigurationClassObjectDefinitionReader(IObjectDefinitionRegistry registry, IProblemReporter problemReporter)
{
_registry = registry;
_problemReporter = problemReporter;
}
public static bool CheckConfigurationClassCandidate(Type type)
{
if (type != null)
{
return (Attribute.GetCustomAttribute(type.GetType(), typeof(ConfigurationAttribute)) != null);
}
return false;
}
public void LoadBeanDefinitions(ISet<ConfigurationClass> configurationModel)
{
foreach (ConfigurationClass configClass in configurationModel)
{
LoadBeanDefinitionsForConfigurationClass(configClass);
}
}
private void LoadBeanDefinitionForConfigurationClassIfNecessary(ConfigurationClass configClass)
{
if (configClass.ObjectName != null)
{
// a bean definition already exists for this configuration class -> nothing to do
return;
}
// no bean definition exists yet -> this must be an imported configuration class (@Import).
GenericObjectDefinition configBeanDef = new GenericObjectDefinition();
String className = configClass.ConfigurationClassType.Name;
configBeanDef.ObjectTypeName = className;
if (CheckConfigurationClassCandidate(configClass.ConfigurationClassType))
{
String configObjectName = ObjectDefinitionReaderUtils.RegisterWithGeneratedName(configBeanDef, _registry);
configClass.ObjectName = configObjectName;
if (_logger.IsDebugEnabled)
{
_logger.Debug(String.Format("Registered bean definition for imported [Configuration] class {0}", configObjectName));
}
}
}
private void LoadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass)
{
LoadBeanDefinitionForConfigurationClassIfNecessary(configClass);
foreach (ConfigurationClassMethod method in configClass.Methods)
{
LoadBeanDefinitionsForModelMethod(method);
}
LoadBeanDefinitionsFromImportedResources(configClass.ImportedResources);
}
private void LoadBeanDefinitionsForModelMethod(ConfigurationClassMethod method)
{
ConfigurationClass configClass = method.ConfigurationClass;
MethodInfo metadata = method.MethodMetadata;
RootObjectDefinition beanDef = new ConfigurationClassObjectDefinition();
//beanDef.Resource = configClass.Resource;
//beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
beanDef.FactoryObjectName = configClass.ObjectName;
beanDef.FactoryMethodName = metadata.Name;
beanDef.AutowireMode = Objects.Factory.Config.AutoWiringMode.Constructor;
//beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
// consider name and any aliases
//Dictionary<String, Object> beanAttributes = metadata.getAnnotationAttributes(Bean.class.getName());
object[] objectAttributes = metadata.GetCustomAttributes(typeof(DefinitionAttribute), true);
List<string> names = new List<string>();
for (int i = 0; i < objectAttributes.Length; i++)
{
string[] namesAndAliases = ((DefinitionAttribute)objectAttributes[i]).Name;
for (int j = 0; j > namesAndAliases.Length; j++)
{
names.Add(namesAndAliases[j]);
}
}
string beanName = (names.Count > 0 ? names[0] : method.MethodMetadata.Name);
for (int i = 1; i < names.Count; i++)
{
_registry.RegisterAlias(beanName, names[i]);
}
// has this already been overridden (e.g. via XML)?
if (_registry.ContainsObjectDefinition(beanName))
{
IObjectDefinition existingBeanDef = _registry.GetObjectDefinition(beanName);
// is the existing bean definition one that was created from a configuration class?
if (!(existingBeanDef is ConfigurationClassObjectDefinition))
{
// no -> then it's an external override, probably XML
// overriding is legal, return immediately
if (_logger.IsDebugEnabled)
{
_logger.Debug(String.Format("Skipping loading bean definition for {0}: a definition for object " +
"'{1}' already exists. This is likely due to an override in XML.", method, beanName));
}
return;
}
}
if (Attribute.GetCustomAttribute(metadata, typeof(PrimaryAttribute)) != null)
{
//TODO: determine how to respond to this attribute's presence
//beanDef.isPrimary = true;
}
// is this bean to be instantiated lazily?
if (Attribute.GetCustomAttribute(metadata, typeof(LazyAttrribute)) != null)
{
beanDef.IsLazyInit = (Attribute.GetCustomAttribute(metadata, typeof(LazyAttrribute)) as LazyAttrribute).LazyInitialize;
}
if (Attribute.GetCustomAttribute(metadata, typeof(DependsOnAttribute)) != null)
{
DependsOnAttribute attrib = Attribute.GetCustomAttribute(metadata, typeof(DependsOnAttribute)) as DependsOnAttribute;
beanDef.DependsOn = (Attribute.GetCustomAttribute(metadata, typeof(DependsOnAttribute)) as DependsOnAttribute).Name;
}
//Autowire autowire = (Autowire) beanAttributes.get("autowire");
//if (autowire.isAutowire()) {
// beanDef.setAutowireMode(autowire.value());
//}
//String initMethodName = (String) beanAttributes.get("initMethod");
//if (StringUtils.hasText(initMethodName)) {
// beanDef.setInitMethodName(initMethodName);
//}
//String destroyMethodName = (String) beanAttributes.get("destroyMethod");
//if (StringUtils.hasText(destroyMethodName)) {
// beanDef.setDestroyMethodName(destroyMethodName);
//}
// consider scoping
if (Attribute.GetCustomAttribute(metadata, typeof(ScopeAttribute)) != null)
{
ScopeAttribute attrib = Attribute.GetCustomAttribute(metadata, typeof(ScopeAttribute)) as ScopeAttribute;
beanDef.Scope = (Attribute.GetCustomAttribute(metadata, typeof(ScopeAttribute)) as ScopeAttribute).ObjectScope.ToString();
}
if (_logger.IsDebugEnabled)
{
_logger.Debug(String.Format("Registering bean definition for [Definition] method {0}.{1}()", configClass.ConfigurationClassType.Name, beanName));
}
_registry.RegisterObjectDefinition(beanName, beanDef);
}
private void LoadBeanDefinitionsFromImportedResources(IDictionary<string, Type> importedResources)
{
IDictionary<Type, IObjectDefinitionReader> readerInstanceCache = new Dictionary<Type, IObjectDefinitionReader>();
foreach (KeyValuePair<string, Type> entry in importedResources)
{
String resource = entry.Key;
Type readerClass = entry.Value;
if (!readerInstanceCache.ContainsKey(readerClass))
{
try
{
IObjectDefinitionReader readerInstance =
(IObjectDefinitionReader)Activator.CreateInstance(readerClass.GetType(), _registry);
readerInstanceCache.Add(readerClass, readerInstance);
}
catch (Exception ex)
{
throw new InvalidOperationException(String.Format("Could not instantiate IObjectDefinitionReader class {0}", readerClass.FullName));
}
}
IObjectDefinitionReader reader = readerInstanceCache[readerClass];
reader.LoadObjectDefinitions(resource);
}
}
private class ConfigurationClassObjectDefinition : RootObjectDefinition
{
}
}
}

View File

@@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spring.Objects.Factory.Parsing;
using Spring.Collections.Generic;
using Spring.Util;
using System.Reflection;
namespace Spring.Context.Annotation
{
public class ConfigurationClassParser
{
private ISet<ConfigurationClass> _configurationClasses = new HashedSet<ConfigurationClass>();
private Stack<ConfigurationClass> _importStack = new Stack<ConfigurationClass>();
private IProblemReporter _problemReporter;
/// <summary>
/// Initializes a new instance of the ConfigurationClassParser class.
/// </summary>
/// <param name="problemReporter"></param>
public ConfigurationClassParser(IProblemReporter problemReporter)
{
_problemReporter = problemReporter;
}
public ISet<ConfigurationClass> ConfigurationClasses
{
get { return _configurationClasses; }
}
//public void Parse(String className, String beanName)
//{
// ProcessConfigurationClass(new ConfigurationClass(className, beanName));
//}
public void Parse(Type type, string objectName)
{
ProcessConfigurationClass(new ConfigurationClass(objectName, type));
}
public void Validate()
{
foreach (ConfigurationClass configClass in ConfigurationClasses)
{
configClass.Validate(_problemReporter);
}
}
protected void ProcessConfigurationClass(ConfigurationClass configurationClass)
{
DoProcessConfigurationClass(configurationClass);
if (ConfigurationClasses.Contains(configurationClass) && configurationClass.ObjectName != null)
{
// Explicit object definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
ConfigurationClasses.Remove(configurationClass);
}
ConfigurationClasses.Add(configurationClass);
}
private void DoProcessConfigurationClass(ConfigurationClass configurationClass)
{
if (Attribute.GetCustomAttribute(configurationClass.ConfigurationClassType, typeof(ImportAttribute)) != null)
{
ImportAttribute attrib = Attribute.GetCustomAttribute(configurationClass.ConfigurationClassType.GetType(), typeof(ImportAttribute)) as ImportAttribute;
ProcessImport(configurationClass, attrib.Types);
}
if (Attribute.GetCustomAttribute(configurationClass.ConfigurationClassType, typeof(ImportResourceAttribute)) != null)
{
ImportResourceAttribute attrib = Attribute.GetCustomAttribute(configurationClass.ConfigurationClassType.GetType(), typeof(ImportResourceAttribute)) as ImportResourceAttribute;
foreach (string resource in attrib.Resources)
{
configurationClass.AddImportedResource(resource, attrib.DefinitionReader.GetType());
}
}
ISet<MethodInfo> definitionMethods = GetAllMethodsWithCustomAttributeForClass(configurationClass.ConfigurationClassType, typeof(DefinitionAttribute));
foreach (MethodInfo definitionMethod in definitionMethods)
{
configurationClass.Methods.Add(new ConfigurationClassMethod(definitionMethod, configurationClass));
}
}
private ISet<MethodInfo> GetAllMethodsWithCustomAttributeForClass(Type theClass, Type customAttribute)
{
ISet<MethodInfo> methods = new HashedSet<MethodInfo>();
foreach (MethodInfo method in theClass.GetMethods())
{
if (Attribute.GetCustomAttribute(method, customAttribute) != null)
{
methods.Add(method);
}
}
return methods;
}
private void ProcessImport(ConfigurationClass configClass, Type[] classesToImport)
{
if (_importStack.Contains(configClass))
{
_problemReporter.Error(new CircularImportProblem(configClass, _importStack, configClass.ConfigurationClassType));
}
else
{
_importStack.Push(configClass);
foreach (Type classToImport in classesToImport)
{
ProcessConfigurationClass(new ConfigurationClass(null, classToImport));
}
_importStack.Pop();
}
}
private class CircularImportProblem : Problem
{
public CircularImportProblem(ConfigurationClass configClass, Stack<ConfigurationClass> importStack, Type configurationClassType)
: base(String.Format("A circular @Import has been detected: " +
"Illegal attempt by [Configuration] class '{0}' to import class '{1}' as '{2}' is " +
"already present in the current import stack [{3}]",
importStack.Peek().SimpleName, configClass.SimpleName,
configClass.SimpleName, importStack),
new Location(importStack.Peek().Resource, configurationClassType)
)
{ }
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Text;
using Common.Logging;
using Spring.Objects.Factory.Parsing;
using Spring.Objects.Factory.Support;
using Spring.Objects.Factory.Config;
using Spring.Collections.Generic;
using Spring.Objects.Factory;
namespace Spring.Context.Annotation
{
public class ConfigurationClassPostProcessor : IObjectDefinitionRegistryPostProcessor
{
private ILog _logger = LogManager.GetLogger(typeof(ConfigurationClassPostProcessor));
private bool _postProcessObjectDefinitionRegistryCalled = false;
private bool _postProcessObjectFactoryCalled = false;
private IProblemReporter _problemReporter = new FailFastProblemReporter();
public IProblemReporter ProblemReporter
{
set { _problemReporter = (value ?? new FailFastProblemReporter()); }
}
//public int getOrder()
//{
// return Ordered.HIGHEST_PRECEDENCE;
//}
public void PostProcessObjectDefinitionRegistry(IObjectDefinitionRegistry registry)
{
if (_postProcessObjectDefinitionRegistryCalled)
{
throw new InvalidOperationException("PostProcessObjectDefinitionRegistry already called for this post-processor");
}
if (_postProcessObjectFactoryCalled)
{
throw new InvalidOperationException("PostProcessObjectFactory already called for this post-processor");
}
_postProcessObjectDefinitionRegistryCalled = true;
ProcessConfigObjectDefinitions(registry);
}
private void ProcessConfigObjectDefinitions(IObjectDefinitionRegistry registry)
{
ISet<ObjectDefinitionHolder> configCandidates = new HashedSet<ObjectDefinitionHolder>();
foreach (string objectName in registry.GetObjectDefinitionNames())
{
IObjectDefinition objectDef = registry.GetObjectDefinition(objectName);
if (ConfigurationClassObjectDefinitionReader.CheckConfigurationClassCandidate(objectDef.ObjectType))
{
configCandidates.Add(new ObjectDefinitionHolder(objectDef, objectName));
}
}
//if nothing to process, bail out
if (configCandidates.Count == 0) { return; }
ConfigurationClassParser parser = new ConfigurationClassParser(_problemReporter);
foreach (ObjectDefinitionHolder holder in configCandidates)
{
IObjectDefinition bd = holder.ObjectDefinition;
try
{
if (bd is AbstractObjectDefinition && ((AbstractObjectDefinition)bd).HasObjectType)
{
parser.Parse(((AbstractObjectDefinition)bd).ObjectType, holder.ObjectName);
}
else
{
//parser.Parse(bd.ObjectTypeName, holder.ObjectName);
}
}
catch (ObjectDefinitionParsingException ex)
{
throw new ObjectDefinitionStoreException("Failed to load object class: " + bd.ObjectTypeName, ex);
}
}
parser.Validate();
// Read the model and create bean definitions based on its content
ConfigurationClassObjectDefinitionReader reader = new ConfigurationClassObjectDefinitionReader(registry, _problemReporter);
reader.LoadBeanDefinitions(parser.ConfigurationClasses);
}
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spring.Objects.Factory.Config;
namespace Spring.Context.Annotation
{
[AttributeUsage(AttributeTargets.Method)]
public class DefinitionAttribute : Attribute
{
private AutoWiringMode _autoWire = AutoWiringMode.No;
private string _initMethod;
private string[] _name;
/// <summary>
/// Are dependencies to be injected via autowiring?
/// </summary>
/// <value>The auto wire.</value>
public AutoWiringMode AutoWire
{
get { return _autoWire; }
set
{
_autoWire = value;
}
}
private string _destroyMethod;
/// <summary>
/// The optional name of a method to call on the bean instance upon closing the
/// application context, for example a Close() method on a DataSource.
/// The method must have no arguments but may throw any exception.
/// <para>
/// Note: Only invoked on objects whose lifecycle is under the full control of the
/// factory, which is always the case for singletons but not guaranteed
/// for any other scope.
/// </para>
/// <see cref="Spring.Context.IConfigurableApplicationContext"/>
/// </summary>
/// <value>The destroy method.</value>
public string DestroyMethod
{
get { return _destroyMethod; }
set
{
_destroyMethod = value;
}
}
/// <summary>
/// The optional name of a method to call on the object instance during initialization.
/// Not commonly used, given that the method may be called programmatically directly
/// within the body of a Bean-annotated method.
/// </summary>
/// <value>The init method.</value>
public string InitMethod
{
get { return _initMethod; }
set
{
_initMethod = value;
}
}
/// <summary>
/// The name of this object, or if plural, aliases for this object. If left unspecified
/// the name of the object is the name of the attributed method. If specified, the method
/// name is ignored.
/// </summary>
/// <value>The name.</value>
public string[] Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Spring.Context.Annotation
{
/// <summary>
/// objects on which the current object depends. Any objects specified are guaranteed to be
/// created by the container before this object. Used infrequently in cases where a object
/// does not explicitly depend on another through properties or constructor arguments,
/// but rather depends on the side effects of another object's initialization.
/// <para>Note: This attribute will not be inherited by child object definitions,
/// hence it needs to be specified per concrete object definition.
/// </para>
/// <para>Using <see cref="DependsOn"/> at the class level has no effect unless component-scanning
/// is being used. If a <see cref="DependsOn"/>-attributed class is declared via XML,
/// <see cref="DependsOn"/> attribute metadata is ignored, and
/// &lt;object depends-on="..."/&gt; is respected instead.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class DependsOnAttribute : Attribute
{
private string[] _name;
/// <summary>
/// Initializes a new instance of the DependsOn class.
/// </summary>
/// <param name="name"></param>
public DependsOnAttribute(string[] name)
{
_name = name;
}
public string[] Name
{
get { return _name; }
set
{
_name = value;
}
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Spring.Context.Annotation
{
/// <summary>
/// Indicates one or more <see cref="Configuration"/> classes to import.
///
/// <para>Provides functionality equivalent to the :lt;import/&gt; element in Spring XML.
/// Only supported for actual <see cref="Configuration"/>-attributed classes.
/// </para>
///
/// <para><see cref="Definition"/> definitions declared in imported <see cref="Configuration"/> classes
/// should be accessed by using <see cref="AutoWired"/> injection. Either the object
/// itself can be autowired, or the configuration class instance declaring the object can be
/// autowired. The latter approach allows for explicit, IDE-friendly navigation between
/// <see cref="Configuration"/> class methods.
/// </para>
///
/// <para>If XML or other non-<see cref="Configuration"/> object definition resources need to be
/// imported, use <see cref="ImportResource"/>
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class ImportAttribute : Attribute
{
private Type[] _types;
/// <summary>
/// Initializes a new instance of the Import class.
/// </summary>
/// <param name="types"></param>
public ImportAttribute(Type[] types)
{
_types = types;
}
/// <summary>
/// The <see cref="Configuration"/> class or classes to import.
/// </summary>
/// <value>The type.</value>
public Type[] Types
{
get { return _types; }
set
{
_types = value;
}
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spring.Objects.Factory.Support;
using Spring.Objects.Factory.Xml;
namespace Spring.Context.Annotation
{
/// <summary>
///
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class ImportResourceAttribute : Attribute
{
private IObjectDefinitionReader _objectDefinitionReader;
private string[] _resources;
/// <summary>
/// Initializes a new instance of the ImportResource class.
/// </summary>
/// <param name="objectDefinitionReader"></param>
/// <param name="resources"></param>
public ImportResourceAttribute(IObjectDefinitionReader objectDefinitionReader, string[] resources)
{
_objectDefinitionReader = objectDefinitionReader;
_resources = resources;
}
/// <summary>
/// <see cref="IObjectDefinitionReader"/> implementation to use when processing resources specified
/// by the <see cref="Resources"/> attribute.
/// </summary>
/// <value>The <see cref="IObjectDefinitionReader"/>.</value>
public IObjectDefinitionReader DefinitionReader
{
get { return _objectDefinitionReader; }
set
{
_objectDefinitionReader = value;
}
}
/// <summary>
/// Resource paths to import. Resource-loading prefixes such as <code>assembly://</code> and
/// <code>file://</code>, etc may be used.
/// </summary>
/// <value>The resources.</value>
public string[] Resources
{
get { return _resources; }
set
{
_resources = value;
}
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Spring.Context.Annotation
{
/// <summary>
/// Indicates whether a object is to be lazily initialized.
///
/// <para>If this annotation is not present on a Component or object definition, eager
/// initialization will occur. If present and set to true, the
/// object/Component will not be initialized until referenced by another object or
/// explicitly retrieved from the enclosing <see cref="Spring.Objects.Factory.IObjectFactory"/>.
/// If present and set to false, the object will be instantiated on startup by object factories
/// that perform eager initialization of singletons.
/// </para>
/// <para>
/// If Lazy is present on a <see cref="Configuration"/> class, this indicates that all
/// <see cref="Definition"/> methods within that <see cref="Configuration"/> should be lazily
/// initialized. If Lazy is present and false on a object method within a
/// Lazy-annotated Configuration class, this indicates overriding the 'default
/// lazy' behavior and that the object should be eagerly initialized.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class LazyAttrribute : Attribute
{
private bool _lazyInitialize = true;
/// <summary>
/// Initializes a new instance of the Lazy class.
/// </summary>
/// <param name="lazyInitialize"></param>
public LazyAttrribute(bool lazyInitialize)
{
_lazyInitialize = lazyInitialize;
}
/// <summary>
/// Whether lazy initialization should occur.
/// </summary>
/// <value><c>true</c> if [lazy initialize]; otherwise, <c>false</c>.</value>
public bool LazyInitialize
{
get { return _lazyInitialize; }
set
{
_lazyInitialize = value;
}
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Spring.Context.Annotation
{
/// <summary>
/// Indicates that an object should be given preference when multiple candidates
/// are qualified to autowire a single-valued dependency. If exactly one 'primary'
/// object exists among the candidates, it will be the autowired value.
///
/// <para>Using <see cref="Primary"/> at the class level has no effect unless component-scanning
/// is being used. If a <see cref="Primary"/>-attributed class is declared via XML,
/// <see cref="Primary"/> attribute metadata is ignored, and
/// <code>&lt;object primary="true|false"/&gt;></code> is respected instead.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class PrimaryAttribute : Attribute
{
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spring.Objects.Factory.Support;
namespace Spring.Context.Annotation
{
/// <summary>
/// When used as a type-level attribute, indicates the name of a scope to use
/// for instances of the attributed type.
///
/// <para>When used as a method-level attribute in conjunction with the
/// <see cref="Definition"/> attribute, indicates the name of a scope to use for
/// the instance returned from the method.
/// </para>
/// <para>In this context, scope means the lifecycle of an instance, such as
/// <code>singleton</code>, <code>prototype</code>, and so forth.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ScopeAttribute : Attribute
{
private ObjectScope _scope = ObjectScope.Singleton;
/// <summary>
/// Initializes a new instance of the Scope class.
/// </summary>
/// <param name="scope"></param>
public ScopeAttribute(ObjectScope scope)
{
_scope = scope;
}
/// <summary>
/// Specifies the scope to use for the annotated object.
/// </summary>
/// <value>The scope.</value>
public ObjectScope ObjectScope
{
get { return _scope; }
set
{
_scope = value;
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Text;
using Common.Logging;
namespace Spring.Objects.Factory.Parsing
{
public class FailFastProblemReporter : IProblemReporter
{
private ILog _logger = LogManager.GetLogger(typeof(FailFastProblemReporter));
public ILog Logger
{
get { return _logger; }
}
public void Error(Problem problem)
{
_logger.Error(problem.Message);
throw new ObjectDefinitionParsingException(problem);
}
public void Fatal(Problem problem)
{
_logger.Fatal(problem.Message);
throw new ObjectDefinitionParsingException(problem);
}
public void Warning(Problem problem)
{
_logger.Warn(problem.Message);
}
}
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spring.Core.IO;
using Spring.Util;
namespace Spring.Objects.Factory.Parsing
{
public class Location
{
private IResource resource;
private object source;
/// <summary>
/// Initializes a new instance of the Location class.
/// </summary>
/// <param name="resource"></param>
/// <param name="source"></param>
public Location(IResource resource, object source)
{
AssertUtils.ArgumentNotNull(resource, "resource");
this.resource = resource;
this.source = source;
}
/// <summary>
/// Initializes a new instance of the Location class.
/// </summary>
/// <param name="resource"></param>
public Location(IResource resource)
: this(resource, null)
{
}
public IResource Resource
{
get
{
return resource;
}
}
public object Source
{
get
{
return source;
}
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace Spring.Objects.Factory.Parsing
{
[Serializable]
public class ObjectDefinitionParsingException : ObjectDefinitionStoreException
{
protected ObjectDefinitionParsingException(SerializationInfo info, StreamingContext context)
{
}
/// <summary>
/// Initializes a new instance of the ObjectDefinitionParsingException class.
/// </summary>
public ObjectDefinitionParsingException(Problem problem)
: base(problem.Location.Resource, problem.ResourceDescription, problem.Message)
{
}
}
}

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spring.Core.IO;
using Spring.Util;
namespace Spring.Objects.Factory.Parsing
{
public class Problem
{
private string _message;
private Location _location;
private Exception _rootCause;
/// <summary>
/// Initializes a new instance of the Problem class.
/// </summary>
/// <param name="message"></param>
/// <param name="resource"></param>
public Problem(string message, Location location)
: this(message, location, null)
{
}
/// <summary>
/// Initializes a new instance of the Problem class.
/// </summary>
/// <param name="message"></param>
/// <param name="location"></param>
/// <param name="rootCause"></param>
public Problem(string message, Location location, Exception rootCause)
{
AssertUtils.ArgumentNotNull(message, "message");
AssertUtils.ArgumentNotNull(location, "resource");
_message = message;
_location = location;
_rootCause = rootCause;
}
public string Message
{
get
{
return _message;
}
}
public Location Location
{
get
{
return _location;
}
}
public string ResourceDescription
{
get { return _location.Resource.Description; }
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("Configuration problem: ");
sb.Append(Message);
sb.Append("\nOffending resource: ").Append(ResourceDescription);
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Spring.Objects.Factory.Support
{
public interface IObjectDefinitionRegistryPostProcessor
{
void PostProcessObjectDefinitionRegistry(IObjectDefinitionRegistry registry);
}
}

View File

@@ -143,7 +143,7 @@ namespace Spring.Objects.Factory.Support
{
if (objectDefinition is ChildObjectDefinition)
{
generatedObjectName = ((ChildObjectDefinition) objectDefinition).ParentName + "$child";
generatedObjectName = ((ChildObjectDefinition)objectDefinition).ParentName + "$child";
}
else if (objectDefinition.FactoryObjectName != null)
{
@@ -157,7 +157,7 @@ namespace Spring.Objects.Factory.Support
throw new ObjectDefinitionStoreException(
objectDefinition.ResourceDescription, String.Empty,
"Unnamed object definition specifies neither 'Type' nor 'Parent' " +
"nor 'FactoryObject' property values so a unique name cannot be generated.");
"nor 'FactoryObject' property values so a unique name cannot be generated.");
}
generatedObjectName = "$nested";
}
@@ -166,14 +166,15 @@ namespace Spring.Objects.Factory.Support
if (isInnerObject)
{
id = generatedObjectName + GENERATED_OBJECT_NAME_SEPARATOR + ObjectUtils.GetIdentityHexString(objectDefinition);
} else
}
else
{
int counter = -1;
while (counter == -1 || registry.ContainsObjectDefinition(id))
{
counter++;
id = generatedObjectName + GENERATED_OBJECT_NAME_SEPARATOR + counter;
}
}
}
return id;
@@ -257,6 +258,15 @@ namespace Spring.Objects.Factory.Support
return myHandler;
}
public static String RegisterWithGeneratedName(AbstractObjectDefinition definition, IObjectDefinitionRegistry registry)
{
String generatedName = GenerateObjectName(definition, registry, false);
registry.RegisterObjectDefinition(generatedName, definition);
return generatedName;
}
#region Constructor (s) / Destructor
// CLOVER:OFF

View File

@@ -125,7 +125,15 @@
<Compile Include="Collections\DictionarySet.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Collections\Generic\DictionarySet.cs" />
<Compile Include="Collections\Generic\HashedSet.cs" />
<Compile Include="Collections\Generic\ImmutableSet.cs" />
<Compile Include="Collections\Generic\ISet.cs" />
<Compile Include="Collections\Generic\OrderedSet.cs" />
<Compile Include="Collections\Generic\ReadOnlyDictionary.cs" />
<Compile Include="Collections\Generic\Set.cs" />
<Compile Include="Collections\Generic\SortedSet.cs" />
<Compile Include="Collections\Generic\SynchronizedSet.cs" />
<Compile Include="Collections\HashedSet.cs">
<SubType>Code</SubType>
</Compile>
@@ -163,6 +171,19 @@
<Compile Include="Collections\SynchronizedSet.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Context\Annotation\ConfigurationAttribute.cs" />
<Compile Include="Context\Annotation\ConfigurationClass.cs" />
<Compile Include="Context\Annotation\ConfigurationClassMethod.cs" />
<Compile Include="Context\Annotation\ConfigurationClassObjectDefinitionReader.cs" />
<Compile Include="Context\Annotation\ConfigurationClassParser.cs" />
<Compile Include="Context\Annotation\ConfigurationClassPostProcessor.cs" />
<Compile Include="Context\Annotation\DefinitionAttribute.cs" />
<Compile Include="Context\Annotation\DependsOnAttribute.cs" />
<Compile Include="Context\Annotation\ImportAttribute.cs" />
<Compile Include="Context\Annotation\ImportResourceAttribute.cs" />
<Compile Include="Context\Annotation\LazyAttrribute.cs" />
<Compile Include="Context\Annotation\PrimaryAttribute.cs" />
<Compile Include="Context\Annotation\ScopeAttribute.cs" />
<Compile Include="Context\ApplicationContextException.cs">
<SubType>Code</SubType>
</Compile>
@@ -695,7 +716,14 @@
<Compile Include="Objects\Factory\Config\IVariableSource.cs" />
<Compile Include="Objects\Factory\Config\VariableAccessor.cs" />
<Compile Include="Objects\Factory\Config\VariablePlaceholderConfigurer.cs" />
<Compile Include="Objects\Factory\Parsing\FailFastProblemReporter.cs" />
<Compile Include="Objects\Factory\Parsing\IProblemReporter.cs" />
<Compile Include="Objects\Factory\Parsing\Location.cs" />
<Compile Include="Objects\Factory\Parsing\ObjectDefinitionParsingException.cs" />
<Compile Include="Objects\Factory\Parsing\Problem.cs" />
<Compile Include="Objects\Factory\Parsing\ReaderContext.cs" />
<Compile Include="Objects\Factory\Support\IObjectDefinitionRegistryPostProcessor.cs" />
<Compile Include="Objects\Factory\Support\ObjectScope.cs" />
<Compile Include="Util\ConstructorInstantiationInfo.cs" />
<Compile Include="Objects\Factory\Support\GenericObjectDefinition.cs" />
<Compile Include="Objects\Factory\Support\IAutowireCandidateResolver.cs" />

View File

@@ -169,7 +169,6 @@
</Compile>
<Compile Include="Objects\Factory\Support\ChildWebObjectDefinition.cs" />
<Compile Include="Objects\Factory\Support\IWebObjectDefinition.cs" />
<Compile Include="Objects\Factory\Support\ObjectScope.cs" />
<Compile Include="Objects\Factory\Support\RootWebObjectDefinition.cs" />
<Compile Include="Objects\Factory\Support\WebInstantiationStrategy.cs" />
<Compile Include="Objects\Factory\Support\WebObjectDefinitionFactory.cs" />