81eda7eda84229fd999ee87c794a8467520fbf07
[mono.git] / mcs / class / referencesource / mscorlib / system / collections / objectmodel / collection.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 // 
8
9 namespace System.Collections.ObjectModel
10 {
11     using System;
12     using System.Collections;
13     using System.Collections.Generic;
14     using System.Diagnostics;
15     using System.Runtime;
16     
17     [Serializable]
18     [System.Runtime.InteropServices.ComVisible(false)]
19     [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
20     [DebuggerDisplay("Count = {Count}")]    
21     public class Collection<T>: IList<T>, IList, IReadOnlyList<T>
22     {
23         IList<T> items;
24         [NonSerialized]
25         private Object _syncRoot;
26
27         public Collection() {
28             items = new List<T>();
29         }
30
31         public Collection(IList<T> list) {
32             if (list == null) {
33                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list);
34             }
35             items = list;
36         }
37
38         public int Count {
39             get { return items.Count; }
40         }
41
42         protected IList<T> Items {
43             get { return items; }
44         }
45
46         public T this[int index] {
47             get { return items[index]; }
48             set {
49                 if( items.IsReadOnly) {
50                     ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
51                 }
52                 
53                 if (index < 0 || index >= items.Count) {
54                     ThrowHelper.ThrowArgumentOutOfRangeException();
55                 }
56
57                 SetItem(index, value);
58             }
59         }
60
61         public void Add(T item) {
62             if( items.IsReadOnly) {
63                 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
64             }
65             
66             int index = items.Count;
67             InsertItem(index, item);
68         }
69
70         public void Clear() {
71             if( items.IsReadOnly) {
72                 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
73             }
74             
75             ClearItems();
76         }
77
78         public void CopyTo(T[] array, int index) {
79             items.CopyTo(array, index);
80         }
81
82         public bool Contains(T item) {
83             return items.Contains(item);
84         }
85
86         public IEnumerator<T> GetEnumerator() {
87             return items.GetEnumerator();
88         }
89
90         public int IndexOf(T item) {
91             return items.IndexOf(item);
92         }
93
94         public void Insert(int index, T item) {
95             if (items.IsReadOnly) {
96                 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
97             }
98
99             if (index < 0 || index > items.Count) {
100                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
101             }
102
103             InsertItem(index, item);
104         }
105
106         public bool Remove(T item) {
107             if( items.IsReadOnly) {
108                 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
109             }
110             
111             int index = items.IndexOf(item);
112             if (index < 0) return false;
113             RemoveItem(index);
114             return true;
115         }
116
117         public void RemoveAt(int index) {
118             if( items.IsReadOnly) {
119                 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
120             }
121
122             if (index < 0 || index >= items.Count) {
123                 ThrowHelper.ThrowArgumentOutOfRangeException();
124             }
125
126             RemoveItem(index);
127         }
128
129         protected virtual void ClearItems() {
130             items.Clear();
131         }
132
133         protected virtual void InsertItem(int index, T item) {
134             items.Insert(index, item);
135         }
136         
137         protected virtual void RemoveItem(int index) {
138             items.RemoveAt(index);
139         }
140
141         protected virtual void SetItem(int index, T item) {
142             items[index] = item;
143         }
144         
145         bool ICollection<T>.IsReadOnly {
146             get { 
147                 return items.IsReadOnly; 
148             }
149         }
150         
151         IEnumerator IEnumerable.GetEnumerator() {
152             return ((IEnumerable)items).GetEnumerator();
153         }
154
155         bool ICollection.IsSynchronized {
156             get { return false; }
157         }
158
159         object ICollection.SyncRoot { 
160             get {                 
161                 if( _syncRoot == null) {
162                     ICollection c = items as ICollection;
163                     if( c != null) {
164                         _syncRoot = c.SyncRoot;
165                     }
166                     else {
167                         System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);    
168                     }
169                 }
170                 return _syncRoot;                 
171             }
172         }
173
174         void ICollection.CopyTo(Array array, int index) {
175             if (array == null) {
176                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
177             }
178             
179             if (array.Rank != 1) {
180                 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
181             }
182
183             if( array.GetLowerBound(0) != 0 ) {
184                 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
185             }
186             
187             if (index < 0 ) {
188                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
189             }
190
191             if (array.Length - index < Count) {
192                 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
193             }
194
195             T[] tArray = array as T[];
196             if (tArray  != null) {
197                 items.CopyTo(tArray , index);
198             }
199             else {
200                 //
201                 // Catch the obvious case assignment will fail.
202                 // We can found all possible problems by doing the check though.
203                 // For example, if the element type of the Array is derived from T,
204                 // we can't figure out if we can successfully copy the element beforehand.
205                 //
206                 Type targetType = array.GetType().GetElementType(); 
207                 Type sourceType = typeof(T);
208                 if(!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) {
209                     ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
210                 }
211
212                 //
213                 // We can't cast array of value type to object[], so we don't support 
214                 // widening of primitive types here.
215                 //
216                 object[] objects = array as object[];
217                 if( objects == null) {
218                     ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
219                 }
220
221                 int count = items.Count;
222                 try {
223                     for (int i = 0; i < count; i++) {
224                         objects[index++] = items[i];
225                     }
226                 }
227                 catch(ArrayTypeMismatchException) {
228                     ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
229                 }
230             }            
231         }
232
233         object IList.this[int index] {
234             get { return items[index]; }
235             set {
236                 ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
237
238                 try { 
239                     this[index] = (T)value;               
240                 }
241                 catch (InvalidCastException) { 
242                     ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));            
243                 }
244
245             }
246         }
247
248         bool IList.IsReadOnly {
249             get { 
250                 return items.IsReadOnly;
251             }
252         }
253
254         bool IList.IsFixedSize {
255             get { 
256                 // There is no IList<T>.IsFixedSize, so we must assume that only
257                 // readonly collections are fixed size, if our internal item 
258                 // collection does not implement IList.  Note that Array implements
259                 // IList, and therefore T[] and U[] will be fixed-size.
260                 IList list = items as IList;
261                 if(list != null)
262                 {
263                     return list.IsFixedSize;
264                 }
265                 return items.IsReadOnly;
266             }
267         }
268
269         int IList.Add(object value) {
270             if( items.IsReadOnly) {
271                 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
272             }
273             ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
274             
275             try { 
276                 Add((T)value);
277             }
278             catch (InvalidCastException) { 
279                 ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));            
280             }   
281             
282             return this.Count - 1;
283         }
284
285         bool IList.Contains(object value) {
286             if(IsCompatibleObject(value)) {
287                 return Contains((T) value);
288             }
289             return false;
290         }
291
292         int IList.IndexOf(object value) {  
293             if(IsCompatibleObject(value)) {
294                 return IndexOf((T)value);
295             }             
296             return -1;
297         }
298
299         void IList.Insert(int index, object value) {
300             if( items.IsReadOnly) {
301                 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
302             }
303             ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
304             
305             try { 
306                 Insert(index, (T)value);
307             }
308             catch (InvalidCastException) { 
309                 ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));            
310             } 
311
312         }
313
314         void IList.Remove(object value) {
315             if( items.IsReadOnly) {
316                 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
317             }
318
319             if(IsCompatibleObject(value)) {
320                 Remove((T) value);
321             }             
322         }
323
324         private static bool IsCompatibleObject(object value) {
325             // Non-null values are fine.  Only accept nulls if T is a class or Nullable<U>.
326             // Note that default(T) is not equal to null for value types except when T is Nullable<U>. 
327             return ((value is T) || (value == null && default(T) == null));
328         }
329     }
330 }