More patches from Eran Domb <erand@mainsoft.com>.
[mono.git] / mcs / class / System.Data / System.Data / DataRow.cs
1 //
2 // System.Data.DataRow.cs
3 //
4 // Author:
5 //   Rodrigo Moya <rodrigo@ximian.com>
6 //   Daniel Morgan <danmorg@sc.rr.com>
7 //   Tim Coleman <tim@timcoleman.com>
8 //   Ville Palo <vi64pa@koti.soon.fi>
9 //   Alan Tam Siu Lung <Tam@SiuLung.com>
10 //
11 // (C) Ximian, Inc 2002
12 // (C) Daniel Morgan 2002, 2003
13 // Copyright (C) 2002 Tim Coleman
14 //
15
16 using System;
17 using System.Collections;
18 using System.Globalization;
19
20 namespace System.Data {
21         /// <summary>
22         /// Represents a row of data in a DataTable.
23         /// </summary>
24         [Serializable]
25         public class DataRow
26         {
27                 #region Fields
28
29                 private DataTable _table;
30
31                 private object[] original;
32                 private object[] proposed;
33                 private object[] current;
34
35                 private string[] columnErrors;
36                 private string rowError;
37                 private DataRowState rowState;
38                 internal int xmlRowID = 0;
39                 private bool editing = false;
40
41                 #endregion
42
43                 #region Constructors
44
45                 /// <summary>
46                 /// This member supports the .NET Framework infrastructure and is not intended to be 
47                 /// used directly from your code.
48                 /// </summary>
49                 protected internal DataRow (DataRowBuilder builder)
50                 {
51                         _table = builder.Table;
52
53                         original = null; 
54                         current = new object[_table.Columns.Count];
55                         // initialize to DBNull.Value
56                         for (int c = 0; c < _table.Columns.Count; c++) {
57                                 current[c] = DBNull.Value;
58                         }
59                         proposed = new object[_table.Columns.Count];
60                         Array.Copy (current, proposed, _table.Columns.Count);
61
62                         columnErrors = new string[_table.Columns.Count];
63                         rowError = String.Empty;
64
65                         //on first creating a DataRow it is always detached.
66                         rowState = DataRowState.Detached;
67
68                         foreach (DataColumn Col in _table.Columns) {
69                                 
70                                 if (Col.AutoIncrement) {
71                                         this [Col] = Col.AutoIncrementValue();
72                                 }
73                         }
74
75                         _table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
76                 }
77
78                 
79                 #endregion
80
81                 #region Properties
82
83                 /// <summary>
84                 /// Gets a value indicating whether there are errors in a row.
85                 /// </summary>
86                 public bool HasErrors {
87                         [MonoTODO]
88                         get {
89                                 throw new NotImplementedException ();
90                         }
91                 }
92
93                 /// <summary>
94                 /// Gets or sets the data stored in the column specified by name.
95                 /// </summary>
96                 public object this[string columnName] {
97                         get { return this[columnName, DataRowVersion.Default]; }
98                         set {
99                                 int columnIndex = _table.Columns.IndexOf (columnName);
100                                 if (columnIndex == -1)
101                                         throw new IndexOutOfRangeException ();
102                                 this[columnIndex] = value;
103                         }
104                 }
105
106                 /// <summary>
107                 /// Gets or sets the data stored in specified DataColumn
108                 /// </summary>
109                 public object this[DataColumn column] {
110                         get { return this[column, DataRowVersion.Default]; } 
111                         set {
112                                 int columnIndex = _table.Columns.IndexOf (column);
113                                 if (columnIndex == -1)
114                                         throw new ArgumentException ("The column does not belong to this table.");
115                                 this[columnIndex] = value;
116                         }
117                 }
118
119                 /// <summary>
120                 /// Gets or sets the data stored in column specified by index.
121                 /// </summary>
122                 public object this[int columnIndex] {
123                         get { return this[columnIndex, DataRowVersion.Default]; }
124                         set {
125                                 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
126                                         throw new IndexOutOfRangeException ();
127                                 if (rowState == DataRowState.Deleted)
128                                         throw new DeletedRowInaccessibleException ();
129                                 DataColumn column = _table.Columns[columnIndex];
130                                 _table.ChangingDataColumn (this, column, value);
131                                 
132                                 bool orginalEditing = editing;
133                                 if (!orginalEditing) BeginEdit ();
134                                 object v = SetColumnValue (value, columnIndex);
135                                 proposed[columnIndex] = v;
136                                 _table.ChangedDataColumn (this, column, v);
137                                 if (!orginalEditing) EndEdit ();
138                         }
139                 }
140
141                 /// <summary>
142                 /// Gets the specified version of data stored in the named column.
143                 /// </summary>
144                 public object this[string columnName, DataRowVersion version] {
145                         get {
146                                 int columnIndex = _table.Columns.IndexOf (columnName);
147                                 if (columnIndex == -1)
148                                         throw new IndexOutOfRangeException ();
149                                 return this[columnIndex, version];
150                         }
151                 }
152
153                 /// <summary>
154                 /// Gets the specified version of data stored in the specified DataColumn.
155                 /// </summary>
156                 public object this[DataColumn column, DataRowVersion version] {
157                         get {
158                                 int columnIndex = _table.Columns.IndexOf (column);
159                                 if (columnIndex == -1)
160                                         throw new ArgumentException ("The column does not belong to this table.");
161                                 return this[columnIndex, version];
162                         }
163                 }
164
165                 /// <summary>
166                 /// Gets the data stored in the column, specified by index and version of the data to
167                 /// retrieve.
168                 /// </summary>
169                 public object this[int columnIndex, DataRowVersion version] {
170                         get {
171                                 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
172                                         throw new IndexOutOfRangeException ();
173                                 // Non-existent version
174                                 if (rowState == DataRowState.Detached && version == DataRowVersion.Current || !HasVersion (version))
175                                         throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
176                                 // Accessing deleted rows
177                                 if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
178                                         throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
179                                 switch (version) {
180                                 case DataRowVersion.Default:
181                                         if (editing || rowState == DataRowState.Detached)
182                                                 return proposed[columnIndex];
183                                         return current[columnIndex];
184                                 case DataRowVersion.Proposed:
185                                         return proposed[columnIndex];
186                                 case DataRowVersion.Current:
187                                         return current[columnIndex];
188                                 case DataRowVersion.Original:
189                                         return original[columnIndex];
190                                 default:
191                                         throw new ArgumentException ();
192                                 }
193                         }
194                 }
195
196                 /// <summary>
197                 /// Gets or sets all of the values for this row through an array.
198                 /// </summary>
199                 [MonoTODO]
200                 public object[] ItemArray {
201                         get { 
202                                 return current; 
203                         }
204                         set {
205                                 if (value.Length > _table.Columns.Count)
206                                         throw new ArgumentException ();
207
208                                 if (rowState == DataRowState.Deleted)
209                                         throw new DeletedRowInaccessibleException ();
210                                 
211                                 object[] newItems = new object[_table.Columns.Count];                   
212                                 object v = null;
213                                 for (int i = 0; i < _table.Columns.Count; i++) {
214
215                                         if (i < value.Length)
216                                                 v = value[i];
217                                         else
218                                                 v = null;
219
220                                         newItems[i] = SetColumnValue (v, i);
221                                 }
222
223                                 bool orginalEditing = editing;
224                                 if (!orginalEditing) BeginEdit ();
225                                 proposed = newItems;
226                                 if (!orginalEditing) EndEdit ();
227                         }
228                 }
229
230                 private object SetColumnValue (object v, int index) 
231                 {               
232                         object newval = null;
233                         DataColumn col = _table.Columns[index];
234                         
235                         if (col.ReadOnly && v != this[index])
236                                 throw new ReadOnlyException ();
237
238                         if (v == null) {
239                                 if(col.DefaultValue != DBNull.Value) {
240                                         newval = col.DefaultValue;
241                                 }
242                                 else if(col.AutoIncrement == true) {
243                                         newval = this [index];
244                                 }
245                                 else {
246                                         if (!col.AllowDBNull)
247                                                 throw new NoNullAllowedException ();
248                                         newval = DBNull.Value;
249                                 }
250                         }
251                         else if (v == DBNull.Value) {
252                                 if (!col.AllowDBNull)
253                                         throw new NoNullAllowedException ();
254                                 else {
255                                         newval = DBNull.Value;
256                                 }
257                         }
258                         else {  
259                                 Type vType = v.GetType(); // data type of value
260                                 Type cType = col.DataType; // column data type
261                                 if (cType != vType) {
262                                         TypeCode typeCode = Type.GetTypeCode(cType);
263                                         switch(typeCode) {
264                                         case TypeCode.Boolean :
265                                                 v = Convert.ToBoolean (v);
266                                                 break;
267                                         case TypeCode.Byte  :
268                                                 v = Convert.ToByte (v);
269                                                 break;
270                                         case TypeCode.Char  :
271                                                 v = Convert.ToChar (v);
272                                                 break;
273                                         case TypeCode.DateTime  :
274                                                 v = Convert.ToDateTime (v);
275                                                 break;
276                                         case TypeCode.Decimal  :
277                                                 v = Convert.ToDecimal (v);
278                                                 break;
279                                         case TypeCode.Double  :
280                                                 v = Convert.ToDouble (v);
281                                                 break;
282                                         case TypeCode.Int16  :
283                                                 v = Convert.ToInt16 (v);
284                                                 break;
285                                         case TypeCode.Int32  :
286                                                 v = Convert.ToInt32 (v);
287                                                 break;
288                                         case TypeCode.Int64  :
289                                                 v = Convert.ToInt64 (v);
290                                                 break;
291                                         case TypeCode.SByte  :
292                                                 v = Convert.ToSByte (v);
293                                                 break;
294                                         case TypeCode.Single  :
295                                                 v = Convert.ToSingle (v);
296                                                 break;
297                                         case TypeCode.String  :
298                                                 v = Convert.ToString (v);
299                                                 break;
300                                         case TypeCode.UInt16  :
301                                                 v = Convert.ToUInt16 (v);
302                                                 break;
303                                         case TypeCode.UInt32  :
304                                                 v = Convert.ToUInt32 (v);
305                                                 break;
306                                         case TypeCode.UInt64  :
307                                                 v = Convert.ToUInt64 (v);
308                                                 break;
309                                         default :
310                                                 switch(cType.ToString()) {
311                                                 case "System.TimeSpan" :
312                                                         v = (System.TimeSpan) v;
313                                                         break;
314                                                 case "System.Type" :
315                                                         v = (System.Type) v;
316                                                         break;
317                                                 case "System.Object" :
318                                                         //v = (System.Object) v;
319                                                         break;
320                                                 default:
321                                                         // FIXME: is exception correct?
322                                                         throw new InvalidCastException("Type not supported.");
323                                                 }
324                                                 break;
325                                         }
326                                         vType = v.GetType();
327                                 }
328                                 newval = v;
329                                 if(col.AutoIncrement == true) {
330                                         long inc = Convert.ToInt64(v);
331                                         col.UpdateAutoIncrementValue (inc);
332                                 }
333                         }
334                         col.DataHasBeenSet = true;
335                         return newval;
336                 }
337
338                 /// <summary>
339                 /// Gets or sets the custom error description for a row.
340                 /// </summary>
341                 public string RowError {
342                         get { return rowError; }
343                         set { rowError = value; }
344                 }
345
346                 /// <summary>
347                 /// Gets the current state of the row in regards to its relationship to the
348                 /// DataRowCollection.
349                 /// </summary>
350                 public DataRowState RowState {
351                         get { return rowState; }
352                 }
353
354                 //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
355                 //to a Datatable so I added this method. Delete if there is a better way.
356                 internal void AttachRow() {
357                         current = proposed;
358                         proposed = null;
359                         rowState = DataRowState.Added;
360                 }
361
362                 //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
363                 //from a Datatable so I added this method. Delete if there is a better way.
364                 internal void DetachRow() {
365                         proposed = null;
366                         rowState = DataRowState.Detached;
367                 }
368
369                 /// <summary>
370                 /// Gets the DataTable for which this row has a schema.
371                 /// </summary>
372                 public DataTable Table {
373                         get { return _table; }
374                 }
375
376                 /// <summary>
377                 /// Gets and sets index of row. This is used from 
378                 /// XmlDataDocument.
379                 // </summary>
380                 internal int XmlRowID {
381                         get { return xmlRowID; }
382                         set { xmlRowID = value; }
383                 }
384
385                 #endregion
386
387                 #region Methods
388
389                 /// <summary>
390                 /// Commits all the changes made to this row since the last time AcceptChanges was
391                 /// called.
392                 /// </summary>
393                 public void AcceptChanges () 
394                 {
395                         EndEdit(); // in case it hasn't been called
396                         switch (rowState) {
397                         case DataRowState.Added:
398                         case DataRowState.Modified:
399                                 rowState = DataRowState.Unchanged;
400                                 break;
401                         case DataRowState.Deleted:
402                                 _table.Rows.Remove (this);
403                                 break;
404                         case DataRowState.Detached:
405                                 throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
406                         }
407                         // Accept from detached
408                         if (original == null)
409                                 original = new object[_table.Columns.Count];
410                         Array.Copy (current, original, _table.Columns.Count);
411                 }
412
413                 /// <summary>
414                 /// Begins an edit operation on a DataRow object.
415                 /// </summary>
416                 [MonoTODO]
417                 public void BeginEdit () 
418                 {
419                         if (rowState == DataRowState.Deleted)
420                                 throw new DeletedRowInaccessibleException ();
421                         if (!HasVersion (DataRowVersion.Proposed)) {
422                                 proposed = new object[_table.Columns.Count];
423                                 Array.Copy (current, proposed, current.Length);
424                         }
425                         //TODO: Suspend validation
426                         editing = true;
427                 }
428
429                 /// <summary>
430                 /// Cancels the current edit on the row.
431                 /// </summary>
432                 [MonoTODO]
433                 public void CancelEdit () 
434                 {
435                         editing = false;
436                         //TODO: Events
437                         if (HasVersion (DataRowVersion.Proposed)) {
438                                 proposed = null;
439                                 if (rowState == DataRowState.Modified)
440                                     rowState = DataRowState.Unchanged;
441                         }
442                 }
443
444                 /// <summary>
445                 /// Clears the errors for the row, including the RowError and errors set with
446                 /// SetColumnError.
447                 /// </summary>
448                 public void ClearErrors () 
449                 {
450                         rowError = String.Empty;
451                         columnErrors = new String[_table.Columns.Count];
452                 }
453
454                 /// <summary>
455                 /// Deletes the DataRow.
456                 /// </summary>
457                 [MonoTODO]
458                 public void Delete () 
459                 {
460                         switch (rowState) {
461                         case DataRowState.Added:
462                                 Table.Rows.Remove (this);
463                                 break;
464                         case DataRowState.Deleted:
465                                 throw new DeletedRowInaccessibleException ();
466                         default:
467                                 //TODO: Events, Constraints
468                                 rowState = DataRowState.Deleted;
469                                 break;
470                         }
471                 }
472
473                 /// <summary>
474                 /// Ends the edit occurring on the row.
475                 /// </summary>
476                 [MonoTODO]
477                 public void EndEdit () 
478                 {
479                         editing = false;
480                         if (rowState == DataRowState.Detached)
481                                 return;
482                         if (HasVersion (DataRowVersion.Proposed))
483                         {
484                                 if (rowState == DataRowState.Unchanged)
485                                         rowState = DataRowState.Modified;
486                                 
487                                 //Calling next method validates UniqueConstraints
488                                 //and ForeignKeys.
489                                 _table.Rows.ValidateDataRowInternal(this);
490                                 current = proposed;
491                                 proposed = null;
492                         }
493                 }
494
495                 /// <summary>
496                 /// Gets the child rows of this DataRow using the specified DataRelation.
497                 /// </summary>
498                 public DataRow[] GetChildRows (DataRelation relation) 
499                 {
500                         return GetChildRows (relation, DataRowVersion.Current);
501                 }
502
503                 /// <summary>
504                 /// Gets the child rows of a DataRow using the specified RelationName of a
505                 /// DataRelation.
506                 /// </summary>
507                 public DataRow[] GetChildRows (string relationName) 
508                 {
509                         return GetChildRows (Table.DataSet.Relations[relationName]);
510                 }
511
512                 /// <summary>
513                 /// Gets the child rows of a DataRow using the specified DataRelation, and
514                 /// DataRowVersion.
515                 /// </summary>
516                 public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version) 
517                 {
518                         // TODO: Caching for better preformance
519                         ArrayList rows = new ArrayList();
520                         DataColumn[] parentColumns = relation.ParentColumns;
521                         DataColumn[] childColumns = relation.ChildColumns;
522                         int numColumn = parentColumns.Length;
523                         foreach (DataRow row in relation.ChildTable.Rows) {
524                                 bool allColumnsMatch = true;
525                                 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) {
526                                         if (!this[parentColumns[columnCnt], version].Equals(
527                                             row[childColumns[columnCnt], version])) {
528                                                 allColumnsMatch = false;
529                                                 break;
530                                         }
531                                 }
532                                 if (allColumnsMatch) rows.Add(row);
533                         }
534                         return rows.ToArray(typeof(DataRow)) as DataRow[];
535                 }
536
537                 /// <summary>
538                 /// Gets the child rows of a DataRow using the specified RelationName of a
539                 /// DataRelation, and DataRowVersion.
540                 /// </summary>
541                 public DataRow[] GetChildRows (string relationName, DataRowVersion version) 
542                 {
543                         return GetChildRows (Table.DataSet.Relations[relationName], version);
544                 }
545
546                 /// <summary>
547                 /// Gets the error description of the specified DataColumn.
548                 /// </summary>
549                 public string GetColumnError (DataColumn column) 
550                 {
551                         return GetColumnError (_table.Columns.IndexOf(column));
552                 }
553
554                 /// <summary>
555                 /// Gets the error description for the column specified by index.
556                 /// </summary>
557                 public string GetColumnError (int columnIndex) 
558                 {
559                         if (columnIndex < 0 || columnIndex >= columnErrors.Length)
560                                 throw new IndexOutOfRangeException ();
561
562                         return columnErrors[columnIndex];
563                 }
564
565                 /// <summary>
566                 /// Gets the error description for the column, specified by name.
567                 /// </summary>
568                 public string GetColumnError (string columnName) 
569                 {
570                         return GetColumnError (_table.Columns.IndexOf(columnName));
571                 }
572
573                 /// <summary>
574                 /// Gets an array of columns that have errors.
575                 /// </summary>
576                 public DataColumn[] GetColumnsInError () 
577                 {
578                         ArrayList dataColumns = new ArrayList ();
579
580                         for (int i = 0; i < columnErrors.Length; i += 1)
581                         {
582                                 if (columnErrors[i] != String.Empty)
583                                         dataColumns.Add (_table.Columns[i]);
584                         }
585
586                         return (DataColumn[])(dataColumns.ToArray ());
587                 }
588
589                 /// <summary>
590                 /// Gets the parent row of a DataRow using the specified DataRelation.
591                 /// </summary>
592                 public DataRow GetParentRow (DataRelation relation) 
593                 {
594                         return GetParentRow (relation, DataRowVersion.Current);
595                 }
596
597                 /// <summary>
598                 /// Gets the parent row of a DataRow using the specified RelationName of a
599                 /// DataRelation.
600                 /// </summary>
601                 public DataRow GetParentRow (string relationName) 
602                 {
603                         return GetParentRow (relationName, DataRowVersion.Current);
604                 }
605
606                 /// <summary>
607                 /// Gets the parent row of a DataRow using the specified DataRelation, and
608                 /// DataRowVersion.
609                 /// </summary>
610                 public DataRow GetParentRow (DataRelation relation, DataRowVersion version) 
611                 {
612                         DataRow[] rows = GetParentRows(relation, version);
613                         if (rows.Length == 0) return null;
614                         return rows[0];
615                 }
616
617                 /// <summary>
618                 /// Gets the parent row of a DataRow using the specified RelationName of a 
619                 /// DataRelation, and DataRowVersion.
620                 /// </summary>
621                 public DataRow GetParentRow (string relationName, DataRowVersion version) 
622                 {
623                         return GetParentRow (Table.DataSet.Relations[relationName], version);
624                 }
625
626                 /// <summary>
627                 /// Gets the parent rows of a DataRow using the specified DataRelation.
628                 /// </summary>
629                 public DataRow[] GetParentRows (DataRelation relation) 
630                 {
631                         return GetParentRows (relation, DataRowVersion.Current);
632                 }
633
634                 /// <summary>
635                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
636                 /// DataRelation.
637                 /// </summary>
638                 public DataRow[] GetParentRows (string relationName) 
639                 {
640                         return GetParentRows (relationName, DataRowVersion.Current);
641                 }
642
643                 /// <summary>
644                 /// Gets the parent rows of a DataRow using the specified DataRelation, and
645                 /// DataRowVersion.
646                 /// </summary>
647                 public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version) 
648                 {
649                         // TODO: Caching for better preformance
650                         ArrayList rows = new ArrayList();
651                         DataColumn[] parentColumns = relation.ParentColumns;
652                         DataColumn[] childColumns = relation.ChildColumns;
653                         int numColumn = parentColumns.Length;
654                         foreach (DataRow row in relation.ParentTable.Rows) {
655                                 bool allColumnsMatch = true;
656                                 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) {
657                                         if (!this[parentColumns[columnCnt], version].Equals(
658                                             row[childColumns[columnCnt], version])) {
659                                                 allColumnsMatch = false;
660                                                 break;
661                                         }
662                                 }
663                                 if (allColumnsMatch) rows.Add(row);
664                         }
665                         return rows.ToArray(typeof(DataRow)) as DataRow[];
666                 }
667
668                 /// <summary>
669                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
670                 /// DataRelation, and DataRowVersion.
671                 /// </summary>
672                 public DataRow[] GetParentRows (string relationName, DataRowVersion version) 
673                 {
674                         return GetParentRows (Table.DataSet.Relations[relationName], version);
675                 }
676
677                 /// <summary>
678                 /// Gets a value indicating whether a specified version exists.
679                 /// </summary>
680                 public bool HasVersion (DataRowVersion version) 
681                 {
682                         switch (version)
683                         {
684                                 case DataRowVersion.Default:
685                                         return true;
686                                 case DataRowVersion.Proposed:
687                                         return (proposed != null);
688                                 case DataRowVersion.Current:
689                                         return (current != null);
690                                 case DataRowVersion.Original:
691                                         return (original != null);
692                         }
693                         return false;
694                 }
695
696                 /// <summary>
697                 /// Gets a value indicating whether the specified DataColumn contains a null value.
698                 /// </summary>
699                 public bool IsNull (DataColumn column) 
700                 {
701                         object o = this[column];
702                         return (o == null || o == DBNull.Value);
703                 }
704
705                 /// <summary>
706                 /// Gets a value indicating whether the column at the specified index contains a null
707                 /// value.
708                 /// </summary>
709                 public bool IsNull (int columnIndex) 
710                 {
711                         object o = this[columnIndex];
712                         return (o == null || o == DBNull.Value);
713                 }
714
715                 /// <summary>
716                 /// Gets a value indicating whether the named column contains a null value.
717                 /// </summary>
718                 public bool IsNull (string columnName) 
719                 {
720                         object o = this[columnName];
721                         return (o == null || o == DBNull.Value);
722                 }
723
724                 /// <summary>
725                 /// Gets a value indicating whether the specified DataColumn and DataRowVersion
726                 /// contains a null value.
727                 /// </summary>
728                 public bool IsNull (DataColumn column, DataRowVersion version) 
729                 {
730                         object o = this[column, version];
731                         return (o == null || o == DBNull.Value);
732                 }
733
734                 /// <summary>
735                 /// Rejects all changes made to the row since AcceptChanges was last called.
736                 /// </summary>
737                 public void RejectChanges () 
738                 {
739                         // If original is null, then nothing has happened since AcceptChanges
740                         // was last called.  We have no "original" to go back to.
741                         if (original != null)
742                         {
743                                 Array.Copy (original, current, _table.Columns.Count);
744                                
745                                 _table.ChangedDataRow (this, DataRowAction.Rollback);
746                                 CancelEdit ();
747                                 switch (rowState)
748                                 {
749                                         case DataRowState.Added:
750                                                 _table.Rows.Remove (this);
751                                                 break;
752                                         case DataRowState.Modified:
753                                                 rowState = DataRowState.Unchanged;
754                                                 break;
755                                         case DataRowState.Deleted:
756                                                 rowState = DataRowState.Unchanged;
757                                                 break;
758                                 } 
759                                 
760                         }                       
761                         else {
762                                 // If rows are just loaded via Xml the original values are null.
763                                 // So in this case we have to remove all columns.
764                                 // FIXME: I'm not realy sure, does this break something else, but
765                                 // if so: FIXME ;)
766                                 
767                                 if ((rowState & DataRowState.Added) > 0)
768                                         _table.Rows.Remove (this);
769                         }
770                 }
771
772                 /// <summary>
773                 /// Sets the error description for a column specified as a DataColumn.
774                 /// </summary>
775                 public void SetColumnError (DataColumn column, string error) 
776                 {
777                         SetColumnError (_table.Columns.IndexOf (column), error);
778                 }
779
780                 /// <summary>
781                 /// Sets the error description for a column specified by index.
782                 /// </summary>
783                 public void SetColumnError (int columnIndex, string error) 
784                 {
785                         if (columnIndex < 0 || columnIndex >= columnErrors.Length)
786                                 throw new IndexOutOfRangeException ();
787                         columnErrors[columnIndex] = error;
788                 }
789
790                 /// <summary>
791                 /// Sets the error description for a column specified by name.
792                 /// </summary>
793                 public void SetColumnError (string columnName, string error) 
794                 {
795                         SetColumnError (_table.Columns.IndexOf (columnName), error);
796                 }
797
798                 /// <summary>
799                 /// Sets the value of the specified DataColumn to a null value.
800                 /// </summary>
801                 protected void SetNull (DataColumn column) 
802                 {
803                         this[column] = DBNull.Value;
804                 }
805
806                 /// <summary>
807                 /// Sets the parent row of a DataRow with specified new parent DataRow.
808                 /// </summary>
809                 [MonoTODO]
810                 public void SetParentRow (DataRow parentRow) 
811                 {
812                         throw new NotImplementedException ();
813                 }
814
815                 /// <summary>
816                 /// Sets the parent row of a DataRow with specified new parent DataRow and
817                 /// DataRelation.
818                 /// </summary>
819                 [MonoTODO]
820                 public void SetParentRow (DataRow parentRow, DataRelation relation) 
821                 {
822                         throw new NotImplementedException ();
823                 }
824                 
825                 //Copy all values of this DataaRow to the row parameter.
826                 internal void CopyValuesToRow(DataRow row)
827                 {
828                         if (row == null)
829                                 throw new ArgumentNullException("row");
830                         if (row == this)
831                                 throw new ArgumentException("'row' is the same as this object");
832
833                         DataColumnCollection columns = Table.Columns;
834                         
835                         for(int i = 0; i < columns.Count; i++){
836                                 string columnName = columns[i].ColumnName;
837                                 int index = row.Table.Columns.IndexOf(columnName);
838                                 //if a column withe the same name exists in bote rows copy the values
839                                 if(index != -1) {
840                                         if (HasVersion(DataRowVersion.Original))
841                                         {
842                                                 if (row.original == null)
843                                                         row.original = new object[row.Table.Columns.Count];
844                                                 row.original[index] = row.SetColumnValue(original[i], index);
845                                         }
846                                         if (HasVersion(DataRowVersion.Current))
847                                         {
848                                                 if (row.current == null)
849                                                         row.current = new object[row.Table.Columns.Count];
850                                                 row.current[index] = row.SetColumnValue(current[i], index);
851                                         }
852                                         if (HasVersion(DataRowVersion.Proposed))
853                                         {
854                                                 if (row.proposed == null)
855                                                         row.proposed = new object[row.Table.Columns.Count];
856                                                 row.proposed[index] = row.SetColumnValue(proposed[i], index);
857                                         }
858                                 }
859                         }
860
861                         row.rowState = RowState;
862                         row.RowError = RowError;
863                         row.columnErrors = columnErrors;
864                 }
865
866                 
867                 public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
868                 {
869                         // if a column is added we hava to add an additional value the 
870                         // the priginal, current and propoed arrays.
871                         // this scenario can happened in merge operation.
872
873                         if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
874                         {
875                                 object[] tmp = new object[current.Length + 1];
876                                 Array.Copy (current, tmp, current.Length);
877                                 tmp[tmp.Length - 1] = DBNull.Value;
878                                 current = tmp;
879
880                                 if (proposed != null)
881                                 {
882                                         tmp = new object[proposed.Length + 1];
883                                         Array.Copy (proposed, tmp, proposed.Length);
884                                         proposed = tmp;
885                                 }
886                                 if(original != null)
887                                 {
888                                         tmp = new object[original.Length + 1];
889                                         Array.Copy (original, tmp, original.Length);
890                                         original = tmp;
891                                 }
892
893                         }
894                 }
895
896                 internal bool IsRowChanged(DataRowState rowState) {
897                         if((RowState & rowState) != 0)
898                                 return true;
899
900                         //we need to find if child rows of this row changed.
901                         //if yes - we should return true
902
903                         // if the rowState is deleted we should get the original version of the row
904                         // else - we should get the current version of the row.
905                         DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
906                         int count = Table.ChildRelations.Count;
907                         for (int i = 0; i < count; i++){
908                                 DataRelation rel = Table.ChildRelations[i];
909                                 DataRow[] childRows = GetChildRows(rel, version);
910                                 for (int j = 0; j < childRows.Length; j++){
911                                         if (childRows[j].IsRowChanged(rowState))
912                                                 return true;
913                                 }
914                         }
915
916                         return false;
917                 }
918
919                 #endregion // Methods
920         }
921
922         
923
924 }