using System; using System.Diagnostics; using System.Collections; using System.Collections.Generic; namespace MadMilkman.Ini { /// /// Represents a base generic class for INI content item collections, and . /// /// derived type. /// [DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(DebugCollectionViewer<>))] public abstract class IniItemCollection : IItemNameVerifier, IList where T : IniItem { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly bool caseSensitive; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly IniDuplication duplication; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly IList items; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly IniFile parentFile; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly IniItem owner; /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] protected IniFile ParentFile { get { return this.parentFile; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] internal IniItem Owner { get { return this.owner; } } /// /// /// Gets the number of items in this collection. /// public int Count { get { return this.items.Count; } } internal IniItemCollection(IniFile parentFile, IniItem owner, IniDuplication duplication, bool caseSensitive) { this.caseSensitive = caseSensitive; this.duplication = duplication; this.parentFile = parentFile; this.owner = owner; this.items = new List(); } /// /// Adds an item to the end of this collection. /// /// Item to add to this collection. /// public void Add(T item) { if (this.VerifyItem(item)) this.items.Add(item); } /// /// Removes all items from this collection. /// public void Clear() { foreach (var item in this.items) item.ParentCollectionCore = null; this.items.Clear(); } /// /// Determines whether an item is in this collection. /// /// Item to locate in this collection. /// if the specified item is in the collection. public bool Contains(T item) { return this.items.Contains(item); } /// /// Determines whether an item is in this collection. /// /// Name of the item to locate in this collection. /// if the item with specified name is in the collection. public bool Contains(string name) { return this.GetItemIndexByName(name) != -1; } /// /// Shallow copies the items of this collection to an array. /// /// One-dimensional array that is the destination of the items copied from this collection. /// Zero-based index in array at which copying begins. public void CopyTo(T[] array, int arrayIndex) { this.items.CopyTo(array, arrayIndex); } /// /// Searches for the specified item and returns the zero-based index of the first occurrence within this collection. /// /// Item to locate in this collection. /// Index of the first occurrence of specified item in the collection. public int IndexOf(T item) { return this.items.IndexOf(item); } /// /// Searches for the specified item and returns the zero-based index of the first occurrence within this collection. /// /// Name of the item to locate in this collection. /// Index of the first occurrence of the item with specified name in the collection. public int IndexOf(string name) { return this.GetItemIndexByName(name); } /// /// Inserts an item to this collection at the specified index. /// /// Zero-based index at which item should be inserted. /// Item to insert to this collection. /// public void Insert(int index, T item) { if (this.VerifyItem(item)) this.items.Insert(index, item); } /// /// Removes the first occurrence of specific item from this collection. /// /// Item to remove from this collection. /// if the specified item is removed from the collection. public bool Remove(T item) { if (!this.items.Remove(item)) return false; item.ParentCollectionCore = null; return true; } /// /// Removes the first occurrence of specific item from this collection. /// /// Name of the item to remove from this collection. /// if the item with specified name is removed from the collection. public bool Remove(string name) { int index = this.GetItemIndexByName(name); if (index == -1) return false; this.items.RemoveAt(index); return true; } /// /// Removes an item at the specified index from this collection. /// /// Zero-based index at which item should be inserted. public void RemoveAt(int index) { this.items[index].ParentCollectionCore = null; this.items.RemoveAt(index); } /// /// Gets or sets the item at the specified index. /// /// Zero-based index of the item to get or set. /// /// If item duplicates are ignored and value is a duplicate item, has an existing name in this collection, then this value is ignored. /// public T this[int index] { get { return this.items[index]; } set { if (this.VerifyItem(value)) { this.items[index].ParentCollectionCore = null; this.items[index] = value; } } } /// /// Gets the first item of the specified name. /// /// Name of the item to get. /// If item with the specified name doesn't exist a value is returned. public T this[string name] { get { int index = this.GetItemIndexByName(name); return (index != -1) ? this.items[index] : null; } } /// /// Gets the first items of the specified names. /// /// Names of the items to get. /// If item with any specified name doesn't exist a value is returned in its place. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers", Justification = "I believe that this non-standard indexer can provide some useful data store access.")] public IEnumerable this[params string[] names] { get { var returnedNames = new Dictionary(); int index; for (int i = 0; i < names.Length; i++) { string name = names[i]; if (returnedNames.TryGetValue(name, out index)) { if (index != -1) { index = GetItemIndexByName(name, index + 1); returnedNames[name] = index; } } else { index = GetItemIndexByName(name); returnedNames.Add(name, index); } yield return (index != -1) ? this.items[index] : null; } } } /// /// Returns an enumerator that iterates through the collection. /// /// object that can be used to iterate through the collection. public IEnumerator GetEnumerator() { return this.items.GetEnumerator(); } private int GetItemIndexByName(string name, int startIndex = 0) { var iniItems = (IEnumerable)this.items; int index = 0; foreach (var item in iniItems) { if (index >= startIndex && item.Name.Equals(name, (this.caseSensitive) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) return index; else index++; } return -1; } private bool VerifyItem(IniItem item) { if (item == null) throw new ArgumentNullException("item"); if (item.ParentFile != this.parentFile) throw new InvalidOperationException(); if (item.ParentCollectionCore != null) throw new InvalidOperationException(); if (!this.VerifyItemName(item.Name)) return false; item.ParentCollectionCore = this; return true; } private bool VerifyItemName(string name) { return ((IItemNameVerifier)this).VerifyItemName(name); } bool IItemNameVerifier.VerifyItemName(string name) { if (this.duplication != IniDuplication.Allowed && this.Contains(name)) { if (this.duplication == IniDuplication.Disallowed) throw new InvalidOperationException(); return false; } return true; } /// IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] bool ICollection.IsReadOnly { get { return false; } } } }