2 // System.Collections.Specialized.ListDictionary.cs
4 // Copyright (C) 2004, 2005 Novell (http://www.novell.com)
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System.Runtime.Serialization;
30 namespace System.Collections.Specialized
33 public class ListDictionary : IDictionary, ICollection, IEnumerable {
36 private DictionaryNode head;
37 private IComparer comparer;
39 public ListDictionary ()
47 public ListDictionary (IComparer comparer) : this ()
49 this.comparer = comparer;
52 private DictionaryNode FindEntry (object key, out DictionaryNode prev)
55 throw new ArgumentNullException ("key", "Attempted lookup for a null key.");
57 DictionaryNode entry = head;
59 if (comparer == null) {
60 while (entry != null) {
61 if (key.Equals (entry.key))
67 while (entry != null) {
68 if (comparer.Compare (key, entry.key) == 0)
77 private void AddImpl (object key, object value, DictionaryNode prev)
80 // Code in the MCS compiler (doc.cs) appears to depend on the new entry being
81 // added at the end, even though we don't promise any such thing.
82 // Preferably, this code would just have been:
84 // head = new DictionaryNode (key, value, head);
87 head = new DictionaryNode (key, value, head);
89 prev.next = new DictionaryNode (key, value, prev.next);
94 // IEnumerable Interface
95 IEnumerator IEnumerable.GetEnumerator ()
97 return new DictionaryNodeEnumerator (this);
100 // ICollection Interface
102 get { return count; }
105 public bool IsSynchronized {
106 get { return false; }
109 public object SyncRoot {
113 public void CopyTo (Array array, int index)
116 throw new ArgumentNullException ("array", "Array cannot be null.");
118 throw new ArgumentOutOfRangeException ("index", "index is less than 0");
119 if (index > array.Length)
120 throw new IndexOutOfRangeException ("index is too large");
121 if (Count > array.Length - index)
122 throw new ArgumentException ("Not enough room in the array");
124 foreach (DictionaryEntry entry in this)
125 array.SetValue (entry, index++);
128 // IDictionary Interface
129 public bool IsFixedSize {
130 get { return false; }
133 public bool IsReadOnly {
134 get { return false; }
138 public object this [object key] {
141 DictionaryNode entry = FindEntry (key, out prev);
142 return entry == null ? null : entry.value;
147 DictionaryNode entry = FindEntry (key, out prev);
151 AddImpl (key, value, prev);
155 public ICollection Keys {
156 get { return new DictionaryNodeCollection (this, true); }
159 public ICollection Values {
160 get { return new DictionaryNodeCollection (this, false); }
163 public void Add (object key, object value)
166 DictionaryNode entry = FindEntry (key, out prev);
168 throw new ArgumentException ("key", "Duplicate key in add.");
170 AddImpl (key, value, prev);
180 public bool Contains (object key)
183 return FindEntry (key, out prev) != null;
186 public IDictionaryEnumerator GetEnumerator ()
188 return new DictionaryNodeEnumerator (this);
191 public void Remove (object key)
194 DictionaryNode entry = FindEntry (key, out prev);
200 prev.next = entry.next;
208 private class DictionaryNode {
211 public DictionaryNode next;
212 public DictionaryNode (object key, object value, DictionaryNode next)
220 private class DictionaryNodeEnumerator : IEnumerator, IDictionaryEnumerator {
221 private ListDictionary dict;
222 private bool isAtStart;
223 private DictionaryNode current;
226 public DictionaryNodeEnumerator (ListDictionary dict)
229 version = dict.version;
233 private void FailFast()
235 if (version != dict.version) {
236 throw new InvalidOperationException (
237 "The ListDictionary's contents changed after this enumerator was instantiated.");
241 public bool MoveNext ()
244 if (current == null && !isAtStart)
246 current = isAtStart ? dict.head : current.next;
248 return current != null;
258 public object Current {
259 get { return Entry; }
262 private DictionaryNode DictionaryNode {
266 throw new InvalidOperationException (
267 "Enumerator is positioned before the collection's first element or after the last element.");
272 // IDictionaryEnumerator
273 public DictionaryEntry Entry {
275 object key = DictionaryNode.key;
276 return new DictionaryEntry (key, current.value);
281 get { return DictionaryNode.key; }
284 public object Value {
285 get { return DictionaryNode.value; }
289 private class DictionaryNodeCollection : ICollection {
290 private ListDictionary dict;
291 private bool isKeyList;
293 public DictionaryNodeCollection (ListDictionary dict, bool isKeyList)
296 this.isKeyList = isKeyList;
299 // ICollection Interface
301 get { return dict.Count; }
304 public bool IsSynchronized {
305 get { return false; }
308 public object SyncRoot {
309 get { return dict.SyncRoot; }
312 public void CopyTo (Array array, int index)
315 throw new ArgumentNullException ("array", "Array cannot be null.");
317 throw new ArgumentOutOfRangeException ("index", "index is less than 0");
318 if (index > array.Length)
319 throw new IndexOutOfRangeException ("index is too large");
320 if (Count > array.Length - index)
321 throw new ArgumentException ("Not enough room in the array");
323 foreach (object obj in this)
324 array.SetValue (obj, index++);
327 // IEnumerable Interface
328 public IEnumerator GetEnumerator()
330 return new DictionaryNodeCollectionEnumerator (dict.GetEnumerator (), isKeyList);
333 private class DictionaryNodeCollectionEnumerator : IEnumerator {
334 private IDictionaryEnumerator inner;
335 private bool isKeyList;
337 public DictionaryNodeCollectionEnumerator (IDictionaryEnumerator inner, bool isKeyList)
340 this.isKeyList = isKeyList;
343 public object Current {
344 get { return isKeyList ? inner.Key : inner.Value; }
347 public bool MoveNext ()
349 return inner.MoveNext ();