Click here to Skip to main content
15,885,767 members
Articles / Desktop Programming / WPF

Auto-filter for Microsoft WPF DataGrid

Rate me:
Please Sign up or sign in to vote.
4.81/5 (25 votes)
29 Jan 2009Eclipse3 min read 226.2K   8.4K   62  
Allows auto filtering functionality for DataGrid columns.
//******************************
// Written by Peter Golde
// Copyright (c) 2004-2005, Wintellect
//
// Use and restribution of this code is subject to the license agreement 
// contained in the file "License.txt" accompanying this file.
//******************************

using System;
using System.Collections.Generic;
using System.Collections;

// CONSIDER: always create a small initial buffer, so that the checks in Add for a null buffer aren't needed. This 
// would improve performance slightly, but make empty Deque's take more memory. 

namespace Wintellect.PowerCollections
{
    /// <summary>
    /// <para>The Deque class implements a type of list known as a Double Ended Queue. A Deque
    /// is quite similar to a List, in that items have indices (starting at 0), and the item at any
    /// index can be efficiently retrieved. The difference between a List and a Deque lies in the
    /// efficiency of inserting elements at the beginning. In a List, items can be efficiently added
    /// to the end, but inserting an item at the beginning of the List is slow, taking time 
    /// proportional to the size of the List. In a Deque, items can be added to the beginning 
    /// or end equally efficiently, regardless of the number of items in the Deque. As a trade-off
    /// for this increased flexibility, Deque is somewhat slower than List (but still constant time) when
    /// being indexed to get or retrieve elements. </para>
    /// </summary>
    /// <remarks>
    /// <para>The Deque class can also be used as a more flexible alternative to the Queue 
    /// and Stack classes. Deque is as efficient as Queue and Stack for adding or removing items, 
    /// but is more flexible: it allows access
    /// to all items in the queue, and allows adding or removing from either end.</para>
    /// <para>Deque is implemented as a ring buffer, which is grown as necessary. The size
    /// of the buffer is doubled whenever the existing capacity is too small to hold all the
    /// elements.</para>
    /// </remarks>
    /// <typeparam name="T">The type of items stored in the Deque.</typeparam>
    [Serializable]
    public class Deque<T> : ListBase<T>, ICloneable
    {
        // The initial size of the buffer.
        private const int INITIAL_SIZE = 8;

        // A ring buffer containing all the items in the deque. Shrinks or grows as needed.
        // Except temporarily during an add, there is always at least one empty item.
        private T[] buffer;

        // Index of the first item (index 0) in the deque.
        // Always in the range 0 through buffer.Length - 1.
        private int start;

        // Index just beyond the last item in the deque. If equal to start, the deque is empty.
        // Always in the range 0 through buffer.Length - 1.
        private int end;

        // Holds the change stamp for the collection.
        private int changeStamp;

        /// <summary>
        /// Must be called whenever there is a structural change in the tree. Causes
        /// changeStamp to be changed, which causes any in-progress enumerations
        /// to throw exceptions.
        /// </summary>
        private void StopEnumerations()
        {
            ++changeStamp;
        }

        /// <summary>
        /// Checks the given stamp against the current change stamp. If different, the
        /// collection has changed during enumeration and an InvalidOperationException
        /// must be thrown
        /// </summary>
        /// <param name="startStamp">changeStamp at the start of the enumeration.</param>
        private void CheckEnumerationStamp(int startStamp)
        {
            if (startStamp != changeStamp) {
                throw new InvalidOperationException(Strings.ChangeDuringEnumeration);
            }
        }

        /// <summary>
        /// Create a new Deque that is initially empty.
        /// </summary>
        public Deque()
        {
        }

        /// <summary>
        /// Create a new Deque initialized with the items from the passed collection,
        /// in order.
        /// </summary>
        /// <param name="collection">A collection of items to initialize the Deque with.</param>
        public Deque(IEnumerable<T> collection)
        {
            AddManyToBack(collection);
        }

        /// <summary>
        /// Gets the number of items currently stored in the Deque. The last item
        /// in the Deque has index Count-1.
        /// </summary>
        /// <remarks>Getting the count of items in the Deque takes a small constant
        /// amount of time.</remarks>
        /// <value>The number of items stored in this Deque.</value>
        public sealed override int Count
        {
            get {
                if (end >= start)
                    return end - start;
                else
                    return end + buffer.Length - start;
            }
        }

        /// <summary>
        /// Copies all the items in the Deque into an array.
        /// </summary>
        /// <param name="array">Array to copy to.</param>
        /// <param name="arrayIndex">Starting index in <paramref name="array"/> to copy to.</param>
        public sealed override void CopyTo(T[] array, int arrayIndex)
        {
            if (array == null)
                throw new ArgumentNullException("array");

            // This override is provided to give a more efficient implementation to CopyTo than
            // the default one provided by CollectionBase.

            int length = (buffer == null) ? 0 : buffer.Length;

            if (start > end) {
                Array.Copy(buffer, start, array, arrayIndex, length - start);
                Array.Copy(buffer, 0, array, arrayIndex + length - start, end);
            }
            else {
                if (end > start)
                    Array.Copy(buffer, start, array, arrayIndex, end - start);
            }
        }

        /// <summary>
        /// Gets or sets the capacity of the Deque. The Capacity is the number of
        /// items that this Deque can hold without expanding its internal buffer. Since
        /// Deque will automatically expand its buffer when necessary, in almost all cases
        /// it is unnecessary to worry about the capacity. However, if it is known that a
        /// Deque will contain exactly 1000 items eventually, it can slightly improve 
        /// efficiency to set the capacity to 1000 up front, so that the Deque does not
        /// have to expand automatically.
        /// </summary>
        /// <value>The number of items that this Deque can hold without expanding its
        /// internal buffer.</value>
        /// <exception cref="ArgumentOutOfRangeException">The capacity is being set
        /// to less than Count, or to too large a value.</exception>
        public int Capacity
        {
            get
            {
                if (buffer == null)
                    return 0;
                else
                    return buffer.Length - 1;
            }
            set
            {
                if (value < Count)
                    throw new ArgumentOutOfRangeException("value", Strings.CapacityLessThanCount);
                if (value > int.MaxValue - 1)
                    throw new ArgumentOutOfRangeException("value");
                if (value == Capacity)
                    return;

                T[] newBuffer = new T[value + 1];
                CopyTo(newBuffer, 0);
                end = Count;
                start = 0;
                buffer = newBuffer;
            }
        }

        /// <summary>
        /// Trims the amount of memory used by the Deque by changing
        /// the Capacity to be equal to Count. If no more items will be added
        /// to the Deque, calling TrimToSize will reduce the amount of memory
        /// used by the Deque.
        /// </summary>
        public void TrimToSize()
        {
            Capacity = Count;
        }

        /// <summary>
        /// Removes all items from the Deque.
        /// </summary>
        /// <remarks>Clearing the Deque takes a small constant amount of time, regardless of
        /// how many items are currently in the Deque.</remarks>
        public sealed override void Clear()
        {
            StopEnumerations();
            buffer = null;
            start = end = 0;
        }

        /// <summary>
        /// Gets or sets an item at a particular index in the Deque. 
        /// </summary>
        /// <remarks>Getting or setting the item at a particular index takes a small constant amount
        /// of time, no matter what index is used.</remarks>
        /// <param name="index">The index of the item to retrieve or change. The front item has index 0, and
        /// the back item has index Count-1.</param>
        /// <returns>The value at the indicated index.</returns>
        /// <exception cref="ArgumentOutOfRangeException">The index is less than zero or greater than or equal
        /// to Count.</exception>
        public sealed override T this[int index]
        {
            get
            {
                int i = index + start;
                if (i < start)  // handles both the case where index < 0, or the above addition overflow to a negative number.
                    throw new ArgumentOutOfRangeException("index");

                if (end >= start) {
                    if (i >= end)
                        throw new ArgumentOutOfRangeException("index");
                    return buffer[i];
                }
                else {
                    int length = buffer.Length;
                    if (i >= length) {
                        i -= length;
                        if (i >= end)
                            throw new ArgumentOutOfRangeException("index");
                    }
                    return buffer[i];
                }
            }

            set
            {
                // Like List<T>, we stop enumerations after a set operation. There is no
                // technical reason to do this, however.
                StopEnumerations();

                int i = index + start;
                if (i < start)  // handles both the case where index < 0, or the above addition overflow to a negative number.
                    throw new ArgumentOutOfRangeException("index");

                if (end >= start) {
                    if (i >= end)
                        throw new ArgumentOutOfRangeException("index");
                    buffer[i] = value;
                }
                else {
                    int length = buffer.Length;
                    if (i >= length) {
                        i -= length;
                        if (i >= end)
                            throw new ArgumentOutOfRangeException("index");
                    }
                    buffer[i] = value;
                }
            }
        }

        /// <summary>
        /// Enumerates all of the items in the list, in order. The item at index 0
        /// is enumerated first, then the item at index 1, and so on. If the items
        /// are added to or removed from the Deque during enumeration, the 
        /// enumeration ends with an InvalidOperationException.
        /// </summary>
        /// <returns>An IEnumerator&lt;T&gt; that enumerates all the
        /// items in the list.</returns>
        /// <exception cref="InvalidOperationException">The Deque has an item added or deleted during the enumeration.</exception>
        public sealed override IEnumerator<T> GetEnumerator()
        {
            int startStamp = changeStamp;
            int count = Count;

            for (int i = 0; i < count; ++i) {
                yield return this[i];
                CheckEnumerationStamp(startStamp);
            }
        }

        /// <summary>
        /// Creates the initial buffer and initialized the Deque to contain one initial
        /// item.
        /// </summary>
        /// <param name="firstItem">First and only item for the Deque.</param>
        private void CreateInitialBuffer(T firstItem)
        {
            // The buffer hasn't been created yet.
            buffer = new T[INITIAL_SIZE];
            start = 0;
            end = 1;
            buffer[0] = firstItem;
            return;
        }

        /// <summary>
        /// Inserts a new item at the given index in the Deque. All items at indexes 
        /// equal to or greater than <paramref name="index"/> move up one index
        /// in the Deque.
        /// </summary>
        /// <remarks>The amount of time to insert an item in the Deque is proportional
        /// to the distance of index from the closest end of the Deque: 
        /// O(Min(<paramref name="index"/>, Count - <paramref name="index"/>)).
        /// Thus, inserting an item at the front or end of the Deque is always fast; the middle of
        /// of the Deque is the slowest place to insert.
        /// </remarks>
        /// <param name="index">The index in the Deque to insert the item at. After the
        /// insertion, the inserted item is located at this index. The
        /// front item in the Deque has index 0.</param>
        /// <param name="item">The item to insert at the given index.</param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
        /// less than zero or greater than Count.</exception>
        public sealed override void Insert(int index, T item)
        {
            StopEnumerations();

            int count = Count;
            if (index < 0 || index > Count)
                throw new ArgumentOutOfRangeException("index");

            if (buffer == null) {
                // The buffer hasn't been created yet.
                CreateInitialBuffer(item);
                return;
            }

            int length = buffer.Length;
            int i;  // The location the new item was placed at.

            if (index < count / 2) {
                // Inserting into the first half of the list. Move items with
                // lower index down in the buffer.
                start -= 1;
                if (start < 0)
                    start += length;
                i = index + start;
                if (i >= length) {
                    i -= length;
                    if (length - 1 > start)
                        Array.Copy(buffer, start + 1, buffer, start, length - 1 - start);
                    buffer[length - 1] = buffer[0];  // unneeded if end == 0, but doesn't hurt
                    if (i > 0)
                        Array.Copy(buffer, 1, buffer, 0, i);
                }
                else {
                    if (i > start)
                        Array.Copy(buffer, start + 1, buffer, start, i - start);
                }
            }
            else {
                // Inserting into the last half of the list. Move items with higher
                // index up in the buffer.
                i = index + start;
                if (i >= length)
                    i -= length;
                if (i <= end) {  
                    if (end > i)
                        Array.Copy(buffer, i, buffer, i + 1, end - i);
                    end += 1;
                    if (end >= length)
                        end -= length;
                }
                else {
                    if (end > 0)
                        Array.Copy(buffer, 0, buffer, 1, end);
                    buffer[0] = buffer[length - 1];
                    if (length - 1 > i)
                        Array.Copy(buffer, i, buffer, i + 1, length - 1 - i);
                    end += 1;
                }
            }

            buffer[i] = item;
            if (start == end)
                IncreaseBuffer();
        }

        /// <summary>
        /// Inserts a collection of items at the given index in the Deque. All items at indexes 
        /// equal to or greater than <paramref name="index"/> increase their indices in the Deque
        /// by the number of items inserted.
        /// </summary>
        /// <remarks>The amount of time to insert a collection in the Deque is proportional
        /// to the distance of index from the closest end of the Deque, plus the number of items
        /// inserted (M): 
        /// O(M + Min(<paramref name="index"/>, Count - <paramref name="index"/>)).
        /// </remarks>
        /// <param name="index">The index in the Deque to insert the collection at. After the
        /// insertion, the first item of the inserted collection is located at this index. The
        /// front item in the Deque has index 0.</param>
        /// <param name="collection">The collection of items to insert at the given index.</param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
        /// less than zero or greater than Count.</exception>
        public void InsertRange(int index, IEnumerable<T> collection)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");

            StopEnumerations();

            int count = Count;
            if (index < 0 || index > Count)
                throw new ArgumentOutOfRangeException("index");

            // We need an ICollection, because we need the count of the collection.
            // If needed, copy the items to a temporary list.
            ICollection<T> coll;
            if (collection is ICollection<T>)
                coll = (ICollection<T>) collection;
            else {
                coll = new List<T>(collection);
            }
            if (coll.Count == 0)
                return;          // nothing to do.

            // Create enough capacity in the list for the new items.
            if (Capacity < Count + coll.Count)
                Capacity = Count + coll.Count;

            int length = buffer.Length;
            int s, d;

            if (index < count / 2) {
                // Inserting into the first half of the list. Move items with
                // lower index down in the buffer.
                s = start;
                d = s - coll.Count;
                if (d < 0)
                    d += length;
                start = d;
                int c = index;

                while (c > 0) {
                    int chunk = c;
                    if (length - d < chunk)
                        chunk = length - d;
                    if (length - s < chunk)
                        chunk = length - s;
                    Array.Copy(buffer, s, buffer, d, chunk);
                    c -= chunk;
                    if ((d += chunk) >= length)
                        d -= length;
                    if ((s += chunk) >= length)
                        s -= length;
                }
            }
            else {
                // Inserting into the last half of the list. Move items with higher
                // index up in the buffer.
                s = end;
                d = s + coll.Count;
                if (d >= length)
                    d -= length;
                end = d;
                int move = count - index;  // number of items at end to move

                int c = move;
                while (c > 0) {
                    int chunk = c;
                    if (d > 0 && d < chunk)
                        chunk = d;
                    if (s > 0 && s < chunk)
                        chunk = s;
                    if ((d -= chunk) < 0)
                        d += length;
                    if ((s -= chunk) < 0)
                        s += length;
                    Array.Copy(buffer, s, buffer, d, chunk);
                    c -= chunk;
                }

                d -= coll.Count;
                if (d < 0)
                    d += length;
            }

            // Copy the items into the space vacated, which starts at d.
            foreach (T item in coll) {
                buffer[d] = item;
                if (++d >= length)
                    d -= length;
            }
        }

        /// <summary>
        /// Removes the item at the given index in the Deque. All items at indexes 
        /// greater than <paramref name="index"/> move down one index
        /// in the Deque.
        /// </summary>
        /// <remarks>The amount of time to delete an item in the Deque is proportional
        /// to the distance of index from the closest end of the Deque: 
        /// O(Min(<paramref name="index"/>, Count - 1 - <paramref name="index"/>)).
        /// Thus, deleting an item at the front or end of the Deque is always fast; the middle of
        /// of the Deque is the slowest place to delete.
        /// </remarks>
        /// <param name="index">The index in the list to remove the item at. The
        /// first item in the list has index 0.</param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
        /// less than zero or greater than or equal to Count.</exception>
        public sealed override void RemoveAt(int index)
        {
            StopEnumerations();

            int count = Count;

            if (index < 0 || index >= count)
                throw new ArgumentOutOfRangeException("index");

            int length = buffer.Length;
            int i; // index of removed item
            if (index < count / 2) {
                // Removing in the first half of the list. Move items with
                // lower index up in the buffer.
                i = index + start;

                if (i >= length) {
                    i -= length;

                    if (i > 0)
                        Array.Copy(buffer, 0, buffer, 1, i);
                    buffer[0] = buffer[length - 1];   
                    if (length - 1 > start)
                        Array.Copy(buffer, start, buffer, start + 1, length - 1 - start);
                }
                else {
                    if (i > start)
                        Array.Copy(buffer, start, buffer, start + 1, i - start);
                }

                buffer[start] = default(T);
                start += 1;
                if (start >= length)
                    start -= length;
            }
            else {
                // Removing in the second half of the list. Move items with
                // higher indexes down in the buffer.
                i = index + start;
                if (i >= length)
                    i -= length;
                end -= 1;
                if (end < 0)
                    end = length - 1;

                if (i <= end) {
                    if (end > i)
                        Array.Copy(buffer, i + 1, buffer, i, end - i);
                }
                else {
                    if (length - 1 > i)
                        Array.Copy(buffer, i + 1, buffer, i, length - 1 - i);
                    buffer[length - 1] = buffer[0];  
                    if (end > 0)
                        Array.Copy(buffer, 1, buffer, 0, end);
                }

                buffer[end] = default(T);
            }
        }

        /// <summary>
        /// Removes a range of items at the given index in the Deque. All items at indexes 
        /// greater than <paramref name="index"/> move down <paramref name="count"/> indices
        /// in the Deque.
        /// </summary>
        /// <remarks>The amount of time to delete <paramref name="count"/> items in the Deque is proportional
        /// to the distance to the closest end of the Deque: 
        /// O(Min(<paramref name="index"/>, Count - <paramref name="index"/> - <paramref name="count"/>)).
        /// </remarks>
        /// <param name="index">The index in the list to remove the range at. The
        /// first item in the list has index 0.</param>
        /// <param name="count">The number of items to remove.</param>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
        /// less than zero or greater than or equal to Count, or <paramref name="count"/> is less than zero
        /// or too large.</exception>
        public void RemoveRange(int index, int count)
        {
            StopEnumerations();

            int dequeCount = Count;

            if (index < 0 || index >= dequeCount)
                throw new ArgumentOutOfRangeException("index");
            if (count < 0 || count > dequeCount - index)
                throw new ArgumentOutOfRangeException("count");
            if (count == 0)
                return;

            int length = buffer.Length;
            int s, d;
            if (index < dequeCount / 2) {
                // Removing in the first half of the list. Move items with
                // lower index up in the buffer.
                s = start + index;
                if (s >= length)
                    s -= length;
                d = s + count;
                if (d >= length)
                    d -= length;

                int c = index;
                while (c > 0) {
                    int chunk = c;
                    if (d > 0 && d < chunk)
                        chunk = d;
                    if (s > 0 && s < chunk)
                        chunk = s;
                    if ((d -= chunk) < 0)
                        d += length;
                    if ((s -= chunk) < 0)
                        s += length;
                    Array.Copy(buffer, s, buffer, d, chunk);
                    c -= chunk;
                }

                // At this point, s == start
                for (c = 0; c < count; ++c) {
                    buffer[s] = default(T);
                    if (++s >= length)
                        s -= length;
                }
                start = s;
            }
            else {
                // Removing in the second half of the list. Move items with
                // higher indexes down in the buffer.
                int move = dequeCount - index - count;
                s = end - move;
                if (s < 0)
                    s += length;
                d = s - count;
                if (d < 0)
                    d += length;

                int c = move;
                while (c > 0) {
                    int chunk = c;
                    if (length - d < chunk)
                        chunk = length - d;
                    if (length - s < chunk)
                        chunk = length - s;
                    Array.Copy(buffer, s, buffer, d, chunk);
                    c -= chunk;
                    if ((d += chunk) >= length)
                        d -= length;
                    if ((s += chunk) >= length)
                        s -= length;
                }

                // At this point, s == end.
                for (c = 0; c < count; ++c) {
                    if (--s < 0)
                        s += length;
                    buffer[s] = default(T);
                }
                end = s;
            }
        }

        /// <summary>
        /// Increase the amount of buffer space. When calling this method, the Deque
        /// must not be empty. If start and end are equal, that indicates a completely
        /// full Deque.
        /// </summary>
        private void IncreaseBuffer()
        {
            int count = Count;
            int length = buffer.Length;

            T[] newBuffer = new T[length * 2];
            if (start >= end) {
                Array.Copy(buffer, start, newBuffer, 0, length - start);
                Array.Copy(buffer, 0, newBuffer, length - start, end);
                end = end + length - start;
                start = 0;
            }
            else {
                Array.Copy(buffer, start, newBuffer, 0, end - start);
                end = end - start;
                start = 0;
            }

            buffer = newBuffer;
        }

        /// <summary>
        /// Adds an item to the front of the Deque. The indices of all existing items
        /// in the Deque are increased by 1. This method is 
        /// equivalent to <c>Insert(0, item)</c> but is a little more
        /// efficient.
        /// </summary>
        /// <remarks>Adding an item to the front of the Deque takes
        /// a small constant amount of time, regardless of how many items are in the Deque.</remarks>
        /// <param name="item">The item to add.</param>
        public void AddToFront(T item)
        {
            StopEnumerations();

            if (buffer == null) {
                // The buffer hasn't been created yet.
                CreateInitialBuffer(item);
                return;
            }

            if (--start < 0)
                start += buffer.Length;
            buffer[start] = item;
            if (start == end)
                IncreaseBuffer();
        }

        /// <summary>
        /// Adds a collection of items to the front of the Deque. The indices of all existing items
        /// in the Deque are increased by the number of items inserted. The first item in the added collection becomes the
        /// first item in the Deque. 
        /// </summary>
        /// <remarks>This method takes time O(M), where M is the number of items in the 
        /// <paramref name="collection"/>.</remarks>
        /// <param name="collection">The collection of items to add.</param>
        public void AddManyToFront(IEnumerable<T> collection)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");

            InsertRange(0, collection);
        }

        /// <summary>
        /// Adds an item to the back of the Deque. The indices of all existing items
        /// in the Deque are unchanged. This method is 
        /// equivalent to <c>Insert(Count, item)</c> but is a little more
        /// efficient.
        /// </summary>
        /// <remarks>Adding an item to the back of the Deque takes
        /// a small constant amount of time, regardless of how many items are in the Deque.</remarks>
        /// <param name="item">The item to add.</param>
        public void AddToBack(T item)
        {
            StopEnumerations();

            if (buffer == null) {
                // The buffer hasn't been created yet.
                CreateInitialBuffer(item);
                return;
            }

            buffer[end] = item;
            if (++end >= buffer.Length)
                end -= buffer.Length;
            if (start == end)
                IncreaseBuffer();
        }

        /// <summary>
        /// Adds an item to the back of the Deque. The indices of all existing items
        /// in the Deque are unchanged. This method is 
        /// equivalent to <c>AddToBack(item)</c>.
        /// </summary>
        /// <remarks>Adding an item to the back of the Deque takes
        /// a small constant amount of time, regardless of how many items are in the Deque.</remarks>
        /// <param name="item">The item to add.</param>
        public sealed override void Add(T item)
        {
            AddToBack(item);
        }


        /// <summary>
        /// Adds a collection of items to the back of the Deque. The indices of all existing items
        /// in the Deque are unchanged. The last item in the added collection becomes the
        /// last item in the Deque.
        /// </summary>
        /// <remarks>This method takes time O(M), where M is the number of items in the 
        /// <paramref name="collection"/>.</remarks>
        /// <param name="collection">The collection of item to add.</param>
        public void AddManyToBack(IEnumerable<T> collection)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");

            foreach (T item in collection)
                AddToBack(item);
        }

        /// <summary>
        /// Removes an item from the front of the Deque. The indices of all existing items
        /// in the Deque are decreased by 1. This method is 
        /// equivalent to <c>RemoveAt(0)</c> but is a little more
        /// efficient.
        /// </summary>
        /// <remarks>Removing an item from the front of the Deque takes
        /// a small constant amount of time, regardless of how many items are in the Deque.</remarks>
        /// <returns>The item that was removed.</returns>
        /// <exception cref="InvalidOperationException">The Deque is empty.</exception>
        public T RemoveFromFront()
        {
            if (start == end)
                throw new InvalidOperationException(Strings.CollectionIsEmpty);

            StopEnumerations();

            T item = buffer[start];
            buffer[start] = default(T);
            if (++start >= buffer.Length)
                start -= buffer.Length;
            return item;
        }

        /// <summary>
        /// Removes an item from the back of the Deque. The indices of all existing items
        /// in the Deque are unchanged. This method is 
        /// equivalent to <c>RemoveAt(Count-1)</c> but is a little more
        /// efficient.
        /// </summary>
        /// <remarks>Removing an item from the back of the Deque takes
        /// a small constant amount of time, regardless of how many items are in the Deque.</remarks>
        /// <exception cref="InvalidOperationException">The Deque is empty.</exception>
        public T RemoveFromBack()
        {
            if (start == end)
                throw new InvalidOperationException(Strings.CollectionIsEmpty);

            StopEnumerations();

            if (--end < 0)
                end += buffer.Length;
            T item = buffer[end];
            buffer[end] = default(T);
            return item;
        }

        /// <summary>
        /// Retreives the item currently at the front of the Deque. The Deque is 
        /// unchanged. This method is 
        /// equivalent to <c>deque[0]</c> (except that a different exception is thrown).
        /// </summary>
        /// <remarks>Retreiving the item at the front of the Deque takes
        /// a small constant amount of time, regardless of how many items are in the Deque.</remarks>
        /// <returns>The item at the front of the Deque.</returns>
        /// <exception cref="InvalidOperationException">The Deque is empty.</exception>
        public T GetAtFront()
        {
            if (start == end)
                throw new InvalidOperationException(Strings.CollectionIsEmpty);

            return buffer[start];
        }

        /// <summary>
        /// Retreives the item currently at the back of the Deque. The Deque is 
        /// unchanged. This method is 
        /// equivalent to <c>deque[deque.Count - 1]</c> (except that a different exception is thrown).
        /// </summary>
        /// <remarks>Retreiving the item at the back of the Deque takes
        /// a small constant amount of time, regardless of how many items are in the Deque.</remarks>
        /// <returns>The item at the back of the Deque.</returns>
        /// <exception cref="InvalidOperationException">The Deque is empty.</exception>
        public T GetAtBack()
        {
            if (start == end)
                throw new InvalidOperationException(Strings.CollectionIsEmpty);

            if (end == 0)
                return buffer[buffer.Length - 1];
            else
                return buffer[end - 1];
        }

        /// <summary>
        /// Creates a new Deque that is a copy of this one.
        /// </summary>
        /// <remarks>Copying a Deque takes O(N) time, where N is the number of items in this Deque..</remarks>
        /// <returns>A copy of the current deque.</returns>
        public Deque<T> Clone()
        {
            return new Deque<T>(this);
        }

        /// <summary>
        /// Creates a new Deque that is a copy of this one.
        /// </summary>
        /// <remarks>Copying a Deque takes O(N) time, where N is the number of items in this Deque..</remarks>
        /// <returns>A copy of the current deque.</returns>
        object ICloneable.Clone()
        {
            return this.Clone();
        }

        /// <summary>
        /// Makes a deep clone of this Deque. A new Deque is created with a clone of
        /// each element of this set, by calling ICloneable.Clone on each element. If T is
        /// a value type, then each element is copied as if by simple assignment.
        /// </summary>
        /// <remarks><para>If T is a reference type, it must implement
        /// ICloneable. Otherwise, an InvalidOperationException is thrown.</para>
        /// <para>Cloning the Deque takes time O(N), where N is the number of items in the Deque.</para></remarks>
        /// <returns>The cloned Deque.</returns>
        /// <exception cref="InvalidOperationException">T is a reference type that does not implement ICloneable.</exception>
        public Deque<T> CloneContents()
        {
            bool itemIsValueType;
            if (!Util.IsCloneableType(typeof(T), out itemIsValueType))
                throw new InvalidOperationException(string.Format(Strings.TypeNotCloneable, typeof(T).FullName));

            Deque<T> clone = new Deque<T>();

            // Clone each item, and add it to the new ordered set.
            foreach (T item in this) {
                T itemClone;

                if (itemIsValueType)
                    itemClone = item;
                else {
                    if (item == null)
                        itemClone = default(T);    // Really null, because we know T is a reference type
                    else
                        itemClone = (T)(((ICloneable)item).Clone());
                }

                clone.AddToBack(itemClone);
            }

            return clone;
        }

#if DEBUG
        /// <summary>
        /// Print out the internal state of the Deque for debugging.
        /// </summary>
        internal void Print()
        {
            Console.WriteLine("length={0}  start={1}  end={2}", buffer.Length, start, end);
            for (int i = 0; i < buffer.Length; ++i) {
                if (i == start)
                    Console.Write("start-> ");
                else
                    Console.Write("        ");
                if (i == end)
                    Console.Write("end-> ");
                else
                    Console.Write("      ");
                Console.WriteLine("{0,4} {1}", i, buffer[i]);
            }
            Console.WriteLine();
        }
#endif // DEBUG
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Eclipse Public License 1.0


Written By
Software Developer (Senior) Lab49
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions