File: System\Xml\Schema\XmlSchemaObjectTable.cs
Project: ndp\fx\src\Xml\System.Xml.csproj (System.Xml)
//------------------------------------------------------------------------------
// <copyright file="XmlSchemaObjectTable.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>                                                                 
//------------------------------------------------------------------------------
 
namespace System.Xml.Schema {
 
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
 
    /// <include file='doc\XmlSchemaObjectTable.uex' path='docs/doc[@for="XmlSchemaObjectTable"]/*' />
    public class XmlSchemaObjectTable {
        Dictionary<XmlQualifiedName, XmlSchemaObject> table = new Dictionary<XmlQualifiedName,XmlSchemaObject>();
        List<XmlSchemaObjectEntry> entries = new List<XmlSchemaObjectEntry>();
        
        internal XmlSchemaObjectTable() {
        }
 
        internal void Add(XmlQualifiedName name,  XmlSchemaObject value) {
            Debug.Assert(!table.ContainsKey(name), "XmlSchemaObjectTable.Add: entry already exists");
            table.Add(name, value);
            entries.Add(new XmlSchemaObjectEntry(name, value));
        }
 
        internal void Insert(XmlQualifiedName name,  XmlSchemaObject value) {
            XmlSchemaObject oldValue = null;
            if (table.TryGetValue(name, out oldValue)) {
                table[name] = value; //set new value
                Debug.Assert(oldValue != null);
                int matchedIndex = FindIndexByValue(oldValue);
                Debug.Assert(matchedIndex >= 0);
                //set new entry
                Debug.Assert(entries[matchedIndex].qname == name);
                entries[matchedIndex] = new XmlSchemaObjectEntry(name, value);
            }
            else {
                Add(name, value);
            }
            
        }
 
        internal void Replace(XmlQualifiedName name,  XmlSchemaObject value) {
            XmlSchemaObject oldValue;
            if (table.TryGetValue(name, out oldValue)) {
                table[name] = value; //set new value
                Debug.Assert(oldValue != null);
                int matchedIndex = FindIndexByValue(oldValue);
                Debug.Assert(entries[matchedIndex].qname == name);
                entries[matchedIndex] = new XmlSchemaObjectEntry(name, value);
            }
        }
 
        internal void Clear() {
            table.Clear();
            entries.Clear();
        }
 
        internal void Remove(XmlQualifiedName name) {
            XmlSchemaObject value;
            if (table.TryGetValue(name, out value)) {
                table.Remove(name);
                int matchedIndex = FindIndexByValue(value);
                Debug.Assert(matchedIndex >= 0);
                Debug.Assert(entries[matchedIndex].qname == name);
                entries.RemoveAt(matchedIndex);                    
            }
        }
 
        private int FindIndexByValue(XmlSchemaObject xso) {
            int index;
            for(index = 0; index < entries.Count; index++) {
                if((object)entries[index].xso == (object)xso) {
                    return index;
                }    
            }
            return -1;
        }
        /// <include file='doc\XmlSchemaObjectTable.uex' path='docs/doc[@for="XmlSchemaObjectTable.Count"]/*' />
        public int Count {
            get {
                Debug.Assert(table.Count == entries.Count);
                return table.Count;
            }
        }
 
        /// <include file='doc\XmlSchemaObjectTable.uex' path='docs/doc[@for="XmlSchemaObjectTable.Contains"]/*' />
        public bool Contains(XmlQualifiedName name) {
            return table.ContainsKey(name);
        }
 
        /// <include file='doc\XmlSchemaObjectTable.uex' path='docs/doc[@for="XmlSchemaObjectTable.this"]/*' />
        public XmlSchemaObject this[XmlQualifiedName name] {
            get { 
                XmlSchemaObject value;
                if (table.TryGetValue(name, out value)) {
                    return value;
                }
                return null;
            }
        }
        
        /// <include file='doc\XmlSchemaObjectTable.uex' path='docs/doc[@for="XmlSchemaObjectTable.Names"]/*' />
        public ICollection Names {
            get { 
                return new NamesCollection(entries, table.Count);
            }
        }
 
        /// <include file='doc\XmlSchemaObjectTable.uex' path='docs/doc[@for="XmlSchemaObjectTable.Values"]/*' />
        public ICollection Values {
            get { 
                return new ValuesCollection(entries, table.Count);
            }
        }
        
        /// <include file='doc\XmlSchemaObjectTable.uex' path='docs/doc[@for="XmlSchemaObjectTable.GetEnumerator"]/*' />
        public IDictionaryEnumerator GetEnumerator() {
            return new XSODictionaryEnumerator(this.entries, table.Count, EnumeratorType.DictionaryEntry); 
        }
 
        internal enum EnumeratorType {
            Keys,
            Values,
            DictionaryEntry,
        }
 
        internal struct XmlSchemaObjectEntry {
            internal XmlQualifiedName qname;
            internal XmlSchemaObject xso;
 
            public XmlSchemaObjectEntry(XmlQualifiedName name, XmlSchemaObject value) {
                qname = name;
                xso = value;
            }
 
            public XmlSchemaObject IsMatch(string localName, string ns) {
                if (localName == qname.Name && ns == qname.Namespace) {
                    return xso;
                }
                return null;
            }
 
            public void Reset() {
                qname = null;
                xso = null;
            }
        }
 
        internal class NamesCollection : ICollection {
            private List<XmlSchemaObjectEntry> entries;
            int size;
        
            internal NamesCollection(List<XmlSchemaObjectEntry> entries, int size) {
                this.entries = entries;
                this.size = size;
            }
            
            public int Count { 
    	        get { return size; }
    	    }
 
            public Object SyncRoot {
    	        get {
                    return ((ICollection)entries).SyncRoot;
                }
    	    }
 
            public bool IsSynchronized {
                get {
                    return ((ICollection)entries).IsSynchronized;
                }
            }
 
            public void CopyTo(Array array, int arrayIndex) {
                if (array == null)
                    throw new ArgumentNullException("array");
 
			    if (arrayIndex < 0) 
                    throw new ArgumentOutOfRangeException("arrayIndex");
 
                Debug.Assert(array.Length >= size, "array is not big enough to hold all the items in the ICollection");
 
                for (int i = 0; i < size; i++) {
                    array.SetValue(entries[i].qname, arrayIndex++);
                }
            }
 
            public IEnumerator GetEnumerator() {
                return new XSOEnumerator(this.entries, this.size, EnumeratorType.Keys);
            }
        }
 
        //ICollection for Values 
        internal class ValuesCollection : ICollection {
            private List<XmlSchemaObjectEntry> entries;
            int size;
        
            internal ValuesCollection(List<XmlSchemaObjectEntry> entries, int size) {
                this.entries = entries;
                this.size = size;
            }
            
            public int Count { 
    	        get { return size; }
    	    }
 
            public Object SyncRoot {
    	        get {
                    return ((ICollection)entries).SyncRoot;
                }
    	    }
 
            public bool IsSynchronized {
                get {
                    return ((ICollection)entries).IsSynchronized;
                }
            }
 
            public void CopyTo(Array array, int arrayIndex) {
                if (array == null)
                    throw new ArgumentNullException("array");
 
			    if (arrayIndex < 0) 
                    throw new ArgumentOutOfRangeException("arrayIndex");
 
                Debug.Assert(array.Length >= size, "array is not big enough to hold all the items in the ICollection");
 
                for (int i = 0; i < size; i++) {
                    array.SetValue(entries[i].xso, arrayIndex++);
                }
            }
            
            public IEnumerator GetEnumerator() {
                return new XSOEnumerator(this.entries, this.size, EnumeratorType.Values);
            }
        }
 
        internal class XSOEnumerator : IEnumerator {
            private List<XmlSchemaObjectEntry> entries;
            private EnumeratorType enumType;
 
            protected int currentIndex;
            protected int size;
            protected XmlQualifiedName currentKey;
            protected XmlSchemaObject currentValue;
            
 
            internal XSOEnumerator(List<XmlSchemaObjectEntry> entries, int size, EnumeratorType enumType) {
                this.entries = entries;
                this.size = size;
                this.enumType = enumType;
                currentIndex = -1;
            }
 
            public Object Current {
                get {
                    if (currentIndex == -1) {
                        throw new InvalidOperationException(Res.GetString(Res.Sch_EnumNotStarted, string.Empty));
                    }
                    if (currentIndex >= size) {
                        throw new InvalidOperationException(Res.GetString(Res.Sch_EnumFinished, string.Empty));
                    }
                    switch(enumType) {
                        case EnumeratorType.Keys:
                            return currentKey;
                    
                        case EnumeratorType.Values:
                            return currentValue;
 
                        case EnumeratorType.DictionaryEntry:
                            return new DictionaryEntry(currentKey, currentValue);
 
                        default:
                            break;
                    }
                    return null;
                }
            }
 
            public bool MoveNext() {
                if (currentIndex >= size - 1) {
                    currentValue = null;
                    currentKey = null;
                    return false;
                }
                currentIndex++;
                currentValue = entries[currentIndex].xso;
                currentKey = entries[currentIndex].qname;
                return true;
            }
 
            public void Reset() {
                currentIndex = -1;
                currentValue = null;
                currentKey = null;
            }
        }
 
        internal class XSODictionaryEnumerator : XSOEnumerator, IDictionaryEnumerator {
           
            internal XSODictionaryEnumerator(List<XmlSchemaObjectEntry> entries, int size, EnumeratorType enumType) : base(entries, size, enumType) {
            }
            
            //IDictionaryEnumerator members
            public DictionaryEntry Entry {
                get {
                    if (currentIndex == -1) {
                        throw new InvalidOperationException(Res.GetString(Res.Sch_EnumNotStarted, string.Empty));
                    }
                    if (currentIndex >= size) {
                        throw new InvalidOperationException(Res.GetString(Res.Sch_EnumFinished, string.Empty));
                    }
                    return new DictionaryEntry(currentKey, currentValue);
                }
            }
 
            public object Key {
                get {
                    if (currentIndex == -1) {
                        throw new InvalidOperationException(Res.GetString(Res.Sch_EnumNotStarted, string.Empty));
                    }
                    if (currentIndex >= size) {
                        throw new InvalidOperationException(Res.GetString(Res.Sch_EnumFinished, string.Empty));
                    }
                    return currentKey;
                }
            }
 
            public object Value {
                get {
                    if (currentIndex == -1) {
                        throw new InvalidOperationException(Res.GetString(Res.Sch_EnumNotStarted, string.Empty));
                    }
                    if (currentIndex >= size) {
                        throw new InvalidOperationException(Res.GetString(Res.Sch_EnumFinished, string.Empty));
                    }
                    return currentValue;
                }
            }
        }
 
    }
}