/* Copyright © 2002-2004 by Aidant Systems, Inc., and by Jason Smith. */

#region License

/*
 * Copyright © 2002-2005 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#endregion

#region Imports 

using System;
using System.Collections;

#endregion

namespace Spring.Collections
{
	/// <summary>
	/// Implements a thread-safe <see cref="Spring.Collections.ISet"/> wrapper.
	/// </summary>
	/// <remarks>
	/// <p>
	/// The implementation is extremely conservative, serializing critical
	/// sections to prevent possible deadlocks, and locking on everything. The
	/// one exception is for enumeration, which is inherently not thread-safe.
	/// For this, you have to <c>lock</c> the <c>SyncRoot</c> object for the
	/// duration of the enumeration.
	/// </p>
	/// </remarks>
	/// <seealso cref="Spring.Collections.ISet"/>
    [Serializable]
    public sealed class SynchronizedSet : Set
	{
		private ISet _mBasisSet;
		private object _mSyncRoot;

		/// <summary>
		/// Constructs a thread-safe <see cref="Spring.Collections.ISet"/>
		/// wrapper.
		/// </summary>
		/// <param name="basisSet">
		/// The <see cref="Spring.Collections.ISet"/> object that this object
		/// will wrap.
		/// </param>
		/// <exception cref="System.NullReferenceException">
		/// If the supplied <paramref name="basisSet"/> ecposes a
		/// <see langword="null"/> <c>SyncRoot</c> value.
		/// </exception>
		public SynchronizedSet(ISet basisSet)
		{
			_mBasisSet = basisSet;
			_mSyncRoot = basisSet.SyncRoot;
			if (_mSyncRoot == null)
			{
				throw new NullReferenceException(
					"The Set you specified returned a null SyncRoot.");
			}
		}

		/// <summary>
		/// Adds the specified element to this set if it is not already present.
		/// </summary>
		/// <param name="element">The object to add to the set.</param>
		/// <returns>
		/// <see langword="true"/> is the object was added,
		/// <see langword="true"/> if the object was already present.
		/// </returns>
		public override sealed bool Add(object element)
		{
			lock (_mSyncRoot)
			{
				return _mBasisSet.Add(element);
			}
		}

		/// <summary>
		/// Adds all the elements in the specified collection to the set if
		/// they are not already present.
		/// </summary>
		/// <param name="collection">A collection of objects to add to the set.</param>
		/// <returns>
		/// <see langword="true"/> is the set changed as a result of this
		/// operation.
		/// </returns>
		public override sealed bool AddAll(ICollection collection)
		{
			if(collection == null) 
			{
				return false;
			}
			Set temp;
			lock (collection.SyncRoot)
			{
				temp = new HybridSet(collection);
			}
			lock (_mSyncRoot)
			{
				return _mBasisSet.AddAll(temp);
			}
		}

		/// <summary>
		/// Removes all objects from this set.
		/// </summary>
		public override sealed void Clear()
		{
			lock (_mSyncRoot)
			{
				_mBasisSet.Clear();
			}
		}

		/// <summary>
		/// Returns <see langword="true"/> if this set contains the specified
		/// element.
		/// </summary>
		/// <param name="element">The element to look for.</param>
		/// <returns>
		/// <see langword="true"/> if this set contains the specified element.
		/// </returns>
		public override sealed bool Contains(object element)
		{
			lock (_mSyncRoot)
			{
				return _mBasisSet.Contains(element);
			}
		}

		/// <summary>
		/// Returns <see langword="true"/> if the set contains all the
		/// elements in the specified collection.
		/// </summary>
		/// <param name="collection">A collection of objects.</param>
		/// <returns>
		/// <see langword="true"/> if the set contains all the elements in the
		/// specified collection; also <see langword="false"/> if the
		/// supplied <paramref name="collection"/> is <see langword="null"/>.
		/// </returns>
		public override sealed bool ContainsAll(ICollection collection)
		{
			if(collection == null) 
			{
				return false;
			}
			Set temp;
			lock (collection.SyncRoot)
			{
				temp = new HybridSet(collection);
			}
			lock (_mSyncRoot)
			{
				return _mBasisSet.ContainsAll(temp);
			}
		}

		/// <summary>
		/// Returns <see langword="true"/> if this set contains no elements.
		/// </summary>
		public override sealed bool IsEmpty
		{
			get
			{
				lock (_mSyncRoot)
				{
					return _mBasisSet.IsEmpty;
				}
			}
		}

		/// <summary>
		/// Removes the specified element from the set.
		/// </summary>
		/// <param name="element">The element to be removed.</param>
		/// <returns>
		/// <see langword="true"/> if the set contained the specified element.
		/// </returns>
		public override sealed bool Remove(object element)
		{
			lock (_mSyncRoot)
			{
				return _mBasisSet.Remove(element);
			}
		}

		/// <summary>
		/// Remove all the specified elements from this set, if they exist in
		/// this set.
		/// </summary>
		/// <param name="collection">A collection of elements to remove.</param>
		/// <returns>
		/// <see langword="true"/> if the set was modified as a result of this
		/// operation.
		/// </returns>
		public override sealed bool RemoveAll(ICollection collection)
		{
			Set temp;
			lock (collection.SyncRoot)
			{
				temp = new HybridSet(collection);
			}
			lock (_mSyncRoot)
			{
				return _mBasisSet.RemoveAll(temp);
			}
		}

		/// <summary>
		/// Retains only the elements in this set that are contained in the
		/// specified collection.
		/// </summary>
		/// <param name="c">
		/// The collection that defines the set of elements to be retained.
		/// </param>
		/// <returns>
		/// <see langword="true"/> if this set changed as a result of this
		/// operation.
		/// </returns>
		public override sealed bool RetainAll(ICollection c)
		{
			Set temp;
			lock (c.SyncRoot)
			{
				temp = new HybridSet(c);
			}
			lock (_mSyncRoot)
			{
				return _mBasisSet.RetainAll(temp);
			}
		}

		/// <summary>
		/// Copies the elements in the <see cref="Spring.Collections.ISet"/> to
		/// an array.
		/// </summary>
		/// <remarks>
		/// <p>
		/// The type of array needs to be compatible with the objects in the
		/// <see cref="Spring.Collections.ISet"/>, obviously.
		/// </p>
		/// </remarks>
		/// <param name="array">
		/// An array that will be the target of the copy operation.
		/// </param>
		/// <param name="index">
		/// The zero-based index where copying will start.
		/// </param>
		public override sealed void CopyTo(Array array, int index)
		{
			lock (_mSyncRoot)
			{
				_mBasisSet.CopyTo(array, index);
			}
		}

		/// <summary>
		/// The number of elements currently contained in this collection.
		/// </summary>
		public override sealed int Count
		{
			get
			{
				lock (_mSyncRoot)
				{
					return _mBasisSet.Count;
				}
			}
		}

		/// <summary>
		/// Returns <see langword="true"/> if the
		/// <see cref="Spring.Collections.ISet"/> is synchronized across
		/// threads.
		/// </summary>
		/// <seealso cref="Spring.Collections.Set.IsSynchronized"/>
		public override sealed bool IsSynchronized
		{
			get { return true; }
		}

		/// <summary>
		/// An object that can be used to synchronize this collection to make
		/// it thread-safe.
		/// </summary>
		/// <value>
		/// An object that can be used to synchronize this collection to make
		/// it thread-safe.
		/// </value>
		/// <seealso cref="Spring.Collections.Set.SyncRoot"/>
		public override sealed object SyncRoot
		{
			get { return _mSyncRoot; }
		}

		/// <summary>
		/// Gets an enumerator for the elements in the
		/// <see cref="Spring.Collections.ISet"/>.
		/// </summary>
		/// <returns>
		/// An <see cref="System.Collections.IEnumerator"/> over the elements
		/// in the <see cref="Spring.Collections.ISet"/>.
		/// </returns>
		public override sealed IEnumerator GetEnumerator()
		{
			return _mBasisSet.GetEnumerator();
		}

		/// <summary>
		/// Returns a clone of the <see cref="Spring.Collections.ISet"/> instance.  
		/// </summary>
		/// <returns>A clone of this object.</returns>
		public override object Clone()
		{
			return new SynchronizedSet((ISet) _mBasisSet.Clone());
		}
	}
}