Wed May 1 17:05:40 CEST 2002 Paolo Molaro <lupus@ximian.com>
[mono.git] / mcs / class / corlib / System.Collections / SortedList.cs
1 // \r
2 // System.Collections.SortedList\r
3 // \r
4 // Author:\r
5 //   Sergey Chaban (serge@wildwestsoftware.com)\r
6 // \r
7 \r
8 \r
9 using System;\r
10 using System.Collections;\r
11 \r
12 \r
13 namespace System.Collections {\r
14 \r
15         /// <summary>\r
16         ///  Represents a collection of associated keys and values\r
17         ///  that are sorted by the keys and are accessible by key\r
18         ///  and by index.\r
19         /// </summary>\r
20         [Serializable]\r
21         public class SortedList : IDictionary, ICollection,\r
22                                   IEnumerable, ICloneable {\r
23 \r
24 \r
25                 internal struct Slot {\r
26                         internal Object key;\r
27                         internal Object value;\r
28                 }\r
29 \r
30                 private readonly static int INITIAL_SIZE = 16;\r
31 \r
32                 public enum EnumeratorMode : int {KEY_MODE = 0, VALUE_MODE}\r
33 \r
34                 private int inUse;\r
35                 private int modificationCount;\r
36                 private Slot[] table;\r
37                 private IComparer comparer;\r
38 \r
39 \r
40 \r
41                 //\r
42                 // Constructors\r
43                 //\r
44                 public SortedList () : this (INITIAL_SIZE)\r
45                 {\r
46                 }\r
47 \r
48                 public SortedList (int initialCapacity)\r
49                         : this (null, initialCapacity)\r
50                 {\r
51                 }\r
52 \r
53                 public SortedList (IComparer comparer, int initialCapacity)\r
54                 {\r
55                         this.comparer = comparer;\r
56                         InitTable (initialCapacity);\r
57                 }\r
58 \r
59                 public SortedList (IComparer comparer)\r
60                         : this (comparer, 0)\r
61                 {\r
62                 }\r
63 \r
64 \r
65                 public SortedList (IDictionary d) : this (d, null)\r
66                 {\r
67                 }\r
68 \r
69                 public SortedList (IDictionary d, IComparer comparer)\r
70                 {\r
71                         if (d  ==  null)\r
72                                 throw new ArgumentNullException ("dictionary");\r
73 \r
74                         InitTable (d.Count);\r
75                         this.comparer = comparer;\r
76 \r
77                         IDictionaryEnumerator it = d.GetEnumerator ();\r
78                         while (it.MoveNext ()) {\r
79                                 if (it.Key is IComparable) {\r
80                                         Add (it.Key, it.Value);\r
81                                 } else {\r
82                                         throw new InvalidCastException("!IComparable");\r
83                                 }\r
84                         }\r
85                 }\r
86 \r
87 \r
88 \r
89 \r
90                 //\r
91                 // Properties\r
92                 //\r
93 \r
94 \r
95                 // ICollection\r
96 \r
97                 public virtual int Count {\r
98                         get {\r
99                                 return inUse;\r
100                         }\r
101                 }\r
102 \r
103                 public virtual bool IsSynchronized {\r
104                         get {\r
105                                 return false;\r
106                         }\r
107                 }\r
108 \r
109                 public virtual Object SyncRoot {\r
110                         get {\r
111                                 return this;\r
112                         }\r
113                 }\r
114 \r
115 \r
116                 // IDictionary\r
117 \r
118                 public virtual bool IsFixedSize {\r
119                         get {\r
120                                 return false;\r
121                         }\r
122                 }\r
123 \r
124 \r
125                 public virtual bool IsReadOnly {\r
126                         get {\r
127                                 return false;\r
128                         }\r
129                 }\r
130 \r
131                 public virtual ICollection Keys {\r
132                         get {\r
133                                 return new ListKeys (this);\r
134                         }\r
135                 }\r
136 \r
137                 public virtual ICollection Values {\r
138                         get {\r
139                                 return new ListValues (this);\r
140                         }\r
141                 }\r
142 \r
143 \r
144 \r
145                 public virtual Object this [Object key] {\r
146                         get {\r
147                                 return GetImpl (key);\r
148                         }\r
149                         set {\r
150                                 PutImpl (key, value, true);\r
151                         }\r
152                 }\r
153 \r
154 \r
155 \r
156 \r
157                 public virtual int Capacity {\r
158                         get {\r
159                                 return table.Length;\r
160                         }\r
161                         set {\r
162                                 Slot [] table = this.table;\r
163                                 int current = table.Length;\r
164 \r
165                                 if (inUse > value)\r
166                                         throw new ArgumentOutOfRangeException("capacity too small");\r
167 \r
168                                 if (value > current) {\r
169                                         Slot [] newTable = new Slot [value];\r
170                                         Array.Copy (table, newTable, current);\r
171                                         this.table = newTable;\r
172                                 }\r
173                         }\r
174                 }\r
175 \r
176 \r
177 \r
178                 //\r
179                 // Public instance methods.\r
180                 //\r
181 \r
182 \r
183                 // IEnumerable\r
184 \r
185                 IEnumerator IEnumerable.GetEnumerator ()\r
186                 {\r
187                         return new Enumerator (this, EnumeratorMode.KEY_MODE);\r
188                 }\r
189 \r
190 \r
191                 // IDictionary\r
192 \r
193                 public virtual void Add (object key, object value)\r
194                 {\r
195                         PutImpl (key, value, false);\r
196                 }\r
197 \r
198 \r
199                 public virtual void Clear () \r
200                 {\r
201                         this.table = new Slot [Capacity];\r
202                         inUse = 0;\r
203                         modificationCount++;\r
204                 }\r
205 \r
206                 public virtual bool Contains (object key)\r
207                 {\r
208                         return (Find (key) >= 0);\r
209                 }\r
210 \r
211 \r
212                 public virtual IDictionaryEnumerator GetEnumerator ()\r
213                 {\r
214                         return new Enumerator (this, EnumeratorMode.KEY_MODE);\r
215                 }\r
216 \r
217                 public virtual void Remove (object key)\r
218                 {\r
219                         int i = IndexOfKey (key);\r
220                         if (i >= 0) RemoveAt (i);\r
221                 }\r
222 \r
223 \r
224                 // ICollection\r
225 \r
226                 public virtual void CopyTo (Array array, int arrayIndex)\r
227                 {\r
228                         IDictionaryEnumerator it = GetEnumerator ();\r
229                         int i = arrayIndex;\r
230 \r
231                         while (it.MoveNext ()) {\r
232                                 array.SetValue (it.Entry, i++);\r
233                         }\r
234                 }\r
235 \r
236 \r
237 \r
238                 // ICloneable\r
239 \r
240                 public virtual object Clone ()\r
241                 {\r
242                         SortedList sl = new SortedList (this, comparer);\r
243                         sl.modificationCount = this.modificationCount;\r
244                         return sl;\r
245                 }\r
246 \r
247 \r
248 \r
249 \r
250                 //\r
251                 // SortedList\r
252                 //\r
253 \r
254                 public virtual IList GetKeyList ()\r
255                 {\r
256                         return new ListKeys (this);\r
257                 }\r
258 \r
259 \r
260                 public virtual IList GetValueList ()\r
261                 {\r
262                         return new ListValues (this);\r
263                 }\r
264 \r
265 \r
266                 public virtual void RemoveAt (int index)\r
267                 {\r
268                         Slot [] table = this.table;\r
269                         int cnt = Count;\r
270                         if (index >= 0 && index < cnt) {\r
271                                 if (index != cnt - 1) {\r
272                                         Array.Copy (table, index+1, table, index, cnt-1-index);\r
273                                 } else {\r
274                                         table [index].key = null;\r
275                                         table [index].value = null;\r
276                                 }\r
277                                 --inUse;\r
278                                 ++modificationCount;\r
279                         } else {\r
280                                 throw new ArgumentOutOfRangeException("index out of range");\r
281                         }\r
282                 }\r
283 \r
284 \r
285 \r
286 \r
287 \r
288                 public virtual int IndexOfKey (object key)\r
289                 {\r
290                         int indx = Find (key);\r
291                         return (indx | (indx >> 31));\r
292                 }\r
293 \r
294 \r
295                 public virtual int IndexOfValue (object value)\r
296                 {\r
297                         Slot [] table = this.table;\r
298                         int len = table.Length;\r
299 \r
300                         for (int i=0; i < len; i++) {\r
301                                 if (table[i].value.Equals (value)) {\r
302                                         return i;\r
303                                 }\r
304                         }\r
305 \r
306                         return -1;\r
307                 }\r
308 \r
309 \r
310                 public virtual bool ContainsKey (object key)\r
311                 {\r
312                         return Contains (key);\r
313                 }\r
314 \r
315 \r
316                 public virtual bool ContainsValue (object value)\r
317                 {\r
318                         return IndexOfValue (value) >= 0;\r
319                 }\r
320 \r
321 \r
322                 public virtual object GetByIndex (int index)\r
323                 {\r
324                         if (index >= 0 && index < Count) {\r
325                                 return table [index].value;\r
326                         } else {\r
327                                 throw new ArgumentOutOfRangeException("index out of range");\r
328                         }\r
329                 }\r
330 \r
331 \r
332                 public virtual void SetByIndex (int index, object value)\r
333                 {\r
334                         if (index >= 0 && index < Count) {\r
335                                 table [index].value = value;\r
336                         } else {\r
337                                 throw new ArgumentOutOfRangeException("index out of range");\r
338                         }\r
339                 }\r
340 \r
341 \r
342                 public virtual object GetKey (int index)\r
343                 {\r
344                         if (index >= 0 && index < Count) {\r
345                                 return table [index].key;\r
346                         } else {\r
347                                 throw new ArgumentOutOfRangeException("index out of range");\r
348                         }\r
349                 }\r
350 \r
351                 [MonoTODO]\r
352                 public static SortedList Synchronized (SortedList list)\r
353                 {\r
354                         return null;\r
355                 }\r
356 \r
357                 public virtual void TrimToSize ()\r
358                 {\r
359                         // From Beta2:\r
360                         // Trimming an empty SortedList sets the capacity\r
361                         // of the SortedList to the default capacity,\r
362                         // not zero.\r
363                         if (Count == 0) Resize (INITIAL_SIZE, false);\r
364                         else Resize (Count, true);\r
365                 }\r
366 \r
367 \r
368                 //\r
369                 // Private methods\r
370                 //\r
371 \r
372 \r
373                 private void Resize (int n, bool copy)\r
374                 {\r
375                         Slot [] table = this.table;\r
376                         Slot [] newTable = new Slot [n];\r
377                         if (copy) Array.Copy (table, 0, newTable, 0, n);\r
378                         this.table = newTable;\r
379                 }\r
380 \r
381 \r
382                 private void EnsureCapacity (int n, int free)\r
383                 {\r
384                         Slot [] table = this.table;\r
385                         Slot [] newTable = null;\r
386                         int cap = Capacity;\r
387                         bool gap = (free >=0 && free < Count);\r
388 \r
389                         if (n > cap) {\r
390                                 newTable = new Slot [n << 1];\r
391                         }\r
392 \r
393                         if (newTable != null) {\r
394                                 if (gap) {\r
395                                         int copyLen = free;\r
396                                         if (copyLen > 0) {\r
397                                                 Array.Copy (table, 0, newTable, 0, copyLen);\r
398                                         }\r
399                                         copyLen = Count - free;\r
400                                         if (copyLen > 0) {\r
401                                                 Array.Copy (table, free, newTable, free+1, copyLen);\r
402                                         }\r
403                                 } else {\r
404                                         // Just a resizing, copy the entire table.\r
405                                         Array.Copy (table, newTable, Count);\r
406                                 }\r
407                                 this.table = newTable;\r
408                         } else if (gap) {\r
409                                 Array.Copy (table, free, table, free+1, Count - free);\r
410                         }\r
411                 }\r
412 \r
413 \r
414                 private void PutImpl (object key, object value, bool overwrite)\r
415                 {\r
416                         if (key == null)\r
417                                 throw new ArgumentNullException ("null key");\r
418 \r
419                         Slot [] table = this.table;\r
420                         int freeIndx = Find (key);\r
421 \r
422 \r
423                         if (freeIndx >= 0) {\r
424                                 if (!overwrite)\r
425                                         throw new ArgumentException("element already exists");\r
426 \r
427                                 table [freeIndx].value = value;\r
428                                 return;\r
429                         }\r
430 \r
431                         freeIndx = ~freeIndx;\r
432 \r
433                         if (freeIndx > Capacity + 1)\r
434                                 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");\r
435 \r
436 \r
437                         EnsureCapacity (Count+1, freeIndx);\r
438 \r
439                         table = this.table;\r
440                         table [freeIndx].key = key;\r
441                         table [freeIndx].value = value;\r
442 \r
443                         ++inUse;\r
444                         ++modificationCount;\r
445 \r
446                 }\r
447 \r
448 \r
449                 private object GetImpl (object key)\r
450                 {\r
451                         int i = Find (key);\r
452 \r
453                         if (i >= 0)\r
454                                 return table [i].value;\r
455                         else\r
456                                 return null;\r
457                 }\r
458 \r
459 \r
460                 private void InitTable (int capacity)\r
461                 {\r
462                         int size = (capacity + 1) & (~1);\r
463                         if (size < INITIAL_SIZE) size = INITIAL_SIZE;\r
464                         this.table = new Slot [size];\r
465                         this.inUse = 0;\r
466                         this.modificationCount = 0;\r
467                 }\r
468 \r
469 \r
470                 private void  CopyToArray (Array arr, int i, \r
471                                            EnumeratorMode mode)\r
472                 {\r
473                         IEnumerator it = new Enumerator (this, mode);\r
474 \r
475                         while (it.MoveNext ()) {\r
476                                 arr.SetValue (it.Current, i++);\r
477                         }\r
478                 }\r
479 \r
480 \r
481                 private int Find (object key)\r
482                 {\r
483                         Slot [] table = this.table;\r
484                         int len = Count;\r
485 \r
486                         if (len == 0) return ~0;\r
487 \r
488                         IComparer comparer = (this.comparer == null)\r
489                                               ? Comparer.Default\r
490                                               : this.comparer;\r
491 \r
492                         int left = 0;\r
493                         int right = len-1;\r
494 \r
495                         while (left <= right) {\r
496                                 int guess = (left + right) >> 1;\r
497 \r
498                                 int cmp = comparer.Compare (key, table[guess].key);\r
499 \r
500                                 if (cmp == 0) return guess;\r
501 \r
502                                 cmp &= ~Int32.MaxValue;\r
503 \r
504                                 if (cmp == 0) left = guess+1;\r
505                                 else right = guess-1;\r
506                         }\r
507 \r
508                         return ~left;\r
509                 }\r
510 \r
511 \r
512 \r
513                 //\r
514                 // Inner classes\r
515                 //\r
516 \r
517 \r
518                 protected sealed class Enumerator : IDictionaryEnumerator,\r
519                                                     IEnumerator {\r
520 \r
521                         private SortedList host;\r
522                         private int stamp;\r
523                         private int pos;\r
524                         private int size;\r
525                         private EnumeratorMode mode;\r
526 \r
527                         private object currentKey;\r
528                         private object currentValue;\r
529 \r
530                         private readonly static string xstr = "SortedList.Enumerator: snapshot out of sync.";\r
531 \r
532                         public Enumerator (SortedList host, EnumeratorMode mode)\r
533                         {\r
534                                 this.host = host;\r
535                                 stamp = host.modificationCount;\r
536                                 size = host.Count;\r
537                                 this.mode = mode;\r
538                                 Reset ();\r
539                         }\r
540 \r
541                         public Enumerator (SortedList host)\r
542                                    : this (host, EnumeratorMode.KEY_MODE)\r
543                         {\r
544                         }\r
545 \r
546 \r
547                         private void FailFast ()\r
548                         {\r
549                                 if (host.modificationCount != stamp) {\r
550                                         throw new InvalidOperationException (xstr);\r
551                                 }\r
552                         }\r
553 \r
554                         public void Reset ()\r
555                         {\r
556                                 FailFast ();\r
557 \r
558                                 pos = -1;\r
559                                 currentKey = null;\r
560                                 currentValue = null;\r
561                         }\r
562 \r
563                         public bool MoveNext ()\r
564                         {\r
565                                 FailFast ();\r
566 \r
567                                 Slot [] table = host.table;\r
568 \r
569                                 if (++pos < size) {\r
570                                         Slot entry = table [pos];\r
571 \r
572                                         currentKey = entry.key;\r
573                                         currentValue = entry.value;\r
574                                         return true;\r
575                                 }\r
576 \r
577                                 currentKey = null;\r
578                                 currentValue = null;\r
579                                 return false;\r
580                         }\r
581 \r
582                         public DictionaryEntry Entry\r
583                         {\r
584                                 get {\r
585                                         FailFast ();\r
586                                         return new DictionaryEntry (currentKey,\r
587                                                                     currentValue);\r
588                                 }\r
589                         }\r
590 \r
591                         public Object Key {\r
592                                 get {\r
593                                         FailFast ();\r
594                                         return currentKey;\r
595                                 }\r
596                         }\r
597 \r
598                         public Object Value {\r
599                                 get {\r
600                                         FailFast ();\r
601                                         return currentValue;\r
602                                 }\r
603                         }\r
604 \r
605                         public Object Current {\r
606                                 get {\r
607                                         FailFast ();\r
608                                         return (mode == EnumeratorMode.KEY_MODE)\r
609                                                 ? currentKey\r
610                                                 : currentValue;\r
611                                 }\r
612                         }\r
613                 }\r
614 \r
615 \r
616                 protected class ListKeys : IList, IEnumerable {\r
617 \r
618                         private SortedList host;\r
619 \r
620 \r
621                         public ListKeys (SortedList host)\r
622                         {\r
623                                 if (host == null)\r
624                                         throw new ArgumentNullException ();\r
625 \r
626                                 this.host = host;\r
627                         }\r
628 \r
629                         //\r
630                         // ICollection\r
631                         //\r
632 \r
633                         public virtual int Count {\r
634                                 get {\r
635                                         return host.Count;\r
636                                 }\r
637                         }\r
638 \r
639                         public virtual bool IsSynchronized {\r
640                                 get {\r
641                                         return host.IsSynchronized;\r
642                                 }\r
643                         }\r
644 \r
645                         public virtual Object SyncRoot {\r
646                                 get {\r
647                                         return host.SyncRoot;\r
648                                 }\r
649                         }\r
650 \r
651                         public virtual void CopyTo (Array array, int arrayIndex)\r
652                         {\r
653                                 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);\r
654                         }\r
655 \r
656 \r
657                         //\r
658                         // IList\r
659                         //\r
660 \r
661                         public virtual bool IsFixedSize {\r
662                                 get {\r
663                                         return true;\r
664                                 }\r
665                         }\r
666 \r
667                         public virtual bool IsReadOnly {\r
668                                 get {\r
669                                         return true;\r
670                                 }\r
671                         }\r
672 \r
673 \r
674                         public virtual object this [int index] {\r
675                                 get {\r
676                                         return host.GetKey (index);\r
677                                 }\r
678                                 set {\r
679                                         throw new NotSupportedException("attempt to modify a key");\r
680                                 }\r
681                         }\r
682 \r
683                         public virtual int Add (object value)\r
684                         {\r
685                                 throw new NotSupportedException("IList::Add not supported");\r
686                         }\r
687 \r
688                         public virtual void Clear ()\r
689                         {\r
690                                 throw new NotSupportedException("IList::Clear not supported");\r
691                         }\r
692 \r
693                         public virtual bool Contains (object key)\r
694                         {\r
695                                 return host.Contains (key);\r
696                         }\r
697 \r
698 \r
699                         public virtual int IndexOf (object key)\r
700                         {\r
701                                 return host.IndexOfKey (key);\r
702                         }\r
703 \r
704 \r
705                         public virtual void Insert (int index, object value)\r
706                         {\r
707                                 throw new NotSupportedException("IList::Insert not supported");\r
708                         }\r
709 \r
710 \r
711                         public virtual void Remove (object value)\r
712                         {\r
713                                 throw new NotSupportedException("IList::Remove not supported");\r
714                         }\r
715 \r
716 \r
717                         public virtual void RemoveAt (int index)\r
718                         {\r
719                                 throw new NotSupportedException("IList::RemoveAt not supported");\r
720                         }\r
721 \r
722 \r
723                         //\r
724                         // IEnumerable\r
725                         //\r
726 \r
727                         public virtual IEnumerator GetEnumerator ()\r
728                         {\r
729                                 return new SortedList.Enumerator (host, EnumeratorMode.KEY_MODE);\r
730                         }\r
731 \r
732 \r
733                 }\r
734 \r
735 \r
736                 protected class ListValues : IList, IEnumerable {\r
737 \r
738                         private SortedList host;\r
739 \r
740 \r
741                         public ListValues (SortedList host)\r
742                         {\r
743                                 if (host == null)\r
744                                         throw new ArgumentNullException ();\r
745 \r
746                                 this.host = host;\r
747                         }\r
748 \r
749                         //\r
750                         // ICollection\r
751                         //\r
752 \r
753                         public virtual int Count {\r
754                                 get {\r
755                                         return host.Count;\r
756                                 }\r
757                         }\r
758 \r
759                         public virtual bool IsSynchronized {\r
760                                 get {\r
761                                         return host.IsSynchronized;\r
762                                 }\r
763                         }\r
764 \r
765                         public virtual Object SyncRoot {\r
766                                 get {\r
767                                         return host.SyncRoot;\r
768                                 }\r
769                         }\r
770 \r
771                         public virtual void CopyTo (Array array, int arrayIndex)\r
772                         {\r
773                                 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);\r
774                         }\r
775 \r
776 \r
777                         //\r
778                         // IList\r
779                         //\r
780 \r
781                         public virtual bool IsFixedSize {\r
782                                 get {\r
783                                         return true;\r
784                                 }\r
785                         }\r
786 \r
787                         public virtual bool IsReadOnly {\r
788                                 get {\r
789                                         return true;\r
790                                 }\r
791                         }\r
792 \r
793 \r
794                         [MonoTODO]\r
795                         public virtual object this [int index] {\r
796                                 get {\r
797                                         return host.GetByIndex (index);\r
798                                 }\r
799                                 set {\r
800                                         // FIXME: It seems (according to tests)\r
801                                         // that modifications are allowed\r
802                                         // in Beta2.\r
803                                         // ? host.SetByIndex (index, value);\r
804                                         throw new NotSupportedException("attempt to modify a value");\r
805                                 }\r
806                         }\r
807 \r
808                         public virtual int Add (object value)\r
809                         {\r
810                                 throw new NotSupportedException("IList::Add not supported");\r
811                         }\r
812 \r
813                         public virtual void Clear ()\r
814                         {\r
815                                 throw new NotSupportedException("IList::Clear not supported");\r
816                         }\r
817 \r
818                         public virtual bool Contains (object value)\r
819                         {\r
820                                 return host.ContainsValue (value);\r
821                         }\r
822 \r
823 \r
824                         public virtual int IndexOf (object value)\r
825                         {\r
826                                 return host.IndexOfValue (value);\r
827                         }\r
828 \r
829 \r
830                         public virtual void Insert (int index, object value)\r
831                         {\r
832                                 throw new NotSupportedException("IList::Insert not supported");\r
833                         }\r
834 \r
835 \r
836                         public virtual void Remove (object value)\r
837                         {\r
838                                 throw new NotSupportedException("IList::Remove not supported");\r
839                         }\r
840 \r
841 \r
842                         public virtual void RemoveAt (int index)\r
843                         {\r
844                                 throw new NotSupportedException("IList::RemoveAt not supported");\r
845                         }\r
846 \r
847 \r
848                         //\r
849                         // IEnumerable\r
850                         //\r
851 \r
852                         public virtual IEnumerator GetEnumerator ()\r
853                         {\r
854                                 return new SortedList.Enumerator (host, EnumeratorMode.VALUE_MODE);\r
855                         }\r
856 \r
857 \r
858                 }\r
859 \r
860         } // SortedList\r
861 \r
862 } // System.Collections\r