#region Apache License // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to you 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 using System; using System.Collections; namespace log4net.Appender { /// /// A strongly-typed collection of objects. /// /// Nicko Cadell public class AppenderCollection : ICollection, IList, IEnumerable #if !NETSTANDARD1_3 , ICloneable #endif { #region Interfaces /// /// Supports type-safe iteration over a . /// /// public interface IAppenderCollectionEnumerator { /// /// Gets the current element in the collection. /// IAppender Current { get; } /// /// Advances the enumerator to the next element in the collection. /// /// /// true if the enumerator was successfully advanced to the next element; /// false if the enumerator has passed the end of the collection. /// /// /// The collection was modified after the enumerator was created. /// bool MoveNext(); /// /// Sets the enumerator to its initial position, before the first element in the collection. /// void Reset(); } #endregion private const int DEFAULT_CAPACITY = 16; #region Implementation (data) private IAppender[] m_array; private int m_count = 0; private int m_version = 0; #endregion #region Static Wrappers /// /// Creates a read-only wrapper for a AppenderCollection instance. /// /// list to create a readonly wrapper arround /// /// An AppenderCollection wrapper that is read-only. /// public static AppenderCollection ReadOnly(AppenderCollection list) { if(list==null) throw new ArgumentNullException("list"); return new ReadOnlyAppenderCollection(list); } #endregion #region Static Fields /// /// An empty readonly static AppenderCollection /// public static readonly AppenderCollection EmptyCollection = ReadOnly(new AppenderCollection(0)); #endregion #region Constructors /// /// Initializes a new instance of the AppenderCollection class /// that is empty and has the default initial capacity. /// public AppenderCollection() { m_array = new IAppender[DEFAULT_CAPACITY]; } /// /// Initializes a new instance of the AppenderCollection class /// that has the specified initial capacity. /// /// /// The number of elements that the new AppenderCollection is initially capable of storing. /// public AppenderCollection(int capacity) { m_array = new IAppender[capacity]; } /// /// Initializes a new instance of the AppenderCollection class /// that contains elements copied from the specified AppenderCollection. /// /// The AppenderCollection whose elements are copied to the new collection. public AppenderCollection(AppenderCollection c) { m_array = new IAppender[c.Count]; AddRange(c); } /// /// Initializes a new instance of the AppenderCollection class /// that contains elements copied from the specified array. /// /// The array whose elements are copied to the new list. public AppenderCollection(IAppender[] a) { m_array = new IAppender[a.Length]; AddRange(a); } /// /// Initializes a new instance of the AppenderCollection class /// that contains elements copied from the specified collection. /// /// The collection whose elements are copied to the new list. public AppenderCollection(ICollection col) { m_array = new IAppender[col.Count]; AddRange(col); } /// /// Type visible only to our subclasses /// Used to access protected constructor /// /// internal protected enum Tag { /// /// A value /// Default } /// /// Allow subclasses to avoid our default constructors /// /// /// internal protected AppenderCollection(Tag tag) { m_array = null; } #endregion #region Operations (type-safe ICollection) /// /// Gets the number of elements actually contained in the AppenderCollection. /// public virtual int Count { get { return m_count; } } /// /// Copies the entire AppenderCollection to a one-dimensional /// array. /// /// The one-dimensional array to copy to. public virtual void CopyTo(IAppender[] array) { this.CopyTo(array, 0); } /// /// Copies the entire AppenderCollection to a one-dimensional /// array, starting at the specified index of the target array. /// /// The one-dimensional array to copy to. /// The zero-based index in at which copying begins. public virtual void CopyTo(IAppender[] array, int start) { if (m_count > array.GetUpperBound(0) + 1 - start) { throw new System.ArgumentException("Destination array was not long enough."); } Array.Copy(m_array, 0, array, start, m_count); } /// /// Gets a value indicating whether access to the collection is synchronized (thread-safe). /// /// false, because the backing type is an array, which is never thread-safe. public virtual bool IsSynchronized { get { return false; } } /// /// Gets an object that can be used to synchronize access to the collection. /// public virtual object SyncRoot { get { return m_array; } } #endregion #region Operations (type-safe IList) /// /// Gets or sets the at the specified index. /// /// The zero-based index of the element to get or set. /// /// is less than zero /// -or- /// is equal to or greater than . /// public virtual IAppender this[int index] { get { ValidateIndex(index); // throws return m_array[index]; } set { ValidateIndex(index); // throws ++m_version; m_array[index] = value; } } /// /// Adds a to the end of the AppenderCollection. /// /// The to be added to the end of the AppenderCollection. /// The index at which the value has been added. public virtual int Add(IAppender item) { if (m_count == m_array.Length) { EnsureCapacity(m_count + 1); } m_array[m_count] = item; m_version++; return m_count++; } /// /// Removes all elements from the AppenderCollection. /// public virtual void Clear() { ++m_version; m_array = new IAppender[DEFAULT_CAPACITY]; m_count = 0; } /// /// Creates a shallow copy of the . /// /// A new with a shallow copy of the collection data. public virtual object Clone() { AppenderCollection newCol = new AppenderCollection(m_count); Array.Copy(m_array, 0, newCol.m_array, 0, m_count); newCol.m_count = m_count; newCol.m_version = m_version; return newCol; } /// /// Determines whether a given is in the AppenderCollection. /// /// The to check for. /// true if is found in the AppenderCollection; otherwise, false. public virtual bool Contains(IAppender item) { for (int i=0; i != m_count; ++i) { if (m_array[i].Equals(item)) { return true; } } return false; } /// /// Returns the zero-based index of the first occurrence of a /// in the AppenderCollection. /// /// The to locate in the AppenderCollection. /// /// The zero-based index of the first occurrence of /// in the entire AppenderCollection, if found; otherwise, -1. /// public virtual int IndexOf(IAppender item) { for (int i=0; i != m_count; ++i) { if (m_array[i].Equals(item)) { return i; } } return -1; } /// /// Inserts an element into the AppenderCollection at the specified index. /// /// The zero-based index at which should be inserted. /// The to insert. /// /// is less than zero /// -or- /// is equal to or greater than . /// public virtual void Insert(int index, IAppender item) { ValidateIndex(index, true); // throws if (m_count == m_array.Length) { EnsureCapacity(m_count + 1); } if (index < m_count) { Array.Copy(m_array, index, m_array, index + 1, m_count - index); } m_array[index] = item; m_count++; m_version++; } /// /// Removes the first occurrence of a specific from the AppenderCollection. /// /// The to remove from the AppenderCollection. /// /// The specified was not found in the AppenderCollection. /// public virtual void Remove(IAppender item) { int i = IndexOf(item); if (i < 0) { throw new System.ArgumentException("Cannot remove the specified item because it was not found in the specified Collection."); } ++m_version; RemoveAt(i); } /// /// Removes the element at the specified index of the AppenderCollection. /// /// The zero-based index of the element to remove. /// /// is less than zero /// -or- /// is equal to or greater than . /// public virtual void RemoveAt(int index) { ValidateIndex(index); // throws m_count--; if (index < m_count) { Array.Copy(m_array, index + 1, m_array, index, m_count - index); } // We can't set the deleted entry equal to null, because it might be a value type. // Instead, we'll create an empty single-element array of the right type and copy it // over the entry we want to erase. IAppender[] temp = new IAppender[1]; Array.Copy(temp, 0, m_array, m_count, 1); m_version++; } /// /// Gets a value indicating whether the collection has a fixed size. /// /// true if the collection has a fixed size; otherwise, false. The default is false public virtual bool IsFixedSize { get { return false; } } /// /// Gets a value indicating whether the IList is read-only. /// /// true if the collection is read-only; otherwise, false. The default is false public virtual bool IsReadOnly { get { return false; } } #endregion #region Operations (type-safe IEnumerable) /// /// Returns an enumerator that can iterate through the AppenderCollection. /// /// An for the entire AppenderCollection. public virtual IAppenderCollectionEnumerator GetEnumerator() { return new Enumerator(this); } #endregion #region Public helpers (just to mimic some nice features of ArrayList) /// /// Gets or sets the number of elements the AppenderCollection can contain. /// public virtual int Capacity { get { return m_array.Length; } set { if (value < m_count) { value = m_count; } if (value != m_array.Length) { if (value > 0) { IAppender[] temp = new IAppender[value]; Array.Copy(m_array, 0, temp, 0, m_count); m_array = temp; } else { m_array = new IAppender[DEFAULT_CAPACITY]; } } } } /// /// Adds the elements of another AppenderCollection to the current AppenderCollection. /// /// The AppenderCollection whose elements should be added to the end of the current AppenderCollection. /// The new of the AppenderCollection. public virtual int AddRange(AppenderCollection x) { if (m_count + x.Count >= m_array.Length) { EnsureCapacity(m_count + x.Count); } Array.Copy(x.m_array, 0, m_array, m_count, x.Count); m_count += x.Count; m_version++; return m_count; } /// /// Adds the elements of a array to the current AppenderCollection. /// /// The array whose elements should be added to the end of the AppenderCollection. /// The new of the AppenderCollection. public virtual int AddRange(IAppender[] x) { if (m_count + x.Length >= m_array.Length) { EnsureCapacity(m_count + x.Length); } Array.Copy(x, 0, m_array, m_count, x.Length); m_count += x.Length; m_version++; return m_count; } /// /// Adds the elements of a collection to the current AppenderCollection. /// /// The collection whose elements should be added to the end of the AppenderCollection. /// The new of the AppenderCollection. public virtual int AddRange(ICollection col) { if (m_count + col.Count >= m_array.Length) { EnsureCapacity(m_count + col.Count); } foreach(object item in col) { Add((IAppender)item); } return m_count; } /// /// Sets the capacity to the actual number of elements. /// public virtual void TrimToSize() { this.Capacity = m_count; } /// /// Return the collection elements as an array /// /// the array public virtual IAppender[] ToArray() { IAppender[] resultArray = new IAppender[m_count]; if (m_count > 0) { Array.Copy(m_array, 0, resultArray, 0, m_count); } return resultArray; } #endregion #region Implementation (helpers) /// /// is less than zero /// -or- /// is equal to or greater than . /// private void ValidateIndex(int i) { ValidateIndex(i, false); } /// /// is less than zero /// -or- /// is equal to or greater than . /// private void ValidateIndex(int i, bool allowEqualEnd) { int max = (allowEqualEnd) ? (m_count) : (m_count-1); if (i < 0 || i > max) { throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("i", (object)i, "Index was out of range. Must be non-negative and less than the size of the collection. [" + (object)i + "] Specified argument was out of the range of valid values."); } } private void EnsureCapacity(int min) { int newCapacity = ((m_array.Length == 0) ? DEFAULT_CAPACITY : m_array.Length * 2); if (newCapacity < min) { newCapacity = min; } this.Capacity = newCapacity; } #endregion #region Implementation (ICollection) void ICollection.CopyTo(Array array, int start) { if (m_count > 0) { Array.Copy(m_array, 0, array, start, m_count); } } #endregion #region Implementation (IList) object IList.this[int i] { get { return (object)this[i]; } set { this[i] = (IAppender)value; } } int IList.Add(object x) { return this.Add((IAppender)x); } bool IList.Contains(object x) { return this.Contains((IAppender)x); } int IList.IndexOf(object x) { return this.IndexOf((IAppender)x); } void IList.Insert(int pos, object x) { this.Insert(pos, (IAppender)x); } void IList.Remove(object x) { this.Remove((IAppender)x); } void IList.RemoveAt(int pos) { this.RemoveAt(pos); } #endregion #region Implementation (IEnumerable) IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)(this.GetEnumerator()); } #endregion #region Nested enumerator class /// /// Supports simple iteration over a . /// /// private sealed class Enumerator : IEnumerator, IAppenderCollectionEnumerator { #region Implementation (data) private readonly AppenderCollection m_collection; private int m_index; private int m_version; #endregion #region Construction /// /// Initializes a new instance of the Enumerator class. /// /// internal Enumerator(AppenderCollection tc) { m_collection = tc; m_index = -1; m_version = tc.m_version; } #endregion #region Operations (type-safe IEnumerator) /// /// Gets the current element in the collection. /// public IAppender Current { get { return m_collection[m_index]; } } /// /// Advances the enumerator to the next element in the collection. /// /// /// true if the enumerator was successfully advanced to the next element; /// false if the enumerator has passed the end of the collection. /// /// /// The collection was modified after the enumerator was created. /// public bool MoveNext() { if (m_version != m_collection.m_version) { throw new System.InvalidOperationException("Collection was modified; enumeration operation may not execute."); } ++m_index; return (m_index < m_collection.Count); } /// /// Sets the enumerator to its initial position, before the first element in the collection. /// public void Reset() { m_index = -1; } #endregion #region Implementation (IEnumerator) object IEnumerator.Current { get { return this.Current; } } #endregion } #endregion #region Nested Read Only Wrapper class /// private sealed class ReadOnlyAppenderCollection : AppenderCollection, ICollection { #region Implementation (data) private readonly AppenderCollection m_collection; #endregion #region Construction internal ReadOnlyAppenderCollection(AppenderCollection list) : base(Tag.Default) { m_collection = list; } #endregion #region Type-safe ICollection public override void CopyTo(IAppender[] array) { m_collection.CopyTo(array); } public override void CopyTo(IAppender[] array, int start) { m_collection.CopyTo(array,start); } void ICollection.CopyTo(Array array, int start) { ((ICollection)m_collection).CopyTo(array, start); } public override int Count { get { return m_collection.Count; } } public override bool IsSynchronized { get { return m_collection.IsSynchronized; } } public override object SyncRoot { get { return this.m_collection.SyncRoot; } } #endregion #region Type-safe IList public override IAppender this[int i] { get { return m_collection[i]; } set { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } } public override int Add(IAppender x) { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } public override void Clear() { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } public override bool Contains(IAppender x) { return m_collection.Contains(x); } public override int IndexOf(IAppender x) { return m_collection.IndexOf(x); } public override void Insert(int pos, IAppender x) { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } public override void Remove(IAppender x) { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } public override void RemoveAt(int pos) { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } public override bool IsFixedSize { get { return true; } } public override bool IsReadOnly { get { return true; } } #endregion #region Type-safe IEnumerable public override IAppenderCollectionEnumerator GetEnumerator() { return m_collection.GetEnumerator(); } #endregion #region Public Helpers // (just to mimic some nice features of ArrayList) public override int Capacity { get { return m_collection.Capacity; } set { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } } public override int AddRange(AppenderCollection x) { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } public override int AddRange(IAppender[] x) { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } public override IAppender[] ToArray() { return m_collection.ToArray(); } public override void TrimToSize() { throw new NotSupportedException("This is a Read Only Collection and can not be modified"); } #endregion } #endregion } }