/* 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>
/// <see cref="Spring.Collections.DictionarySet"/> is an
/// <see langword="abstract"/> class that supports the creation of new
/// <see cref="Spring.Collections.ISet"/> types where the underlying data
/// store is an <see cref="System.Collections.IDictionary"/> instance.
/// </summary>
/// <remarks>
/// <p>
/// You can use any object that implements the
/// <see cref="System.Collections.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
/// <see cref="System.Collections.IDictionary"/> you
/// choose will affect both the performance and the behavior of the
/// <see cref="Spring.Collections.ISet"/> using it.
/// </p>
/// <p>
/// This object overrides the <see cref="System.Object.Equals(object)"/> method,
/// but not the <see cref="System.Object.GetHashCode"/> method, because
/// the <see cref="Spring.Collections.DictionarySet"/> class is mutable.
/// Therefore, it is not safe to use as a key value in a dictionary.
/// </p>
/// <p>
/// To make a <see cref="Spring.Collections.ISet"/> typed based on your
/// own <see cref="System.Collections.IDictionary"/>, simply derive a new
/// class with a constructor that takes no parameters. Some
/// <see cref="Spring.Collections.ISet"/> implmentations cannot be defined
/// with a default constructor. If this is the case for your class, you
/// will need to override <b>clone</b> as well.
/// </p>
/// <p>
/// It is also standard practice that at least one of your constructors
/// takes an <see cref="System.Collections.ICollection"/> or an
/// <see cref="Spring.Collections.ISet"/> as an argument.
/// </p>
/// </remarks>
/// <seealso cref="Spring.Collections.ISet"/>
[Serializable]
public abstract class DictionarySet : Set
{
private IDictionary _internalDictionary;
private static readonly object PlaceholderObject = new object();
private static readonly object NullPlaceHolderKey = new object();
/// <summary>
/// Provides the storage for elements in the
/// <see cref="Spring.Collections.ISet"/>, stored as the key-set
/// of the <see cref="System.Collections.IDictionary"/> object.
/// </summary>
/// <remarks>
/// <p>
/// Set this object in the constructor if you create your own
/// <see cref="Spring.Collections.ISet"/> class.
/// </p>
/// </remarks>
protected IDictionary InternalDictionary
{
get { return _internalDictionary; }
set { _internalDictionary = value; }
}
/// <summary>
/// The placeholder object used as the value for the
/// <see cref="System.Collections.IDictionary"/> instance.
/// </summary>
/// <remarks>
/// There is a single instance of this object globally, used for all
/// <see cref="Spring.Collections.ISet"/>s.
/// </remarks>
protected static object Placeholder
{
get { return PlaceholderObject; }
}
/// <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="false"/> if the object was already present.
/// </returns>
public override bool Add(object element)
{
element = MaskNull(element);
if (InternalDictionary[element] != null)
{
return false;
}
//The object we are adding is just a placeholder. The thing we are
//really concerned with is 'o', the key.
InternalDictionary.Add(element, PlaceholderObject);
return true;
}
/// <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 bool AddAll(ICollection collection)
{
bool changed = false;
foreach (object o in collection)
{
changed |= this.Add(o);
}
return changed;
}
/// <summary>
/// Removes all objects from this set.
/// </summary>
public override void Clear()
{
InternalDictionary.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 bool Contains(object element)
{
element = MaskNull(element);
return InternalDictionary[element] != null;
}
/// <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 bool ContainsAll(ICollection collection)
{
if (collection == null)
{
return false;
}
foreach (object o in collection)
{
if (!this.Contains(MaskNull(o)))
{
return false;
}
}
return true;
}
/// <summary>
/// Returns <see langword="true"/> if this set contains no elements.
/// </summary>
public override bool IsEmpty
{
get { return InternalDictionary.Count == 0; }
}
/// <summary>
/// Removes the specified element from the set.
/// </summary>
/// <param name="element">The element to be removed.</param>
/// <returns>
/// <see langword="true"/> if the set contained the specified element.
/// </returns>
public override bool Remove(object element)
{
element = MaskNull(element);
bool contained = this.Contains(element);
if (contained)
{
InternalDictionary.Remove(element);
}
return contained;
}
/// <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 bool RemoveAll(ICollection collection)
{
bool changed = false;
foreach (object o in collection)
{
changed |= this.Remove(o);
}
return changed;
}
/// <summary>
/// Retains only the elements in this set that are contained in the
/// specified collection.
/// </summary>
/// <param name="collection">
/// 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 bool RetainAll(ICollection collection)
{
//Put data from C into a set so we can use the Contains() method.
Set cSet = new HybridSet(collection);
//We are going to build a set of elements to remove.
Set removeSet = new HybridSet();
foreach (object o in this)
{
//If C does not contain O, then we need to remove O from our
//set. We can't do this while iterating through our set, so
//we put it into RemoveSet for later.
if (!cSet.Contains(o))
{
removeSet.Add(o);
}
}
return this.RemoveAll(removeSet);
}
/// <summary>
/// Copies the elements in the <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 void CopyTo(Array array, int index)
{
int i = index;
foreach (object o in this)
{
array.SetValue(UnmaskNull(o), i++);
}
}
/// <summary>
/// The number of elements currently contained in this collection.
/// </summary>
public override int Count
{
get { return InternalDictionary.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 bool IsSynchronized
{
get { return false; }
}
/// <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 object SyncRoot
{
get { return InternalDictionary.SyncRoot; }
}
/// <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 IEnumerator GetEnumerator()
{
return new DictionarySetEnumerator(InternalDictionary.Keys.GetEnumerator());
}
private static object MaskNull(object key)
{
return key == null ? NullPlaceHolderKey : key;
}
private static object UnmaskNull(object key)
{
return key == NullPlaceHolderKey ? null : key;
}
#region Inner Class : DictionarySetEnumerator
private sealed class DictionarySetEnumerator : IEnumerator
{
#region Constructor (s) / Destructor
public DictionarySetEnumerator(IEnumerator enumerator)
{
_enumerator = enumerator;
}
#endregion
#region IEnumerator Members
public void Reset()
{
_enumerator.Reset();
}
public object Current
{
get { return UnmaskNull(_enumerator.Current); }
}
public bool MoveNext()
{
return _enumerator.MoveNext();
}
#endregion
private IEnumerator _enumerator;
}
#endregion
}
}
|