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