//---------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Linq;
namespace System.Data.Common.Utils {
// An interface for a set abstraction
internal class Set : InternalBase, IEnumerable
{
#region Fields
///
/// Instance of set value comparer.
///
internal static readonly IEqualityComparer> ValueComparer =
new SetValueComparer();
///
/// Instance of empty set with default comparer.
///
internal static readonly Set Empty = new Set().MakeReadOnly();
private readonly HashSet _values;
private bool _isReadOnly;
#endregion
#region Constructors
///
/// Initialize set with the same values and comparer as other set.
///
internal Set(Set other)
: this(other._values, other.Comparer)
{
}
///
/// Initialize empty set with default comparer.
///
internal Set()
: this(null, null)
{
}
///
/// Initialize a set with the given elements and using default comparer.
///
internal Set(IEnumerable elements)
: this(elements, null)
{
}
///
/// Initializes an empty set with the given comparer.
///
internal Set(IEqualityComparer comparer)
: this(null, comparer)
{
}
///
/// Initialize a set with the given elements and comparer.
///
internal Set(IEnumerable elements, IEqualityComparer comparer)
{
_values = new HashSet(
elements ?? Enumerable.Empty(),
comparer ?? EqualityComparer.Default);
}
#endregion
#region Properties
///
/// Gets the number of elements in this set.
///
internal int Count
{
get
{
return _values.Count;
}
}
///
/// Gets the comparer used to determine equality and hash codes for elements of the set.
///
internal IEqualityComparer Comparer
{
get
{
return _values.Comparer;
}
}
#endregion
#region Methods
///
/// Determines whether the given element exists in the set.
///
internal bool Contains(TElement element)
{
return _values.Contains(element);
}
///
/// Requires: !IsReadOnly
/// Adds given element to the set. If the set already contains
/// the element, does nothing.
///
internal void Add(TElement element)
{
AssertReadWrite();
_values.Add(element);
}
///
/// Requires: !IsReadOnly
/// Adds given elements to the set. If the set already contains
/// one of the elements, does nothing.
///
internal void AddRange(IEnumerable elements)
{
AssertReadWrite();
foreach (TElement element in elements)
{
Add(element);
}
}
///
/// Requires: !IsReadOnly
/// Removes given element from the set. If the set does not contain
/// the element, does nothing.
///
internal void Remove(TElement element)
{
AssertReadWrite();
_values.Remove(element);
}
///
/// Requires: !IsReadOnly
/// Removes all elements from the set.
///
internal void Clear()
{
AssertReadWrite();
_values.Clear();
}
///
/// Returns an array containing all elements of the set. Order is arbitrary.
///
internal TElement[] ToArray()
{
return _values.ToArray();
}
///
/// Requires: other set must not be null and must have the same comparer.
/// Returns true if this set contains the same elements as the other set.
///
internal bool SetEquals(Set other)
{
AssertSetCompatible(other);
return _values.Count == other._values.Count
&& _values.IsSubsetOf(other._values);
}
///
/// Requires: other set must not be null and must have the same comparer.
/// Returns true if all elements in this set are contained in the other set.
///
internal bool IsSubsetOf(Set other)
{
AssertSetCompatible(other);
return _values.IsSubsetOf(other._values);
}
///
/// Requires: other set must not be null and must have the same comparer.
/// Returns true if this set and other set have some elements in common.
///
internal bool Overlaps(Set other)
{
AssertSetCompatible(other);
return _values.Overlaps(other._values);
}
///
/// Requires: !IsReadOnly
/// Requires: other collection must not be null.
/// Subtracts other set from this set, leaving the result in this.
///
internal void Subtract(IEnumerable other)
{
AssertReadWrite();
_values.ExceptWith(other);
}
///
/// Requires: other collection must not be null.
/// Subtracts other set from this set, returning result.
///
internal Set Difference(IEnumerable other)
{
Set copy = new Set(this);
copy.Subtract(other);
return copy;
}
///
/// Requires: !IsReadOnly
/// Requires: other collection must not be null.
/// Unions other set with this set, leaving the result in this set.
///
internal void Unite(IEnumerable other)
{
AssertReadWrite();
_values.UnionWith(other);
}
///
/// Requires: other collection must not be null.
/// Unions other set with this set, returning the result.
///
internal Set Union(IEnumerable other)
{
Set copy = new Set(this);
copy.Unite(other);
return copy;
}
///
/// Requires: !IsReadOnly
/// Requires: other set must not be null and must have the same comparer.
/// Intersects this set and other set, leaving the result in this set.
///
internal void Intersect(Set other)
{
AssertReadWrite();
AssertSetCompatible(other);
_values.IntersectWith(other._values);
}
///
/// Returns a readonly version of this set.
///
internal Set AsReadOnly()
{
if (_isReadOnly)
{
// once it's readonly, it's always readonly
return this;
}
Set copy = new Set(this);
copy._isReadOnly = true;
return copy;
}
///
/// Makes this set readonly and returns this set.
///
internal Set MakeReadOnly()
{
_isReadOnly = true;
return this;
}
///
/// Returns aggregate hash code of all elements in this set.
///
internal int GetElementsHashCode()
{
int hashCode = 0;
foreach (TElement element in this)
{
hashCode ^= Comparer.GetHashCode(element);
}
return hashCode;
}
///
/// Returns typed enumerator over elements of the set.
/// Uses HashSet<TElement>.Enumerator to avoid boxing struct.
///
public HashSet.Enumerator GetEnumerator()
{
return _values.GetEnumerator();
}
[Conditional("DEBUG")]
private void AssertReadWrite()
{
Debug.Assert(!_isReadOnly, "attempting to modify readonly collection");
}
[Conditional("DEBUG")]
private void AssertSetCompatible(Set other)
{
Debug.Assert(other != null, "other set null");
Debug.Assert(other.Comparer.GetType().Equals(this.Comparer.GetType()));
}
#endregion
#region IEnumerable Members
public class Enumerator : IEnumerator
{
private Dictionary.KeyCollection.Enumerator keys;
internal Enumerator(Dictionary.KeyCollection.Enumerator keys)
{
this.keys = keys;
}
public TElement Current { get { return keys.Current; } }
public void Dispose() { keys.Dispose(); }
object IEnumerator.Current { get { return ((IEnumerator)keys).Current; } }
public bool MoveNext() { return keys.MoveNext(); }
void System.Collections.IEnumerator.Reset() { ((System.Collections.IEnumerator)keys).Reset(); }
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region IEnumerable Members
///
/// Returns an untyped enumeration of elements in the set.
///
/// Enumeration of set members.
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region InternalBase
internal override void ToCompactString(StringBuilder builder)
{
StringUtil.ToCommaSeparatedStringSorted(builder, this);
}
#endregion
#region Nested types
private class SetValueComparer : IEqualityComparer>
{
bool IEqualityComparer>.Equals(Set x, Set y)
{
Debug.Assert(null != x && null != y, "comparer must be used only in context of Dictionary/HashSet");
return x.SetEquals(y);
}
int IEqualityComparer>.GetHashCode(Set obj)
{
Debug.Assert(null != obj, "comparer must be used only in context of Dictionary/HashSet");
return obj.GetElementsHashCode();
}
}
#endregion
}
}