fe0869907486b8a9370bb3edf6d71b8e206099e8
[mono.git] / mcs / class / System.Data / System.Data / DataRow.cs
1 //
2 // System.Data.DataRow.cs
3 //
4 // Author:
5 //   Rodrigo Moya <rodrigo@ximian.com>
6 //   Daniel Morgan <danmorg@sc.rr.com>
7 //   Tim Coleman <tim@timcoleman.com>
8 //   Ville Palo <vi64pa@koti.soon.fi>
9 //   Alan Tam Siu Lung <Tam@SiuLung.com>
10 //
11 // (C) Ximian, Inc 2002
12 // (C) Daniel Morgan 2002, 2003
13 // Copyright (C) 2002 Tim Coleman
14 //
15
16 using System;
17 using System.Collections;
18 using System.Globalization;
19 using System.Xml;
20
21 namespace System.Data {
22         /// <summary>
23         /// Represents a row of data in a DataTable.
24         /// </summary>
25         [Serializable]
26         public class DataRow
27         {
28                 #region Fields
29
30                 private DataTable _table;
31
32                 private object[] original;
33                 private object[] proposed;
34                 private object[] current;
35
36                 private string[] columnErrors;
37                 private string rowError;
38                 private DataRowState rowState;
39                 internal int xmlRowID = 0;
40                 internal bool _nullConstraintViolation;
41                 private string _nullConstraintMessage;
42                 private bool editing = false;
43                 private bool _hasParentCollection;
44                 private bool _inChangingEvent;
45                 private int _rowId;
46                 internal bool _inExpressionEvaluation = false;
47
48                 private XmlDataDocument.XmlDataElement mappedElement;
49
50                 #endregion
51
52                 #region Constructors
53
54                 /// <summary>
55                 /// This member supports the .NET Framework infrastructure and is not intended to be 
56                 /// used directly from your code.
57                 /// </summary>
58                 protected internal DataRow (DataRowBuilder builder)
59                 {
60                         _table = builder.Table;
61                         // Get the row id from the builder.
62                         _rowId = builder._rowId;
63
64                         original = null; 
65                         
66                         proposed = new object[_table.Columns.Count];
67                         // Initialise the data coloumns of the row with the dafault values, if any 
68                         for (int c = 0; c < _table.Columns.Count; c++) 
69                         {
70                                 if(_table.Columns [c].DefaultValue == null)
71                                         proposed[c] = DBNull.Value;
72                                 else
73                                         proposed [c] = _table.Columns[c].DefaultValue;
74                         }
75                         
76                         columnErrors = new string[_table.Columns.Count];
77                         rowError = String.Empty;
78
79                         //on first creating a DataRow it is always detached.
80                         rowState = DataRowState.Detached;
81                         
82                         ArrayList aiColumns = _table.Columns.AutoIncrmentColumns;
83                         foreach (string col in aiColumns) {
84                                 DataColumn dc = _table.Columns[col];
85                                 this [dc] = dc.AutoIncrementValue();
86                         }
87                         _table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
88
89                         // create mapped XmlDataElement
90                         DataSet ds = _table.DataSet;
91                         if (ds != null && ds._xmlDataDocument != null)
92                                 mappedElement = new XmlDataDocument.XmlDataElement (this, _table.Prefix, _table.TableName, _table.Namespace, ds._xmlDataDocument);
93                 }
94
95                 
96                 #endregion
97
98                 #region Properties
99
100                 /// <summary>
101                 /// Gets a value indicating whether there are errors in a row.
102                 /// </summary>
103                 public bool HasErrors {
104                         get {
105                                 if (RowError != string.Empty)
106                                         return true;
107
108                                 for (int i= 0; i < columnErrors.Length; i++){
109                                         if (columnErrors[i] != null && columnErrors[i] != string.Empty)
110                                                 return true;
111                                 }
112
113                                 return false;
114                         }
115                 }
116
117                 /// <summary>
118                 /// Gets or sets the data stored in the column specified by name.
119                 /// </summary>
120                 public object this[string columnName] {
121                         get { return this[columnName, DataRowVersion.Default]; }
122                         set {
123                                 int columnIndex = _table.Columns.IndexOf (columnName);
124                                 if (columnIndex == -1)
125                                         throw new IndexOutOfRangeException ();
126                                 this[columnIndex] = value;
127                         }
128                 }
129
130                 /// <summary>
131                 /// Gets or sets the data stored in specified DataColumn
132                 /// </summary>
133                 public object this[DataColumn column] {
134
135                         get {
136                                 return this[column, DataRowVersion.Default];} 
137                         set {
138                                 int columnIndex = _table.Columns.IndexOf (column);
139                                 if (columnIndex == -1)
140                                         throw new ArgumentException ("The column does not belong to this table.");
141                                 this[columnIndex] = value;
142                         }
143                 }
144
145                 /// <summary>
146                 /// Gets or sets the data stored in column specified by index.
147                 /// </summary>
148                 public object this[int columnIndex] {
149                         get { return this[columnIndex, DataRowVersion.Default]; }
150                         set {
151                                 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
152                                         throw new IndexOutOfRangeException ();
153                                 if (rowState == DataRowState.Deleted)
154                                         throw new DeletedRowInaccessibleException ();
155                                 DataColumn column = _table.Columns[columnIndex];
156                                 _table.ChangingDataColumn (this, column, value);
157                                 
158                                 
159                                 bool orginalEditing = editing;
160                                 if (!orginalEditing) BeginEdit ();
161                                 object v = SetColumnValue (value, column, columnIndex);
162                                 proposed[columnIndex] = v;
163                                 _table.ChangedDataColumn (this, column, v);
164                                 if (!orginalEditing) EndEdit ();
165                         }
166                 }
167
168                 /// <summary>
169                 /// Gets the specified version of data stored in the named column.
170                 /// </summary>
171                 public object this[string columnName, DataRowVersion version] {
172                         get {
173                                 int columnIndex = _table.Columns.IndexOf (columnName);
174                                 if (columnIndex == -1)
175                                         throw new IndexOutOfRangeException ();
176                                 return this[columnIndex, version];
177                         }
178                 }
179
180                 /// <summary>
181                 /// Gets the specified version of data stored in the specified DataColumn.
182                 /// </summary>
183                 public object this[DataColumn column, DataRowVersion version] {
184                         get {
185                                 if (column.Table != Table)
186                                         throw new ArgumentException ("The column does not belong to this table.");
187                                 int columnIndex = column.Ordinal;
188                                 return this[columnIndex, version];
189                         }
190                 }
191
192                 /// <summary>
193                 /// Gets the data stored in the column, specified by index and version of the data to
194                 /// retrieve.
195                 /// </summary>
196                 public object this[int columnIndex, DataRowVersion version] {
197                         get {
198                                 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
199                                         throw new IndexOutOfRangeException ();
200                                 // Accessing deleted rows
201                                 if (!_inExpressionEvaluation && rowState == DataRowState.Deleted && version != DataRowVersion.Original)
202                                         throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
203                                 
204                                 DataColumn col = _table.Columns[columnIndex];
205                                 if (col.Expression != String.Empty) {
206                                         object o = col.CompiledExpression.Eval (this);
207                                         return Convert.ChangeType (o, col.DataType);
208                                 }
209                                 
210                                 if (HasVersion(version))
211                                 {
212                                         switch (version) 
213                                         {
214                                                 case DataRowVersion.Default:
215                                                         if (editing || rowState == DataRowState.Detached)
216                                                                 return proposed[columnIndex];
217                                                         return current[columnIndex];
218                                                 case DataRowVersion.Proposed:
219                                                         return proposed[columnIndex];
220                                                 case DataRowVersion.Current:
221                                                         return current[columnIndex];
222                                                 case DataRowVersion.Original:
223                                                         return original[columnIndex];
224                                                 default:
225                                                         throw new ArgumentException ();
226                                         }
227                                 }
228                                 if (rowState == DataRowState.Detached && version == DataRowVersion.Default && proposed == null)
229                                         throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
230                                 
231                                 throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
232                         }
233                 }
234                 
235                 internal void SetOriginalValue (string columnName, object val)
236                 {
237                         int columnIndex = _table.Columns.IndexOf (columnName);
238                         DataColumn column = _table.Columns[columnIndex];
239                         _table.ChangingDataColumn (this, column, val);
240                                 
241                         if (original == null) original = new object [_table.Columns.Count];
242                         val = SetColumnValue (val, column, columnIndex);
243                         original[columnIndex] = val;
244                         rowState = DataRowState.Modified;
245                 }
246
247                 /// <summary>
248                 /// Gets or sets all of the values for this row through an array.
249                 /// </summary>
250                 public object[] ItemArray {
251                         get { 
252                                 // row not in table
253                                 if (rowState == DataRowState.Detached)
254                                         throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
255                                 // Accessing deleted rows
256                                 if (rowState == DataRowState.Deleted)
257                                         throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
258                                 
259                                 return current; 
260                         }
261                         set {
262                                 if (value.Length > _table.Columns.Count)
263                                         throw new ArgumentException ();
264
265                                 if (rowState == DataRowState.Deleted)
266                                         throw new DeletedRowInaccessibleException ();
267                                 
268                                 object[] newItems = new object[_table.Columns.Count];                   
269                                 object v = null;
270                                 for (int i = 0; i < _table.Columns.Count; i++) {
271
272                                         if (i < value.Length)
273                                                 v = value[i];
274                                         else
275                                                 v = null;
276
277                                         newItems[i] = SetColumnValue (v, _table.Columns[i], i);
278                                 }
279
280                                 bool orginalEditing = editing;
281                                 if (!orginalEditing) BeginEdit ();
282                                 proposed = newItems;
283                                 if (!orginalEditing) EndEdit ();
284                         }
285                 }
286
287                 private object SetColumnValue (object v, DataColumn col, int index) 
288                 {               
289                         object newval = null;
290                         
291                         if (_hasParentCollection && col.ReadOnly && v != this[index])
292                                 throw new ReadOnlyException ();
293
294                         if (v == null)
295                         {
296                                 if (col.DataType.ToString().Equals("System.Guid"))
297                                         throw new ArgumentException("Cannot set column to be null, Please use DBNull instead");
298
299                                 if(col.DefaultValue != DBNull.Value) 
300                                 {
301                                         newval = col.DefaultValue;
302                                 }
303                                 else if(col.AutoIncrement == true && CanAccess(index,DataRowVersion.Default)) 
304                                 {
305                                         // AutoIncrement column is already filled,
306                                         // so it just need to return existing value.
307                                         newval = this [index];
308                                 }
309                                 else 
310                                 {
311                                         if (!col.AllowDBNull)
312                                         {
313                                                 //Constraint violations during data load is raise in DataTable EndLoad
314                                                 this._nullConstraintViolation = true;
315                                                 if (this.Table._duringDataLoad) {
316                                                         this.Table._nullConstraintViolationDuringDataLoad = true;
317                                                 }
318                                                 _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
319                                         }
320
321                                         newval = DBNull.Value;
322                                 }
323                         }               
324                         else if (v == DBNull.Value) 
325                         {
326                                 
327                                 if (!col.AllowDBNull)
328                                 {
329                                         //Constraint violations during data load is raise in DataTable EndLoad
330                                         this._nullConstraintViolation = true;
331                                         if (this.Table._duringDataLoad) {
332                                                         this.Table._nullConstraintViolationDuringDataLoad = true;
333                                         }
334                                         _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
335                                 }
336                                 
337                                 newval = DBNull.Value;
338                         }
339                         else 
340                         {       
341                                 Type vType = v.GetType(); // data type of value
342                                 Type cType = col.DataType; // column data type
343                                 if (cType != vType) 
344                                 {
345                                         TypeCode typeCode = Type.GetTypeCode(cType);
346                                         switch(typeCode) {
347                                                 case TypeCode.Boolean :
348                                                         v = Convert.ToBoolean (v);
349                                                         break;
350                                                 case TypeCode.Byte  :
351                                                         v = Convert.ToByte (v);
352                                                         break;
353                                                 case TypeCode.Char  :
354                                                         v = Convert.ToChar (v);
355                                                         break;
356                                                 case TypeCode.DateTime  :
357                                                         v = Convert.ToDateTime (v);
358                                                         break;
359                                                 case TypeCode.Decimal  :
360                                                         v = Convert.ToDecimal (v);
361                                                         break;
362                                                 case TypeCode.Double  :
363                                                         v = Convert.ToDouble (v);
364                                                         break;
365                                                 case TypeCode.Int16  :
366                                                         v = Convert.ToInt16 (v);
367                                                         break;
368                                                 case TypeCode.Int32  :
369                                                         v = Convert.ToInt32 (v);
370                                                         break;
371                                                 case TypeCode.Int64  :
372                                                         v = Convert.ToInt64 (v);
373                                                         break;
374                                                 case TypeCode.SByte  :
375                                                         v = Convert.ToSByte (v);
376                                                         break;
377                                                 case TypeCode.Single  :
378                                                         v = Convert.ToSingle (v);
379                                                         break;
380                                                 case TypeCode.String  :
381                                                         v = Convert.ToString (v);
382                                                         break;
383                                                 case TypeCode.UInt16  :
384                                                         v = Convert.ToUInt16 (v);
385                                                         break;
386                                                 case TypeCode.UInt32  :
387                                                         v = Convert.ToUInt32 (v);
388                                                         break;
389                                                 case TypeCode.UInt64  :
390                                                         v = Convert.ToUInt64 (v);
391                                                         break;
392                                                 default :
393                                                                 
394                                                 switch(cType.ToString()) {
395                                                         case "System.TimeSpan" :
396                                                                 v = (System.TimeSpan) v;
397                                                                 break;
398                                                         case "System.Type" :
399                                                                 v = (System.Type) v;
400                                                                 break;
401                                                         case "System.Object" :
402                                                                 //v = (System.Object) v;
403                                                                 break;
404                                                         default:
405                                                                 if (!cType.IsArray)
406                                                                         throw new InvalidCastException("Type not supported.");
407                                                                 break;
408                                                 }
409                                                         break;
410                                         }
411                                         vType = v.GetType();
412                                 }
413
414                                 // The MaxLength property is ignored for non-text columns
415                                 if ((Type.GetTypeCode(vType) == TypeCode.String) && (col.MaxLength != -1) && 
416                                         (this.Table.Columns[index].MaxLength < ((string)v).Length)) {
417                                         throw new ArgumentException("Cannot set column '" + col.ColumnName + "' to '" + v + "'. The value violates the MaxLength limit of this column.");
418                                 }
419                                 newval = v;
420                                 if(col.AutoIncrement == true) {
421                                         long inc = Convert.ToInt64(v);
422                                         col.UpdateAutoIncrementValue (inc);
423                                 }
424                         }
425                         col.DataHasBeenSet = true;
426                         return newval;
427                 }
428
429                 /// <summary>
430                 /// Gets or sets the custom error description for a row.
431                 /// </summary>
432                 public string RowError {
433                         get { return rowError; }
434                         set { rowError = value; }
435                 }
436
437                 /// <summary>
438                 /// Gets the current state of the row in regards to its relationship to the
439                 /// DataRowCollection.
440                 /// </summary>
441                 public DataRowState RowState {
442                         get { return rowState; }
443                 }
444
445                 //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
446                 //to a Datatable so I added this method. Delete if there is a better way.
447                 internal void AttachRow() {
448                         current = proposed;
449                         proposed = null;
450                         rowState = DataRowState.Added;
451                 }
452
453                 //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
454                 //from a Datatable so I added this method. Delete if there is a better way.
455                 internal void DetachRow() {
456                         proposed = null;
457                         _rowId = -1;
458                         _hasParentCollection = false;
459                         rowState = DataRowState.Detached;
460                 }
461
462                 /// <summary>
463                 /// Gets the DataTable for which this row has a schema.
464                 /// </summary>
465                 public DataTable Table {
466                         get { return _table; }
467                 }
468
469                 internal XmlDataDocument.XmlDataElement DataElement {
470                         get { return mappedElement; }
471                         set { mappedElement = value; }
472                 }
473
474                 /// <summary>
475                 /// Gets and sets index of row. This is used from 
476                 /// XmlDataDocument.
477                 // </summary>
478                 internal int XmlRowID {
479                         get { return xmlRowID; }
480                         set { xmlRowID = value; }
481                 }
482                 
483                 /// <summary>
484                 /// Gets and sets index of row.
485                 // </summary>
486                 internal int RowID {
487                         get { return _rowId; }
488                         set { _rowId = value; }
489                 }
490
491                 #endregion
492
493                 #region Methods
494
495                 /// <summary>
496                 /// Commits all the changes made to this row since the last time AcceptChanges was
497                 /// called.
498                 /// </summary>
499                 public void AcceptChanges () 
500                 {
501                         EndEdit(); // in case it hasn't been called
502                         switch (rowState) {
503                         case DataRowState.Added:
504                         case DataRowState.Modified:
505                                 rowState = DataRowState.Unchanged;
506                                 break;
507                         case DataRowState.Deleted:
508                                 _table.Rows.RemoveInternal (this);
509                                 DetachRow();
510                                 break;
511                         case DataRowState.Detached:
512                                 throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
513                         }
514                         // Accept from detached
515                         if (original == null)
516                                 original = new object[_table.Columns.Count];
517                         Array.Copy (current, original, _table.Columns.Count);
518                 }
519
520                 /// <summary>
521                 /// Begins an edit operation on a DataRow object.
522                 /// </summary>
523                 public void BeginEdit () 
524                 {
525                         
526                         if (_inChangingEvent)
527                                 throw new InRowChangingEventException("Cannot call BeginEdit inside an OnRowChanging event.");
528                         if (rowState == DataRowState.Deleted)
529                                 throw new DeletedRowInaccessibleException ();
530                         if (!HasVersion (DataRowVersion.Proposed)) {
531                                 proposed = new object[_table.Columns.Count];
532                                 Array.Copy (current, proposed, current.Length);
533                         }
534                         // setting editing to true stops validations on the row
535                         editing = true;
536                 }
537
538                 /// <summary>
539                 /// Cancels the current edit on the row.
540                 /// </summary>
541                 public void CancelEdit () 
542                 {
543                          if (_inChangingEvent)
544                                 throw new InRowChangingEventException("Cannot call CancelEdit inside an OnRowChanging event.");
545                         editing = false;
546                         if (HasVersion (DataRowVersion.Proposed)) {
547                                 proposed = null;
548                                 if (rowState == DataRowState.Modified)
549                                     rowState = DataRowState.Unchanged;
550                         }
551                 }
552
553                 /// <summary>
554                 /// Clears the errors for the row, including the RowError and errors set with
555                 /// SetColumnError.
556                 /// </summary>
557                 public void ClearErrors () 
558                 {
559                         rowError = String.Empty;
560                         columnErrors = new String[_table.Columns.Count];
561                 }
562
563                 /// <summary>
564                 /// Deletes the DataRow.
565                 /// </summary>
566                 public void Delete () 
567                 {
568                         _table.DeletingDataRow(this, DataRowAction.Delete);
569                         switch (rowState) {
570                         case DataRowState.Added:
571                                 // check what to do with child rows
572                                 CheckChildRows(DataRowAction.Delete);
573                                 _table.DeleteRowFromIndexes (this);
574                                 Table.Rows.RemoveInternal (this);
575
576                                 // if row was in Added state we move it to Detached.
577                                 DetachRow();
578                                 break;
579                         case DataRowState.Deleted:
580                                 break;          
581                         default:
582                                 // check what to do with child rows
583                                 CheckChildRows(DataRowAction.Delete);
584                                 _table.DeleteRowFromIndexes (this);
585                                 rowState = DataRowState.Deleted;
586                                 break;
587                         }
588                         _table.DeletedDataRow(this, DataRowAction.Delete);
589                 }
590
591                 // check the child rows of this row before deleting the row.
592                 private void CheckChildRows(DataRowAction action)
593                 {
594                         
595                         // in this method we find the row that this row is in a relation with them.
596                         // in shortly we find all child rows of this row.
597                         // then we function according to the DeleteRule of the foriegnkey.
598
599
600                         // 1. find if this row is attached to dataset.
601                         // 2. find if EnforceConstraints is true.
602                         // 3. find if there are any constraint on the table that the row is in.
603                         if (_table.DataSet != null && _table.DataSet.EnforceConstraints && _table.Constraints.Count > 0)
604                         {
605                                 foreach (DataTable table in _table.DataSet.Tables)
606                                 {
607                                         // loop on all ForeignKeyConstrain of the table.
608                                         foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
609                                         {
610                                                 if (fk.RelatedTable == _table)
611                                                 {
612                                                         Rule rule;
613                                                         if (action == DataRowAction.Delete)
614                                                                 rule = fk.DeleteRule;
615                                                         else
616                                                                 rule = fk.UpdateRule;
617                                                         CheckChildRows(fk, action, rule);
618                                                 }                       
619                                         }
620                                 }
621                         }
622                 }
623
624                 private void CheckChildRows(ForeignKeyConstraint fkc, DataRowAction action, Rule rule)
625                 {                               
626                         DataRow[] childRows = GetChildRows(fkc, DataRowVersion.Default);
627                         switch (rule)
628                         {
629                                 case Rule.Cascade:  // delete or change all relted rows.
630                                         if (childRows != null)
631                                         {
632                                                 for (int j = 0; j < childRows.Length; j++)
633                                                 {
634                                                         // if action is delete we delete all child rows
635                                                         if (action == DataRowAction.Delete)
636                                                         {
637                                                                 if (childRows[j].RowState != DataRowState.Deleted)
638                                                                         childRows[j].Delete();
639                                                         }
640                                                         // if action is change we change the values in the child row
641                                                         else if (action == DataRowAction.Change)
642                                                         {
643                                                                 // change only the values in the key columns
644                                                                 // set the childcolumn value to the new parent row value
645                                                                 for (int k = 0; k < fkc.Columns.Length; k++)
646                                                                         childRows[j][fkc.Columns[k]] = this[fkc.RelatedColumns[k], DataRowVersion.Proposed];
647                                                         }
648                                                 }
649                                         }
650                                         break;
651                                 case Rule.None: // throw an exception if there are any child rows.
652                                         if (childRows != null)
653                                         {
654                                                 for (int j = 0; j < childRows.Length; j++)
655                                                 {
656                                                         if (childRows[j].RowState != DataRowState.Deleted)
657                                                         {
658                                                                 string changeStr = "Cannot change this row because constraints are enforced on relation " + fkc.ConstraintName +", and changing this row will strand child rows.";
659                                                                 string delStr = "Cannot delete this row because constraints are enforced on relation " + fkc.ConstraintName +", and deleting this row will strand child rows.";
660                                                                 string message = action == DataRowAction.Delete ? delStr : changeStr;
661                                                                 throw new InvalidConstraintException(message);
662                                                         }
663                                                 }
664                                         }
665                                         break;
666                                 case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
667                                         if (childRows != null)
668                                         {
669                                                 for (int j = 0; j < childRows.Length; j++)
670                                                 {
671                                                         DataRow child = childRows[j];
672                                                         if (childRows[j].RowState != DataRowState.Deleted)
673                                                         {
674                                                                 //set only the key columns to default
675                                                                 for (int k = 0; k < fkc.Columns.Length; k++)
676                                                                         child[fkc.Columns[k]] = fkc.Columns[k].DefaultValue;
677                                                         }
678                                                 }
679                                         }
680                                         break;
681                                 case Rule.SetNull: // set the values in the child row to null.
682                                         if (childRows != null)
683                                         {
684                                                 for (int j = 0; j < childRows.Length; j++)
685                                                 {
686                                                         DataRow child = childRows[j];
687                                                         if (childRows[j].RowState != DataRowState.Deleted)
688                                                         {
689                                                                 // set only the key columns to DBNull
690                                                                 for (int k = 0; k < fkc.Columns.Length; k++)
691                                                                         child.SetNull(fkc.Columns[k]);
692                                                         }
693                                                 }
694                                         }
695                                         break;
696                         }
697
698                 }
699
700                 /// <summary>
701                 /// Ends the edit occurring on the row.
702                 /// </summary>
703                 public void EndEdit () 
704                 {
705                         if (_inChangingEvent)
706                                 throw new InRowChangingEventException("Cannot call EndEdit inside an OnRowChanging event.");
707                         if (rowState == DataRowState.Detached)
708                         {
709                                 editing = false;
710                                 return;
711                         }
712                         
713                         CheckReadOnlyStatus();
714                         if (HasVersion (DataRowVersion.Proposed))
715                         {
716                                 _inChangingEvent = true;
717                                 try
718                                 {
719                                         _table.ChangingDataRow(this, DataRowAction.Change);
720                                 }
721                                 finally
722                                 {
723                                         _inChangingEvent = false;
724                                 }
725                                 if (rowState == DataRowState.Unchanged)
726                                         rowState = DataRowState.Modified;
727                                 
728                                 //Calling next method validates UniqueConstraints
729                                 //and ForeignKeys.
730                                 try
731                                 {
732                                         if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
733                                                 _table.Rows.ValidateDataRowInternal(this);
734                                 }
735                                 catch (Exception e)
736                                 {
737                                         editing = false;
738                                         proposed = null;
739                                         throw e;
740                                 }
741
742                                 // Now we are going to check all child rows of current row.
743                                 // In the case the cascade is true the child rows will look up for
744                                 // parent row. since lookup in index is always on current,
745                                 // we have to move proposed version of current row to current
746                                 // in the case of check child row failure we are rolling 
747                                 // current row state back.
748                                 object[] backup = current;
749                                 current = proposed;
750                                 bool editing_backup = editing;
751                                 editing = false;
752                                 try {
753                                         // check all child rows.
754                                         CheckChildRows(DataRowAction.Change);
755                                         proposed = null;
756                                 }
757                                 catch (Exception ex) {
758                                         // if check child rows failed - rollback to previous state
759                                         // i.e. restore proposed and current versions
760                                         proposed = current;
761                                         current = backup;
762                                         editing = editing_backup;
763                                         // since we failed - propagate an exception
764                                         throw ex;
765                                 }
766                                 _table.ChangedDataRow(this, DataRowAction.Change);
767                         }
768                 }
769
770                 /// <summary>
771                 /// Gets the child rows of this DataRow using the specified DataRelation.
772                 /// </summary>
773                 public DataRow[] GetChildRows (DataRelation relation) 
774                 {
775                         return GetChildRows (relation, DataRowVersion.Current);
776                 }
777
778                 /// <summary>
779                 /// Gets the child rows of a DataRow using the specified RelationName of a
780                 /// DataRelation.
781                 /// </summary>
782                 public DataRow[] GetChildRows (string relationName) 
783                 {
784                         return GetChildRows (Table.DataSet.Relations[relationName]);
785                 }
786
787                 /// <summary>
788                 /// Gets the child rows of a DataRow using the specified DataRelation, and
789                 /// DataRowVersion.
790                 /// </summary>
791                 public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version) 
792                 {
793                         if (relation == null)
794                                 return new DataRow[0];
795
796                         if (this.Table == null || RowState == DataRowState.Detached)
797                                 throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
798
799                         if (relation.DataSet != this.Table.DataSet)
800                                 throw new ArgumentException();
801
802                         if (relation.ChildKeyConstraint != null)
803                                 return GetChildRows (relation.ChildKeyConstraint, version);
804
805                         ArrayList rows = new ArrayList();
806                         DataColumn[] parentColumns = relation.ParentColumns;
807                         DataColumn[] childColumns = relation.ChildColumns;
808                         int numColumn = parentColumns.Length;
809                         if (HasVersion(version))
810                         {
811                                 object[] vals = new object[parentColumns.Length];
812                                 for (int i = 0; i < vals.Length; i++)
813                                         vals[i] = this[parentColumns[i], version];
814                                 
815                                 foreach (DataRow row in relation.ChildTable.Rows) 
816                                 {
817                                         bool allColumnsMatch = false;
818                                         if (row.HasVersion(DataRowVersion.Default))
819                                         {
820                                                 allColumnsMatch = true;
821                                                 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
822                                                 {
823                                                         if (!vals[columnCnt].Equals(
824                                                                 row[childColumns[columnCnt], DataRowVersion.Default])) 
825                                                         {
826                                                                 allColumnsMatch = false;
827                                                                 break;
828                                                         }
829                                                 }
830                                         }
831                                         if (allColumnsMatch) rows.Add(row);
832                                 }
833                         }
834                         DataRow[] result = relation.ChildTable.NewRowArray(rows.Count);
835                         rows.CopyTo(result, 0);
836                         return result;
837                 }
838
839                 /// <summary>
840                 /// Gets the child rows of a DataRow using the specified RelationName of a
841                 /// DataRelation, and DataRowVersion.
842                 /// </summary>
843                 public DataRow[] GetChildRows (string relationName, DataRowVersion version) 
844                 {
845                         return GetChildRows (Table.DataSet.Relations[relationName], version);
846                 }
847
848                 private DataRow[] GetChildRows (ForeignKeyConstraint fkc, DataRowVersion version) 
849                 {
850                         ArrayList rows = new ArrayList();
851                         DataColumn[] parentColumns = fkc.RelatedColumns;
852                         DataColumn[] childColumns = fkc.Columns;
853                         int numColumn = parentColumns.Length;
854                         if (HasVersion(version))
855                         {
856                                 object[] vals = new object[parentColumns.Length];
857                                 for (int i = 0; i < vals.Length; i++)
858                                         vals[i] = this[parentColumns[i], version];
859
860                                 Index index = fkc.Index;
861                                 if (index != null) {
862                                         // get the child rows from the index
863                                         Node[] childNodes = index.FindAllSimple (vals);
864                                         for (int i = 0; i < childNodes.Length; i++) {
865                                                 rows.Add (childNodes[i].Row);
866                                         }
867                                 }
868                                 else { // if there is no index we search manualy.
869                                         foreach (DataRow row in fkc.Table.Rows) 
870                                         {
871                                                 bool allColumnsMatch = false;
872                                                 if (row.HasVersion(DataRowVersion.Default))
873                                                 {
874                                                         allColumnsMatch = true;
875                                                         for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
876                                                         {
877                                                                 if (!vals[columnCnt].Equals(
878                                                                         row[childColumns[columnCnt], DataRowVersion.Default])) 
879                                                                 {
880                                                                         allColumnsMatch = false;
881                                                                         break;
882                                                                 }
883                                                         }
884                                                 }
885                                                 if (allColumnsMatch) rows.Add(row);
886                                         }
887                                 }
888                         }
889
890                         DataRow[] result = fkc.Table.NewRowArray(rows.Count);
891                         rows.CopyTo(result, 0);
892                         return result;
893                 }
894
895                 /// <summary>
896                 /// Gets the error description of the specified DataColumn.
897                 /// </summary>
898                 public string GetColumnError (DataColumn column) 
899                 {
900                         return GetColumnError (_table.Columns.IndexOf(column));
901                 }
902
903                 /// <summary>
904                 /// Gets the error description for the column specified by index.
905                 /// </summary>
906                 public string GetColumnError (int columnIndex) 
907                 {
908                         if (columnIndex < 0 || columnIndex >= columnErrors.Length)
909                                 throw new IndexOutOfRangeException ();
910
911                         string retVal = columnErrors[columnIndex];
912                         if (retVal == null)
913                                 retVal = string.Empty;
914                         return retVal;
915                 }
916
917                 /// <summary>
918                 /// Gets the error description for the column, specified by name.
919                 /// </summary>
920                 public string GetColumnError (string columnName) 
921                 {
922                         return GetColumnError (_table.Columns.IndexOf(columnName));
923                 }
924
925                 /// <summary>
926                 /// Gets an array of columns that have errors.
927                 /// </summary>
928                 public DataColumn[] GetColumnsInError () 
929                 {
930                         ArrayList dataColumns = new ArrayList ();
931
932                         for (int i = 0; i < columnErrors.Length; i += 1)
933                         {
934                                 if (columnErrors[i] != null && columnErrors[i] != String.Empty)
935                                         dataColumns.Add (_table.Columns[i]);
936                         }
937
938                         return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
939                 }
940
941                 /// <summary>
942                 /// Gets the parent row of a DataRow using the specified DataRelation.
943                 /// </summary>
944                 public DataRow GetParentRow (DataRelation relation) 
945                 {
946                         return GetParentRow (relation, DataRowVersion.Current);
947                 }
948
949                 /// <summary>
950                 /// Gets the parent row of a DataRow using the specified RelationName of a
951                 /// DataRelation.
952                 /// </summary>
953                 public DataRow GetParentRow (string relationName) 
954                 {
955                         return GetParentRow (relationName, DataRowVersion.Current);
956                 }
957
958                 /// <summary>
959                 /// Gets the parent row of a DataRow using the specified DataRelation, and
960                 /// DataRowVersion.
961                 /// </summary>
962                 public DataRow GetParentRow (DataRelation relation, DataRowVersion version) 
963                 {
964                         DataRow[] rows = GetParentRows(relation, version);
965                         if (rows.Length == 0) return null;
966                         return rows[0];
967                 }
968
969                 /// <summary>
970                 /// Gets the parent row of a DataRow using the specified RelationName of a 
971                 /// DataRelation, and DataRowVersion.
972                 /// </summary>
973                 public DataRow GetParentRow (string relationName, DataRowVersion version) 
974                 {
975                         return GetParentRow (Table.DataSet.Relations[relationName], version);
976                 }
977
978                 /// <summary>
979                 /// Gets the parent rows of a DataRow using the specified DataRelation.
980                 /// </summary>
981                 public DataRow[] GetParentRows (DataRelation relation) 
982                 {
983                         return GetParentRows (relation, DataRowVersion.Current);
984                 }
985
986                 /// <summary>
987                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
988                 /// DataRelation.
989                 /// </summary>
990                 public DataRow[] GetParentRows (string relationName) 
991                 {
992                         return GetParentRows (relationName, DataRowVersion.Current);
993                 }
994
995                 /// <summary>
996                 /// Gets the parent rows of a DataRow using the specified DataRelation, and
997                 /// DataRowVersion.
998                 /// </summary>
999                 public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version) 
1000                 {
1001                         // TODO: Caching for better preformance
1002                         if (relation == null)
1003                                 return new DataRow[0];
1004
1005                         if (this.Table == null || RowState == DataRowState.Detached)
1006                                 throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
1007
1008                         if (relation.DataSet != this.Table.DataSet)
1009                                 throw new ArgumentException();
1010
1011                         ArrayList rows = new ArrayList();
1012                         DataColumn[] parentColumns = relation.ParentColumns;
1013                         DataColumn[] childColumns = relation.ChildColumns;
1014                         int numColumn = parentColumns.Length;
1015                         if (HasVersion(version))
1016                         {
1017                                 object[] vals = new object[childColumns.Length];
1018                                 for (int i = 0; i < vals.Length; i++)
1019                                         vals[i] = this[childColumns[i], version];
1020                                 
1021                                 Index indx = relation.ParentTable.GetIndexByColumns (parentColumns);
1022                                 if (indx != null) { // get the child rows from the index
1023                                         Node[] childNodes = indx.FindAllSimple (vals);
1024                                         for (int i = 0; i < childNodes.Length; i++) {
1025                                                 rows.Add (childNodes[i].Row);
1026                                         }
1027                                 }
1028                                 else { // no index so we have to search manualy.
1029                                         foreach (DataRow row in relation.ParentTable.Rows) 
1030                                         {
1031                                                 bool allColumnsMatch = false;
1032                                                 if (row.HasVersion(DataRowVersion.Default))
1033                                                 {
1034                                                         allColumnsMatch = true;
1035                                                         for (int columnCnt = 0; columnCnt < numColumn; columnCnt++) 
1036                                                         {
1037                                                                 if (!this[childColumns[columnCnt], version].Equals(
1038                                                                         row[parentColumns[columnCnt], DataRowVersion.Default])) 
1039                                                                 {
1040                                                                         allColumnsMatch = false;
1041                                                                         break;
1042                                                                 }
1043                                                         }
1044                                                 }
1045                                                 if (allColumnsMatch) rows.Add(row);
1046                                         }
1047                                 }
1048                         }
1049
1050                         DataRow[] result = relation.ParentTable.NewRowArray(rows.Count);
1051                         rows.CopyTo(result, 0);
1052                         return result;
1053                 }
1054
1055                 /// <summary>
1056                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
1057                 /// DataRelation, and DataRowVersion.
1058                 /// </summary>
1059                 public DataRow[] GetParentRows (string relationName, DataRowVersion version) 
1060                 {
1061                         return GetParentRows (Table.DataSet.Relations[relationName], version);
1062                 }
1063
1064                 /// <summary>
1065                 /// Gets a value indicating whether a specified version exists.
1066                 /// </summary>
1067                 public bool HasVersion (DataRowVersion version) 
1068                 {
1069                         switch (version)
1070                         {
1071                                 case DataRowVersion.Default:
1072                                         if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
1073                                                 return false;
1074                                         if (rowState == DataRowState.Detached)
1075                                                 return proposed != null;
1076                                         return true;
1077                                 case DataRowVersion.Proposed:
1078                                         if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
1079                                                 return false;
1080                                         return (proposed != null);
1081                                 case DataRowVersion.Current:
1082                                         if ((rowState == DataRowState.Deleted && !_inExpressionEvaluation) || rowState == DataRowState.Detached)
1083                                                 return false;
1084                                         return (current != null);
1085                                 case DataRowVersion.Original:
1086                                         if (rowState == DataRowState.Detached)
1087                                                 return false;
1088                                         return (original != null);
1089                         }
1090                         return false;
1091                 }
1092
1093                 /// <summary>
1094                 /// Gets a value indicating whether the specified DataColumn contains a null value.
1095                 /// </summary>
1096                 public bool IsNull (DataColumn column) 
1097                 {
1098                         object o = this[column];
1099                         return (o == DBNull.Value);
1100                 }
1101
1102                 /// <summary>
1103                 /// Gets a value indicating whether the column at the specified index contains a null
1104                 /// value.
1105                 /// </summary>
1106                 public bool IsNull (int columnIndex) 
1107                 {
1108                         object o = this[columnIndex];
1109                         return (o == DBNull.Value);
1110                 }
1111
1112                 /// <summary>
1113                 /// Gets a value indicating whether the named column contains a null value.
1114                 /// </summary>
1115                 public bool IsNull (string columnName) 
1116                 {
1117                         object o = this[columnName];
1118                         return (o == DBNull.Value);
1119                 }
1120
1121                 /// <summary>
1122                 /// Gets a value indicating whether the specified DataColumn and DataRowVersion
1123                 /// contains a null value.
1124                 /// </summary>
1125                 public bool IsNull (DataColumn column, DataRowVersion version) 
1126                 {
1127                         object o = this[column, version];
1128                         return (o == DBNull.Value);
1129                 }
1130
1131                 /// <summary>
1132                 /// Returns a value indicating whether all of the row columns specified contain a null value.
1133                 /// </summary>
1134                 internal bool IsNullColumns(DataColumn[] columns)
1135                 {
1136                         bool allNull = true;
1137                         for (int i = 0; i < columns.Length; i++) 
1138                         {
1139                                 if (!IsNull(columns[i])) 
1140                                 {
1141                                         allNull = false;
1142                                         break;
1143                                 }
1144                         }
1145                         return allNull;
1146                 }
1147
1148                 /// <summary>
1149                 /// Rejects all changes made to the row since AcceptChanges was last called.
1150                 /// </summary>
1151                 public void RejectChanges () 
1152                 {
1153                         if (RowState == DataRowState.Detached)
1154                                 throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
1155                         // If original is null, then nothing has happened since AcceptChanges
1156                         // was last called.  We have no "original" to go back to.
1157                         if (original != null)
1158                         {
1159                                 Array.Copy (original, current, _table.Columns.Count);
1160                                
1161                                 _table.ChangedDataRow (this, DataRowAction.Rollback);
1162                                 CancelEdit ();
1163                                 switch (rowState)
1164                                 {
1165                                         case DataRowState.Added:
1166                                                 _table.DeleteRowFromIndexes (this);
1167                                                 _table.Rows.RemoveInternal (this);
1168                                                 break;
1169                                         case DataRowState.Modified:
1170                                                 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1171                                                         _table.Rows.ValidateDataRowInternal(this);
1172                                                 rowState = DataRowState.Unchanged;
1173                                                 break;
1174                                         case DataRowState.Deleted:
1175                                                 rowState = DataRowState.Unchanged;
1176                                                 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1177                                                         _table.Rows.ValidateDataRowInternal(this);
1178                                                 break;
1179                                 } 
1180                                 
1181                         }                       
1182                         else {
1183                                 // If rows are just loaded via Xml the original values are null.
1184                                 // So in this case we have to remove all columns.
1185                                 // FIXME: I'm not realy sure, does this break something else, but
1186                                 // if so: FIXME ;)
1187                                 
1188                                 if ((rowState & DataRowState.Added) > 0)
1189                                 {
1190                                         _table.DeleteRowFromIndexes (this);
1191                                         _table.Rows.RemoveInternal (this);
1192                                         // if row was in Added state we move it to Detached.
1193                                         DetachRow();
1194                                 }
1195                         }
1196                 }
1197
1198                 /// <summary>
1199                 /// Sets the error description for a column specified as a DataColumn.
1200                 /// </summary>
1201                 public void SetColumnError (DataColumn column, string error) 
1202                 {
1203                         SetColumnError (_table.Columns.IndexOf (column), error);
1204                 }
1205
1206                 /// <summary>
1207                 /// Sets the error description for a column specified by index.
1208                 /// </summary>
1209                 public void SetColumnError (int columnIndex, string error) 
1210                 {
1211                         if (columnIndex < 0 || columnIndex >= columnErrors.Length)
1212                                 throw new IndexOutOfRangeException ();
1213                         columnErrors[columnIndex] = error;
1214                 }
1215
1216                 /// <summary>
1217                 /// Sets the error description for a column specified by name.
1218                 /// </summary>
1219                 public void SetColumnError (string columnName, string error) 
1220                 {
1221                         SetColumnError (_table.Columns.IndexOf (columnName), error);
1222                 }
1223
1224                 /// <summary>
1225                 /// Sets the value of the specified DataColumn to a null value.
1226                 /// </summary>
1227                 protected void SetNull (DataColumn column) 
1228                 {
1229                         this[column] = DBNull.Value;
1230                 }
1231
1232                 /// <summary>
1233                 /// Sets the parent row of a DataRow with specified new parent DataRow.
1234                 /// </summary>
1235                 public void SetParentRow (DataRow parentRow) 
1236                 {
1237                         SetParentRow(parentRow, null);
1238                 }
1239
1240                 /// <summary>
1241                 /// Sets the parent row of a DataRow with specified new parent DataRow and
1242                 /// DataRelation.
1243                 /// </summary>
1244                 public void SetParentRow (DataRow parentRow, DataRelation relation) 
1245                 {
1246                         if (_table == null || parentRow.Table == null || RowState == DataRowState.Detached)
1247                                 throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
1248
1249                         if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
1250                                 throw new ArgumentException();
1251                         
1252                         BeginEdit();
1253                         if (relation == null)
1254                         {
1255                                 foreach (DataRelation parentRel in _table.ParentRelations)
1256                                 {
1257                                         DataColumn[] childCols = parentRel.ChildKeyConstraint.Columns;
1258                                         DataColumn[] parentCols = parentRel.ChildKeyConstraint.RelatedColumns;
1259                                         
1260                                         for (int i = 0; i < parentCols.Length; i++)
1261                                         {
1262                                                 if (parentRow == null)
1263                                                         this[childCols[i].Ordinal] = DBNull.Value;
1264                                                 else
1265                                                         this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1266                                         }
1267                                         
1268                                 }
1269                         }
1270                         else
1271                         {
1272                                 DataColumn[] childCols = relation.ChildKeyConstraint.Columns;
1273                                 DataColumn[] parentCols = relation.ChildKeyConstraint.RelatedColumns;
1274                                         
1275                                 for (int i = 0; i < parentCols.Length; i++)
1276                                 {
1277                                         if (parentRow == null)
1278                                                 this[childCols[i].Ordinal] = DBNull.Value;
1279                                         else
1280                                                 this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1281                                 }
1282                         }
1283                         EndEdit();
1284                 }
1285                 
1286                 //Copy all values of this DataaRow to the row parameter.
1287                 internal void CopyValuesToRow(DataRow row)
1288                 {
1289                                                 
1290                         if (row == null)
1291                                 throw new ArgumentNullException("row");
1292                         if (row == this)
1293                                 throw new ArgumentException("'row' is the same as this object");
1294
1295                         DataColumnCollection columns = Table.Columns;
1296                         
1297                         for(int i = 0; i < columns.Count; i++){
1298
1299                                 string columnName = columns[i].ColumnName;
1300                                 DataColumn column = row.Table.Columns[columnName];
1301                                 //if a column with the same name exists in both rows copy the values
1302                                 if(column != null) {
1303                                         int index = column.Ordinal;
1304                                         if (HasVersion(DataRowVersion.Original))
1305                                         {
1306                                                 if (row.original == null)
1307                                                         row.original = new object[row.Table.Columns.Count];
1308                                                 row.original[index] = row.SetColumnValue(original[i], column, index);
1309                                         }
1310                                         if (HasVersion(DataRowVersion.Current))
1311                                         {
1312                                                 if (row.current == null)
1313                                                         row.current = new object[row.Table.Columns.Count];
1314                                                 row.current[index] = row.SetColumnValue(current[i], column, index);
1315                                         }
1316                                         if (HasVersion(DataRowVersion.Proposed))
1317                                         {
1318                                                 if (row.proposed == null)
1319                                                         row.proposed = new object[row.Table.Columns.Count];
1320                                                 row.proposed[index] = row.SetColumnValue(proposed[i], column, index);
1321                                         }
1322                                         
1323                                         //Saving the current value as the column value
1324                                         row[index] = row.current[index];
1325                                         
1326                                 }
1327                         }
1328
1329                         row.rowState = RowState;
1330                         row.RowError = RowError;
1331                         row.columnErrors = columnErrors;
1332                 }
1333
1334                 
1335                 private void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
1336                 {
1337                         // if a column is added we hava to add an additional value the 
1338                         // the priginal, current and propoed arrays.
1339                         // this scenario can happened in merge operation.
1340
1341                         if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
1342                         {
1343                                 object[] tmp;
1344                                 int index = this.Table.Columns.Count - 1;
1345                                 if (current != null)
1346                                 {
1347                                         tmp = new object [index + 1];
1348                                         Array.Copy (current, tmp, current.Length);
1349                                         tmp[tmp.Length - 1] = SetColumnValue(null, this.Table.Columns[index], index);
1350                                         current = tmp;
1351                                 }
1352                                 if (proposed != null)
1353                                 {
1354                                         tmp = new object [index + 1];
1355                                         Array.Copy (proposed, tmp, proposed.Length);
1356                                         tmp[tmp.Length - 1] = SetColumnValue(null, this.Table.Columns[index], index);
1357                                         proposed = tmp;
1358                                 }
1359                                 if(original != null)
1360                                 {
1361                                         tmp = new object [index + 1];
1362                                         Array.Copy (original, tmp, original.Length);
1363                                         tmp[tmp.Length - 1] = SetColumnValue(null, this.Table.Columns[index], index);
1364                                         original = tmp;
1365                                 }
1366
1367                         }
1368                 }
1369
1370                 internal void onColumnRemoved(int columnIndex)
1371                 {
1372                         // when column removed we have to compress row values in the way 
1373                         // they will correspond to new column ordinals
1374
1375                         object[] tmp;
1376                         if (current != null)
1377                         {
1378                                 tmp = new object[current.Length - 1];
1379                                 // copy values before removed column
1380                                 if (columnIndex > 0)
1381                                         Array.Copy (current, 0, tmp, 0, columnIndex);
1382                                 // copy values after removed column
1383                                 if(columnIndex < current.Length - 1)
1384                                         Array.Copy(current, columnIndex + 1, tmp, columnIndex, current.Length - 1 - columnIndex);
1385
1386                                 current = tmp;
1387                         }
1388                         if (proposed != null)
1389                         {
1390                                 tmp = new object[proposed.Length - 1];
1391                                 // copy values before removed column
1392                                 if (columnIndex > 0)
1393                                         Array.Copy (proposed, 0, tmp, 0, columnIndex);
1394                                 // copy values after removed column
1395                                 if(columnIndex < proposed.Length - 1)
1396                                         Array.Copy(proposed, columnIndex + 1, tmp, columnIndex, proposed.Length - 1 - columnIndex);
1397
1398                                 proposed = tmp;
1399                         }
1400                         if (original != null)
1401                         {
1402                                 tmp = new object[original.Length - 1];
1403                                 // copy values before removed column
1404                                 if (columnIndex > 0)
1405                                         Array.Copy (original, 0, tmp, 0, columnIndex);
1406                                 // copy values after removed column
1407                                 if(columnIndex < original.Length - 1)
1408                                         Array.Copy(original, columnIndex + 1, tmp, columnIndex, original.Length - 1 - columnIndex);
1409
1410                                 original = tmp;
1411                         }
1412                 }
1413
1414                 internal bool IsRowChanged(DataRowState rowState) {
1415                         if((RowState & rowState) != 0)
1416                                 return true;
1417
1418                         //we need to find if child rows of this row changed.
1419                         //if yes - we should return true
1420
1421                         // if the rowState is deleted we should get the original version of the row
1422                         // else - we should get the current version of the row.
1423                         DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
1424                         int count = Table.ChildRelations.Count;
1425                         for (int i = 0; i < count; i++){
1426                                 DataRelation rel = Table.ChildRelations[i];
1427                                 DataRow[] childRows = GetChildRows(rel, version);
1428                                 for (int j = 0; j < childRows.Length; j++){
1429                                         if (childRows[j].IsRowChanged(rowState))
1430                                                 return true;
1431                                 }
1432                         }
1433
1434                         return false;
1435                 }
1436
1437                 internal bool HasParentCollection
1438                 {
1439                         get
1440                         {
1441                                 return _hasParentCollection;
1442                         }
1443                         set
1444                         {
1445                                 _hasParentCollection = value;
1446                         }
1447                 }
1448
1449                 // checks existance of value in version pecified
1450                 // note : this one relies on the same algorithm as this[int,DataRowVersion]
1451                 private bool CanAccess(int columnIndex, DataRowVersion version) 
1452                 {
1453                         if (columnIndex < 0 || columnIndex > _table.Columns.Count)
1454                                 return false;
1455                         // Accessing deleted rows
1456                         if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
1457                                 return false;
1458
1459                         if (HasVersion(version))
1460                         {
1461                                 object[] versionArr;
1462                                 switch (version) 
1463                                 {
1464                                         case DataRowVersion.Default:
1465                                                 if (editing || rowState == DataRowState.Detached) {
1466                                                         versionArr = proposed;
1467                                                 }
1468                                                 else {
1469                                                         versionArr = current;
1470                                                 }
1471                                                 break;
1472                                         case DataRowVersion.Proposed:
1473                                                 versionArr = proposed;
1474                                                 break;
1475                                         case DataRowVersion.Current:
1476                                                 versionArr = current;
1477                                                 break;
1478                                         case DataRowVersion.Original:
1479                                                 versionArr = original;
1480                                                 break;
1481                                         default:
1482                                                 return false;
1483                                 }
1484
1485                                 return (versionArr != null && columnIndex < versionArr.Length);
1486                         }
1487                         
1488                         return false;
1489                 }
1490
1491                 internal void CheckNullConstraints()
1492                 {
1493                         if (_nullConstraintViolation) {
1494                                 if (proposed != null) {
1495                                         for (int i = 0; i < proposed.Length; i++) {
1496                                                 if (this[i] == DBNull.Value && !_table.Columns[i].AllowDBNull)
1497                                                         throw new NoNullAllowedException(_nullConstraintMessage);
1498                                         }
1499                                 }
1500                                 _nullConstraintViolation = false;
1501                         }
1502                 }
1503                 
1504                 internal void CheckReadOnlyStatus()
1505                 {
1506                         if (proposed == null)
1507                                 return;
1508
1509                         for (int i = 0; i < proposed.Length; i++) {
1510                                 if (this[i] != proposed[i] && _table.Columns[i].ReadOnly)
1511                                         throw new ReadOnlyException();
1512                         }
1513                            
1514                 }
1515         
1516                 #endregion // Methods
1517         }
1518
1519         
1520
1521 }