#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2007 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
namespace Nuclex.Support.Collections {
/// Collection that transforms the contents of another collection.
///
/// Type of the items contained in the wrapped collection.
///
///
/// Type this collection exposes its items as.
///
///
///
/// This collection is useful if you want to expose the objects of an arbitrary
/// collection under a different type. It can be used, for example, to construct
/// wrappers for the items in a collection on-the-fly, eliminating the need to
/// manage the wrappers in parallel to the real items and improving performance
/// by only constructing a wrapper when an item is actually requested.
///
///
/// Another common use would be if you have a private collection of a non-public
/// type that's derived from some publicly visible type. By using this collection,
/// you can return the items under the publicly visible type while still having
/// your private collection under the non-public type, eliminating the need to
/// downcast each time you need to access elements of the non-public type.
///
///
public abstract class TransformingReadOnlyCollection :
IList, IList {
#region class TransformingEnumerator
///
/// An enumerator that transforms the items returned by an enumerator of the
/// wrapped collection into the exposed type on-the-fly.
///
private class TransformingEnumerator : IEnumerator {
/// Initializes a new transforming enumerator
/// Owner; used to invoke the Transform() method
/// Enumerator of the wrapped collection
public TransformingEnumerator(
TransformingReadOnlyCollection transformer,
IEnumerator containedTypeEnumerator
) {
this.transformer = transformer;
this.containedTypeEnumerator = containedTypeEnumerator;
}
/// Immediately releases all resources used by the instance
public void Dispose() {
this.containedTypeEnumerator.Dispose();
}
///
/// The element in the collection at the current position of the enumerator.
///
public ExposedItemType Current {
get {
return this.transformer.Transform(this.containedTypeEnumerator.Current);
}
}
/// Gets the current element in the collection.
/// The current element in the collection.
///
/// The enumerator is positioned before the first element of the collection
/// or after the last element.
///
public bool MoveNext() {
return this.containedTypeEnumerator.MoveNext();
}
///
/// Sets the enumerator to its initial position, which is before the first element
/// in the collection.
///
///
/// The collection was modified after the enumerator was created.
///
public void Reset() {
this.containedTypeEnumerator.Reset();
}
/// The current element in the collection.
///
/// The enumerator is positioned before the first element of the collection
/// or after the last element.
///
object IEnumerator.Current {
get { return Current; }
}
///
/// Collection that owns this enumerator; required to invoke the item
/// transformation method.
///
private TransformingReadOnlyCollection transformer;
/// An enumerator from the wrapped collection
private IEnumerator containedTypeEnumerator;
}
#endregion // class TransformingEnumerator
/// Initializes a new transforming collection wrapper
///
/// Internal list of items that are transformed into the exposed type when
/// accessed through the TransformingReadOnlyCollection.
///
public TransformingReadOnlyCollection(IList items) {
this.items = items;
}
///
/// Determines whether an element is in the TransformingReadOnlyCollection
///
///
/// The object to locate in the TransformingReadOnlyCollection.
/// The value can be null for reference types.
///
///
/// True if value is found in the TransformingReadOnlyCollection; otherwise, false.
///
///
/// The default implementation of this method is very unoptimized and will
/// enumerate all the items in the collection, transforming one after another
/// to check whether the transformed item matches the item the user was
/// looking for. It is recommended to provide a custom implementation of
/// this method, if possible.
///
public virtual bool Contains(ExposedItemType item) {
return (IndexOf(item) != -1);
}
///
/// Copies the entire TransformingReadOnlyCollection to a compatible one-dimensional
/// System.Array, starting at the specified index of the target array.
///
///
/// The one-dimensional System.Array that is the destination of the elements copied
/// from the TransformingReadOnlyCollection. The System.Array must have
/// zero-based indexing.
///
///
/// The zero-based index in array at which copying begins.
///
///
/// Index is equal to or greater than the length of array or the number of elements
/// in the source TransformingReadOnlyCollection is greater than the available space
/// from index to the end of the destination array.
///
///
/// Index is less than zero.
///
///
/// Array is null.
///
public void CopyTo(ExposedItemType[] array, int index) {
if(this.items.Count > (array.Length - index))
throw new ArgumentException(
"Array too small to fit the collection items starting at the specified index"
);
for(int itemIndex = 0; itemIndex < this.items.Count; ++itemIndex)
array[itemIndex + index] = Transform(this.items[itemIndex]);
}
///
/// Returns an enumerator that iterates through the TransformingReadOnlyCollection.
///
///
/// An enumerator or the TransformingReadOnlyCollection.
///
public IEnumerator GetEnumerator() {
return new TransformingEnumerator(this, this.items.GetEnumerator());
}
///
/// Searches for the specified object and returns the zero-based index of the
/// first occurrence within the entire TransformingReadOnlyCollection.
///
///
/// The object to locate in the TransformingReadOnlyCollection. The value can
/// be null for reference types.
///
///
/// The zero-based index of the first occurrence of item within the entire
/// TransformingReadOnlyCollection, if found; otherwise, -1.
///
///
/// The default implementation of this method is very unoptimized and will
/// enumerate all the items in the collection, transforming one after another
/// to check whether the transformed item matches the item the user was
/// looking for. It is recommended to provide a custom implementation of
/// this method, if possible.
///
public int IndexOf(ExposedItemType item) {
if(item == null) {
for(int index = 0; index < this.items.Count; ++index) {
if(Transform(this.items[index]) == null) {
return index;
}
}
} else {
EqualityComparer comparer =
EqualityComparer.Default;
for(int index = 0; index < this.items.Count; ++index) {
if(comparer.Equals(Transform(this.items[index]), item)) {
return index;
}
}
}
return -1;
}
///
/// The number of elements contained in the TransformingReadOnlyCollection instance
///
public int Count {
get { return this.items.Count; }
}
/// Gets the element at the specified index.
/// The zero-based index of the element to get.
/// The element at the specified index.
///
/// Index is less than zero or index is equal to or greater than
/// TransformingReadOnlyCollection.Count.
///
public ExposedItemType this[int index] {
get { return Transform(this.items[index]); }
}
/// Transforms an item into the exposed type
/// Item to be transformed
/// The transformed item
///
/// This method is used to transform an item in the wrapped collection into
/// the exposed item type whenever the user accesses an item. Expect it to
/// be called frequently, because the TransformingReadOnlyCollection does
/// not cache or otherwise store the transformed items.
///
protected abstract ExposedItemType Transform(ContainedItemType item);
#region IList Members
///
/// Determines the index of a specific item in the TransformingReadOnlyCollection.
///
///
/// The object to locate in the TransformingReadOnlyCollection.
///
///
/// The index of item if found in the list; otherwise, -1.
///
int IList.IndexOf(ExposedItemType item) {
return IndexOf(item);
}
///
/// Inserts an item to the TransformingReadOnlyCollection at the specified index.
///
///
/// The zero-based index at which item should be inserted.
///
///
/// The object to insert into the TransformingReadOnlyCollection
///
///
/// The TransformingReadOnlyCollection is read-only.
///
///
/// index is not a valid index in the TransformingReadOnlyCollection.
///
void IList.Insert(int index, ExposedItemType item) {
throw new NotSupportedException("The collection is ready-only");
}
///
/// Removes the TransformingReadOnlyCollection item at the specified index.
///
/// The zero-based index of the item to remove.
///
/// The TransformingReadOnlyCollection is read-only.
///
///
/// Index is not a valid index in the TransformingReadOnlyCollection.
///
void IList.RemoveAt(int index) {
throw new NotSupportedException("The collection is ready-only");
}
/// Gets or sets the element at the specified index.
/// The zero-based index of the element to get or set.
/// The element at the specified index.
///
/// Index is not a valid index in the TransformingReadOnlyCollection.
///
///
/// The property is set and the TransformingReadOnlyCollection is read-only
///
ExposedItemType IList.this[int index] {
get {
return this[index];
}
set {
throw new NotSupportedException("The collection is ready-only");
}
}
#endregion
#region ICollection Members
/// Adds an item to the TransformingReadOnlyCollection.
/// The object to add to the TransformingReadOnlyCollection
///
/// The TransformingReadOnlyCollection is read-only.
///
void ICollection.Add(ExposedItemType item) {
throw new NotSupportedException("The collection is ready-only");
}
/// Removes all items from the TransformingReadOnlyCollection
///
/// The TransformingReadOnlyCollection is read-only.
///
void ICollection.Clear() {
throw new NotSupportedException("The collection is ready-only");
}
///
/// Determines whether the TransformingReadOnlyCollection contains a specific value.
///
///
/// The object to locate in the TransformingReadOnlyCollection.
///
///
/// True if item is found in the TransformingReadOnlyCollection; otherwise, false.
///
bool ICollection.Contains(ExposedItemType item) {
return Contains(item);
}
///
/// Copies the elements of the TransformingReadOnlyCollection to an System.Array,
/// starting at a particular System.Array index.
///
///
/// The one-dimensional System.Array that is the destination of the elements
/// copied from TransformingReadOnlyCollection. The System.Array must have
/// zero-based indexing.
///
///
/// The zero-based index in array at which copying begins
///
///
/// ArrayIndex is less than 0.
///
///
/// Array is null.
///
///
/// Array is multidimensional or arrayIndex is equal to or greater than the
/// length of array or the number of elements in the source
/// TransformingReadOnlyCollection is greater than the available
/// space from arrayIndex to the end of the destination array or type T cannot
/// be cast automatically to the type of the destination array.
///
void ICollection.CopyTo(ExposedItemType[] array, int arrayIndex) {
CopyTo(array, arrayIndex);
}
///
/// Removes the first occurrence of a specific object from the
/// TransformingReadOnlyCollection.
///
///
/// The object to remove from the TransformingReadOnlyCollection
///
///
/// True if item was successfully removed from the TransformingReadOnlyCollection;
/// otherwise, false. This method also returns false if item is not found in the
/// original TransformingReadOnlyCollection.
///
///
/// The TransformingReadOnlyCollection is read-only.
///
bool ICollection.Remove(ExposedItemType item) {
throw new NotSupportedException("The collection is ready-only");
}
///
/// The number of elements contained in the TransformingReadOnlyCollection.
///
int ICollection.Count {
get { return Count; }
}
///
/// A value indicating whether the TransformingReadOnlyCollection is read-only.
///
bool ICollection.IsReadOnly {
get { return true; }
}
#endregion
#region IEnumerable Members
/// Returns an enumerator that iterates through the collection.
///
/// A System.Collections.Generic.IEnumerator<ExposedItemType> that can be used
/// to iterate through the collection.
///
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
#endregion
#region IEnumerable Members
/// Returns an enumerator that iterates through a collection.
///
/// A System.Collections.IEnumerator object that can be used to iterate through
/// the collection.
///
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
#endregion
#region IList Members
/// Adds an item to the TransformingReadOnlyCollection.
///
/// The System.Object to add to the TransformingReadOnlyCollection.
///
/// The position into which the new element was inserted.
///
/// The System.Collections.IList is read-only or the TransformingReadOnlyCollection
/// has a fixed size.
///
int IList.Add(object value) {
throw new NotSupportedException("The collection is ready-only");
}
/// Removes all items from the TransformingReadOnlyCollection.
///
/// The TransformingReadOnlyCollection is read-only.
///
void IList.Clear() {
throw new NotSupportedException("The collection is ready-only");
}
///
/// Determines whether the TransformingReadOnlyCollection contains a specific value.
///
///
/// The System.Object to locate in the TransformingReadOnlyCollection.
///
///
/// True if the System.Object is found in the TransformingReadOnlyCollection;
/// otherwise, false.
///
bool IList.Contains(object value) {
return Contains((ExposedItemType)value);
}
///
/// Determines the index of a specific item in the TransformingReadOnlyCollection.
///
///
/// The System.Object to locate in the TransformingReadOnlyCollection.
///
///
/// The index of value if found in the list; otherwise, -1.
///
int IList.IndexOf(object value) {
return IndexOf((ExposedItemType)value);
}
///
/// Inserts an item to the TransformingReadOnlyCollection at the specified index.
///
///
/// The zero-based index at which value should be inserted.
///
///
/// The System.Object to insert into the TransformingReadOnlyCollection.
///
///
/// Index is not a valid index in the TransformingReadOnlyCollection.
///
///
/// The System.Collections.IList is read-only or the TransformingReadOnlyCollection
/// has a fixed size.
///
///
/// Value is null reference in the TransformingReadOnlyCollection.
///
void IList.Insert(int index, object value) {
throw new NotSupportedException("The collection is ready-only");
}
///
/// A value indicating whether the TransformingReadOnlyCollection has a fixed
/// size.
///
bool IList.IsFixedSize {
get { return true; }
}
///
/// A value indicating whether the index is not a valid index in the is read-only.
///
bool IList.IsReadOnly {
get { return true; }
}
///
/// Removes the first occurrence of a specific object from the
/// TransformingReadOnlyCollection.
///
///
/// The System.Object to remove from the TransformingReadOnlyCollection.
///
///
/// The TransformingReadOnlyCollection is read-only or the
/// TransformingReadOnlyCollection has a fixed size.
///
void IList.Remove(object value) {
throw new NotSupportedException("The collection is ready-only");
}
///
/// Removes the TransformingReadOnlyCollection item at the specified index.
///
/// The zero-based index of the item to remove.
///
/// Index is not a valid index in the TransformingReadOnlyCollection.
///
///
/// The TransformingReadOnlyCollection is read-only or the
/// TransformingReadOnlyCollection has a fixed size.
///
void IList.RemoveAt(int index) {
throw new NotSupportedException("The collection is ready-only");
}
/// Gets or sets the element at the specified index.
/// The zero-based index of the element to get or set.
/// The element at the specified index
///
/// Index is not a valid index in the TransformingReadOnlyCollection
///
///
/// The property is set and the TransformingReadOnlyCollection is read-only.
///
object IList.this[int index] {
get {
return this[index];
}
set {
throw new NotSupportedException("The collection is ready-only");
}
}
#endregion
#region ICollection Members
///
/// Copies the elements of the TransformingReadOnlyCollection to an System.Array,
/// starting at a particular System.Array index.
///
///
/// The one-dimensional System.Array that is the destination of the elements
/// copied from TransformingReadOnlyCollection. The System.Array must have zero-based
/// indexing.
///
/// The zero-based index in array at which copying begins.
///
/// Array is null.
///
///
/// Index is less than zero.
///
///
/// Array is multidimensional or index is equal to or greater than the length
/// of array or the number of elements in the source TransformingReadOnlyCollection
/// is greater than the available space from index to the end of the destination
/// array.
///
///
/// The type of the source TransformingReadOnlyCollection cannot be cast
/// automatically to the type of the destination array.
///
void ICollection.CopyTo(Array array, int index) {
CopyTo((ExposedItemType[])array, index);
}
///
/// The number of elements contained in the TransformingReadOnlyCollection.
///
int ICollection.Count {
get { return Count; }
}
///
/// A value indicating whether access to the TransformingReadOnlyCollection
/// is synchronized (thread safe).
///
bool ICollection.IsSynchronized {
get { return false; }
}
///
/// An object that can be used to synchronize access to the
/// TransformingReadOnlyCollection.
///
object ICollection.SyncRoot {
get {
if(this.syncRoot == null) {
ICollection is2 = this.items as ICollection;
if(is2 != null) {
this.syncRoot = is2.SyncRoot;
} else {
Interlocked.CompareExchange(ref this.syncRoot, new object(), null);
}
}
return this.syncRoot;
}
}
#endregion
/// Items being transformed upon exposure by this collection
private IList items;
/// Synchronization root for threaded accesses to this collection
private object syncRoot;
}
} // namespace Nuclex.Support.Collections