* DataRow.cs : Throw exceptions if Row is Detached.
[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 using System;
14 using System.Collections;
15 using System.ComponentModel;
16
17 namespace System.Data
18 {
19         /// <summary>
20         /// Collection of DataRows in a DataTable
21         /// </summary>
22         [Serializable]
23         public class DataRowCollection : InternalDataCollectionBase 
24         {
25                 private DataTable table;
26
27                 /// <summary>
28                 /// Internal constructor used to build a DataRowCollection.
29                 /// </summary>
30                 internal DataRowCollection (DataTable table) : base ()
31                 {
32                         this.table = table;
33                 }
34
35                 /// <summary>
36                 /// Gets the row at the specified index.
37                 /// </summary>
38                 public DataRow this[int index] 
39                 {
40                         get { 
41                                 if (index >= Count)
42                                         throw new IndexOutOfRangeException ("There is no row at position " + index + ".");
43
44                                 return (DataRow) list[index]; 
45                         }
46                 }
47
48                 /// <summary>
49                 /// This member overrides InternalDataCollectionBase.List
50                 /// </summary>
51                 protected override ArrayList List 
52                 {
53                         get { return list; }
54                 }               
55
56                 /// <summary>
57                 /// Adds the specified DataRow to the DataRowCollection object.
58                 /// </summary>
59                 public void Add (DataRow row) 
60                 {
61                         //TODO: validation
62                         if (row == null)
63                                 throw new ArgumentNullException("row", "'row' argument cannot be null.");
64
65                         if (row.Table != this.table)
66                                 throw new ArgumentException ("This row already belongs to another table.");
67
68                         if (list.IndexOf(row) != -1)
69                                 throw new ArgumentException ("This row already belongs to this table.");
70                         
71
72                         if ((table.DataSet == null || table.DataSet.EnforceConstraints) && !table._duringDataLoad)
73                                 // we have to check that the new row doesn't colide with existing row
74                                 ValidateDataRowInternal(row);
75                         
76                         row.HasParentCollection = true;
77                         list.Add (row);
78                         row.AttachRow ();
79                         row.Table.ChangedDataRow (row, DataRowAction.Add);
80                 }
81
82                 /// <summary>
83                 /// Creates a row using specified values and adds it to the DataRowCollection.
84                 /// </summary>
85                 public virtual DataRow Add (object[] values) 
86                 {
87                         DataRow row = table.NewRow ();
88                         row.ItemArray = values;
89                         Add (row);
90                         return row;
91                 }
92
93                 /// <summary>
94                 /// Clears the collection of all rows.
95                 /// </summary>
96                 public void Clear () 
97                 {
98                         if (this.table.DataSet != null)
99                         {
100                                 foreach (DataTable table in this.table.DataSet.Tables)
101                                 {
102                                         foreach (Constraint c in table.Constraints)
103                                         {
104                                                 if (c is ForeignKeyConstraint)
105                                                 {
106                                                         if (((ForeignKeyConstraint) c).RelatedTable.Equals(this.table))
107                                                                 throw new InvalidConstraintException("Cannot clear table Parent because ForeignKeyConstraint " + c.ConstraintName + " enforces Child.");
108                                                 }
109                                         }
110                                 }
111                         }
112                         list.Clear ();
113                 }
114
115                 /// <summary>
116                 /// Gets a value indicating whether the primary key of any row in the collection contains
117                 /// the specified value.
118                 /// </summary>
119                 public bool Contains (object key) 
120                 {
121                         return Find (key) != null;
122                 }
123
124                 /// <summary>
125                 /// Gets a value indicating whether the primary key column(s) of any row in the 
126                 /// collection contains the values specified in the object array.
127                 /// </summary>
128                 public bool Contains (object[] keys) 
129                 {
130                         if (table.PrimaryKey.Length != keys.Length)
131                                 throw new ArgumentException ("Expecting " + table.PrimaryKey.Length + " value(s) for the key " + 
132                                                              "being indexed, but received " + keys.Length + " value(s).");
133
134                         return Find (keys) != null;
135                 }
136
137                 /// <summary>
138                 /// Gets the row specified by the primary key value.
139                 /// </summary>
140                 [MonoTODO]
141                 public DataRow Find (object key) 
142                 {
143                         if (table.PrimaryKey.Length == 0)
144                                 throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
145                         if (table.PrimaryKey.Length > 1)
146                                 throw new ArgumentException ("Expecting " + table.PrimaryKey.Length + 
147                                                              " value(s) for the key being indexed, but received 1 value(s).");
148
149                         string primColumnName = table.PrimaryKey [0].ColumnName;
150                         Type coltype = null;
151                         object newKey = null;
152                         
153                         foreach (DataRow row in this) {
154                                 
155                                 if (row.RowState != DataRowState.Deleted)
156                                 {
157                                         object primValue = row [primColumnName];
158                                         if (key == null) 
159                                         {
160                                                 if (primValue == null)
161                                                         return row;
162                                                 else 
163                                                         continue;
164                                         }
165                                        
166                                         newKey = Convert.ChangeType (key, Type.GetTypeCode(primValue.GetType ()));
167
168                                         if (primValue.Equals (newKey))
169                                                 return row;
170                                 }
171                         }
172                                                 
173                         // FIXME: is the correct value null?
174                         return null;
175                 }
176
177                 /// <summary>
178                 /// Gets the row containing the specified primary key values.
179                 /// </summary>
180                 [MonoTODO]
181                 public DataRow Find (object[] keys) 
182                 {
183                         if (table.PrimaryKey.Length == 0)
184                                 throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
185
186                         string  [] primColumnNames = new string [table.PrimaryKey.Length];
187                         
188                         for (int i = 0; i < primColumnNames.Length; i++)
189                                 primColumnNames [i] = table.PrimaryKey [i].ColumnName;
190
191                         Type coltype = null;
192                         object newKey = null;
193                         
194                         foreach (DataRow row in this) {
195                                 
196                                 if (row.RowState != DataRowState.Deleted)
197                                 {
198                                         bool eq = true;
199                                         for (int i = 0; i < keys.Length; i++) 
200                                         {
201                                         
202                                                 object primValue = row [primColumnNames [i]];
203                                                 object keyValue = keys [i];
204                                                 if (keyValue == null) 
205                                                 {
206                                                         if (primValue == null)
207                                                                 return row;
208                                                         else 
209                                                                 continue;
210                                                 }
211                                                                        
212                                                 newKey = Convert.ChangeType (keyValue, Type.GetTypeCode(primValue.GetType ()));
213
214                                                 if (!primValue.Equals (newKey)) 
215                                                 {
216                                                         eq = false;
217                                                         break;
218                                                 }                                               
219                                         }
220
221                                         if (eq)
222                                                 return row;
223                                 }
224                         }
225                                                 
226                         // FIXME: is the correct value null?
227                         return null;
228                 }
229
230                 /// <summary>
231                 /// Inserts a new row into the collection at the specified location.
232                 /// </summary>
233                 public void InsertAt (DataRow row, int pos) 
234                 {
235                         if (pos < 0)
236                                 throw new IndexOutOfRangeException ("The row insert position " + pos + " is invalid.");
237                                 
238                         if (pos >= list.Count)
239                                 list.Add (row);
240                         else
241                                 list.Insert (pos, row);
242                 }
243
244                 /// <summary>
245                 /// Removes the specified DataRow from the internal list. Used by DataRow to commit the removing.
246                 /// </summary>
247                 internal void RemoveInternal (DataRow row) {
248                         if (row == null) {
249                                 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
250                         }
251                         int index = list.IndexOf(row);
252                         if (index < 0) {
253                                 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
254                         }
255                         list.RemoveAt(index);
256                 }
257
258                 /// <summary>
259                 /// Removes the specified DataRow from the collection.
260                 /// </summary>
261                 public void Remove (DataRow row) 
262                 {
263                         if (row == null)
264                                 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
265                         int index = list.IndexOf(row);
266                         if (index < 0)
267                                 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
268                         row.Delete();
269                         row.AcceptChanges();
270                 }
271
272                 /// <summary>
273                 /// Removes the row at the specified index from the collection.
274                 /// </summary>
275                 public void RemoveAt (int index) 
276                 {                       
277                         if (index < 0 || index >= list.Count)
278                                 throw new IndexOutOfRangeException ("There is no row at position " + index + ".");
279                         DataRow row = (DataRow)list [index];
280                         row.Delete();
281                         row.AcceptChanges();
282                 }
283
284                 ///<summary>
285                 ///Internal method used to validate a given DataRow with respect
286                 ///to the DataRowCollection
287                 ///</summary>
288                 [MonoTODO]
289                 internal void ValidateDataRowInternal(DataRow row)
290                 {
291                         //first check for null violations.
292                         row.CheckNullConstraints();
293                         //FIXME: this validates constraints in the order they appear
294                         //in the collection. Most probably we need to do it in a 
295                         //specific order like unique/primary keys first, then Foreignkeys, etc
296                         foreach(Constraint constraint in table.Constraints)
297                         {
298                                 constraint.AssertConstraint(row);
299                         }
300
301                 }
302                 
303         }
304
305
306 }