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)
55 throw new ArgumentNullException ("key", "Attempted lookup for a null key.");
57 DictionaryNode entry = head;
58 if (comparer == null) {
59 while (entry != null) {
60 if (key.Equals (entry.key))
65 while (entry != null) {
66 if (comparer.Compare (key, entry.key) == 0)
73 private DictionaryNode FindEntry (object key, out DictionaryNode prev)
76 throw new ArgumentNullException ("key", "Attempted lookup for a null key.");
78 DictionaryNode entry = head;
80 if (comparer == null) {
81 while (entry != null) {
82 if (key.Equals (entry.key))
88 while (entry != null) {
89 if (comparer.Compare (key, entry.key) == 0)
98 private void AddImpl (object key, object value, DictionaryNode prev)
101 // Code in the MCS compiler (doc.cs) appears to depend on the new entry being
102 // added at the end, even though we don't promise any such thing.
103 // Preferably, this code would just have been:
105 // head = new DictionaryNode (key, value, head);
108 head = new DictionaryNode (key, value, head);
110 prev.next = new DictionaryNode (key, value, prev.next);
115 // IEnumerable Interface
116 IEnumerator IEnumerable.GetEnumerator ()
118 return new DictionaryNodeEnumerator (this);
121 // ICollection Interface
123 get { return count; }
126 public bool IsSynchronized {
127 get { return false; }
130 public object SyncRoot {
134 public void CopyTo (Array array, int index)
137 throw new ArgumentNullException ("array", "Array cannot be null.");
139 throw new ArgumentOutOfRangeException ("index", "index is less than 0");
140 if (index > array.Length)
141 throw new IndexOutOfRangeException ("index is too large");
142 if (Count > array.Length - index)
143 throw new ArgumentException ("Not enough room in the array");
145 foreach (DictionaryEntry entry in this)
146 array.SetValue (entry, index++);
149 // IDictionary Interface
150 public bool IsFixedSize {
151 get { return false; }
154 public bool IsReadOnly {
155 get { return false; }
159 public object this [object key] {
161 DictionaryNode entry = FindEntry (key);
162 return entry == null ? null : entry.value;
167 DictionaryNode entry = FindEntry (key, out prev);
171 AddImpl (key, value, prev);
175 public ICollection Keys {
176 get { return new DictionaryNodeCollection (this, true); }
179 public ICollection Values {
180 get { return new DictionaryNodeCollection (this, false); }
183 public void Add (object key, object value)
186 DictionaryNode entry = FindEntry (key, out prev);
188 throw new ArgumentException ("key", "Duplicate key in add.");
190 AddImpl (key, value, prev);
200 public bool Contains (object key)
202 return FindEntry (key) != null;
205 public IDictionaryEnumerator GetEnumerator ()
207 return new DictionaryNodeEnumerator (this);
210 public void Remove (object key)
213 DictionaryNode entry = FindEntry (key, out prev);
219 prev.next = entry.next;
227 private class DictionaryNode {
230 public DictionaryNode next;
231 public DictionaryNode (object key, object value, DictionaryNode next)
239 private class DictionaryNodeEnumerator : IEnumerator, IDictionaryEnumerator {
240 private ListDictionary dict;
241 private bool isAtStart;
242 private DictionaryNode current;
245 public DictionaryNodeEnumerator (ListDictionary dict)
248 version = dict.version;
252 private void FailFast()
254 if (version != dict.version) {
255 throw new InvalidOperationException (
256 "The ListDictionary's contents changed after this enumerator was instantiated.");
260 public bool MoveNext ()
263 if (current == null && !isAtStart)
265 current = isAtStart ? dict.head : current.next;
267 return current != null;
277 public object Current {
278 get { return Entry; }
281 private DictionaryNode DictionaryNode {
285 throw new InvalidOperationException (
286 "Enumerator is positioned before the collection's first element or after the last element.");
291 // IDictionaryEnumerator
292 public DictionaryEntry Entry {
294 object key = DictionaryNode.key;
295 return new DictionaryEntry (key, current.value);
300 get { return DictionaryNode.key; }
303 public object Value {
304 get { return DictionaryNode.value; }
308 private class DictionaryNodeCollection : ICollection {
309 private ListDictionary dict;
310 private bool isKeyList;
312 public DictionaryNodeCollection (ListDictionary dict, bool isKeyList)
315 this.isKeyList = isKeyList;
318 // ICollection Interface
320 get { return dict.Count; }
323 public bool IsSynchronized {
324 get { return false; }
327 public object SyncRoot {
328 get { return dict.SyncRoot; }
331 public void CopyTo (Array array, int index)
334 throw new ArgumentNullException ("array", "Array cannot be null.");
336 throw new ArgumentOutOfRangeException ("index", "index is less than 0");
337 if (index > array.Length)
338 throw new IndexOutOfRangeException ("index is too large");
339 if (Count > array.Length - index)
340 throw new ArgumentException ("Not enough room in the array");
342 foreach (object obj in this)
343 array.SetValue (obj, index++);
346 // IEnumerable Interface
347 public IEnumerator GetEnumerator()
349 return new DictionaryNodeCollectionEnumerator (dict.GetEnumerator (), isKeyList);
352 private class DictionaryNodeCollectionEnumerator : IEnumerator {
353 private IDictionaryEnumerator inner;
354 private bool isKeyList;
356 public DictionaryNodeCollectionEnumerator (IDictionaryEnumerator inner, bool isKeyList)
359 this.isKeyList = isKeyList;
362 public object Current {
363 get { return isKeyList ? inner.Key : inner.Value; }
366 public bool MoveNext ()
368 return inner.MoveNext ();