**** Merged r40732-r40872 from MCS ****
[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                                 if (rowState == DataRowState.Detached && version == DataRowVersion.Default && _proposed < 0)
255                                         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.");
256                                 
257                                 int recordIndex = IndexFromVersion(version);
258
259                                 if (recordIndex >= 0) {
260                                         return column[recordIndex];
261                                 }
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                 internal bool IsEditing {
314                         get { return editing; }
315                 }
316
317                 /// <summary>
318                 /// Gets the current state of the row in regards to its relationship to the
319                 /// DataRowCollection.
320                 /// </summary>
321                 public DataRowState RowState {
322                         get { 
323                                 return rowState; 
324                         }
325                 }
326
327                 /// <summary>
328                 /// Gets the DataTable for which this row has a schema.
329                 /// </summary>
330                 public DataTable Table {
331                         get { 
332                                 return _table; 
333                         }
334                 }
335
336                 /// <summary>
337                 /// Gets and sets index of row. This is used from 
338                 /// XmlDataDocument.
339                 // </summary>
340                 internal int XmlRowID {
341                         get { 
342                                 return xmlRowID; 
343                         }
344                         set { 
345                                 xmlRowID = value; 
346                         }
347                 }
348                 
349                 /// <summary>
350                 /// Gets and sets index of row.
351                 // </summary>
352                 internal int RowID {
353                         get { 
354                                 return _rowId; 
355                         }
356                         set { 
357                                 _rowId = value; 
358                         }
359                 }
360
361                 #endregion
362
363                 #region Methods
364
365                 //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
366                 //to a Datatable so I added this method. Delete if there is a better way.
367                 internal void AttachRow() {
368                         if (_current >= 0) {
369                                 Table.RecordCache.DisposeRecord(_current);
370                         }
371                         _current = _proposed;
372                         _proposed = -1;
373                         rowState = DataRowState.Added;
374                 }
375
376                 //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
377                 //from a Datatable so I added this method. Delete if there is a better way.
378                 internal void DetachRow() {
379                         if (_proposed >= 0) {
380                                 _table.RecordCache.DisposeRecord(_proposed);
381                                 _proposed = -1;
382                         }
383                         _rowId = -1;
384                         _hasParentCollection = false;
385                         rowState = DataRowState.Detached;
386                 }
387
388                 private void CheckValue (object v, DataColumn col) 
389                 {               
390                         if (_hasParentCollection && col.ReadOnly) {
391                                 throw new ReadOnlyException ();
392                         }
393
394                         if (v == null || v == DBNull.Value) {
395                                 if (col.AllowDBNull || col.AutoIncrement || col.DefaultValue != DBNull.Value) {
396                                         return;
397                                 }
398
399                                 //Constraint violations during data load is raise in DataTable EndLoad
400                                 this._nullConstraintViolation = true;
401                                 if (this.Table._duringDataLoad) {
402                                         this.Table._nullConstraintViolationDuringDataLoad = true;
403                                 }
404                                 _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
405                         
406                         }
407                 }
408
409                 internal void SetValuesFromDataRecord(IDataRecord record, int[] mapping)
410                 {
411                         if ( mapping.Length > Table.Columns.Count)
412                                 throw new ArgumentException ();
413
414 //                      bool orginalEditing = editing;
415 //                      if (!orginalEditing) { 
416 //                              BeginEdit ();
417 //                      }
418                         
419                         if (!HasVersion(DataRowVersion.Proposed)) {
420                                 _proposed = Table.RecordCache.NewRecord();
421                         }
422
423                         try {
424                                 for(int i=0; i < mapping.Length; i++) {
425                                         DataColumn column = Table.Columns[i];
426                                         column.DataContainer.SetItemFromDataRecord(_proposed, record,mapping[i]);
427                                         if ( column.AutoIncrement ) { 
428                                                 column.UpdateAutoIncrementValue(column.DataContainer.GetInt64(_proposed));
429                                         }
430                                 }
431                         }
432                         catch (Exception e){
433                                 Table.RecordCache.DisposeRecord(_proposed);
434                                 _proposed = -1;
435                                 throw e;
436                         }
437
438 //                      if (!orginalEditing) {
439 //                              EndEdit ();
440 //                      }
441                 }
442
443                 /// <summary>
444                 /// Gets or sets the custom error description for a row.
445                 /// </summary>
446                 public string RowError {
447                         get { 
448                                 return rowError; 
449                         }
450                         set { 
451                                 rowError = value; 
452                         }
453                 }
454
455                 internal int IndexFromVersion(DataRowVersion version)
456                 {
457                         if (HasVersion(version))
458                         {
459                                 int recordIndex;
460                                 switch (version) {
461                                         case DataRowVersion.Default:
462                                                 if (editing || rowState == DataRowState.Detached) {
463                                                         recordIndex = _proposed;
464                                                 }
465                                                 else {
466                                                         recordIndex = _current;
467                                                 }
468                                                 break;
469                                         case DataRowVersion.Proposed:
470                                                 recordIndex = _proposed;
471                                                 break;
472                                         case DataRowVersion.Current:
473                                                 recordIndex = _current;
474                                                 break;
475                                         case DataRowVersion.Original:
476                                                 recordIndex = _original;
477                                                 break;
478                                         default:
479                                                 throw new ArgumentException ();
480                                 }
481                                 return recordIndex;
482                         }
483                         return -1;
484                 }
485
486                 internal XmlDataDocument.XmlDataElement DataElement {
487                         get { return mappedElement; }
488                         set { mappedElement = value; }
489                 }
490
491                 internal void SetOriginalValue (string columnName, object val)
492                 {
493                         DataColumn column = _table.Columns[columnName];
494                         _table.ChangingDataColumn (this, column, val);
495                                 
496                         if (_original < 0 || _original == _current) { 
497                                 // This really creates a new record version if one does not exist
498                                 _original = Table.RecordCache.NewRecord();
499                         }
500                         CheckValue (val, column);
501                         column[_original] = val;
502                         rowState = DataRowState.Modified;
503                 }
504
505                 /// <summary>
506                 /// Commits all the changes made to this row since the last time AcceptChanges was
507                 /// called.
508                 /// </summary>
509                 public void AcceptChanges () 
510                 {
511                         EndEdit(); // in case it hasn't been called
512                         switch (rowState) {
513                                 case DataRowState.Unchanged:
514                                         return;
515                         case DataRowState.Added:
516                         case DataRowState.Modified:
517                                 rowState = DataRowState.Unchanged;
518                                 break;
519                         case DataRowState.Deleted:
520                                 _table.Rows.RemoveInternal (this);
521                                 DetachRow();
522                                 break;
523                         case DataRowState.Detached:
524                                 throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
525                         }
526                         // Accept from detached
527                         if (_original >= 0) {
528                                 Table.RecordCache.DisposeRecord(_original);
529                         }
530                         _original = _current;
531                 }
532
533                 /// <summary>
534                 /// Begins an edit operation on a DataRow object.
535                 /// </summary>
536                 public void BeginEdit () 
537                 {
538                         if (_inChangingEvent)
539                                 throw new InRowChangingEventException("Cannot call BeginEdit inside an OnRowChanging event.");
540                         if (rowState == DataRowState.Deleted)
541                                 throw new DeletedRowInaccessibleException ();
542                         if (!HasVersion (DataRowVersion.Proposed)) {
543                                 _proposed = Table.RecordCache.NewRecord();
544                                 foreach(DataColumn column in Table.Columns) {
545                                         column.DataContainer.CopyValue(_current,_proposed);
546                                 }
547                         }
548                         // setting editing to true stops validations on the row
549                         editing = true;
550                 }
551
552                 /// <summary>
553                 /// Cancels the current edit on the row.
554                 /// </summary>
555                 public void CancelEdit () 
556                 {
557                          if (_inChangingEvent)
558                                 throw new InRowChangingEventException("Cannot call CancelEdit inside an OnRowChanging event.");
559                         editing = false;
560                         if (HasVersion (DataRowVersion.Proposed)) {
561                                 Table.RecordCache.DisposeRecord(_proposed);
562                                 _proposed = -1;
563                                 if (rowState == DataRowState.Modified) {
564                                     rowState = DataRowState.Unchanged;
565                                 }
566                         }
567                 }
568
569                 /// <summary>
570                 /// Clears the errors for the row, including the RowError and errors set with
571                 /// SetColumnError.
572                 /// </summary>
573                 public void ClearErrors () 
574                 {
575                         rowError = String.Empty;
576                         ColumnErrors.Clear();
577                 }
578
579                 /// <summary>
580                 /// Deletes the DataRow.
581                 /// </summary>
582                 public void Delete () 
583                 {
584                         _table.DeletingDataRow(this, DataRowAction.Delete);
585                         switch (rowState) {
586                         case DataRowState.Added:
587                                 // check what to do with child rows
588                                 CheckChildRows(DataRowAction.Delete);
589                                 _table.DeleteRowFromIndexes (this);
590                                 Table.Rows.RemoveInternal (this);
591
592                                 // if row was in Added state we move it to Detached.
593                                 DetachRow();
594                                 break;
595                         case DataRowState.Deleted:
596                                 break;          
597                         default:
598                                 // check what to do with child rows
599                                 CheckChildRows(DataRowAction.Delete);
600                                 _table.DeleteRowFromIndexes (this);
601                                 rowState = DataRowState.Deleted;
602                                 break;
603                         }
604                         _table.DeletedDataRow(this, DataRowAction.Delete);
605                 }
606
607                 // check the child rows of this row before deleting the row.
608                 private void CheckChildRows(DataRowAction action)
609                 {
610                         
611                         // in this method we find the row that this row is in a relation with them.
612                         // in shortly we find all child rows of this row.
613                         // then we function according to the DeleteRule of the foriegnkey.
614
615
616                         // 1. find if this row is attached to dataset.
617                         // 2. find if EnforceConstraints is true.
618                         // 3. find if there are any constraint on the table that the row is in.
619                         if (_table.DataSet != null && _table.DataSet.EnforceConstraints && _table.Constraints.Count > 0)
620                         {
621                                 foreach (DataTable table in _table.DataSet.Tables)
622                                 {
623                                         // loop on all ForeignKeyConstrain of the table.
624                                         foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
625                                         {
626                                                 if (fk.RelatedTable == _table)
627                                                 {
628                                                         Rule rule;
629                                                         if (action == DataRowAction.Delete)
630                                                                 rule = fk.DeleteRule;
631                                                         else
632                                                                 rule = fk.UpdateRule;
633                                                         CheckChildRows(fk, action, rule);
634                                                 }                       
635                                         }
636                                 }
637                         }
638                 }
639
640                 private void CheckChildRows(ForeignKeyConstraint fkc, DataRowAction action, Rule rule)
641                 {                               
642                         DataRow[] childRows = GetChildRows(fkc, DataRowVersion.Default);
643                         switch (rule)
644                         {
645                                 case Rule.Cascade:  // delete or change all relted rows.
646                                         if (childRows != null)
647                                         {
648                                                 for (int j = 0; j < childRows.Length; j++)
649                                                 {
650                                                         // if action is delete we delete all child rows
651                                                         if (action == DataRowAction.Delete)
652                                                         {
653                                                                 if (childRows[j].RowState != DataRowState.Deleted)
654                                                                         childRows[j].Delete();
655                                                         }
656                                                         // if action is change we change the values in the child row
657                                                         else if (action == DataRowAction.Change)
658                                                         {
659                                                                 // change only the values in the key columns
660                                                                 // set the childcolumn value to the new parent row value
661                                                                 for (int k = 0; k < fkc.Columns.Length; k++)
662                                                                         childRows[j][fkc.Columns[k]] = this[fkc.RelatedColumns[k], DataRowVersion.Proposed];
663                                                         }
664                                                 }
665                                         }
666                                         break;
667                                 case Rule.None: // throw an exception if there are any child rows.
668                                         if (childRows != null)
669                                         {
670                                                 for (int j = 0; j < childRows.Length; j++)
671                                                 {
672                                                         if (childRows[j].RowState != DataRowState.Deleted)
673                                                         {
674                                                                 string changeStr = "Cannot change this row because constraints are enforced on relation " + fkc.ConstraintName +", and changing this row will strand child rows.";
675                                                                 string delStr = "Cannot delete this row because constraints are enforced on relation " + fkc.ConstraintName +", and deleting this row will strand child rows.";
676                                                                 string message = action == DataRowAction.Delete ? delStr : changeStr;
677                                                                 throw new InvalidConstraintException(message);
678                                                         }
679                                                 }
680                                         }
681                                         break;
682                                 case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
683                                         if (childRows != null && childRows.Length > 0) {
684                                                 int defaultValuesRowIndex = childRows[0].Table.DefaultValuesRowIndex;
685                                                 foreach(DataRow childRow in childRows) {
686                                                         if (childRow.RowState != DataRowState.Deleted) {
687                                                                 int defaultIdx = childRow.IndexFromVersion(DataRowVersion.Default);
688                                                                 foreach(DataColumn column in fkc.Columns) {
689                                                                         column.DataContainer.CopyValue(defaultValuesRowIndex,defaultIdx);
690                                                                 }
691                                                         }
692                                                 }
693                                         }
694                                         break;
695                                 case Rule.SetNull: // set the values in the child row to null.
696                                         if (childRows != null)
697                                         {
698                                                 for (int j = 0; j < childRows.Length; j++)
699                                                 {
700                                                         DataRow child = childRows[j];
701                                                         if (childRows[j].RowState != DataRowState.Deleted)
702                                                         {
703                                                                 // set only the key columns to DBNull
704                                                                 for (int k = 0; k < fkc.Columns.Length; k++)
705                                                                         child.SetNull(fkc.Columns[k]);
706                                                         }
707                                                 }
708                                         }
709                                         break;
710                         }
711
712                 }
713
714                 /// <summary>
715                 /// Ends the edit occurring on the row.
716                 /// </summary>
717                 public void EndEdit () 
718                 {
719                         if (_inChangingEvent)
720                                 throw new InRowChangingEventException("Cannot call EndEdit inside an OnRowChanging event.");
721                         if (rowState == DataRowState.Detached)
722                         {
723                                 editing = false;
724                                 return;
725                         }
726                         
727                         CheckReadOnlyStatus();
728                         if (HasVersion (DataRowVersion.Proposed))
729                         {
730                                 _inChangingEvent = true;
731                                 try
732                                 {
733                                         _table.ChangingDataRow(this, DataRowAction.Change);
734                                 }
735                                 finally
736                                 {
737                                         _inChangingEvent = false;
738                                 }
739                                 if (rowState == DataRowState.Unchanged)
740                                         rowState = DataRowState.Modified;
741                                 
742                                 //Calling next method validates UniqueConstraints
743                                 //and ForeignKeys.
744                                 try
745                                 {
746                                         if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
747                                                 _table.Rows.ValidateDataRowInternal(this);
748                                 }
749                                 catch (Exception e)
750                                 {
751                                         editing = false;
752                                         Table.RecordCache.DisposeRecord(_proposed);
753                                         _proposed = -1;
754                                         throw e;
755                                 }
756
757                                 // Now we are going to check all child rows of current row.
758                                 // In the case the cascade is true the child rows will look up for
759                                 // parent row. since lookup in index is always on current,
760                                 // we have to move proposed version of current row to current
761                                 // in the case of check child row failure we are rolling 
762                                 // current row state back.
763                                 int backup = _current;
764                                 _current = _proposed;
765                                 bool editing_backup = editing;
766                                 editing = false;
767                                 try {
768                                         // check all child rows.
769                                         CheckChildRows(DataRowAction.Change);
770                                         _proposed = -1;
771                                         if (_original != backup) {
772                                                 Table.RecordCache.DisposeRecord(backup);
773                                         }
774                                 }
775                                 catch (Exception ex) {
776                                         // if check child rows failed - rollback to previous state
777                                         // i.e. restore proposed and current versions
778                                         _proposed = _current;
779                                         _current = backup;
780                                         editing = editing_backup;
781                                         // since we failed - propagate an exception
782                                         throw ex;
783                                 }
784                                 _table.ChangedDataRow(this, DataRowAction.Change);
785                         }
786                 }
787
788                 /// <summary>
789                 /// Gets the child rows of this DataRow using the specified DataRelation.
790                 /// </summary>
791                 public DataRow[] GetChildRows (DataRelation relation) 
792                 {
793                         return GetChildRows (relation, DataRowVersion.Default);
794                 }
795
796                 /// <summary>
797                 /// Gets the child rows of a DataRow using the specified RelationName of a
798                 /// DataRelation.
799                 /// </summary>
800                 public DataRow[] GetChildRows (string relationName) 
801                 {
802                         return GetChildRows (Table.DataSet.Relations[relationName]);
803                 }
804
805                 /// <summary>
806                 /// Gets the child rows of a DataRow using the specified DataRelation, and
807                 /// DataRowVersion.
808                 /// </summary>
809                 public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version) 
810                 {
811                         if (relation == null)
812                                 return new DataRow[0];
813
814                         //if (this.Table == null || RowState == DataRowState.Detached)
815                         if (this.Table == null)
816                                 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.");
817
818                         if (relation.DataSet != this.Table.DataSet)
819                                 throw new ArgumentException();
820
821                         if (_table != relation.ParentTable)
822                                 throw new InvalidConstraintException ("GetChildRow requires a row whose Table is " + relation.ParentTable + ", but the specified row's table is " + _table);
823
824                         if (relation.ChildKeyConstraint != null)
825                                 return GetChildRows (relation.ChildKeyConstraint, version);
826
827                         ArrayList rows = new ArrayList();
828                         DataColumn[] parentColumns = relation.ParentColumns;
829                         DataColumn[] childColumns = relation.ChildColumns;
830                         int numColumn = parentColumns.Length;
831                         if (HasVersion(version))
832                         {
833                                 object[] vals = new object[parentColumns.Length];
834                                 for (int i = 0; i < vals.Length; i++)
835                                         vals[i] = this[parentColumns[i], version];
836                                 
837                                 foreach (DataRow row in relation.ChildTable.Rows) 
838                                 {
839                                         bool allColumnsMatch = false;
840                                         if (row.HasVersion(DataRowVersion.Default))
841                                         {
842                                                 allColumnsMatch = true;
843                                                 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
844                                                 {
845                                                         if (!vals[columnCnt].Equals(
846                                                                 row[childColumns[columnCnt], DataRowVersion.Default])) 
847                                                         {
848                                                                 allColumnsMatch = false;
849                                                                 break;
850                                                         }
851                                                 }
852                                         }
853                                         if (allColumnsMatch) rows.Add(row);
854                                 }
855                         }else
856                                 throw new VersionNotFoundException("There is no " + version + " data to accces.");
857
858                         DataRow[] result = relation.ChildTable.NewRowArray(rows.Count);
859                         rows.CopyTo(result, 0);
860                         return result;
861                 }
862
863                 /// <summary>
864                 /// Gets the child rows of a DataRow using the specified RelationName of a
865                 /// DataRelation, and DataRowVersion.
866                 /// </summary>
867                 public DataRow[] GetChildRows (string relationName, DataRowVersion version) 
868                 {
869                         return GetChildRows (Table.DataSet.Relations[relationName], version);
870                 }
871
872                 private DataRow[] GetChildRows (ForeignKeyConstraint fkc, DataRowVersion version) 
873                 {
874                         ArrayList rows = new ArrayList();
875                         DataColumn[] parentColumns = fkc.RelatedColumns;
876                         DataColumn[] childColumns = fkc.Columns;
877                         int numColumn = parentColumns.Length;
878                         if (HasVersion(version)) {
879                                 Index index = fkc.Index;
880                                 if (index != null) {
881                                         // get the child rows from the index
882                                         Node[] childNodes = index.FindAllSimple (parentColumns, IndexFromVersion(version));
883                                         for (int i = 0; i < childNodes.Length; i++) {
884                                                 rows.Add (childNodes[i].Row);
885                                         }
886                                 }
887                                 else { // if there is no index we search manualy.
888                                         int curIndex = IndexFromVersion(DataRowVersion.Default);
889                                         int tmpRecord = fkc.Table.RecordCache.NewRecord();
890
891                                         try {
892                                                 for (int i = 0; i < numColumn; i++) {
893                                                         // according to MSDN: the DataType value for both columns must be identical.
894                                                         childColumns[i].DataContainer.CopyValue(parentColumns[i].DataContainer, curIndex, tmpRecord);
895                                                 }
896
897                                                 foreach (DataRow row in fkc.Table.Rows) {
898                                                         bool allColumnsMatch = false;
899                                                         if (row.HasVersion(DataRowVersion.Default)) {
900                                                                 allColumnsMatch = true;
901                                                                 int childIndex = row.IndexFromVersion(DataRowVersion.Default);
902                                                                 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) {
903                                                                         if (childColumns[columnCnt].DataContainer.CompareValues(childIndex, tmpRecord) != 0) {
904                                                                                 allColumnsMatch = false;
905                                                                                 break;
906                                                                         }
907                                                                 }
908                                                         }
909                                                         if (allColumnsMatch) {
910                                                                 rows.Add(row);
911                                                         }
912                                                 }
913                                         }
914                                         finally {
915                                                 fkc.Table.RecordCache.DisposeRecord(tmpRecord);
916                                         }
917                                 }
918                         }else
919                                 throw new VersionNotFoundException("There is no " + version + " data to accces.");
920
921                         DataRow[] result = fkc.Table.NewRowArray(rows.Count);
922                         rows.CopyTo(result, 0);
923                         return result;
924                 }
925
926                 /// <summary>
927                 /// Gets the error description of the specified DataColumn.
928                 /// </summary>
929                 public string GetColumnError (DataColumn column) 
930                 {
931                         return GetColumnError (_table.Columns.IndexOf(column));
932                 }
933
934                 /// <summary>
935                 /// Gets the error description for the column specified by index.
936                 /// </summary>
937                 public string GetColumnError (int columnIndex) 
938                 {
939                         if (columnIndex < 0 || columnIndex >= Table.Columns.Count)
940                                 throw new IndexOutOfRangeException ();
941
942                         string retVal = null;
943                         if (columnIndex < ColumnErrors.Count) {
944                                 retVal = (String) ColumnErrors[columnIndex];
945                         }
946                         return (retVal != null) ? retVal : String.Empty;
947                 }
948
949                 /// <summary>
950                 /// Gets the error description for the column, specified by name.
951                 /// </summary>
952                 public string GetColumnError (string columnName) 
953                 {
954                         return GetColumnError (_table.Columns.IndexOf(columnName));
955                 }
956
957                 /// <summary>
958                 /// Gets an array of columns that have errors.
959                 /// </summary>
960                 public DataColumn[] GetColumnsInError () 
961                 {
962                         ArrayList dataColumns = new ArrayList ();
963
964                         int columnOrdinal = 0;
965                         foreach(String columnError in ColumnErrors) {
966                                 if (columnError != null && columnError != String.Empty) {
967                                         dataColumns.Add (_table.Columns[columnOrdinal]);
968                                 }
969                                 columnOrdinal++;
970                         }
971
972                         return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
973                 }
974
975                 /// <summary>
976                 /// Gets the parent row of a DataRow using the specified DataRelation.
977                 /// </summary>
978                 public DataRow GetParentRow (DataRelation relation) 
979                 {
980                         return GetParentRow (relation, DataRowVersion.Default);
981                 }
982
983                 /// <summary>
984                 /// Gets the parent row of a DataRow using the specified RelationName of a
985                 /// DataRelation.
986                 /// </summary>
987                 public DataRow GetParentRow (string relationName) 
988                 {
989                         return GetParentRow (relationName, DataRowVersion.Default);
990                 }
991
992                 /// <summary>
993                 /// Gets the parent row of a DataRow using the specified DataRelation, and
994                 /// DataRowVersion.
995                 /// </summary>
996                 public DataRow GetParentRow (DataRelation relation, DataRowVersion version) 
997                 {
998                         DataRow[] rows = GetParentRows(relation, version);
999                         if (rows.Length == 0) return null;
1000                         return rows[0];
1001                 }
1002
1003                 /// <summary>
1004                 /// Gets the parent row of a DataRow using the specified RelationName of a 
1005                 /// DataRelation, and DataRowVersion.
1006                 /// </summary>
1007                 public DataRow GetParentRow (string relationName, DataRowVersion version) 
1008                 {
1009                         return GetParentRow (Table.DataSet.Relations[relationName], version);
1010                 }
1011
1012                 /// <summary>
1013                 /// Gets the parent rows of a DataRow using the specified DataRelation.
1014                 /// </summary>
1015                 public DataRow[] GetParentRows (DataRelation relation) 
1016                 {
1017                         return GetParentRows (relation, DataRowVersion.Default);
1018                 }
1019
1020                 /// <summary>
1021                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
1022                 /// DataRelation.
1023                 /// </summary>
1024                 public DataRow[] GetParentRows (string relationName) 
1025                 {
1026                         return GetParentRows (relationName, DataRowVersion.Default);
1027                 }
1028
1029                 /// <summary>
1030                 /// Gets the parent rows of a DataRow using the specified DataRelation, and
1031                 /// DataRowVersion.
1032                 /// </summary>
1033                 public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version) 
1034                 {
1035                         // TODO: Caching for better preformance
1036                         if (relation == null)
1037                                 return new DataRow[0];
1038
1039                         if (this.Table == null)
1040                                 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.");
1041
1042                         if (relation.DataSet != this.Table.DataSet)
1043                                 throw new ArgumentException();
1044
1045                         if (_table != relation.ChildTable)
1046                                 throw new InvalidConstraintException ("GetParentRows requires a row whose Table is " + relation.ChildTable + ", but the specified row's table is " + _table);
1047
1048                         ArrayList rows = new ArrayList();
1049                         DataColumn[] parentColumns = relation.ParentColumns;
1050                         DataColumn[] childColumns = relation.ChildColumns;
1051                         int numColumn = parentColumns.Length;
1052                         if (HasVersion(version)) {
1053                                 Index indx = relation.ParentTable.GetIndexByColumns (parentColumns);
1054                                 if (indx != null && 
1055     (Table == null || Table.DataSet == null || 
1056      Table.DataSet.EnforceConstraints)) { // get the child rows from the index
1057                                         Node[] childNodes = indx.FindAllSimple(childColumns, IndexFromVersion(version));
1058                                         for (int i = 0; i < childNodes.Length; i++) {
1059                                                 rows.Add (childNodes[i].Row);
1060                                         }
1061                                 }
1062                                 else { // no index so we have to search manualy.
1063                                         int curIndex = IndexFromVersion(DataRowVersion.Default);
1064                                         int tmpRecord = relation.ParentTable.RecordCache.NewRecord();
1065                                         try {
1066                                                 for (int i = 0; i < numColumn; i++) {
1067                                                         // according to MSDN: the DataType value for both columns must be identical.
1068                                                         parentColumns[i].DataContainer.CopyValue(childColumns[i].DataContainer, curIndex, tmpRecord);
1069                                                 }
1070
1071                                                 foreach (DataRow row in relation.ParentTable.Rows) {
1072                                                         bool allColumnsMatch = false;
1073                                                         if (row.HasVersion(DataRowVersion.Default)) {
1074                                                                 allColumnsMatch = true;
1075                                                                 int parentIndex = row.IndexFromVersion(DataRowVersion.Default);
1076                                                                 for (int columnCnt = 0; columnCnt < numColumn; columnCnt++) {
1077                                                                         if (parentColumns[columnCnt].DataContainer.CompareValues(parentIndex, tmpRecord) != 0) {
1078                                                                                 allColumnsMatch = false;
1079                                                                                 break;
1080                                                                         }
1081                                                                 }
1082                                                         }
1083                                                         if (allColumnsMatch) {
1084                                                                 rows.Add(row);
1085                                                         }
1086                                                 }
1087                                         }
1088                                         finally {
1089                                                 relation.ParentTable.RecordCache.DisposeRecord(tmpRecord);
1090                                         }
1091                                 }
1092                         }else
1093                                 throw new VersionNotFoundException("There is no " + version + " data to accces.");
1094
1095                         DataRow[] result = relation.ParentTable.NewRowArray(rows.Count);
1096                         rows.CopyTo(result, 0);
1097                         return result;
1098                 }
1099
1100                 /// <summary>
1101                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
1102                 /// DataRelation, and DataRowVersion.
1103                 /// </summary>
1104                 public DataRow[] GetParentRows (string relationName, DataRowVersion version) 
1105                 {
1106                         return GetParentRows (Table.DataSet.Relations[relationName], version);
1107                 }
1108
1109                 /// <summary>
1110                 /// Gets a value indicating whether a specified version exists.
1111                 /// </summary>
1112                 public bool HasVersion (DataRowVersion version) 
1113                 {
1114                         switch (version) {
1115                                 case DataRowVersion.Default:
1116                                         if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
1117                                                 return false;
1118                                         if (rowState == DataRowState.Detached)
1119                                                 return _proposed >= 0;
1120                                         return true;
1121                                 case DataRowVersion.Proposed:
1122                                         if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
1123                                                 return false;
1124                                         return _proposed >= 0;
1125                                 case DataRowVersion.Current:
1126                                         if ((rowState == DataRowState.Deleted && !_inExpressionEvaluation) || rowState == DataRowState.Detached)
1127                                                 return false;
1128                                         return _current >= 0;
1129                                 case DataRowVersion.Original:
1130                                         if (rowState == DataRowState.Detached)
1131                                                 return false;
1132                                         return _original >= 0;
1133                         }
1134                         return false;
1135                 }
1136
1137                 /// <summary>
1138                 /// Gets a value indicating whether the specified DataColumn contains a null value.
1139                 /// </summary>
1140                 public bool IsNull (DataColumn column) 
1141                 {
1142                         return IsNull(column, DataRowVersion.Default);
1143                 }
1144
1145                 /// <summary>
1146                 /// Gets a value indicating whether the column at the specified index contains a null
1147                 /// value.
1148                 /// </summary>
1149                 public bool IsNull (int columnIndex) 
1150                 {
1151                         return IsNull(Table.Columns[columnIndex]);
1152                 }
1153
1154                 /// <summary>
1155                 /// Gets a value indicating whether the named column contains a null value.
1156                 /// </summary>
1157                 public bool IsNull (string columnName) 
1158                 {
1159                         return IsNull(Table.Columns[columnName]);
1160                 }
1161
1162                 /// <summary>
1163                 /// Gets a value indicating whether the specified DataColumn and DataRowVersion
1164                 /// contains a null value.
1165                 /// </summary>
1166                 public bool IsNull (DataColumn column, DataRowVersion version) 
1167                 {
1168                         return column.DataContainer.IsNull(IndexFromVersion(version));
1169                 }
1170
1171                 /// <summary>
1172                 /// Returns a value indicating whether all of the row columns specified contain a null value.
1173                 /// </summary>
1174                 internal bool IsNullColumns(DataColumn[] columns)
1175                 {
1176                         bool allNull = true;
1177                         for (int i = 0; i < columns.Length; i++) 
1178                         {
1179                                 if (!IsNull(columns[i])) 
1180                                 {
1181                                         allNull = false;
1182                                         break;
1183                                 }
1184                         }
1185                         return allNull;
1186                 }
1187
1188                 /// <summary>
1189                 /// Rejects all changes made to the row since AcceptChanges was last called.
1190                 /// </summary>
1191                 public void RejectChanges () 
1192                 {
1193                         if (RowState == DataRowState.Detached)
1194                                 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.");
1195                         // If original is null, then nothing has happened since AcceptChanges
1196                         // was last called.  We have no "original" to go back to.
1197                         if (HasVersion(DataRowVersion.Original)) {
1198                                 if (_current >= 0 ) {
1199                                         Table.RecordCache.DisposeRecord(_current);
1200                                 }
1201                                 _current = _original;
1202                                
1203                                 _table.ChangedDataRow (this, DataRowAction.Rollback);
1204                                 CancelEdit ();
1205                                 switch (rowState)
1206                                 {
1207                                         case DataRowState.Added:
1208                                                 _table.DeleteRowFromIndexes (this);
1209                                                 _table.Rows.RemoveInternal (this);
1210                                                 break;
1211                                         case DataRowState.Modified:
1212                                                 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1213                                                         _table.Rows.ValidateDataRowInternal(this);
1214                                                 rowState = DataRowState.Unchanged;
1215                                                 break;
1216                                         case DataRowState.Deleted:
1217                                                 rowState = DataRowState.Unchanged;
1218                                                 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1219                                                         _table.Rows.ValidateDataRowInternal(this);
1220                                                 break;
1221                                 } 
1222                                 
1223                         }                       
1224                         else {
1225                                 // If rows are just loaded via Xml the original values are null.
1226                                 // So in this case we have to remove all columns.
1227                                 // FIXME: I'm not realy sure, does this break something else, but
1228                                 // if so: FIXME ;)
1229                                 
1230                                 if ((rowState & DataRowState.Added) > 0)
1231                                 {
1232                                         _table.DeleteRowFromIndexes (this);
1233                                         _table.Rows.RemoveInternal (this);
1234                                         // if row was in Added state we move it to Detached.
1235                                         DetachRow();
1236                                 }
1237                         }
1238                 }
1239
1240                 /// <summary>
1241                 /// Sets the error description for a column specified as a DataColumn.
1242                 /// </summary>
1243                 public void SetColumnError (DataColumn column, string error) 
1244                 {
1245                         SetColumnError (_table.Columns.IndexOf (column), error);
1246                 }
1247
1248                 /// <summary>
1249                 /// Sets the error description for a column specified by index.
1250                 /// </summary>
1251                 public void SetColumnError (int columnIndex, string error) 
1252                 {
1253                         if (columnIndex < 0 || columnIndex >= Table.Columns.Count)
1254                                 throw new IndexOutOfRangeException ();
1255
1256                         while(ColumnErrors.Count < columnIndex) {
1257                                 ColumnErrors.Add(null);
1258                         }
1259                         ColumnErrors.Add(error);
1260                 }
1261
1262                 /// <summary>
1263                 /// Sets the error description for a column specified by name.
1264                 /// </summary>
1265                 public void SetColumnError (string columnName, string error) 
1266                 {
1267                         SetColumnError (_table.Columns.IndexOf (columnName), error);
1268                 }
1269
1270                 /// <summary>
1271                 /// Sets the value of the specified DataColumn to a null value.
1272                 /// </summary>
1273                 protected void SetNull (DataColumn column) 
1274                 {
1275                         this[column] = DBNull.Value;
1276                 }
1277
1278                 /// <summary>
1279                 /// Sets the parent row of a DataRow with specified new parent DataRow.
1280                 /// </summary>
1281                 public void SetParentRow (DataRow parentRow) 
1282                 {
1283                         SetParentRow(parentRow, null);
1284                 }
1285
1286                 /// <summary>
1287                 /// Sets the parent row of a DataRow with specified new parent DataRow and
1288                 /// DataRelation.
1289                 /// </summary>
1290                 public void SetParentRow (DataRow parentRow, DataRelation relation) 
1291                 {
1292                         if (_table == null || parentRow.Table == null)
1293                                 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.");
1294
1295                         if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
1296                                 throw new ArgumentException();
1297                         
1298                         BeginEdit();
1299                         if (relation == null)
1300                         {
1301                                 foreach (DataRelation parentRel in _table.ParentRelations)
1302                                 {
1303                                         DataColumn[] childCols = parentRel.ChildColumns;
1304                                         DataColumn[] parentCols = parentRel.ParentColumns;
1305                                         
1306                                         for (int i = 0; i < parentCols.Length; i++)
1307                                         {
1308                                                 if (parentRow == null)
1309                                                         this[childCols[i].Ordinal] = DBNull.Value;
1310                                                 else
1311                                                         this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1312                                         }
1313                                         
1314                                 }
1315                         }
1316                         else
1317                         {
1318                                 DataColumn[] childCols = relation.ChildColumns;
1319                                 DataColumn[] parentCols = relation.ParentColumns;
1320                                         
1321                                 for (int i = 0; i < parentCols.Length; i++)
1322                                 {
1323                                         if (parentRow == null)
1324                                                 this[childCols[i].Ordinal] = DBNull.Value;
1325                                         else
1326                                                 this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1327                                 }
1328                         }
1329                         EndEdit();
1330                 }
1331                 
1332                 //Copy all values of this DataaRow to the row parameter.
1333                 internal void CopyValuesToRow(DataRow row)
1334                 {
1335                         if (row == null)
1336                                 throw new ArgumentNullException("row");
1337                         if (row == this)
1338                                 throw new ArgumentException("'row' is the same as this object");
1339
1340                         foreach(DataColumn column in Table.Columns) {
1341                                 DataColumn targetColumn = row.Table.Columns[column.ColumnName];
1342                                 //if a column with the same name exists in both rows copy the values
1343                                 if(targetColumn != null) {
1344                                         int index = targetColumn.Ordinal;
1345                                         if (HasVersion(DataRowVersion.Original)) {
1346                                                 if (row._original < 0) {
1347                                                         row._original = row.Table.RecordCache.NewRecord();
1348                                                 }
1349                                                 object val = column[_original];
1350                                                 row.CheckValue(val, targetColumn);
1351                                                 targetColumn[row._original] = val;
1352                                         }
1353                                         if (HasVersion(DataRowVersion.Current)) {
1354                                                 if (row._current < 0) {
1355                                                         row._current = row.Table.RecordCache.NewRecord();
1356                                                 }
1357                                                 object val = column[_current];
1358                                                 row.CheckValue(val, targetColumn);
1359                                                 targetColumn[row._current] = val;
1360                                         }
1361                                         if (HasVersion(DataRowVersion.Proposed)) {
1362                                                 if (row._proposed < 0) {
1363                                                         row._proposed = row.Table.RecordCache.NewRecord();
1364                                                 }
1365                                                 object val = column[row._proposed];
1366                                                 row.CheckValue(val, targetColumn);
1367                                                 targetColumn[row._proposed] = val;
1368                                         }
1369                                         
1370                                         //Saving the current value as the column value
1371                                         object defaultVal = column [IndexFromVersion (DataRowVersion.Default)];
1372                                         row [index] = defaultVal;
1373                                 }
1374                         }
1375                         CopyState(row);
1376                 }
1377
1378                 // Copy row state - rowState and errors
1379                 internal void CopyState(DataRow row)
1380                 {
1381                         row.rowState = RowState;
1382                         row.RowError = RowError;
1383                         row.ColumnErrors = (ArrayList)ColumnErrors.Clone();
1384                 }
1385
1386                 internal bool IsRowChanged(DataRowState rowState) {
1387                         if((RowState & rowState) != 0)
1388                                 return true;
1389
1390                         //we need to find if child rows of this row changed.
1391                         //if yes - we should return true
1392
1393                         // if the rowState is deleted we should get the original version of the row
1394                         // else - we should get the current version of the row.
1395                         DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
1396                         int count = Table.ChildRelations.Count;
1397                         for (int i = 0; i < count; i++){
1398                                 DataRelation rel = Table.ChildRelations[i];
1399                                 DataRow[] childRows = GetChildRows(rel, version);
1400                                 for (int j = 0; j < childRows.Length; j++){
1401                                         if (childRows[j].IsRowChanged(rowState))
1402                                                 return true;
1403                                 }
1404                         }
1405
1406                         return false;
1407                 }
1408
1409                 internal bool HasParentCollection
1410                 {
1411                         get
1412                         {
1413                                 return _hasParentCollection;
1414                         }
1415                         set
1416                         {
1417                                 _hasParentCollection = value;
1418                         }
1419                 }
1420
1421                 internal void CheckNullConstraints()
1422                 {
1423                         if (_nullConstraintViolation) {
1424                                 if (HasVersion(DataRowVersion.Proposed)) {
1425                                         foreach(DataColumn column in Table.Columns) {
1426                                                 if (IsNull(column) && !column.AllowDBNull) {
1427                                                         throw new NoNullAllowedException(_nullConstraintMessage);
1428                                         }
1429                                 }
1430                                 }
1431                                 _nullConstraintViolation = false;
1432                         }
1433                 }
1434                 
1435                 internal void CheckReadOnlyStatus()
1436                 {
1437                         if (HasVersion(DataRowVersion.Proposed)) {
1438                                 int defaultIdx = IndexFromVersion(DataRowVersion.Default); 
1439                                 foreach(DataColumn column in Table.Columns) {
1440                                         if ((column.DataContainer.CompareValues(defaultIdx,_proposed) != 0) && column.ReadOnly) {
1441                                         throw new ReadOnlyException();
1442                         }
1443                 }
1444                         }                       
1445                 }
1446         
1447                 #endregion // Methods
1448         }
1449
1450         
1451
1452 }