From ff4dac6ea9045b559badea6843438fffbf96005b Mon Sep 17 00:00:00 2001 From: sbohlen Date: Wed, 10 Nov 2010 01:16:59 +0000 Subject: [PATCH] initial port of lower-level classes --- .../Collections/Generic/DictionarySet.cs | 249 ++++++++ .../Collections/Generic/HashedSet.cs | 33 ++ .../Spring.Core/Collections/Generic/ISet.cs | 139 +++++ .../Collections/Generic/ImmutableSet.cs | 308 ++++++++++ .../Collections/Generic/OrderedSet.cs | 30 + .../Spring.Core/Collections/Generic/Set.cs | 561 ++++++++++++++++++ .../Collections/Generic/SortedSet.cs | 69 +++ .../Collections/Generic/SynchronizedSet.cs | 262 ++++++++ .../Annotation/ConfigurationAttribute.cs | 64 ++ .../Context/Annotation/ConfigurationClass.cs | 163 +++++ .../Annotation/ConfigurationClassMethod.cs | 122 ++++ ...onfigurationClassObjectDefinitionReader.cs | 219 +++++++ .../Annotation/ConfigurationClassParser.cs | 138 +++++ .../ConfigurationClassPostProcessor.cs | 90 +++ .../Context/Annotation/DefinitionAttribute.cs | 85 +++ .../Context/Annotation/DependsOnAttribute.cs | 45 ++ .../Context/Annotation/ImportAttribute.cs | 53 ++ .../Annotation/ImportResourceAttribute.cs | 59 ++ .../Context/Annotation/LazyAttrribute.cs | 53 ++ .../Context/Annotation/PrimaryAttribute.cs | 23 + .../Context/Annotation/ScopeAttribute.cs | 48 ++ .../Parsing/FailFastProblemReporter.cs | 36 ++ .../Objects/Factory/Parsing/Location.cs | 52 ++ .../ObjectDefinitionParsingException.cs | 23 + .../Objects/Factory/Parsing/Problem.cs | 78 +++ .../IObjectDefinitionRegistryPostProcessor.cs | 11 + .../Support/ObjectDefinitionReaderUtils.cs | 18 +- .../Objects/Factory/Support/ObjectScope.cs | 0 .../Spring.Core/Spring.Core.2010.csproj | 28 + src/Spring/Spring.Web/Spring.Web.2010.csproj | 1 - 30 files changed, 3055 insertions(+), 5 deletions(-) create mode 100644 src/Spring/Spring.Core/Collections/Generic/DictionarySet.cs create mode 100644 src/Spring/Spring.Core/Collections/Generic/HashedSet.cs create mode 100644 src/Spring/Spring.Core/Collections/Generic/ISet.cs create mode 100644 src/Spring/Spring.Core/Collections/Generic/ImmutableSet.cs create mode 100644 src/Spring/Spring.Core/Collections/Generic/OrderedSet.cs create mode 100644 src/Spring/Spring.Core/Collections/Generic/Set.cs create mode 100644 src/Spring/Spring.Core/Collections/Generic/SortedSet.cs create mode 100644 src/Spring/Spring.Core/Collections/Generic/SynchronizedSet.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/ConfigurationAttribute.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/ConfigurationClass.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/ConfigurationClassMethod.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/ConfigurationClassObjectDefinitionReader.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/ConfigurationClassParser.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/ConfigurationClassPostProcessor.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/DefinitionAttribute.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/DependsOnAttribute.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/ImportAttribute.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/ImportResourceAttribute.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/LazyAttrribute.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/PrimaryAttribute.cs create mode 100644 src/Spring/Spring.Core/Context/Annotation/ScopeAttribute.cs create mode 100644 src/Spring/Spring.Core/Objects/Factory/Parsing/FailFastProblemReporter.cs create mode 100644 src/Spring/Spring.Core/Objects/Factory/Parsing/Location.cs create mode 100644 src/Spring/Spring.Core/Objects/Factory/Parsing/ObjectDefinitionParsingException.cs create mode 100644 src/Spring/Spring.Core/Objects/Factory/Parsing/Problem.cs create mode 100644 src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistryPostProcessor.cs rename src/Spring/{Spring.Web => Spring.Core}/Objects/Factory/Support/ObjectScope.cs (100%) diff --git a/src/Spring/Spring.Core/Collections/Generic/DictionarySet.cs b/src/Spring/Spring.Core/Collections/Generic/DictionarySet.cs new file mode 100644 index 00000000..d41228fb --- /dev/null +++ b/src/Spring/Spring.Core/Collections/Generic/DictionarySet.cs @@ -0,0 +1,249 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Spring.Collections.Generic +{ + /// + ///

DictionarySet is an abstract class that supports the creation of new Set + /// types where the underlying data store is an IDictionary instance.

+ /// + ///

You can use any object that implements the IDictionary 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 IDictionary you choose will affect both the performance and the behavior + /// of the Set using it.

+ /// + ///

To make a Set typed based on your own IDictionary, simply derive a + /// new class with a constructor that takes no parameters. Some Set implmentations + /// cannot be defined with a default constructor. If this is the case for your class, + /// you will need to override Clone() as well.

+ /// + ///

It is also standard practice that at least one of your constructors takes an ICollection or + /// an ISet as an argument.

+ ///
+ [Serializable] + public abstract class DictionarySet : Set + { + /// + /// Provides the storage for elements in the Set, stored as the key-set + /// of the IDictionary object. Set this object in the constructor + /// if you create your own Set class. + /// + protected IDictionary InternalDictionary = null; + + private static readonly object PlaceholderObject = new object(); + + /// + /// The placeholder object used as the value for the IDictionary instance. + /// + /// + /// There is a single instance of this object globally, used for all Sets. + /// + protected object Placeholder + { + get { return PlaceholderObject; } + } + + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The to add to the set. + /// is the object was added, if it was already present. + 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; + } + } + + /// + /// Adds all the elements in the specified collection to the set if they are not already present. + /// + /// A collection of objects to add to the set. + /// is the set changed as a result of this operation, if not. + public override bool AddAll(ICollection c) + { + bool changed = false; + foreach (T o in c) + { + changed |= this.Add(o); + } + return changed; + } + + /// + /// Removes all objects from the set. + /// + public override void Clear() + { + InternalDictionary.Clear(); + } + + /// + /// Returns if this set contains the specified element. + /// + /// The element to look for. + /// if this set contains the specified element, otherwise. + public override bool Contains(T o) + { + return InternalDictionary.ContainsKey(o); + } + + /// + /// Returns if the set contains all the elements in the specified collection. + /// + /// A collection of objects. + /// if the set contains all the elements in the specified collection, otherwise. + public override bool ContainsAll(ICollection c) + { + foreach (T o in c) + { + if (!this.Contains(o)) + { + return false; + } + } + return true; + } + + /// + /// Returns if this set contains no elements. + /// + public override bool IsEmpty + { + get { return InternalDictionary.Count == 0; } + } + + /// + /// Removes the specified element from the set. + /// + /// The element to be removed. + /// if the set contained the specified element, otherwise. + public override bool Remove(T o) + { + bool contained = this.Contains(o); + if (contained) + { + InternalDictionary.Remove(o); + } + return contained; + } + + /// + /// Remove all the specified elements from this set, if they exist in this set. + /// + /// A collection of elements to remove. + /// if the set was modified as a result of this operation. + public override bool RemoveAll(ICollection c) + { + bool changed = false; + foreach (T o in c) + { + changed |= this.Remove(o); + } + return changed; + } + + /// + /// Retains only the elements in this set that are contained in the specified collection. + /// + /// Collection that defines the set of elements to be retained. + /// if this set changed as a result of this operation. + public override bool RetainAll(ICollection c) + { + //Put data from C into a set so we can use the Contains() method. + Set cSet = new HashedSet(c); + + //We are going to build a set of elements to remove. + Set removeSet = new HashedSet(); + + 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); + } + + + /// + /// Copies the elements in the Set to an array of T. The type of array needs + /// to be compatible with the objects in the Set, obviously. + /// + /// An array that will be the target of the copy operation. + /// The zero-based index where copying will start. + public override void CopyTo(T[] array, int index) + { + InternalDictionary.Keys.CopyTo(array, index); + } + + /// + /// The number of elements contained in this collection. + /// + public override int Count + { + get { return InternalDictionary.Count; } + } + + /// + /// None of the objects based on DictionarySet are synchronized. Use the + /// SyncRoot property instead. + /// + public override bool IsSynchronized + { + get { return false; } + } + + /// + /// Returns an object that can be used to synchronize the Set between threads. + /// + public override object SyncRoot + { + get { return ((ICollection) InternalDictionary).SyncRoot; } + } + + /// + /// Gets an enumerator for the elements in the Set. + /// + /// An IEnumerator over the elements in the Set. + public override IEnumerator GetEnumerator() + { + return InternalDictionary.Keys.GetEnumerator(); + } + + /// + /// Indicates wether the Set is read-only or not + /// + public override bool IsReadOnly + { + get { return InternalDictionary.IsReadOnly; } + } + + /// + /// Copies the elements in the Set to an array. The type of array needs + /// to be compatible with the objects in the Set, obviously. Needed for + /// non-generic ISet methods implementation + /// + /// An array that will be the target of the copy operation. + /// The zero-based index where copying will start. + protected override void NonGenericCopyTo(Array array, int index) + { + ((ICollection) InternalDictionary.Keys).CopyTo(array, index); + } + } +} diff --git a/src/Spring/Spring.Core/Collections/Generic/HashedSet.cs b/src/Spring/Spring.Core/Collections/Generic/HashedSet.cs new file mode 100644 index 00000000..4cfcf694 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/Generic/HashedSet.cs @@ -0,0 +1,33 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ +using System; +using System.Collections.Generic; + +namespace Spring.Collections.Generic +{ + /// + /// Implements a Set based on a Dictionary (which is equivalent of + /// non-genric HashTable) This will give the best lookup, add, and remove + /// performance for very large data-sets, but iteration will occur in no particular order. + /// + [Serializable] + public class HashedSet : DictionarySet + { + /// + /// Creates a new set instance based on a Dictinary. + /// + public HashedSet() + { + InternalDictionary = new Dictionary(); + } + + /// + /// Creates a new set instance based on a Dictinary and + /// initializes it based on a collection of elements. + /// + /// A collection of elements that defines the initial set contents. + public HashedSet(ICollection initialValues) : this() + { + this.AddAll(initialValues); + } + } +} diff --git a/src/Spring/Spring.Core/Collections/Generic/ISet.cs b/src/Spring/Spring.Core/Collections/Generic/ISet.cs new file mode 100644 index 00000000..c2182a2c --- /dev/null +++ b/src/Spring/Spring.Core/Collections/Generic/ISet.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Spring.Collections.Generic +{ + /// + ///

A collection that contains no duplicate elements. This interface models the mathematical + /// Set abstraction. + /// The order of elements in a set is dependant on (a)the data-structure implementation, and + /// (b)the implementation of the various Set methods, and thus is not guaranteed.

+ /// + ///

None of the Set implementations in this library are guranteed to be thread-safe + /// in any way unless wrapped in a SynchronizedSet.

+ /// + ///

The following table summarizes the binary operators that are supported by the Set class.

+ /// + /// + /// Operation + /// Description + /// Method + /// + /// + /// Union (OR) + /// Element included in result if it exists in either A OR B. + /// Union() + /// + /// + /// Intersection (AND) + /// Element included in result if it exists in both A AND B. + /// InterSect() + /// + /// + /// Exclusive Or (XOR) + /// Element included in result if it exists in one, but not both, of A and B. + /// ExclusiveOr() + /// + /// + /// Minus (n/a) + /// Take all the elements in A. Now, if any of them exist in B, remove + /// them. Note that unlike the other operators, A - B is not the same as B - A. + /// Minus() + /// + /// + ///
+ public interface ISet : ICollection, IEnumerable, IEnumerable, ICloneable + { + // Clear is declared in ICollection, but not in ICollection + // void Clear(); + + // Remove is declared in ICollection, but not in ICollection + // bool Remove(T o); + + // Contains is declared in ICollection, but not in ICollection + // bool Contains(T o); + + /// + /// 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 a or b. + /// Neither this set nor the input set are modified during the operation. The return value + /// is a Clone() of this set with the extra elements added in. + /// + /// A collection of elements. + /// A new Set containing the union of this Set with the specified collection. + /// Neither of the input objects is modified by the union. + ISet Union(ISet a); + + /// + /// 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 Intersect() operation does not modify the input sets. It returns + /// a Clone() of this set with the appropriate elements removed. + /// + /// A set of elements. + /// The intersection of this set with a. + ISet Intersect(ISet a); + + /// + /// Performs a "minus" of set b from set a. This returns a set of all + /// the elements in set a, removing the elements that are also in set b. + /// The original sets are not modified during this operation. The result set is a Clone() + /// of this Set containing the elements from the operation. + /// + /// A set of elements. + /// A set containing the elements from this set with the elements in a removed. + ISet Minus(ISet a); + + /// + /// 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 Clone() of this set containing + /// the elements from the exclusive-or operation. + /// + /// A set of elements. + /// A set containing the result of a ^ b. + ISet ExclusiveOr(ISet a); + + /// + /// Returns if the set contains all the elements in the specified collection. + /// + /// A collection of objects. + /// if the set contains all the elements in the specified collection, otherwise. + bool ContainsAll(ICollection c); + + /// + /// Returns if this set contains no elements. + /// + bool IsEmpty { get; } + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The object to add to the set. + /// is the object was added, if it was already present. + new bool Add(T o); + + /// + /// Adds all the elements in the specified collection to the set if they are not already present. + /// + /// A collection of objects to add to the set. + /// is the set changed as a result of this operation, if not. + bool AddAll(ICollection c); + + /// + /// Remove all the specified elements from this set, if they exist in this set. + /// + /// A collection of elements to remove. + /// if the set was modified as a result of this operation. + bool RemoveAll(ICollection c); + + + /// + /// Retains only the elements in this set that are contained in the specified collection. + /// + /// Collection that defines the set of elements to be retained. + /// if this set changed as a result of this operation. + bool RetainAll(ICollection c); + } +} diff --git a/src/Spring/Spring.Core/Collections/Generic/ImmutableSet.cs b/src/Spring/Spring.Core/Collections/Generic/ImmutableSet.cs new file mode 100644 index 00000000..1f3fea0e --- /dev/null +++ b/src/Spring/Spring.Core/Collections/Generic/ImmutableSet.cs @@ -0,0 +1,308 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Spring.Collections.Generic +{ + /// + ///

Implements an immutable (read-only) Set wrapper.

+ ///

Although this is advertised as immutable, it really isn't. Anyone with access to the + /// basisSet can still change the data-set. So GetHashCode() is not implemented + /// for this Set, as is the case for all Set implementations in this library. + /// This design decision was based on the efficiency of not having to Clone() the + /// basisSet every time you wrap a mutable Set.

+ ///
+ [Serializable] + public sealed class ImmutableSet : Set + { + private const string ERROR_MESSAGE = "Object is immutable."; + private ISet mBasisSet; + + internal ISet BasisSet + { + get { return mBasisSet; } + } + + /// + /// Constructs an immutable (read-only) Set wrapper. + /// + /// The Set that is wrapped. + public ImmutableSet(ISet basisSet) + { + mBasisSet = basisSet; + } + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The object to add to the set. + /// nothing + /// is always thrown + public override sealed bool Add(T o) + { + throw new NotSupportedException(ERROR_MESSAGE); + } + + /// + /// Adds all the elements in the specified collection to the set if they are not already present. + /// + /// A collection of objects to add to the set. + /// nothing + /// is always thrown + public override sealed bool AddAll(ICollection c) + { + throw new NotSupportedException(ERROR_MESSAGE); + } + + /// + /// Removes all objects from the set. + /// + /// is always thrown + public override sealed void Clear() + { + throw new NotSupportedException(ERROR_MESSAGE); + } + + /// + /// Returns if this set contains the specified element. + /// + /// The element to look for. + /// if this set contains the specified element, otherwise. + public override sealed bool Contains(T o) + { + return mBasisSet.Contains(o); + } + + /// + /// Returns if the set contains all the elements in the specified collection. + /// + /// A collection of objects. + /// if the set contains all the elements in the specified collection, otherwise. + public override sealed bool ContainsAll(ICollection c) + { + return mBasisSet.ContainsAll(c); + } + + /// + /// Returns if this set contains no elements. + /// + public override sealed bool IsEmpty + { + get { return mBasisSet.IsEmpty; } + } + + /// + /// Removes the specified element from the set. + /// + /// The element to be removed. + /// nothing + /// is always thrown + public override sealed bool Remove(T o) + { + throw new NotSupportedException(ERROR_MESSAGE); + } + + /// + /// Remove all the specified elements from this set, if they exist in this set. + /// + /// A collection of elements to remove. + /// nothing + /// is always thrown + public override sealed bool RemoveAll(ICollection c) + { + throw new NotSupportedException(ERROR_MESSAGE); + } + + /// + /// Retains only the elements in this set that are contained in the specified collection. + /// + /// Collection that defines the set of elements to be retained. + /// nothing + /// is always thrown + public override sealed bool RetainAll(ICollection c) + { + throw new NotSupportedException(ERROR_MESSAGE); + } + + /// + /// Copies the elements in the Set to an array of T. The type of array needs + /// to be compatible with the objects in the Set, obviously. + /// + /// An array that will be the target of the copy operation. + /// The zero-based index where copying will start. + public override sealed void CopyTo(T[] array, int index) + { + mBasisSet.CopyTo(array, index); + } + + /// + /// The number of elements contained in this collection. + /// + public override sealed int Count + { + get { return mBasisSet.Count; } + } + + /// + /// Returns an object that can be used to synchronize use of the Set across threads. + /// + public override sealed bool IsSynchronized + { + get { return ((ICollection) mBasisSet).IsSynchronized; } + } + + /// + /// Returns an object that can be used to synchronize the Set between threads. + /// + public override sealed object SyncRoot + { + get { return ((ICollection) mBasisSet).SyncRoot; } + } + + /// + /// Gets an enumerator for the elements in the Set. + /// + /// An IEnumerator over the elements in the Set. + public override sealed IEnumerator GetEnumerator() + { + return mBasisSet.GetEnumerator(); + } + + /// + /// Returns a clone of the Set instance. + /// + /// A clone of this object. + public override sealed object Clone() + { + return new ImmutableSet(mBasisSet); + } + + /// + /// 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 a or b. + /// Neither this set nor the input set are modified during the operation. The return value + /// is a Clone() of this set with the extra elements added in. + /// + /// A collection of elements. + /// A new Set containing the union of this Set with the specified collection. + /// Neither of the input objects is modified by the union. + public override sealed ISet Union(ISet a) + { + ISet m = GetUltimateBasisSet(); + return new ImmutableSet(m.Union(a)); + } + + /// + /// 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 Intersect() operation does not modify the input sets. It returns + /// a Clone() of this set with the appropriate elements removed. + /// + /// A set of elements. + /// The intersection of this set with a. + public override sealed ISet Intersect(ISet a) + { + ISet m = GetUltimateBasisSet(); + return new ImmutableSet(m.Intersect(a)); + } + + /// + /// Performs a "minus" of set b from set a. This returns a set of all + /// the elements in set a, removing the elements that are also in set b. + /// The original sets are not modified during this operation. The result set is a Clone() + /// of this Set containing the elements from the operation. + /// + /// A set of elements. + /// A set containing the elements from this set with the elements in a removed. + public override sealed ISet Minus(ISet a) + { + ISet m = GetUltimateBasisSet(); + return new ImmutableSet(m.Minus(a)); + } + + /// + /// 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 Clone() of this set containing + /// the elements from the exclusive-or operation. + /// + /// A set of elements. + /// A set containing the result of a ^ b. + public override sealed ISet ExclusiveOr(ISet a) + { + ISet m = GetUltimateBasisSet(); + return new ImmutableSet(m.ExclusiveOr(a)); + } + + /// + /// Indicates that the given instance is read-only + /// + public override sealed bool IsReadOnly + { + get { return true; } + } + + /// + /// Performs CopyTo when called trhough non-generic ISet (ICollection) interface + /// + /// + /// + protected override void NonGenericCopyTo(Array array, int index) + { + ((ICollection) this.BasisSet).CopyTo(array, index); + } + + /// + /// Performs Union when called trhough non-generic ISet interface + /// + /// + /// + protected override sealed ISet NonGenericUnion(ISet a) + { + ISet m = GetUltimateBasisSet(); + return new ImmutableSet(((ISet) m).Union(a)); + } + + /// + /// Performs Minus when called trhough non-generic ISet interface + /// + /// + /// + protected override sealed ISet NonGenericMinus(ISet a) + { + ISet m = GetUltimateBasisSet(); + return new ImmutableSet(((ISet) m).Minus(a)); + } + + /// + /// Performs Intersect when called trhough non-generic ISet interface + /// + /// + /// + protected override sealed ISet NonGenericIntersect(ISet a) + { + ISet m = GetUltimateBasisSet(); + return new ImmutableSet(((ISet) m).Intersect(a)); + } + + /// + /// Performs ExclusiveOr when called trhough non-generic ISet interface + /// + /// + /// + protected override sealed ISet NonGenericExclusiveOr(ISet a) + { + ISet m = GetUltimateBasisSet(); + return new ImmutableSet(((ISet) m).ExclusiveOr(a)); + } + + private ISet GetUltimateBasisSet() + { + ISet m = this.mBasisSet; + while (m is ImmutableSet) + m = ((ImmutableSet) m).mBasisSet; + return m; + } + } +} diff --git a/src/Spring/Spring.Core/Collections/Generic/OrderedSet.cs b/src/Spring/Spring.Core/Collections/Generic/OrderedSet.cs new file mode 100644 index 00000000..30107594 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/Generic/OrderedSet.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace Spring.Collections.Generic +{ + /// + /// Implements an ordered Set based on a dictionary. + /// + [Serializable] + public class OrderedSet : DictionarySet + { + /// + /// Initializes a new instance of the class. + /// + public OrderedSet() + { + InternalDictionary = new Dictionary(); + } + + /// + /// Initializes a new instance of the class. + /// + /// A collection of elements that defines the initial set contents. + public OrderedSet(ICollection initialValues) + : this() + { + AddAll(initialValues); + } + } +} diff --git a/src/Spring/Spring.Core/Collections/Generic/Set.cs b/src/Spring/Spring.Core/Collections/Generic/Set.cs new file mode 100644 index 00000000..84a3ea8c --- /dev/null +++ b/src/Spring/Spring.Core/Collections/Generic/Set.cs @@ -0,0 +1,561 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Spring.Collections.Generic +{ + ///

A collection that contains no duplicate elements. This class models the mathematical + /// Set abstraction, and is the base class for all other Set implementations. + /// The order of elements in a set is dependant on (a)the data-structure implementation, and + /// (b)the implementation of the various Set methods, and thus is not guaranteed.

+ /// + ///

None of the Set implementations in this library are guranteed to be thread-safe + /// in any way unless wrapped in a SynchronizedSet.

+ /// + ///

The following table summarizes the binary operators that are supported by the Set class.

+ /// + /// + /// Operation + /// Description + /// Method + /// Operator + /// + /// + /// Union (OR) + /// Element included in result if it exists in either A OR B. + /// Union() + /// | + /// + /// + /// Intersection (AND) + /// Element included in result if it exists in both A AND B. + /// InterSect() + /// & + /// + /// + /// Exclusive Or (XOR) + /// Element included in result if it exists in one, but not both, of A and B. + /// ExclusiveOr() + /// ^ + /// + /// + /// Minus (n/a) + /// Take all the elements in A. Now, if any of them exist in B, remove + /// them. Note that unlike the other operators, A - B is not the same as B - A. + /// Minus() + /// - + /// + /// + ///
+ [Serializable] + public abstract class Set : ISet, ICollection, IEnumerable, + ISet, ICollection, IEnumerable, ICloneable + { + /// + /// 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 a or b. + /// Neither this set nor the input set are modified during the operation. The return value + /// is a Clone() of this set with the extra elements added in. + /// + /// A collection of elements. + /// A new Set containing the union of this Set with the specified collection. + /// Neither of the input objects is modified by the union. + public virtual ISet Union(ISet a) + { + ISet resultSet = (ISet) this.Clone(); + if (a != null) + { + resultSet.AddAll(a); + } + return resultSet; + } + + /// + /// 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 a or b. + /// The return value is a Clone() of one of the sets (a if it is not ) with elements of the other set + /// added in. Neither of the input sets is modified by the operation. + /// + /// A set of elements. + /// A set of elements. + /// A set containing the union of the input sets. if both sets are . + public static ISet Union(ISet a, ISet b) + { + if (a == null && b == null) + return null; + else if (a == null) + return (ISet) b.Clone(); + else if (b == null) + return (ISet) a.Clone(); + else + return a.Union(b); + } + + /// + /// 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 a or b. + /// The return value is a Clone() of one of the sets (a if it is not ) with elements of the other set + /// added in. Neither of the input sets is modified by the operation. + /// + /// A set of elements. + /// A set of elements. + /// A set containing the union of the input sets. if both sets are . + public static Set operator |(Set a, Set b) + { + return (Set) Union(a, b); + } + + /// + /// 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 Intersect() operation does not modify the input sets. It returns + /// a Clone() of this set with the appropriate elements removed. + /// + /// A set of elements. + /// The intersection of this set with a. + public virtual ISet Intersect(ISet a) + { + ISet resultSet = (ISet) this.Clone(); + if (a != null) + resultSet.RetainAll(a); + else + resultSet.Clear(); + return resultSet; + } + + /// + /// 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 a and b. Neither input object is modified by the operation. + /// The result object is a Clone() of one of the input objects (a if it is not ) containing the + /// elements from the intersect operation. + /// + /// A set of elements. + /// A set of elements. + /// The intersection of the two input sets. if both sets are . + public static ISet Intersect(ISet a, ISet b) + { + if (a == null && b == null) + return null; + else if (a == null) + { + return b.Intersect(a); + } + else + return a.Intersect(b); + } + + /// + /// 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 a and b. Neither input object is modified by the operation. + /// The result object is a Clone() of one of the input objects (a if it is not ) containing the + /// elements from the intersect operation. + /// + /// A set of elements. + /// A set of elements. + /// The intersection of the two input sets. if both sets are . + public static Set operator &(Set a, Set b) + { + return (Set) Intersect(a, b); + } + + /// + /// Performs a "minus" of set b from set a. This returns a set of all + /// the elements in set a, removing the elements that are also in set b. + /// The original sets are not modified during this operation. The result set is a Clone() + /// of this Set containing the elements from the operation. + /// + /// A set of elements. + /// A set containing the elements from this set with the elements in a removed. + public virtual ISet Minus(ISet a) + { + ISet resultSet = (ISet) this.Clone(); + if (a != null) + resultSet.RemoveAll(a); + return resultSet; + } + + /// + /// Performs a "minus" of set b from set a. This returns a set of all + /// the elements in set a, removing the elements that are also in set b. + /// The original sets are not modified during this operation. The result set is a Clone() + /// of set a containing the elements from the operation. + /// + /// A set of elements. + /// A set of elements. + /// A set containing A - B elements. if a is . + public static ISet Minus(ISet a, ISet b) + { + if (a == null) + return null; + else + return a.Minus(b); + } + + /// + /// Performs a "minus" of set b from set a. This returns a set of all + /// the elements in set a, removing the elements that are also in set b. + /// The original sets are not modified during this operation. The result set is a Clone() + /// of set a containing the elements from the operation. + /// + /// A set of elements. + /// A set of elements. + /// A set containing A - B elements. if a is . + public static Set operator -(Set a, Set b) + { + return (Set) Minus(a, b); + } + + + /// + /// 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 Clone() of this set containing + /// the elements from the exclusive-or operation. + /// + /// A set of elements. + /// A set containing the result of a ^ b. + public virtual ISet ExclusiveOr(ISet a) + { + ISet resultSet = (ISet) this.Clone(); + foreach (T element in a) + { + if (resultSet.Contains(element)) + resultSet.Remove(element); + else + resultSet.Add(element); + } + return resultSet; + } + + /// + /// 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 Clone() of one of the sets + /// (a if it is not ) containing + /// the elements from the exclusive-or operation. + /// + /// A set of elements. + /// A set of elements. + /// A set containing the result of a ^ b. if both sets are . + public static ISet ExclusiveOr(ISet a, ISet b) + { + if (a == null && b == null) + return null; + else if (a == null) + return (ISet) b.Clone(); + else if (b == null) + return (ISet) a.Clone(); + else + return a.ExclusiveOr(b); + } + + /// + /// 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 Clone() of one of the sets + /// (a if it is not ) containing + /// the elements from the exclusive-or operation. + /// + /// A set of elements. + /// A set of elements. + /// A set containing the result of a ^ b. if both sets are . + public static Set operator ^(Set a, Set b) + { + return (Set) ExclusiveOr(a, b); + } + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The object to add to the set. + /// is the object was added, if it was already present. + public abstract bool Add(T o); + + /// + /// Adds all the elements in the specified collection to the set if they are not already present. + /// + /// A collection of objects to add to the set. + /// is the set changed as a result of this operation, if not. + public abstract bool AddAll(ICollection c); + + /// + /// Removes all objects from the set. + /// + public abstract void Clear(); + + /// + /// Returns if this set contains the specified element. + /// + /// The element to look for. + /// if this set contains the specified element, otherwise. + public abstract bool Contains(T o); + + /// + /// Returns if the set contains all the elements in the specified collection. + /// + /// A collection of objects. + /// if the set contains all the elements in the specified collection, otherwise. + public abstract bool ContainsAll(ICollection c); + + /// + /// Returns if this set contains no elements. + /// + public abstract bool IsEmpty { get; } + + /// + /// Removes the specified element from the set. + /// + /// The element to be removed. + /// if the set contained the specified element, otherwise. + public abstract bool Remove(T o); + + /// + /// Remove all the specified elements from this set, if they exist in this set. + /// + /// A collection of elements to remove. + /// if the set was modified as a result of this operation. + public abstract bool RemoveAll(ICollection c); + + + /// + /// Retains only the elements in this set that are contained in the specified collection. + /// + /// Collection that defines the set of elements to be retained. + /// if this set changed as a result of this operation. + public abstract bool RetainAll(ICollection c); + + /// + /// Returns a clone of the Set instance. This will work for derived Set + /// classes if the derived class implements a constructor that takes no arguments. + /// + /// A clone of this object. + public virtual object Clone() + { + Set newSet = (Set) Activator.CreateInstance(this.GetType()); + newSet.AddAll(this); + return newSet; + } + + + /// + /// Copies the elements in the Set to an array. The type of array needs + /// to be compatible with the objects in the Set, obviously. + /// + /// An array that will be the target of the copy operation. + /// The zero-based index where copying will start. + public abstract void CopyTo(T[] array, int index); + + /// + /// The number of elements currently contained in this collection. + /// + public abstract int Count { get; } + + /// + /// Returns if the Set is synchronized across threads. Note that + /// enumeration is inherently not thread-safe. Use the SyncRoot to lock the + /// object during enumeration. + /// + public abstract bool IsSynchronized { get; } + + /// + /// 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 IDictionary, + /// or anything that has a SyncRoot, return that object instead of "this". + /// + public abstract object SyncRoot { get; } + + /// + /// Gets an enumerator for the elements in the Set. + /// + /// An IEnumerator over the elements in the Set. + public abstract IEnumerator GetEnumerator(); + + /// + /// Indicates whether the given instance is read-only or not + /// + /// + /// if the ISet is read-only; otherwise, . + /// In the default implementation of Set, this property always returns false. + /// + public virtual bool IsReadOnly + { + get { return false; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + void ICollection.Add(T item) + { + this.Add(item); + } + + #region Protected helpers + + /// + /// Performs CopyTo when called trhough non-generic ISet (ICollection) interface + /// + /// + /// + protected abstract void NonGenericCopyTo(Array array, int index); + + /// + /// Performs Union when called trhough non-generic ISet interface + /// + /// + /// + protected virtual ISet NonGenericUnion(ISet a) + { + ISet resultSet = (ISet) this.Clone(); + if (a != null) + resultSet.AddAll(a); + return resultSet; + } + + /// + /// Performs Minus when called trhough non-generic ISet interface + /// + /// + /// + protected virtual ISet NonGenericMinus(ISet a) + { + ISet resultSet = (ISet) this.Clone(); + if (a != null) + resultSet.RemoveAll(a); + return resultSet; + } + + /// + /// Performs Intersect when called trhough non-generic ISet interface + /// + /// + /// + protected virtual ISet NonGenericIntersect(ISet a) + { + ISet resultSet = (ISet) this.Clone(); + if (a != null) + resultSet.RetainAll(a); + else + resultSet.Clear(); + return resultSet; + } + + /// + /// Performs ExclusiveOr when called trhough non-generic ISet interface + /// + /// + /// + 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 col = new List(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 col = new List(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 col = new List(c.Count); + foreach (object o in c) + { + if (o is T) + col.Add((T) o); + } + return this.RetainAll(col); + } + + #endregion ISet implementation + } +} diff --git a/src/Spring/Spring.Core/Collections/Generic/SortedSet.cs b/src/Spring/Spring.Core/Collections/Generic/SortedSet.cs new file mode 100644 index 00000000..6e3c20c6 --- /dev/null +++ b/src/Spring/Spring.Core/Collections/Generic/SortedSet.cs @@ -0,0 +1,69 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Spring.Collections.Generic +{ + /// + /// Implements a Set based on a sorted tree. This gives good performance for operations on very + /// large data-sets, though not as good - asymptotically - as a HashedSet. However, iteration + /// occurs in order. Elements that you put into this type of collection must implement IComparable, + /// and they must actually be comparable. You can't mix string and int values, for example. + /// + [Serializable] + public class SortedSet : DictionarySet + { + /// + /// Creates a new set instance based on a sorted tree. + /// + public SortedSet() + { + InternalDictionary = new SortedDictionary(); + } + + /// + /// Creates a new set instance based on a sorted tree. + /// + /// The to use for sorting. + public SortedSet(IComparer comparer) + { + InternalDictionary = new SortedList(comparer); + } + + /// + /// Creates a new set instance based on a sorted tree and + /// initializes it based on a collection of elements. + /// + /// A collection of elements that defines the initial set contents. + public SortedSet(ICollection initialValues) + : this() + { + this.AddAll(initialValues); + } + + + /// + /// Creates a new set instance based on a sorted tree and + /// initializes it based on a collection of elements. + /// + /// A collection of elements that defines the initial set contents. + public SortedSet(ICollection initialValues) + : this() + { + ((ISet) this).AddAll(initialValues); + } + + /// + /// Creates a new set instance based on a sorted tree and + /// initializes it based on a collection of elements. + /// + /// A collection of elements that defines the initial set contents. + /// The to use for sorting. + public SortedSet(ICollection initialValues, IComparer comparer) + : this(comparer) + { + this.AddAll(initialValues); + } + } +} diff --git a/src/Spring/Spring.Core/Collections/Generic/SynchronizedSet.cs b/src/Spring/Spring.Core/Collections/Generic/SynchronizedSet.cs new file mode 100644 index 00000000..ee6c77cd --- /dev/null +++ b/src/Spring/Spring.Core/Collections/Generic/SynchronizedSet.cs @@ -0,0 +1,262 @@ +/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Spring.Collections.Generic +{ + /// + ///

Implements a thread-safe Set 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 lock the SyncRoot object for the duration of the enumeration.

+ ///
+ [Serializable] + public sealed class SynchronizedSet : Set + { + private ISet mBasisSet; + private object mSyncRoot; + + /// + /// Constructs a thread-safe Set wrapper. + /// + /// The Set object that this object will wrap. + public SynchronizedSet(ISet basisSet) + { + mBasisSet = basisSet; + mSyncRoot = ((ICollection) basisSet).SyncRoot; + if (mSyncRoot == null) + throw new NullReferenceException("The Set you specified returned a null SyncRoot."); + } + + /// + /// Adds the specified element to this set if it is not already present. + /// + /// The object to add to the set. + /// is the object was added, if it was already present. + public override sealed bool Add(T o) + { + lock (mSyncRoot) + { + return mBasisSet.Add(o); + } + } + + /// + /// Adds all the elements in the specified collection to the set if they are not already present. + /// + /// A collection of objects to add to the set. + /// is the set changed as a result of this operation, if not. + public override sealed bool AddAll(ICollection c) + { + Set temp; + lock (((ICollection) c).SyncRoot) + { + temp = new HashedSet(c); + } + + lock (mSyncRoot) + { + return mBasisSet.AddAll(temp); + } + } + + /// + /// Removes all objects from the set. + /// + public override sealed void Clear() + { + lock (mSyncRoot) + { + mBasisSet.Clear(); + } + } + + /// + /// Returns if this set contains the specified element. + /// + /// The element to look for. + /// if this set contains the specified element, otherwise. + public override sealed bool Contains(T o) + { + lock (mSyncRoot) + { + return mBasisSet.Contains(o); + } + } + + /// + /// Returns if the set contains all the elements in the specified collection. + /// + /// A collection of objects. + /// if the set contains all the elements in the specified collection, otherwise. + public override sealed bool ContainsAll(ICollection c) + { + Set temp; + lock (((ICollection) c).SyncRoot) + { + temp = new HashedSet(c); + } + lock (mSyncRoot) + { + return mBasisSet.ContainsAll(temp); + } + } + + /// + /// Returns if this set contains no elements. + /// + public override sealed bool IsEmpty + { + get + { + lock (mSyncRoot) + { + return mBasisSet.IsEmpty; + } + } + } + + + /// + /// Removes the specified element from the set. + /// + /// The element to be removed. + /// if the set contained the specified element, otherwise. + public override sealed bool Remove(T o) + { + lock (mSyncRoot) + { + return mBasisSet.Remove(o); + } + } + + /// + /// Remove all the specified elements from this set, if they exist in this set. + /// + /// A collection of elements to remove. + /// if the set was modified as a result of this operation. + public override sealed bool RemoveAll(ICollection c) + { + Set temp; + lock (((ICollection) c).SyncRoot) + { + temp = new HashedSet(c); + } + lock (mSyncRoot) + { + return mBasisSet.RemoveAll(temp); + } + } + + /// + /// Retains only the elements in this set that are contained in the specified collection. + /// + /// Collection that defines the set of elements to be retained. + /// if this set changed as a result of this operation. + public override sealed bool RetainAll(ICollection c) + { + Set temp; + lock (((ICollection) c).SyncRoot) + { + temp = new HashedSet(c); + } + lock (mSyncRoot) + { + return mBasisSet.RetainAll(temp); + } + } + + /// + /// Copies the elements in the Set to an array. The type of array needs + /// to be compatible with the objects in the Set, obviously. + /// + /// An array that will be the target of the copy operation. + /// The zero-based index where copying will start. + public override sealed void CopyTo(T[] array, int index) + { + lock (mSyncRoot) + { + mBasisSet.CopyTo(array, index); + } + } + + /// + /// The number of elements contained in this collection. + /// + public override sealed int Count + { + get + { + lock (mSyncRoot) + { + return mBasisSet.Count; + } + } + } + + /// + /// Returns , indicating that this object is thread-safe. The exception to this + /// is enumeration, which is inherently not thread-safe. Use the SyncRoot object to + /// lock this object for the entire duration of the enumeration. + /// + public override sealed bool IsSynchronized + { + get { return true; } + } + + /// + /// Returns an object that can be used to synchronize the Set between threads. + /// + public override sealed object SyncRoot + { + get { return mSyncRoot; } + } + + /// + /// Enumeration is, by definition, not thread-safe. Use a lock on the SyncRoot + /// to synchronize the entire enumeration process. + /// + /// + public override sealed IEnumerator GetEnumerator() + { + return mBasisSet.GetEnumerator(); + } + + /// + /// Returns a clone of the Set instance. + /// + /// A clone of this object. + public override object Clone() + { + return new SynchronizedSet((ISet) mBasisSet.Clone()); + } + + /// + /// Indicates whether given instace is read-only or not + /// + public override bool IsReadOnly + { + get + { + lock (mSyncRoot) + { + return mBasisSet.IsReadOnly; + } + } + } + + /// + /// Performs CopyTo when called trhough non-generic ISet (ICollection) interface + /// + /// + /// + protected override void NonGenericCopyTo(Array array, int index) + { + lock (mSyncRoot) + { + ((ICollection) this.mBasisSet).CopyTo(array, index); + } + } + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/ConfigurationAttribute.cs b/src/Spring/Spring.Core/Context/Annotation/ConfigurationAttribute.cs new file mode 100644 index 00000000..9b00778a --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/ConfigurationAttribute.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Spring.Context.Annotation +{ + /// + /// Indicates that a class declares one or more methods and may be processed + /// by the Spring container to generate object definitions and service requests for those objects + /// at runtime. + /// + /// Configuration is meta-annotated as a {@link Component}, therefore Configuration + /// classes are candidates for component-scanning and may also take advantage of + /// at the field and method but not at the constructor level. + /// + /// May be used in conjunction with the attribute to indicate that all object + /// methods declared within this class are by default lazily initialized. + /// + ///

Constraints

+ ///
    + ///
  • Configuration classes must be non-final
  • + ///
  • Configuration classes must be non-local (may not be declared within a method)
  • + ///
  • Configuration classes must have a default/no-arg constructor and may not use + /// {@link Autowired} constructor parameters
  • + ///
+ ///
+ [AttributeUsage(AttributeTargets.Class)] + public class ConfigurationAttribute : Attribute + { + private string _name; + + /// + /// Initializes a new instance of the Configuration class. + /// + /// + public ConfigurationAttribute(string name) + { + _name = name; + } + + /// + /// 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. + /// + /// The custom name applies only if the Configuration class is picked up via + /// component scanning or supplied directly to a . + /// If the Configuration class is registered as a traditional XML object definition, + /// the name/id of the object element will take precedence. + /// + /// + /// + /// The name. + public string Name + { + get { return _name; } + set + { + _name = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/ConfigurationClass.cs b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClass.cs new file mode 100644 index 00000000..53a594e7 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClass.cs @@ -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 _importedResources = new Dictionary(); + + private ISet _methods = new HashedSet(); + + private string _objectName; + + private IResource _resource; + + /// + /// Initializes a new instance of the ConfigurationClass class. + /// + /// + /// + public ConfigurationClass(string objectName, Type type) + { + this._objectName = objectName; + this._configurationClassType = type; + } + + public Type ConfigurationClassType + { + get + { + return _configurationClassType; + } + } + + public IDictionary ImportedResources + { + get + { + return _importedResources; + } + } + + public ISet 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 methodNameCounts = new Dictionary(); + 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)) + { } + + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassMethod.cs b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassMethod.cs new file mode 100644 index 00000000..91fbe3fd --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassMethod.cs @@ -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; + + /// + /// Initializes a new instance of the ConfigurationClassMethod class. + /// + /// + /// + 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) + { } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassObjectDefinitionReader.cs b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassObjectDefinitionReader.cs new file mode 100644 index 00000000..276b54ad --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassObjectDefinitionReader.cs @@ -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; + + /// + /// Initializes a new instance of the ConfigurationClassObjectDefinitionReader class. + /// + /// + /// + 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 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 beanAttributes = metadata.getAnnotationAttributes(Bean.class.getName()); + object[] objectAttributes = metadata.GetCustomAttributes(typeof(DefinitionAttribute), true); + List names = new List(); + 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 importedResources) + { + IDictionary readerInstanceCache = new Dictionary(); + foreach (KeyValuePair 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 + { + } + + } + +} diff --git a/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassParser.cs b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassParser.cs new file mode 100644 index 00000000..1a681a5a --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassParser.cs @@ -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 _configurationClasses = new HashedSet(); + + private Stack _importStack = new Stack(); + + private IProblemReporter _problemReporter; + + /// + /// Initializes a new instance of the ConfigurationClassParser class. + /// + /// + public ConfigurationClassParser(IProblemReporter problemReporter) + { + _problemReporter = problemReporter; + } + + public ISet 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 definitionMethods = GetAllMethodsWithCustomAttributeForClass(configurationClass.ConfigurationClassType, typeof(DefinitionAttribute)); + foreach (MethodInfo definitionMethod in definitionMethods) + { + configurationClass.Methods.Add(new ConfigurationClassMethod(definitionMethod, configurationClass)); + + } + } + + private ISet GetAllMethodsWithCustomAttributeForClass(Type theClass, Type customAttribute) + { + ISet methods = new HashedSet(); + + 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 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) + ) + { } + + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassPostProcessor.cs b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassPostProcessor.cs new file mode 100644 index 00000000..8c58bcf1 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/ConfigurationClassPostProcessor.cs @@ -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 configCandidates = new HashedSet(); + 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); + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/DefinitionAttribute.cs b/src/Spring/Spring.Core/Context/Annotation/DefinitionAttribute.cs new file mode 100644 index 00000000..8f00d403 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/DefinitionAttribute.cs @@ -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; + + /// + /// Are dependencies to be injected via autowiring? + /// + /// The auto wire. + public AutoWiringMode AutoWire + { + get { return _autoWire; } + set + { + _autoWire = value; + } + } + private string _destroyMethod; + /// + /// 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. + /// + /// 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. + /// + /// + /// + /// The destroy method. + public string DestroyMethod + { + get { return _destroyMethod; } + set + { + _destroyMethod = value; + } + } + + /// + /// 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. + /// + /// The init method. + public string InitMethod + { + get { return _initMethod; } + set + { + _initMethod = value; + } + } + + /// + /// 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. + /// + /// The name. + public string[] Name + { + get + { + return _name; + } + set + { + _name = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/DependsOnAttribute.cs b/src/Spring/Spring.Core/Context/Annotation/DependsOnAttribute.cs new file mode 100644 index 00000000..94f46207 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/DependsOnAttribute.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Spring.Context.Annotation +{ + /// + /// 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. + /// Note: This attribute will not be inherited by child object definitions, + /// hence it needs to be specified per concrete object definition. + /// + /// Using at the class level has no effect unless component-scanning + /// is being used. If a -attributed class is declared via XML, + /// attribute metadata is ignored, and + /// <object depends-on="..."/> is respected instead. + /// + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] + public class DependsOnAttribute : Attribute + { + private string[] _name; + + /// + /// Initializes a new instance of the DependsOn class. + /// + /// + public DependsOnAttribute(string[] name) + { + _name = name; + } + + public string[] Name + { + get { return _name; } + set + { + _name = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/ImportAttribute.cs b/src/Spring/Spring.Core/Context/Annotation/ImportAttribute.cs new file mode 100644 index 00000000..9f01c55d --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/ImportAttribute.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Spring.Context.Annotation +{ + /// + /// Indicates one or more classes to import. + /// + /// Provides functionality equivalent to the :lt;import/> element in Spring XML. + /// Only supported for actual -attributed classes. + /// + /// + /// definitions declared in imported classes + /// should be accessed by using 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 + /// class methods. + /// + /// + /// If XML or other non- object definition resources need to be + /// imported, use + /// + /// + [AttributeUsage(AttributeTargets.Class)] + public class ImportAttribute : Attribute + { + private Type[] _types; + + /// + /// Initializes a new instance of the Import class. + /// + /// + public ImportAttribute(Type[] types) + { + _types = types; + } + + /// + /// The class or classes to import. + /// + /// The type. + public Type[] Types + { + get { return _types; } + set + { + _types = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/ImportResourceAttribute.cs b/src/Spring/Spring.Core/Context/Annotation/ImportResourceAttribute.cs new file mode 100644 index 00000000..1b798e32 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/ImportResourceAttribute.cs @@ -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 +{ + /// + /// + /// + [AttributeUsage(AttributeTargets.Class)] + public class ImportResourceAttribute : Attribute + { + private IObjectDefinitionReader _objectDefinitionReader; + + private string[] _resources; + + /// + /// Initializes a new instance of the ImportResource class. + /// + /// + /// + public ImportResourceAttribute(IObjectDefinitionReader objectDefinitionReader, string[] resources) + { + _objectDefinitionReader = objectDefinitionReader; + _resources = resources; + } + + /// + /// implementation to use when processing resources specified + /// by the attribute. + /// + /// The . + public IObjectDefinitionReader DefinitionReader + { + get { return _objectDefinitionReader; } + set + { + _objectDefinitionReader = value; + } + } + + /// + /// Resource paths to import. Resource-loading prefixes such as assembly:// and + /// file://, etc may be used. + /// + /// The resources. + public string[] Resources + { + get { return _resources; } + set + { + _resources = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/LazyAttrribute.cs b/src/Spring/Spring.Core/Context/Annotation/LazyAttrribute.cs new file mode 100644 index 00000000..1eba0cf4 --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/LazyAttrribute.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Spring.Context.Annotation +{ + /// + /// Indicates whether a object is to be lazily initialized. + /// + /// 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 . + /// If present and set to false, the object will be instantiated on startup by object factories + /// that perform eager initialization of singletons. + /// + /// + /// If Lazy is present on a class, this indicates that all + /// methods within that 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. + /// + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] + public class LazyAttrribute : Attribute + { + private bool _lazyInitialize = true; + + /// + /// Initializes a new instance of the Lazy class. + /// + /// + public LazyAttrribute(bool lazyInitialize) + { + _lazyInitialize = lazyInitialize; + } + + /// + /// Whether lazy initialization should occur. + /// + /// true if [lazy initialize]; otherwise, false. + public bool LazyInitialize + { + get { return _lazyInitialize; } + set + { + _lazyInitialize = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/PrimaryAttribute.cs b/src/Spring/Spring.Core/Context/Annotation/PrimaryAttribute.cs new file mode 100644 index 00000000..5e1b717f --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/PrimaryAttribute.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Spring.Context.Annotation +{ + /// + /// 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. + /// + /// Using at the class level has no effect unless component-scanning + /// is being used. If a -attributed class is declared via XML, + /// attribute metadata is ignored, and + /// <object primary="true|false"/>> is respected instead. + /// + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class PrimaryAttribute : Attribute + { + + } +} diff --git a/src/Spring/Spring.Core/Context/Annotation/ScopeAttribute.cs b/src/Spring/Spring.Core/Context/Annotation/ScopeAttribute.cs new file mode 100644 index 00000000..6ac5d81e --- /dev/null +++ b/src/Spring/Spring.Core/Context/Annotation/ScopeAttribute.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Spring.Objects.Factory.Support; + +namespace Spring.Context.Annotation +{ + /// + /// When used as a type-level attribute, indicates the name of a scope to use + /// for instances of the attributed type. + /// + /// When used as a method-level attribute in conjunction with the + /// attribute, indicates the name of a scope to use for + /// the instance returned from the method. + /// + /// In this context, scope means the lifecycle of an instance, such as + /// singleton, prototype, and so forth. + /// + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class ScopeAttribute : Attribute + { + private ObjectScope _scope = ObjectScope.Singleton; + + /// + /// Initializes a new instance of the Scope class. + /// + /// + public ScopeAttribute(ObjectScope scope) + { + _scope = scope; + } + + /// + /// Specifies the scope to use for the annotated object. + /// + /// The scope. + public ObjectScope ObjectScope + { + get { return _scope; } + set + { + _scope = value; + } + } + + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Parsing/FailFastProblemReporter.cs b/src/Spring/Spring.Core/Objects/Factory/Parsing/FailFastProblemReporter.cs new file mode 100644 index 00000000..54211e89 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Parsing/FailFastProblemReporter.cs @@ -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); + + } + + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Parsing/Location.cs b/src/Spring/Spring.Core/Objects/Factory/Parsing/Location.cs new file mode 100644 index 00000000..48832944 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Parsing/Location.cs @@ -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; + + /// + /// Initializes a new instance of the Location class. + /// + /// + /// + public Location(IResource resource, object source) + { + AssertUtils.ArgumentNotNull(resource, "resource"); + this.resource = resource; + this.source = source; + } + + /// + /// Initializes a new instance of the Location class. + /// + /// + public Location(IResource resource) + : this(resource, null) + { + + } + public IResource Resource + { + get + { + return resource; + } + } + public object Source + { + get + { + return source; + } + } + + + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Parsing/ObjectDefinitionParsingException.cs b/src/Spring/Spring.Core/Objects/Factory/Parsing/ObjectDefinitionParsingException.cs new file mode 100644 index 00000000..31c28370 --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Parsing/ObjectDefinitionParsingException.cs @@ -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) + { + } + /// + /// Initializes a new instance of the ObjectDefinitionParsingException class. + /// + public ObjectDefinitionParsingException(Problem problem) + : base(problem.Location.Resource, problem.ResourceDescription, problem.Message) + { + + } + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Parsing/Problem.cs b/src/Spring/Spring.Core/Objects/Factory/Parsing/Problem.cs new file mode 100644 index 00000000..0848261a --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Parsing/Problem.cs @@ -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; + + /// + /// Initializes a new instance of the Problem class. + /// + /// + /// + public Problem(string message, Location location) + : this(message, location, null) + { + + } + + /// + /// Initializes a new instance of the Problem class. + /// + /// + /// + /// + 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(); + + } + + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistryPostProcessor.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistryPostProcessor.cs new file mode 100644 index 00000000..f4898c0a --- /dev/null +++ b/src/Spring/Spring.Core/Objects/Factory/Support/IObjectDefinitionRegistryPostProcessor.cs @@ -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); + } +} diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionReaderUtils.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionReaderUtils.cs index 8140318b..e007f6cd 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionReaderUtils.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionReaderUtils.cs @@ -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 diff --git a/src/Spring/Spring.Web/Objects/Factory/Support/ObjectScope.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectScope.cs similarity index 100% rename from src/Spring/Spring.Web/Objects/Factory/Support/ObjectScope.cs rename to src/Spring/Spring.Core/Objects/Factory/Support/ObjectScope.cs diff --git a/src/Spring/Spring.Core/Spring.Core.2010.csproj b/src/Spring/Spring.Core/Spring.Core.2010.csproj index f67bb62c..1aab03b2 100644 --- a/src/Spring/Spring.Core/Spring.Core.2010.csproj +++ b/src/Spring/Spring.Core/Spring.Core.2010.csproj @@ -125,7 +125,15 @@ Code + + + + + + + + Code @@ -163,6 +171,19 @@ Code + + + + + + + + + + + + + Code @@ -695,7 +716,14 @@ + + + + + + + diff --git a/src/Spring/Spring.Web/Spring.Web.2010.csproj b/src/Spring/Spring.Web/Spring.Web.2010.csproj index f6f041f2..4069813d 100644 --- a/src/Spring/Spring.Web/Spring.Web.2010.csproj +++ b/src/Spring/Spring.Web/Spring.Web.2010.csproj @@ -169,7 +169,6 @@ -