New tests, and fixed buffer mcs -> gmcs switch.
[mono.git] / mcs / class / System.Data / System.Data / DataRowCollection.cs
1 //
2 // System.Data.DataRowCollection.cs
3 //
4 // Author:
5 //   Daniel Morgan <danmorg@sc.rr.com>
6 //   Tim Coleman <tim@timcoleman.com>
7 //
8 // (C) Ximian, Inc 2002
9 // (C) Copyright 2002 Tim Coleman
10 // (C) Copyright 2002 Daniel Morgan
11 //
12
13 //
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Collections;
38 using System.ComponentModel;
39 using System.Data.Common;
40
41 namespace System.Data
42 {
43         /// <summary>
44         /// Collection of DataRows in a DataTable
45         /// </summary>
46         public partial class DataRowCollection : InternalDataCollectionBase {
47                 private DataTable table;
48
49                 internal event ListChangedEventHandler ListChanged;
50
51                 /// <summary>
52                 /// Internal constructor used to build a DataRowCollection.
53                 /// </summary>
54                 internal DataRowCollection (DataTable table)
55                 {
56                         this.table = table;
57                 }
58
59                 /// <summary>
60                 /// Gets the row at the specified index.
61                 /// </summary>
62                 public DataRow this [int index] {
63                         get {
64                                 if (index < 0 || index >= Count)
65                                         throw new IndexOutOfRangeException ("There is no row at position " + index + ".");
66
67                                 return (DataRow) List [index];
68                         }
69                 }
70
71                 /// <summary>
72                 /// Adds the specified DataRow to the DataRowCollection object.
73                 /// </summary>
74                 public void Add (DataRow row)
75                 {
76                         //TODO: validation
77                         if (row == null)
78                                 throw new ArgumentNullException ("row", "'row' argument cannot be null.");
79
80                         if (row.Table != this.table)
81                                 throw new ArgumentException ("This row already belongs to another table.");
82
83                         // If row id is not -1, we know that it is in the collection.
84                         if (row.RowID != -1)
85                                 throw new ArgumentException ("This row already belongs to this table.");
86
87                         row.BeginEdit ();
88
89                         row.Validate ();
90
91                         AddInternal (row);
92                 }
93
94 #if NET_2_0
95                 public
96 #else
97                 internal
98 #endif
99                 int IndexOf (DataRow row)
100                 {
101                         if (row == null || row.Table != table)
102                                 return -1;
103
104                         int id = row.RowID;
105                         return (id >= 0 && id < List.Count && row == List [id]) ? id : -1;
106                 }
107
108                 internal void AddInternal (DataRow row)
109                 {
110                         AddInternal (row, DataRowAction.Add);
111                 }
112
113                 internal void AddInternal (DataRow row, DataRowAction action)
114                 {
115                         row.Table.ChangingDataRow (row, action);
116                         List.Add (row);
117                         row.AttachAt (List.Count - 1, action);
118                         row.Table.ChangedDataRow (row, action);
119                         if (row._rowChanged)
120                                 row._rowChanged = false;
121                 }
122
123                 /// <summary>
124                 /// Creates a row using specified values and adds it to the DataRowCollection.
125                 /// </summary>
126 #if NET_2_0
127                 public DataRow Add (params object[] values)
128 #else
129                 public virtual DataRow Add (object[] values)
130 #endif
131                 {
132                         if (values == null)
133                                 throw new NullReferenceException ();
134                         DataRow row = table.NewNotInitializedRow ();
135                         int newRecord = table.CreateRecord (values);
136                         row.ImportRecord (newRecord);
137
138                         row.Validate ();
139                         AddInternal (row);
140                         return row;
141                 }
142
143                 /// <summary>
144                 /// Clears the collection of all rows.
145                 /// </summary>
146                 public void Clear ()
147                 {
148                         if (this.table.DataSet != null && this.table.DataSet.EnforceConstraints) {
149                                 foreach (Constraint c in table.Constraints) {
150                                         UniqueConstraint uc = c as UniqueConstraint;
151                                         if (uc == null)
152                                                 continue;
153                                         if (uc.ChildConstraint == null || uc.ChildConstraint.Table.Rows.Count == 0)
154                                                 continue;
155
156                                         string err = String.Format ("Cannot clear table Parent because " +
157                                                                 "ForeignKeyConstraint {0} enforces Child.", uc.ConstraintName);
158 #if NET_1_1
159                                         throw new InvalidConstraintException (err);
160 #else
161                                         throw new ArgumentException (err);
162 #endif
163                                 }
164                         }
165
166                         List.Clear ();
167
168                         // Remove from indexes
169                         table.ResetIndexes ();
170
171                         OnListChanged (this, new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
172                 }
173
174                 /// <summary>
175                 /// Gets a value indicating whether the primary key of any row in the collection contains
176                 /// the specified value.
177                 /// </summary>
178                 public bool Contains (object key)
179                 {
180                         return Find (key) != null;
181                 }
182
183                 /// <summary>
184                 /// Gets a value indicating whether the primary key column(s) of any row in the
185                 /// collection contains the values specified in the object array.
186                 /// </summary>
187                 public bool Contains (object [] keys)
188                 {
189                         return Find (keys) != null;
190                 }
191
192                 /// <summary>
193                 /// Gets the row specified by the primary key value.
194                 /// </summary>
195                 public DataRow Find (object key)
196                 {
197                         return Find (new object []{key}, DataViewRowState.CurrentRows);
198                 }
199
200                 /// <summary>
201                 /// Gets the row containing the specified primary key values.
202                 /// </summary>
203                 public DataRow Find (object[] keys)
204                 {
205                         return Find (keys, DataViewRowState.CurrentRows);
206                 }
207
208                 /// <summary>
209                 /// Gets the row containing the specified primary key values by searching the rows
210                 /// filtered by the state.
211                 /// </summary>
212                 internal DataRow Find (object [] keys, DataViewRowState rowStateFilter)
213                 {
214                         if (table.PrimaryKey.Length == 0)
215                                 throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
216
217                         if (keys == null)
218                                 throw new ArgumentException ("Expecting " + table.PrimaryKey.Length +" value(s) for the key being indexed, but received 0 value(s).");
219
220                         Index index = table.GetIndex (table.PrimaryKey, null, rowStateFilter, null, false);
221                         int record = index.Find (keys);
222
223                         if (record != -1 || !table._duringDataLoad)
224                                 return (record != -1 ? table.RecordCache [record] : null);
225
226                         // If the key is not found using Index *and* if DataTable is under BeginLoadData
227                         // then, check all the DataRows for the key
228                         record = table.RecordCache.NewRecord ();
229                         try {
230                                 for (int i=0; i < table.PrimaryKey.Length; ++i)
231                                         table.PrimaryKey [i].DataContainer [record] = keys [i];
232
233                                 bool found;
234                                 foreach (DataRow row in this) {
235                                         int rowIndex = Key.GetRecord (row, rowStateFilter);
236                                         if (rowIndex == -1)
237                                                 continue;
238
239                                         found = true;
240                                         for (int columnCnt = 0; columnCnt < table.PrimaryKey.Length; ++columnCnt) {
241                                                 if (table.PrimaryKey [columnCnt].CompareValues (rowIndex, record) == 0)
242                                                         continue;
243                                                 found = false;
244                                                 break;
245                                         }
246                                         if (found)
247                                                 return row;
248                                 }
249                                 return null;
250                         } finally {
251                                 table.RecordCache.DisposeRecord (record);
252                         }
253                 }
254
255                 /// <summary>
256                 /// Inserts a new row into the collection at the specified location.
257                 /// </summary>
258                 public void InsertAt (DataRow row, int pos)
259                 {
260                         if (pos < 0)
261                                 throw new IndexOutOfRangeException ("The row insert position " + pos + " is invalid.");
262
263                         if (row == null)
264                                 throw new ArgumentNullException ("row", "'row' argument cannot be null.");
265
266                         if (row.Table != this.table)
267                                 throw new ArgumentException ("This row already belongs to another table.");
268
269                         // If row id is not -1, we know that it is in the collection.
270                         if (row.RowID != -1)
271                                 throw new ArgumentException ("This row already belongs to this table.");
272
273                         row.Validate ();
274
275                         row.Table.ChangingDataRow (row, DataRowAction.Add);
276
277                         if (pos >= List.Count) {
278                                 pos = List.Count;
279                                 List.Add (row);
280                         } else {
281                                 List.Insert (pos, row);
282                                 for (int i = pos+1; i < List.Count; i++)
283                                         ((DataRow) List [i]).RowID = i;
284                         }
285
286                         row.AttachAt (pos, DataRowAction.Add);
287                         row.Table.ChangedDataRow (row, DataRowAction.Add);
288                 }
289
290                 /// <summary>
291                 /// Removes the specified DataRow from the internal list. Used by DataRow to commit the removing.
292                 /// </summary>
293                 internal void RemoveInternal (DataRow row)
294                 {
295                         if (row == null)
296                                 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
297                         int index = List.IndexOf (row);
298                         if (index < 0)
299                                 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
300                         List.RemoveAt (index);
301                         for (; index < List.Count; ++index)
302                                 ((DataRow) List [index]).RowID = index;
303                 }
304
305                 /// <summary>
306                 /// Removes the specified DataRow from the collection.
307                 /// </summary>
308                 public void Remove (DataRow row)
309                 {
310                         if (IndexOf (row) < 0)
311                                 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
312
313                         DataRowState state = row.RowState;
314                         if (state != DataRowState.Deleted && state != DataRowState.Detached) {
315                                 row.Delete ();
316                                 // if the row was in added state it will be in Detached state after the
317                                 // delete operation, so we have to check it.
318                                 if (row.RowState != DataRowState.Detached)
319                                         row.AcceptChanges ();
320                         }
321                 }
322
323                 /// <summary>
324                 /// Removes the row at the specified index from the collection.
325                 /// </summary>
326                 public void RemoveAt (int index)
327                 {
328                         Remove (this [index]);
329                 }
330
331                 internal void OnListChanged (object sender, ListChangedEventArgs args)
332                 {
333                         if (ListChanged != null)
334                                 ListChanged (sender, args);
335                 }
336         }
337
338 #if NET_2_0
339         sealed partial class DataRowCollection {
340                 public override int Count {
341                         get { return List.Count; }
342                 }
343
344                 public void CopyTo (DataRow [] array, int index)
345                 {
346                         CopyTo ((Array) array, index);
347                 }
348
349                 public override void CopyTo (Array array, int index)
350                 {
351                         base.CopyTo (array, index);
352                 }
353
354                 public override IEnumerator GetEnumerator ()
355                 {
356                         return base.GetEnumerator ();
357                 }
358         }
359 #else
360         [Serializable]
361         partial class DataRowCollection {
362                 /// <summary>
363                 /// This member overrides InternalDataCollectionBase.List
364                 /// </summary>
365                 protected override ArrayList List {
366                         get { return base.List; }
367                 }
368         }
369 #endif
370 }