using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using JD.Collections.Details; using JD.Collections.Utilities; using JD.Kernel.StaticHelpers; namespace JD.Collections.ObjectModel { /// /// ReadOnlyCollectionBase is a base class that can be used to more easily implement the /// generic ICollection<T> and non-generic ICollection interfaces for a read-only collection: /// a collection that does not allow adding or removing elements. /// /// /// To use ReadOnlyCollectionBase as a base class, the derived class must override /// the Count and GetEnumerator methods. /// ICollection<T>.Contains need not be implemented by the /// derived class, but it should be strongly considered, because the ReadOnlyCollectionBase implementation /// may not be very efficient. /// /// The item type of the collection. [Serializable] [DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(ReadOnlyCollectionBase<>.DebugView))] public abstract class ReadOnlyCollectionBase : ICollection, ICollection { #region Inner Types private sealed class DebugView { #region Variables private readonly ICollection m_collection; #endregion #region Constructor /// /// Constructor. /// /// public DebugView(ICollection collection) { Throw.IfNull(collection, "collection"); m_collection = collection; } #endregion #region Properties /// /// Items. /// [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public T[] Items { get { T[] array = new T[m_collection.Count]; m_collection.CopyTo(array, 0); return array; } } #endregion } #endregion #region Methods /// /// Throws an NotSupportedException stating that this collection cannot be modified. /// protected virtual void MethodModifiesCollection() { throw new NotSupportedException(string.Format(Strings.CannotModifyCollection, GetSimpleClassName(GetType()))); } /// /// Returns the simple name of the class, for use in exception messages. /// /// The simple name of this class. private static string GetSimpleClassName(Type type) { string name = type.Name; // Just use the simple name. int index = name.IndexOfAny(new [] { '<', '{', '`' }); if (index >= 0) { name = name.Substring(0, index); } return name; } /// /// Overriden. /// /// public override string ToString() { return new CollectionPrinter().Print(this); } #endregion #region ICollection Members /// /// This method throws an NotSupportedException /// stating the collection is read-only. /// /// Item to be added to the collection. /// Always thrown. void ICollection.Add(T item) { MethodModifiesCollection(); } /// /// This method throws an NotSupportedException /// stating the collection is read-only. /// /// Always thrown. void ICollection.Clear() { MethodModifiesCollection(); } /// /// This method throws an NotSupportedException /// stating the collection is read-only. /// /// Item to be removed from the collection. /// Always thrown. bool ICollection.Remove(T item) { MethodModifiesCollection(); return false; } /// /// Determines if the collection contains a particular item. This default implementation /// iterates all of the items in the collection via GetEnumerator, testing each item /// against using IComparable<T>.Equals or /// Object.Equals. /// /// You should strongly consider overriding this method to provide /// a more efficient implementation. /// The item to check for in the collection. /// True if the collection contains , false otherwise. public virtual bool Contains(T item) { IEqualityComparer equalityComparer = EqualityComparer.Default; foreach (T i in this) { if (equalityComparer.Equals(i, item)) { return true; } } return false; } /// /// Copies all the items in the collection into an array. Implemented by /// using the enumerator returned from GetEnumerator to get all the items /// and copy them to the provided array. /// /// Array to copy to. /// Starting index in to copy to. public virtual void CopyTo(T[] array, int arrayIndex) { int count = Count; if (count == 0) { return; } Throw.IfNull(array, "array"); Throw.ArgumentOutOfRangeIf(count < 0, "count", Strings.ArgMustNotBeNegative); Throw.ArgumentOutOfRangeIf(arrayIndex < 0, "arrayIndex", Strings.ArgMustNotBeNegative); Throw.InvalidArgumentIf(arrayIndex >= array.Length || count > array.Length - arrayIndex, "arrayIndex", Strings.ArrayTooSmall); int index = arrayIndex, i = 0; foreach (T item in (ICollection)this) { if (i >= count) { break; } array[index] = item; ++index; ++i; } } /// /// Creates an array of the correct size, and copies all the items in the /// collection into the array, by calling CopyTo. /// /// An array containing all the elements in the collection, in order. public virtual T[] ToArray() { int count = Count; T[] array = new T[count]; CopyTo(array, 0); return array; } /// /// Must be overridden to provide the number of items in the collection. /// /// The number of items in the collection. public abstract int Count { get; } /// /// Indicates whether the collection is read-only. Returns the value /// of readOnly that was provided to the constructor. /// /// Always true. public bool IsReadOnly { get { return true; } } #endregion #region IEnumerable Members /// /// Must be overridden to enumerate all the members of the collection. /// /// A generic IEnumerator<T> that can be used /// to enumerate all the items in the collection. public abstract IEnumerator GetEnumerator(); #endregion #region ICollection Members /// /// Copies all the items in the collection into an array. Implemented by /// using the enumerator returned from GetEnumerator to get all the items /// and copy them to the provided array. /// /// Array to copy to. /// Starting index in to copy to. void ICollection.CopyTo(Array array, int index) { int count = Count; if (count == 0) { return; } Throw.IfNull(array, "array"); Throw.ArgumentOutOfRangeIf(count < 0, "count", Strings.ArgMustNotBeNegative); Throw.ArgumentOutOfRangeIf(index < 0, "arrayIndex", Strings.ArgMustNotBeNegative); Throw.InvalidArgumentIf(index >= array.Length || count > array.Length - index, "arrayIndex", Strings.ArrayTooSmall); int i = 0; foreach (object o in (ICollection)this) { if (i >= count) { break; } array.SetValue(o, index); ++index; ++i; } } /// /// Indicates whether the collection is synchronized. /// /// Always returns false, indicating that the collection is not synchronized. bool ICollection.IsSynchronized { get { return false; } } /// /// Indicates the synchronization object for this collection. /// /// Always returns this. object ICollection.SyncRoot { get { return this; } } #endregion #region IEnumerable Members /// /// Provides an IEnumerator that can be used to iterate all the members of the /// collection. This implementation uses the IEnumerator<T> that was overridden /// by the derived classes to enumerate the members of the collection. /// /// An IEnumerator that can be used to iterate the collection. IEnumerator IEnumerable.GetEnumerator() { foreach (T item in this) { yield return item; } } #endregion #region Debugger display /// /// Display the contents of the collection in the debugger. This is intentionally private, it is called /// only from the debugger due to the presence of the DebuggerDisplay attribute. It is similar /// format to ToString(), but is limited to 250-300 characters or so, so as not to overload the debugger. /// /// The string representation of the items in the collection, similar in format to ToString(). internal string DebuggerDisplayString() { const int MAXLENGTH = 250; System.Text.StringBuilder builder = new System.Text.StringBuilder(); builder.Append('{'); // Call ToString on each item and put it in. bool firstItem = true; foreach (T item in this) { if (builder.Length >= MAXLENGTH) { builder.Append(", ..."); break; } if (!firstItem) builder.Append(", "); if (ReferenceEquals(item, null)) builder.Append("null"); else builder.Append(item.ToString()); firstItem = false; } builder.Append('}'); return builder.ToString(); } #endregion } }