File: System\Configuration\ConfigurationElementCollection.cs
Project: ndp\fx\src\Configuration\System.Configuration.csproj (System.Configuration)
//------------------------------------------------------------------------------
// <copyright file="ConfigurationElementCollection.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Xml;
 
namespace System.Configuration {
 
    [System.Diagnostics.DebuggerDisplay("Count = {Count}")]
    public abstract class ConfigurationElementCollection : ConfigurationElement, ICollection {
        internal const string DefaultAddItemName = "add";
        internal const string DefaultRemoveItemName = "remove";
        internal const string DefaultClearItemsName = "clear";
 
        private int _removedItemCount = 0;    // Number of items removed for this collection (not including parent)
        private int _inheritedCount = 0;      // Total number of inherited items
        private ArrayList _items = new ArrayList();
        private String _addElement = DefaultAddItemName;
        private String _removeElement = DefaultRemoveItemName;
        private String _clearElement = DefaultClearItemsName;
        private bool bEmitClearTag = false;
        private bool bCollectionCleared = false;
        private bool bModified = false;
        private bool bReadOnly = false;
        private IComparer _comparer;
        internal bool internalAddToEnd = false;
        internal String internalElementTagName = string.Empty;
 
        protected ConfigurationElementCollection() {
        }
 
        protected ConfigurationElementCollection(IComparer comparer) {
            if (comparer == null) {
                throw new ArgumentNullException("comparer");
            }
 
            _comparer = comparer;
        }
 
        private ArrayList Items {
            get {
                return _items;
            }
        }
 
        private enum InheritedType {
            inNeither = 0,
            inParent = 1,
            inSelf = 2,
            inBothSame = 3,
            inBothDiff = 4,
            inBothCopyNoRemove = 5,
        }
 
        protected internal string AddElementName {
            get {
                return _addElement;
            }
            set {
                _addElement = value;
                if (BaseConfigurationRecord.IsReservedAttributeName(value)) {
                    throw new ArgumentException(SR.GetString(SR.Item_name_reserved, DefaultAddItemName, value));
                }
            }
        }
 
        protected internal string RemoveElementName {
            get {
                return _removeElement;
            }
            set {
                if (BaseConfigurationRecord.IsReservedAttributeName(value)) {
                    throw new ArgumentException(SR.GetString(SR.Item_name_reserved, DefaultRemoveItemName, value));
                }
                _removeElement = value;
            }
        }
 
        protected internal string ClearElementName {
            get {
                return _clearElement;
            }
            set {
                if (BaseConfigurationRecord.IsReservedAttributeName(value)) {
                    throw new ArgumentException(SR.GetString(SR.Item_name_reserved, DefaultClearItemsName, value));
                }
                _clearElement = value;
            }
        }
 
        // AssociateContext
        //
        // Associate a collection of values with a configRecord
        //
        internal override void AssociateContext(BaseConfigurationRecord configRecord) {
            base.AssociateContext(configRecord);
 
            foreach (Entry entry in _items) {
                if (entry._value != null) {
                    entry._value.AssociateContext(configRecord);
                }
            }
        }
 
        protected internal override bool IsModified() {
            if (bModified == true) {
                return true;
            }
 
            if (base.IsModified() == true) {
                return true;
            }
 
            foreach (Entry entry in _items) {
                if (entry._entryType != EntryType.Removed) {
                    ConfigurationElement elem = entry._value;
                    if (elem.IsModified()) {
                        return true;
                    }
                }
            }
            return false;
        }
 
        protected internal override void ResetModified() {
            bModified = false;
            base.ResetModified();
 
            foreach (Entry entry in _items) {
                if (entry._entryType != EntryType.Removed) {
                    ConfigurationElement elem = entry._value;
                    elem.ResetModified();
                }
            }
        }
 
        public override bool IsReadOnly() {
            return bReadOnly;
        }
 
        protected internal override void SetReadOnly() {
            bReadOnly = true;
            foreach (Entry entry in _items) {
                if (entry._entryType != EntryType.Removed) {
                    ConfigurationElement elem = entry._value;
                    elem.SetReadOnly();
                }
            }
        }
 
        internal virtual IEnumerator GetEnumeratorImpl() {
            return new Enumerator(_items, this);
        }
 
        internal IEnumerator GetElementsEnumerator() {
            // Return an enumerator over the collection's config elements.
            // This is different then the std GetEnumerator because the second one
            // can return different set of items if overriden in a derived class
 
            return new Enumerator(_items, this);
        }
 
        public override bool Equals(object compareTo) {
            if (compareTo.GetType() == this.GetType()) {
                ConfigurationElementCollection compareToElem = (ConfigurationElementCollection)compareTo;
                if (this.Count != compareToElem.Count) {
                    return false;
                }
 
                foreach (Entry thisEntry in Items) {
                    bool found = false;
                    foreach (Entry compareEntry in compareToElem.Items) {
                        if (Object.Equals(thisEntry._value, compareEntry._value)) {
                            found = true;
                            break;
                        }
                    }
                    if (found == false) {
                        // not in the collection must be different
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
 
        public override int GetHashCode() {
            int hHashCode = 0;
            foreach (Entry thisEntry in Items) {
                ConfigurationElement elem = thisEntry._value;
                hHashCode ^= elem.GetHashCode();
            }
            return hHashCode;
        }
 
 
        protected internal override void Unmerge(ConfigurationElement sourceElement,
                                                 ConfigurationElement parentElement,
                                                 ConfigurationSaveMode saveMode) {
 
            base.Unmerge(sourceElement, parentElement, saveMode);
            if (sourceElement != null) {
                ConfigurationElementCollection parentCollection = parentElement as ConfigurationElementCollection;
                ConfigurationElementCollection sourceCollection = sourceElement as ConfigurationElementCollection;
                Hashtable Inheritance = new Hashtable();
                _lockedAllExceptAttributesList = sourceElement._lockedAllExceptAttributesList;
                _lockedAllExceptElementsList = sourceElement._lockedAllExceptElementsList;
                _fItemLocked = sourceElement._fItemLocked;
                _lockedAttributesList = sourceElement._lockedAttributesList;
                _lockedElementsList = sourceElement._lockedElementsList;
 
                AssociateContext(sourceElement._configRecord);
 
                if (parentElement != null) {
                    if (parentElement._lockedAttributesList != null)
                        _lockedAttributesList = UnMergeLockList(sourceElement._lockedAttributesList,
                            parentElement._lockedAttributesList, saveMode);
                    if (parentElement._lockedElementsList != null)
                        _lockedElementsList = UnMergeLockList(sourceElement._lockedElementsList,
                            parentElement._lockedElementsList, saveMode);
                    if (parentElement._lockedAllExceptAttributesList != null)
                        _lockedAllExceptAttributesList = UnMergeLockList(sourceElement._lockedAllExceptAttributesList,
                            parentElement._lockedAllExceptAttributesList, saveMode);
                    if (parentElement._lockedAllExceptElementsList != null)
                        _lockedAllExceptElementsList = UnMergeLockList(sourceElement._lockedAllExceptElementsList,
                            parentElement._lockedAllExceptElementsList, saveMode);
                }
 
                if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                    CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                    // When writing out portable configurations the <clear/> tag should be written
                    bCollectionCleared = sourceCollection.bCollectionCleared;
                    EmitClear = (saveMode == ConfigurationSaveMode.Full && (_clearElement.Length != 0)) ||
                                (saveMode == ConfigurationSaveMode.Modified && bCollectionCleared) ?
                                    true : sourceCollection.EmitClear;
 
                    if ((parentCollection != null) && (EmitClear != true)) {
                        foreach (Entry entry in parentCollection.Items) {
                            if (entry._entryType != EntryType.Removed) {
                                Inheritance[entry.GetKey(this)] = InheritedType.inParent;
                            }
                        }
                    }
 
                    foreach (Entry entry in sourceCollection.Items) {
                        if (entry._entryType != EntryType.Removed) {
                            if (Inheritance.Contains(entry.GetKey(this))) {
                                Entry parentEntry = (Entry)parentCollection.Items[parentCollection.RealIndexOf(entry._value)];
 
                                ConfigurationElement elem = entry._value;
                                if (elem.Equals(parentEntry._value)) {
                                    // in modified mode we consider any change to be different than the parent
                                    Inheritance[entry.GetKey(this)] = InheritedType.inBothSame;
                                    if (saveMode == ConfigurationSaveMode.Modified) {
                                        if (elem.IsModified()) {
                                            Inheritance[entry.GetKey(this)] = InheritedType.inBothDiff;
                                        } else
                                        if (elem.ElementPresent) {
                                            // This is when the source file contained the entry but it was an
                                            // exact copy.  We don't want to emit a remove so we treat it as
                                            // a special case.
                                            Inheritance[entry.GetKey(this)] = InheritedType.inBothCopyNoRemove;
                                        }
                                    }
                                }
                                else {
                                    Inheritance[entry.GetKey(this)] = InheritedType.inBothDiff;
                                    if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate
                                        && entry._entryType == EntryType.Added) {
                                        // this is a special case for deailing with defect number 529517
                                        // this code allow the config to write out the same xml when no remove was
                                        // present during deserialization.
                                        Inheritance[entry.GetKey(this)] = InheritedType.inBothCopyNoRemove;
                                    }
                                }
                            }
                            else { // not in parent
                                Inheritance[entry.GetKey(this)] = InheritedType.inSelf;
                            }
                        }
                    }
 
                    //
                    if ((parentCollection != null) && (EmitClear != true)) {
                        foreach (Entry entry in parentCollection.Items) {
                            if (entry._entryType != EntryType.Removed) {
 
                                InheritedType tp = (InheritedType)Inheritance[entry.GetKey(this)];
                                if (tp == InheritedType.inParent || tp == InheritedType.inBothDiff) {
                                    ConfigurationElement elem = CallCreateNewElement(entry.GetKey(this).ToString());
 
                                    elem.Reset(entry._value); // copy this entry
                                    BaseAdd(elem,ThrowOnDuplicate,true);                          // Add it (so that is once existed in the temp
                                    BaseRemove(entry.GetKey(this), false);   // now remove it to for a remove instruction
                                }
                            }
                        }
                    }
 
                    foreach (Entry entry in sourceCollection.Items) {
                        if (entry._entryType != EntryType.Removed) {
                            InheritedType tp = (InheritedType)Inheritance[entry.GetKey(this)];
 
                            if (tp == InheritedType.inSelf || tp == InheritedType.inBothDiff ||
                                tp == InheritedType.inBothCopyNoRemove) {
                                ConfigurationElement elem = CallCreateNewElement(entry.GetKey(this).ToString());
 
                                elem.Unmerge(entry._value, null, saveMode);
 
                                if (tp == InheritedType.inSelf) {
                                    elem.RemoveAllInheritedLocks(); // If the key changed only local locks are kept
                                }
 
                                BaseAdd(elem,ThrowOnDuplicate,true);                     // Add it
                            }
                        }
                    }
                }
                else {
                    if (CollectionType == ConfigurationElementCollectionType.BasicMap ||
                        CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) {
                        foreach (Entry entry in sourceCollection.Items) {
                            bool FoundKeyInParent = false;
                            Entry parentEntrySaved = null;
 
                            if (entry._entryType == EntryType.Added ||
                                entry._entryType == EntryType.Replaced) {
                                bool InParent = false;
 
                                if (parentCollection != null) {
                                    foreach (Entry parentEntry in parentCollection.Items) {
                                        if (Object.Equals(entry.GetKey(this), parentEntry.GetKey(this))) {
                                            // for basic map collection where the key is actually an element
                                            // we do not want the merging behavior or data will not get written
                                            // out for the properties if they match the first element deamed to be a parent
                                            // For example <allow verbs="NewVerb" users="*"/> will loose the users because
                                            // an entry exists in the root element.
                                            if (!IsElementName(entry.GetKey(this).ToString())) {
                                                // For elements which are not keyed by the element name
                                                // need to be unmerged
                                                FoundKeyInParent = true;
                                                parentEntrySaved = parentEntry;
                                            }
                                        }
 
                                        if (Object.Equals(entry._value, parentEntry._value)) {
                                            FoundKeyInParent = true;
                                            InParent = true;  // in parent and the same exact values
                                            parentEntrySaved = parentEntry;
                                            break;
                                        }
                                    }
                                }
 
                                ConfigurationElement elem = CallCreateNewElement(entry.GetKey(this).ToString());
 
                                if (!FoundKeyInParent) {   // Unmerge is similar to a reset when used like this
                                    // except that it handles the different update modes
                                    // which Reset does not understand
                                    elem.Unmerge(entry._value, null, saveMode); // copy this entry
                                    BaseAdd(-1, elem,true);                          // Add it
                                }
                                else {
                                    ConfigurationElement sourceItem = entry._value;
                                    if (!InParent ||
                                        (saveMode == ConfigurationSaveMode.Modified && sourceItem.IsModified()) ||
                                        (saveMode == ConfigurationSaveMode.Full)) {
                                        elem.Unmerge(entry._value, parentEntrySaved._value, saveMode);
                                        BaseAdd(-1, elem,true);                          // Add it
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
 
        protected internal override void Reset(ConfigurationElement parentElement) {
            ConfigurationElementCollection parentCollection = parentElement as ConfigurationElementCollection;
            ResetLockLists(parentElement);
 
            if (parentCollection != null) {
                foreach (Entry entry in parentCollection.Items) {
                    ConfigurationElement elem = CallCreateNewElement(entry.GetKey(this).ToString());
                    elem.Reset(entry._value);
 
                    if ((CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                        CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) &&
                       (entry._entryType == EntryType.Added ||
                        entry._entryType == EntryType.Replaced)) { // do not add removed items from the parent
                        BaseAdd(elem, true, true); // This version combines dups and throws (unless overridden)
                    }
                    else {
                        if (CollectionType == ConfigurationElementCollectionType.BasicMap ||
                            CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) {
                            BaseAdd(-1, elem, true); // this version appends regardless of if it is a dup.
                        }
                    }
                }
                _inheritedCount = Count; // After reset the count is the number of items actually inherited.
            }
        }
 
        public int Count {
            get {
                return _items.Count - _removedItemCount;
            }
        }
 
        public bool EmitClear {
            get {
                return bEmitClearTag;
            }
            set {
                if (IsReadOnly()) {
                    throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only));
                }
                if (value == true) {
                    CheckLockedElement(_clearElement, null);  // has clear been locked?
                    CheckLockedElement(_removeElement, null); // has remove been locked? Clear implies remove
                }
                bModified = true;
                bEmitClearTag = value;
            }
        }
 
        public bool IsSynchronized {
            get {
                return false;
            }
        }
 
        public Object SyncRoot {
            get {
                return null;
            }
        }
 
        public void CopyTo(ConfigurationElement[] array, int index) {
            ((ICollection)this).CopyTo(array, index);
        }
 
        void ICollection.CopyTo(Array arr, int index) {
            foreach (Entry entry in _items) {
                if (entry._entryType != EntryType.Removed) {
                    arr.SetValue(entry._value, index++);
                }
            }
        }
 
        public IEnumerator GetEnumerator() {
            return GetEnumeratorImpl();
        }
 
        protected virtual void BaseAdd(ConfigurationElement element) {
            BaseAdd(element, ThrowOnDuplicate);
        }
 
        protected internal void BaseAdd(ConfigurationElement element, bool throwIfExists) {
            BaseAdd(element, throwIfExists, false);
        }
 
        private void BaseAdd(ConfigurationElement element, bool throwIfExists, bool ignoreLocks) {
            bool flagAsReplaced = false;
            bool localAddToEnd = internalAddToEnd;
 
            if (IsReadOnly()) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only));
            }
 
            if (LockItem == true && ignoreLocks == false) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_element_locked, _addElement));
            }
 
            Object key = GetElementKeyInternal(element);
            int iFoundItem = -1;
            for (int index = 0; index < _items.Count; index++) {
                Entry entry = (Entry)_items[index];
                if (CompareKeys(key, entry.GetKey(this))) {
                    if (entry._value != null && entry._value.LockItem == true && ignoreLocks == false) {
                        throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_item_locked));
                    }
                    if (entry._entryType != EntryType.Removed && throwIfExists) {
                        if (!element.Equals(entry._value)) {
                            throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_already_exists, key),
                                element.PropertyFileName(""), element.PropertyLineNumber(""));
                        }
                        else {
                            entry._value = element;
                        }
                        return;
                    }
                    if (entry._entryType != EntryType.Added) {
                        if ((CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                             CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) &&
                            entry._entryType == EntryType.Removed &&
                            _removedItemCount > 0) {
                            _removedItemCount--; // account for the value
                        }
                        entry._entryType = EntryType.Replaced;
                        flagAsReplaced = true;
                    }
                    if (localAddToEnd ||
                        CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                        iFoundItem = index;
                        if (entry._entryType == EntryType.Added) {
                            // this is a special case for defect number 529517 to emulate everett behavior
                            localAddToEnd = true;
                        }
                        break;
                    }
                    else {
                        // check to see if the element is trying to set a locked property.
                        if (ignoreLocks == false) {
                            element.HandleLockedAttributes(entry._value);
                            // copy the lock from the removed element before setting the new element
                            element.MergeLocks(entry._value);
                        }
                        entry._value = element;
                        bModified = true;
                        return;
                    }
                }
            }
 
            // Brand new item.
            if (iFoundItem >= 0) {
                _items.RemoveAt(iFoundItem);
 
                // if the item being removed was inherited adjust the cout
                if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate &&
                    iFoundItem > Count + _removedItemCount - _inheritedCount) {
                    _inheritedCount--;
                }
            }
            BaseAddInternal(localAddToEnd ? -1 : iFoundItem, element, flagAsReplaced, ignoreLocks);
            bModified = true;
        }
 
        protected int BaseIndexOf(ConfigurationElement element) {
            int index = 0;
            Object key = GetElementKeyInternal(element);
            foreach (Entry entry in _items) {
                if (entry._entryType != EntryType.Removed) {
                    if (CompareKeys(key, entry.GetKey(this))) {
                        return index;
                    }
                    index++;
                }
            }
            return -1;
        }
 
        internal int RealIndexOf(ConfigurationElement element) {
            int index = 0;
            Object key = GetElementKeyInternal(element);
            foreach (Entry entry in _items) {
                if (CompareKeys(key, entry.GetKey(this))) {
                    return index;
                }
                index++;
            }
            return -1;
        }
 
        private void BaseAddInternal(int index, ConfigurationElement element, bool flagAsReplaced, bool ignoreLocks) {
            // Allow the element to initialize itself after its
            // constructor has been run so that it may access
            // virtual methods.
 
            element.AssociateContext(_configRecord);
            if (element != null) {
                element.CallInit();
            }
 
            if (IsReadOnly()) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only));
            }
 
            if (!ignoreLocks) { // during reset we ignore locks so we can copy the elements
                if(CollectionType == ConfigurationElementCollectionType.BasicMap ||
                    CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) {
                    if (BaseConfigurationRecord.IsReservedAttributeName(ElementName)) {
                        throw new ArgumentException(SR.GetString(SR.Basicmap_item_name_reserved, ElementName));
                    }
                    CheckLockedElement(ElementName, null);
                }
                if(CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                    CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
 
                    CheckLockedElement(_addElement, null);
                }
            }
 
            if (CollectionType == ConfigurationElementCollectionType.BasicMapAlternate ||
                CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                if (index == -1) {
                    index = Count + _removedItemCount - _inheritedCount; // insert before inherited, but after any removed
                }
                else {
                    if (index > Count + _removedItemCount - _inheritedCount && flagAsReplaced == false) {
                        throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_add_items_below_inherited_items)));
                    }
                }
            }
 
            if (CollectionType == ConfigurationElementCollectionType.BasicMap &&
                index >= 0 &&
                index < _inheritedCount) {
                throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_add_items_above_inherited_items)));
            }
 
            EntryType entryType = (flagAsReplaced == false) ? EntryType.Added : EntryType.Replaced;
 
            Object key = GetElementKeyInternal(element);
 
            if (index >= 0) {
                if (index > _items.Count) {
                    throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index));
                }
                _items.Insert(index, new Entry(entryType, key, element));
            }
            else {
                _items.Add(new Entry(entryType, key, element));
            }
 
            bModified = true;
        }
 
 
        protected virtual void BaseAdd(int index, ConfigurationElement element) {
            BaseAdd(index, element, false);
        }
 
        private void BaseAdd(int index, ConfigurationElement element, bool ignoreLocks) {
            if (IsReadOnly()) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only));
            }
            if (index < -1) {
                throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index));
            }
 
            if ((index != -1) &&
                (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate)) {
 
                // If it's an AddRemoveClearMap*** collection, turn the index passed into into a real internal index
                int realIndex = 0;
 
                if (index > 0) {
                    foreach (Entry entryfound in _items) {
                        if (entryfound._entryType != EntryType.Removed) {
                            index--;
                        }
                        if (index == 0) {
                            break;
                        }
                        realIndex++;
                    }
                    index = ++realIndex;
                }
                // check for duplicates
                Object key = GetElementKeyInternal(element);
                foreach (Entry entry in _items) {
                    if (CompareKeys(key, entry.GetKey(this))) {
                        if (entry._entryType != EntryType.Removed) {
                            if (!element.Equals(entry._value)) {
                                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_already_exists, key),
                                        element.PropertyFileName(""), element.PropertyLineNumber(""));
                            }
                            return;
                        }
                    }
                }
 
            }
 
            BaseAddInternal(index, element, false, ignoreLocks);
        }
 
        protected internal void BaseRemove(Object key) {
            BaseRemove(key, false);
        }
 
        private void BaseRemove(Object key, bool throwIfMissing) {
            if (IsReadOnly())
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only));
            
            int index = 0;
            bool foundEntry = false;
            foreach (Entry entry in _items) {
                if (CompareKeys(key, entry.GetKey(this))) {
                    foundEntry = true;
 
                    if (entry._value == null) // A phoney delete is already present
                    {
                        if (throwIfMissing) {
                            throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_not_found, key));
                        }
                        else {
                            return;
                        }
                    }
 
                    if (entry._value.LockItem == true) {
                        throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, key));
                    }
 
                    if (entry._value.ElementPresent == false) {
                        CheckLockedElement(_removeElement, null); // has remove been locked?
                    }
 
                    switch (entry._entryType) {
                        case EntryType.Added:
                            if (CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap &&
                                CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                                if (CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) {
                                    if (index >= Count - _inheritedCount) {
                                        throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_remove_inherited_items)));
                                    }
                                }
                                if (CollectionType == ConfigurationElementCollectionType.BasicMap) {
                                    if (index < _inheritedCount) {
                                        throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_remove_inherited_items)));
                                    }
                                }
 
                                _items.RemoveAt(index);
                            }
                            else {
                                // don't really remove it from the collection just mark it removed
                                entry._entryType = EntryType.Removed;
                                _removedItemCount++;
                            }
                            break;
                        case EntryType.Removed:
                            if (throwIfMissing) {
                                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_already_removed));
                            }
                            break;
                        default:
                            if (CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap &&
                                CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_elements_may_not_be_removed));
                            }
                            entry._entryType = EntryType.Removed;
                            _removedItemCount++;
                            break;
                    }
                    bModified = true;
                    return;
                }
                index++;
            }
            // Note because it is possible for removes to get orphaned by the API they will
            // not cause a throw from the base classes.  The scenerio is:
            //  Add an item in a parent level
            //  remove the item in a child level
            //  remove the item at the parent level.
            //
            // throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_not_found));
            if (foundEntry == false) {
                if (throwIfMissing) {
                    throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_not_found, key));
                }
 
                if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                    CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                    if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                        _items.Insert(Count + _removedItemCount - _inheritedCount, new Entry(EntryType.Removed, key, null));
                    }
                    else {
                        _items.Add(new Entry(EntryType.Removed, key, null));
                    }
                    _removedItemCount++;
                }
 
            }
        }
 
        protected internal ConfigurationElement BaseGet(Object key) {
            foreach (Entry entry in _items) {
                if (entry._entryType != EntryType.Removed) {
                    if (CompareKeys(key, entry.GetKey(this))) {
                        return entry._value;
                    }
                }
            }
            return null;
        }
 
        protected internal bool BaseIsRemoved(Object key) {
            foreach (Entry entry in _items) {
                if (CompareKeys(key, entry.GetKey(this))) {
                    if (entry._entryType == EntryType.Removed) {
                        return true;
                    }
                    else {
                        return false;
                    }
                }
            }
            return false;
        }
 
        protected internal ConfigurationElement BaseGet(int index) {
            if (index < 0) {
                throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index));
            }
 
            int VirtualIndex = 0;
            Entry entry = (Entry)null;
 
            foreach (Entry entryfound in _items) {
                if (VirtualIndex == index && (entryfound._entryType != EntryType.Removed)) {
                    entry = entryfound;
                    break;
                }
                if (entryfound._entryType != EntryType.Removed) {
                    VirtualIndex++;
                }
            }
 
            if (entry != null) {
                return entry._value;
            }
            else {
                throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index));
            }
        }
 
        protected internal object[] BaseGetAllKeys() {
            object[] keys = new object[Count];
            int index = 0;
            foreach (Entry entry in _items) {
                if (entry._entryType != EntryType.Removed) {
                    keys[index] = entry.GetKey(this);
                    index++;
                }
            }
            return keys;
        }
 
        protected internal object BaseGetKey(int index) {
            int VirtualIndex = 0;
            Entry entry = (Entry)null;
            if (index < 0) {
                throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index));
            }
 
            foreach (Entry entryfound in _items) {
                if (VirtualIndex == index && (entryfound._entryType != EntryType.Removed)) {
                    entry = entryfound;
                    break;
                }
 
                if (entryfound._entryType != EntryType.Removed) {
                    VirtualIndex++;
                }
            }
 
            // Entry entry = (Entry)_items[index];
            if (entry != null) {
                object key = entry.GetKey(this);
 
                return key;
            }
            else {
                throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index));
            }
        }
 
        protected internal void BaseClear() {
            if (IsReadOnly()) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only));
            }
 
            CheckLockedElement(_clearElement, null);  // has clear been locked?
            CheckLockedElement(_removeElement, null); // has remove been locked? Clear implies remove
 
            bModified = true;
            bCollectionCleared = true;
            if ((CollectionType == ConfigurationElementCollectionType.BasicMap ||
                 CollectionType == ConfigurationElementCollectionType.BasicMapAlternate)
                && _inheritedCount > 0) {
                int RemoveIndex = 0;
                if (CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) {
                    RemoveIndex = 0; // Inherited items are at the bottom and cannot be removed
                }
                if (CollectionType == ConfigurationElementCollectionType.BasicMap) {
                    RemoveIndex = _inheritedCount; // inherited items are at the top and cannot be removed
                }
                while (Count - _inheritedCount > 0) {
                    _items.RemoveAt(RemoveIndex);
                }
            }
            else {
                // do not clear any locked items
                // _items.Clear();
                int inheritedRemoved = 0;
                int removedRemoved = 0;
                int initialCount = Count;
                
                // check for locks before removing any items from the collection
                for (int CheckIndex = 0; CheckIndex < _items.Count; CheckIndex++) {
                    Entry entry = (Entry)_items[CheckIndex];
                    if (entry._value != null && entry._value.LockItem == true) {
                        throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_item_locked_cannot_clear));
                    }
                }
 
                for (int RemoveIndex = _items.Count - 1; RemoveIndex >= 0; RemoveIndex--) {
                    Entry entry = (Entry)_items[RemoveIndex];
                    if ((CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap &&
                            RemoveIndex < _inheritedCount) ||
                        (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate &&
                            (RemoveIndex >= initialCount - _inheritedCount))) {
                        inheritedRemoved++;
                    }
                    if (entry._entryType == EntryType.Removed) {
                        removedRemoved++;
                    }
 
                    _items.RemoveAt(RemoveIndex);
                }
                _inheritedCount -= inheritedRemoved;
                _removedItemCount -= removedRemoved;
            }
        }
 
        protected internal void BaseRemoveAt(int index) {
            if (IsReadOnly()) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_read_only));
            }
            int VirtualIndex = 0;
            Entry entry = (Entry)null;
 
            foreach (Entry entryfound in _items) {
                if (VirtualIndex == index && (entryfound._entryType != EntryType.Removed)) {
                    entry = entryfound;
                    break;
                }
 
                if (entryfound._entryType != EntryType.Removed) {
                    VirtualIndex++;
                }
            }
 
            if (entry == null) {
                throw new ConfigurationErrorsException(SR.GetString(SR.IndexOutOfRange, index));
            }
            else {
                if (entry._value.LockItem == true) {
                    throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_attribute_locked, entry.GetKey(this)));
                }
 
                if (entry._value.ElementPresent == false) {
                    CheckLockedElement(_removeElement, null); // has remove been locked?
                }
 
                switch (entry._entryType) {
                    case EntryType.Added:
                        if (CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap &&
                            CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                            if (CollectionType == ConfigurationElementCollectionType.BasicMapAlternate) {
                                if (index >= Count - _inheritedCount) {
                                    throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_remove_inherited_items)));
                                }
                            }
 
                            if (CollectionType == ConfigurationElementCollectionType.BasicMap) {
                                if (index < _inheritedCount) {
                                    throw (new ConfigurationErrorsException(SR.GetString(SR.Config_base_cannot_remove_inherited_items)));
                                }
                            }
 
                            _items.RemoveAt(index);
 
                        }
                        else {
                            // don't really remove it from the collection just mark it removed
                            if (entry._value.ElementPresent == false) {
                                CheckLockedElement(_removeElement, null); // has remove been locked?
                            }
 
                            entry._entryType = EntryType.Removed;
                            _removedItemCount++;
                        }
 
                        break;
 
                    case EntryType.Removed:
                        throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_entry_already_removed));
 
                    default:
                        if (CollectionType != ConfigurationElementCollectionType.AddRemoveClearMap &&
                            CollectionType != ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                            throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_collection_elements_may_not_be_removed));
                        }
 
                        entry._entryType = EntryType.Removed;
                        _removedItemCount++;
                        break;
                }
                bModified = true;
            }
        }
 
 
 
        protected internal override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey) {
            ConfigurationElementCollectionType type = CollectionType;
            bool DataToWrite = false;
 
            DataToWrite |= base.SerializeElement(writer, serializeCollectionKey);
 
            if (type == ConfigurationElementCollectionType.AddRemoveClearMap ||
                type == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                // it is possible that the collection only has to be cleared and contains
                // no real elements
                if (bEmitClearTag == true && (_clearElement.Length != 0)) {
                    if (writer != null) {
                        writer.WriteStartElement(_clearElement);
                        writer.WriteEndElement();
                    }
                    DataToWrite = true;
                }
            }
 
            foreach (Entry entry in _items) {
                if (type == ConfigurationElementCollectionType.BasicMap ||
                    type == ConfigurationElementCollectionType.BasicMapAlternate) {
                    if (entry._entryType == EntryType.Added || entry._entryType == EntryType.Replaced) {
                        if (ElementName != null && ElementName.Length != 0) {
                            if (BaseConfigurationRecord.IsReservedAttributeName(ElementName)) {
                                throw new ArgumentException(SR.GetString(SR.Basicmap_item_name_reserved, ElementName));
                            }
                            DataToWrite |= entry._value.SerializeToXmlElement(writer, ElementName);
                        }
                        else {
                            DataToWrite |= entry._value.SerializeElement(writer, false);
                        }
                    }
                }
                else if (type == ConfigurationElementCollectionType.AddRemoveClearMap ||
                         type == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                    if ((entry._entryType == EntryType.Removed ||
                         entry._entryType == EntryType.Replaced) &&
                        entry._value != null) {
 
                        if (writer != null) {
                            writer.WriteStartElement(_removeElement);
                        }
 
                        DataToWrite |= entry._value.SerializeElement(writer, true);
 
                        if (writer != null) {
                            writer.WriteEndElement();
                        }
 
                        DataToWrite = true;
                    }
                    if (entry._entryType == EntryType.Added || entry._entryType == EntryType.Replaced) {
                        DataToWrite |= entry._value.SerializeToXmlElement(writer, _addElement);
                    }
                }
            }
            return DataToWrite;
        }
 
        protected override bool OnDeserializeUnrecognizedElement(String elementName, XmlReader reader) {
            bool handled = false; //
            if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                if (elementName == _addElement) {
                    ConfigurationElement elem = CallCreateNewElement();
                    elem.ResetLockLists(this);
                    elem.DeserializeElement(reader, false);
                    BaseAdd(elem);
                    handled = true;
                }
                else if (elementName == _removeElement) {
                    ConfigurationElement elem = CallCreateNewElement();
                    elem.ResetLockLists(this);
                    elem.DeserializeElement(reader, true);
                    if (IsElementRemovable(elem) == true) {
                        BaseRemove(GetElementKeyInternal(elem), false);
                    }
 
                    handled = true;
                }
                else if (elementName == _clearElement) {
                    if (reader.AttributeCount > 0) {
                        while (reader.MoveToNextAttribute()) {
                            String propertyName = reader.Name;
                            throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_unrecognized_attribute, propertyName), reader);
                        }
                    }
                    CheckLockedElement(elementName, reader);
                    reader.MoveToElement();
                    BaseClear(); //
                    bEmitClearTag = true;
                    handled = true;
                }
            }
            else if (elementName == ElementName) {
                if (BaseConfigurationRecord.IsReservedAttributeName(elementName)) {
                    throw new ArgumentException(SR.GetString(SR.Basicmap_item_name_reserved, elementName));
                }
                ConfigurationElement elem = CallCreateNewElement();
                elem.ResetLockLists(this);
                elem.DeserializeElement(reader, false);
                BaseAdd(elem);
 
                handled = true;
            }
            else if (IsElementName(elementName)) {   // this section handle the collection like the allow deny senario which
                if (BaseConfigurationRecord.IsReservedAttributeName(elementName)) {
                    throw new ArgumentException(SR.GetString(SR.Basicmap_item_name_reserved, elementName));
                }
                // have multiple tags for the collection
                ConfigurationElement elem = CallCreateNewElement(elementName);
                elem.ResetLockLists(this);
                elem.DeserializeElement(reader, false);
                BaseAdd(-1, elem);
                handled = true;
            }
            return handled;
        }
 
        private ConfigurationElement CallCreateNewElement(string elementName) {
            ConfigurationElement elem = CreateNewElement(elementName);
            elem.AssociateContext(_configRecord);
            elem.CallInit();
            return elem;
        }
 
        private ConfigurationElement CallCreateNewElement() {
            ConfigurationElement elem = CreateNewElement();
            elem.AssociateContext(_configRecord);
            elem.CallInit();
            return elem;
        }
 
        protected virtual ConfigurationElement CreateNewElement(string elementName) {
            return CreateNewElement();
        }
        protected abstract ConfigurationElement CreateNewElement();
        protected abstract Object GetElementKey(ConfigurationElement element);
        internal Object GetElementKeyInternal(ConfigurationElement element) {
            Object key = GetElementKey(element);
            if (key == null)
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_base_invalid_element_key));
            return key;
        }
 
        protected virtual bool IsElementRemovable(ConfigurationElement element) {
            return true;
        }
 
        private bool CompareKeys(Object key1, Object key2) {
            if (_comparer != null) {
                return (_comparer.Compare(key1, key2) == 0);
            }
            else {
                return key1.Equals(key2);
            }
        }
 
        protected virtual String ElementName {
            get {
                return "";
            }
        }
 
        protected virtual bool IsElementName(string elementName) {
            return false;
        }
 
        internal bool IsLockableElement(string elementName) {
            if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                return (elementName == AddElementName ||
                        elementName == RemoveElementName ||
                        elementName == ClearElementName);
            }
            else {
                return (elementName == ElementName) || IsElementName(elementName);
            }
        }
 
        internal string LockableElements {
            get {
                if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                    CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
 
                    string ElementNames = "'" + AddElementName + "'"; // Must have an add
                    if (RemoveElementName.Length != 0)
                        ElementNames += ", '" + RemoveElementName + "'";
                    if (ClearElementName.Length != 0)
                        ElementNames += ", '" + ClearElementName + "'";
                    return ElementNames;
                }
                else {
                    if (!String.IsNullOrEmpty(ElementName)) {
                        return "'" + ElementName + "'";
                    }
                    return String.Empty;
                }
            }
        }
 
        protected virtual bool ThrowOnDuplicate {
            get {
                if (CollectionType == ConfigurationElementCollectionType.AddRemoveClearMap ||
                    CollectionType == ConfigurationElementCollectionType.AddRemoveClearMapAlternate) {
                    return true;
                }
                return false;
            }
        }
 
        public virtual ConfigurationElementCollectionType CollectionType {
            get {
                return ConfigurationElementCollectionType.AddRemoveClearMap;
            }
        }
 
        private enum EntryType {
            Inherited,
            Replaced,
            Removed,
            Added,
        }
 
        private class Entry {
 
            internal EntryType _entryType;
            internal Object _key;
            internal ConfigurationElement _value;
 
            internal Object GetKey(ConfigurationElementCollection ThisCollection) {
                // For items that have been really inserted...
                if (_value != null) {
                    return ThisCollection.GetElementKeyInternal(_value);
                }
                else {
                    return _key; // These are items that only have been removed
                }
 
            }
 
            internal Entry(EntryType type, Object key, ConfigurationElement value) {
                _entryType = type;
                _key = key;
                _value = value;
            }
        }
 
        private class Enumerator : IDictionaryEnumerator {
 
            private IEnumerator _itemsEnumerator;
            private DictionaryEntry _current = new DictionaryEntry();
            private ConfigurationElementCollection ThisCollection;
 
 
            internal Enumerator(ArrayList items, ConfigurationElementCollection collection) {
                _itemsEnumerator = items.GetEnumerator();
                ThisCollection = collection;
            }
 
            bool IEnumerator.MoveNext() {
                while (_itemsEnumerator.MoveNext()) {
                    Entry entry = (Entry)_itemsEnumerator.Current;
                    if (entry._entryType != EntryType.Removed) {
                        _current.Key = (entry.GetKey(ThisCollection) != null) ? entry.GetKey(ThisCollection) : "key";
                        _current.Value = entry._value;
                        return true;
                    }
                }
                return false;
            }
 
            void IEnumerator.Reset() {
                _itemsEnumerator.Reset();
            }
 
            Object IEnumerator.Current {
                get {
                    return _current.Value;
                }
            }
 
            DictionaryEntry IDictionaryEnumerator.Entry {
                get {
                    return _current;
                }
            }
 
            Object IDictionaryEnumerator.Key {
                get {
                    return _current.Key;
                }
            }
 
            Object IDictionaryEnumerator.Value {
                get {
                    return _current.Value;
                }
            }
        }
    }
}