Apply patch from David Sandor (David.Sandor@scigames.com): LIKE expression was not...
[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
76                 #endregion
77
78                 #region Properties
79
80                 /// <summary>
81                 /// Gets a value indicating whether there are errors in a row.
82                 /// </summary>
83                 public bool HasErrors {
84                         [MonoTODO]
85                         get {
86                                 throw new NotImplementedException ();
87                         }
88                 }
89
90                 /// <summary>
91                 /// Gets or sets the data stored in the column specified by name.
92                 /// </summary>
93                 public object this[string columnName] {
94                         get { return this[columnName, DataRowVersion.Default]; }
95                         set {
96                                 int columnIndex = _table.Columns.IndexOf (columnName);
97                                 if (columnIndex == -1)
98                                         throw new IndexOutOfRangeException ();
99                                 this[columnIndex] = value;
100                         }
101                 }
102
103                 /// <summary>
104                 /// Gets or sets the data stored in specified DataColumn
105                 /// </summary>
106                 public object this[DataColumn column] {
107                         get { return this[column, DataRowVersion.Default]; } 
108                         set {
109                                 int columnIndex = _table.Columns.IndexOf (column);
110                                 if (columnIndex == -1)
111                                         throw new ArgumentException ("The column does not belong to this table.");
112                                 this[columnIndex] = value;
113                         }
114                 }
115
116                 /// <summary>
117                 /// Gets or sets the data stored in column specified by index.
118                 /// </summary>
119                 public object this[int columnIndex] {
120                         get { return this[columnIndex, DataRowVersion.Default]; }
121                         set {
122                                 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
123                                         throw new IndexOutOfRangeException ();
124                                 if (rowState == DataRowState.Deleted)
125                                         throw new DeletedRowInaccessibleException ();
126                                 DataColumn column = _table.Columns[columnIndex];
127                                 _table.ChangingDataColumn (this, column, value);
128                                 
129                                 bool orginalEditing = editing;
130                                 if (!orginalEditing) BeginEdit ();
131                                 object v = SetColumnValue (value, columnIndex);
132                                 proposed[columnIndex] = v;
133                                 _table.ChangedDataColumn (this, column, v);
134                                 if (!orginalEditing) EndEdit ();
135                         }
136                 }
137
138                 /// <summary>
139                 /// Gets the specified version of data stored in the named column.
140                 /// </summary>
141                 public object this[string columnName, DataRowVersion version] {
142                         get {
143                                 int columnIndex = _table.Columns.IndexOf (columnName);
144                                 if (columnIndex == -1)
145                                         throw new IndexOutOfRangeException ();
146                                 return this[columnIndex, version];
147                         }
148                 }
149
150                 /// <summary>
151                 /// Gets the specified version of data stored in the specified DataColumn.
152                 /// </summary>
153                 public object this[DataColumn column, DataRowVersion version] {
154                         get {
155                                 int columnIndex = _table.Columns.IndexOf (column);
156                                 if (columnIndex == -1)
157                                         throw new ArgumentException ("The column does not belong to this table.");
158                                 return this[columnIndex, version];
159                         }
160                 }
161
162                 /// <summary>
163                 /// Gets the data stored in the column, specified by index and version of the data to
164                 /// retrieve.
165                 /// </summary>
166                 public object this[int columnIndex, DataRowVersion version] {
167                         get {
168                                 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
169                                         throw new IndexOutOfRangeException ();
170                                 // Non-existent version
171                                 if (rowState == DataRowState.Detached && version == DataRowVersion.Current || !HasVersion (version))
172                                         throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
173                                 // Accessing deleted rows
174                                 if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
175                                         throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
176                                 switch (version) {
177                                 case DataRowVersion.Default:
178                                         if (editing || rowState == DataRowState.Detached)
179                                                 return proposed[columnIndex];
180                                         return current[columnIndex];
181                                 case DataRowVersion.Proposed:
182                                         return proposed[columnIndex];
183                                 case DataRowVersion.Current:
184                                         return current[columnIndex];
185                                 case DataRowVersion.Original:
186                                         return original[columnIndex];
187                                 default:
188                                         throw new ArgumentException ();
189                                 }
190                         }
191                 }
192
193                 /// <summary>
194                 /// Gets or sets all of the values for this row through an array.
195                 /// </summary>
196                 [MonoTODO]
197                 public object[] ItemArray {
198                         get { 
199                                 return current; 
200                         }
201                         set {
202                                 if (value.Length > _table.Columns.Count)
203                                         throw new ArgumentException ();
204
205                                 if (rowState == DataRowState.Deleted)
206                                         throw new DeletedRowInaccessibleException ();
207                                 
208                                 object[] newItems = new object[_table.Columns.Count];                   
209                                 object v = null;
210                                 for (int i = 0; i < _table.Columns.Count; i++) {
211
212                                         if (i < value.Length)
213                                                 v = value[i];
214                                         else
215                                                 v = null;
216
217                                         newItems[i] = SetColumnValue (v, i);
218                                 }
219
220                                 bool orginalEditing = editing;
221                                 if (!orginalEditing) BeginEdit ();
222                                 proposed = newItems;
223                                 if (!orginalEditing) EndEdit ();
224                         }
225                 }
226
227                 private object SetColumnValue (object v, int index) 
228                 {               
229                         object newval = null;
230                         DataColumn col = _table.Columns[index];
231                         
232                         if (col.ReadOnly && v != this[index])
233                                 throw new ReadOnlyException ();
234
235                         if (v == null) {
236                                 if(col.DefaultValue != DBNull.Value) {
237                                         newval = col.DefaultValue;
238                                 }
239                                 else if(col.AutoIncrement == true) {
240                                         newval = this [index];
241                                 }
242                                 else {
243                                         if (!col.AllowDBNull)
244                                                 throw new NoNullAllowedException ();
245                                         newval = DBNull.Value;
246                                 }
247                         }
248                         else if (v == DBNull.Value) {
249                                 if (!col.AllowDBNull)
250                                         throw new NoNullAllowedException ();
251                                 else {
252                                         newval = DBNull.Value;
253                                 }
254                         }
255                         else {  
256                                 Type vType = v.GetType(); // data type of value
257                                 Type cType = col.DataType; // column data type
258                                 if (cType != vType) {
259                                         TypeCode typeCode = Type.GetTypeCode(cType);
260                                         switch(typeCode) {
261                                         case TypeCode.Boolean :
262                                                 v = Convert.ToBoolean (v);
263                                                 break;
264                                         case TypeCode.Byte  :
265                                                 v = Convert.ToByte (v);
266                                                 break;
267                                         case TypeCode.Char  :
268                                                 v = Convert.ToChar (v);
269                                                 break;
270                                         case TypeCode.DateTime  :
271                                                 v = Convert.ToDateTime (v);
272                                                 break;
273                                         case TypeCode.Decimal  :
274                                                 v = Convert.ToDecimal (v);
275                                                 break;
276                                         case TypeCode.Double  :
277                                                 v = Convert.ToDouble (v);
278                                                 break;
279                                         case TypeCode.Int16  :
280                                                 v = Convert.ToInt16 (v);
281                                                 break;
282                                         case TypeCode.Int32  :
283                                                 v = Convert.ToInt32 (v);
284                                                 break;
285                                         case TypeCode.Int64  :
286                                                 v = Convert.ToInt64 (v);
287                                                 break;
288                                         case TypeCode.SByte  :
289                                                 v = Convert.ToSByte (v);
290                                                 break;
291                                         case TypeCode.Single  :
292                                                 v = Convert.ToSingle (v);
293                                                 break;
294                                         case TypeCode.String  :
295                                                 v = Convert.ToString (v);
296                                                 break;
297                                         case TypeCode.UInt16  :
298                                                 v = Convert.ToUInt16 (v);
299                                                 break;
300                                         case TypeCode.UInt32  :
301                                                 v = Convert.ToUInt32 (v);
302                                                 break;
303                                         case TypeCode.UInt64  :
304                                                 v = Convert.ToUInt64 (v);
305                                                 break;
306                                         default :
307                                                 switch(cType.ToString()) {
308                                                 case "System.TimeSpan" :
309                                                         v = (System.TimeSpan) v;
310                                                         break;
311                                                 case "System.Type" :
312                                                         v = (System.Type) v;
313                                                         break;
314                                                 case "System.Object" :
315                                                         //v = (System.Object) v;
316                                                         break;
317                                                 default:
318                                                         // FIXME: is exception correct?
319                                                         throw new InvalidCastException("Type not supported.");
320                                                 }
321                                                 break;
322                                         }
323                                         vType = v.GetType();
324                                 }
325                                 newval = v;
326                                 if(col.AutoIncrement == true) {
327                                         long inc = Convert.ToInt64(v);
328                                         col.UpdateAutoIncrementValue (inc);
329                                 }
330                         }
331                         col.DataHasBeenSet = true;
332                         return newval;
333                 }
334
335                 /// <summary>
336                 /// Gets or sets the custom error description for a row.
337                 /// </summary>
338                 public string RowError {
339                         get { return rowError; }
340                         set { rowError = value; }
341                 }
342
343                 /// <summary>
344                 /// Gets the current state of the row in regards to its relationship to the
345                 /// DataRowCollection.
346                 /// </summary>
347                 public DataRowState RowState {
348                         get { return rowState; }
349                 }
350
351                 //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
352                 //to a Datatable so I added this method. Delete if there is a better way.
353                 internal void AttachRow() {
354                         current = proposed;
355                         proposed = null;
356                         rowState = DataRowState.Added;
357                 }
358
359                 //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
360                 //from a Datatable so I added this method. Delete if there is a better way.
361                 internal void DetachRow() {
362                         proposed = null;
363                         rowState = DataRowState.Detached;
364                 }
365
366                 /// <summary>
367                 /// Gets the DataTable for which this row has a schema.
368                 /// </summary>
369                 public DataTable Table {
370                         get { return _table; }
371                 }
372
373                 /// <summary>
374                 /// Gets and sets index of row. This is used from 
375                 /// XmlDataDocument.
376                 // </summary>
377                 internal int XmlRowID {
378                         get { return xmlRowID; }
379                         set { xmlRowID = value; }
380                 }
381
382                 #endregion
383
384                 #region Methods
385
386                 /// <summary>
387                 /// Commits all the changes made to this row since the last time AcceptChanges was
388                 /// called.
389                 /// </summary>
390                 public void AcceptChanges () 
391                 {
392                         EndEdit(); // in case it hasn't been called
393                         switch (rowState) {
394                         case DataRowState.Added:
395                         case DataRowState.Detached:
396                         case DataRowState.Modified:
397                                 rowState = DataRowState.Unchanged;
398                                 break;
399                         case DataRowState.Deleted:
400                                 _table.Rows.Remove (this);
401                                 break;
402                         }
403                         // Accept from detached
404                         if (original == null)
405                                 original = new object[_table.Columns.Count];
406                         Array.Copy (current, original, _table.Columns.Count);
407                 }
408
409                 /// <summary>
410                 /// Begins an edit operation on a DataRow object.
411                 /// </summary>
412                 [MonoTODO]
413                 public void BeginEdit () 
414                 {
415                         if (rowState == DataRowState.Deleted)
416                                 throw new DeletedRowInaccessibleException ();
417                         if (!HasVersion (DataRowVersion.Proposed)) {
418                                 proposed = new object[_table.Columns.Count];
419                                 Array.Copy (current, proposed, current.Length);
420                         }
421                         //TODO: Suspend validation
422                         editing = true;
423                 }
424
425                 /// <summary>
426                 /// Cancels the current edit on the row.
427                 /// </summary>
428                 [MonoTODO]
429                 public void CancelEdit () 
430                 {
431                         //TODO: Events
432                         if (HasVersion (DataRowVersion.Proposed)) {
433                                 proposed = null;
434                                 if (rowState == DataRowState.Modified)
435                                     rowState = DataRowState.Unchanged;
436                         }
437                 }
438
439                 /// <summary>
440                 /// Clears the errors for the row, including the RowError and errors set with
441                 /// SetColumnError.
442                 /// </summary>
443                 public void ClearErrors () 
444                 {
445                         rowError = String.Empty;
446                         columnErrors = new String[_table.Columns.Count];
447                 }
448
449                 /// <summary>
450                 /// Deletes the DataRow.
451                 /// </summary>
452                 [MonoTODO]
453                 public void Delete () 
454                 {
455                         switch (rowState) {
456                         case DataRowState.Added:
457                                 Table.Rows.Remove (this);
458                                 break;
459                         case DataRowState.Deleted:
460                                 throw new DeletedRowInaccessibleException ();
461                         default:
462                                 //TODO: Events, Constraints
463                                 rowState = DataRowState.Deleted;
464                                 break;
465                         }
466                 }
467
468                 /// <summary>
469                 /// Ends the edit occurring on the row.
470                 /// </summary>
471                 [MonoTODO]
472                 public void EndEdit () 
473                 {
474                         editing = false;
475                         if (rowState == DataRowState.Detached)
476                                 return;
477                         if (HasVersion (DataRowVersion.Proposed))
478                         {
479                                 if (rowState == DataRowState.Unchanged)
480                                         rowState = DataRowState.Modified;
481                                 
482                                 //Calling next method validates UniqueConstraints
483                                 //and ForeignKeys.
484                                 _table.Rows.ValidateDataRowInternal(this);
485                                 current = proposed;
486                                 proposed = null;
487                         }
488                 }
489
490                 /// <summary>
491                 /// Gets the child rows of this DataRow using the specified DataRelation.
492                 /// </summary>
493                 public DataRow[] GetChildRows (DataRelation relation) 
494                 {
495                         return GetChildRows (relation, DataRowVersion.Current);
496                 }
497
498                 /// <summary>
499                 /// Gets the child rows of a DataRow using the specified RelationName of a
500                 /// DataRelation.
501                 /// </summary>
502                 public DataRow[] GetChildRows (string relationName) 
503                 {
504                         return GetChildRows (Table.DataSet.Relations[relationName]);
505                 }
506
507                 /// <summary>
508                 /// Gets the child rows of a DataRow using the specified DataRelation, and
509                 /// DataRowVersion.
510                 /// </summary>
511                 public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version) 
512                 {
513                         // TODO: Caching for better preformance
514                         ArrayList rows = new ArrayList();
515                         DataColumn[] parentColumns = relation.ParentColumns;
516                         DataColumn[] childColumns = relation.ChildColumns;
517                         int numColumn = parentColumns.Length;
518                         foreach (DataRow row in relation.ChildTable.Rows) {
519                                 bool allColumnsMatch = true;
520                                 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) {
521                                         if (!this[parentColumns[columnCnt], version].Equals(
522                                             row[childColumns[columnCnt], version])) {
523                                                 allColumnsMatch = false;
524                                                 break;
525                                         }
526                                 }
527                                 if (allColumnsMatch) rows.Add(row);
528                         }
529                         return rows.ToArray(typeof(DataRow)) as DataRow[];
530                 }
531
532                 /// <summary>
533                 /// Gets the child rows of a DataRow using the specified RelationName of a
534                 /// DataRelation, and DataRowVersion.
535                 /// </summary>
536                 public DataRow[] GetChildRows (string relationName, DataRowVersion version) 
537                 {
538                         return GetChildRows (Table.DataSet.Relations[relationName], version);
539                 }
540
541                 /// <summary>
542                 /// Gets the error description of the specified DataColumn.
543                 /// </summary>
544                 public string GetColumnError (DataColumn column) 
545                 {
546                         return GetColumnError (_table.Columns.IndexOf(column));
547                 }
548
549                 /// <summary>
550                 /// Gets the error description for the column specified by index.
551                 /// </summary>
552                 public string GetColumnError (int columnIndex) 
553                 {
554                         if (columnIndex < 0 || columnIndex >= columnErrors.Length)
555                                 throw new IndexOutOfRangeException ();
556
557                         return columnErrors[columnIndex];
558                 }
559
560                 /// <summary>
561                 /// Gets the error description for the column, specified by name.
562                 /// </summary>
563                 public string GetColumnError (string columnName) 
564                 {
565                         return GetColumnError (_table.Columns.IndexOf(columnName));
566                 }
567
568                 /// <summary>
569                 /// Gets an array of columns that have errors.
570                 /// </summary>
571                 public DataColumn[] GetColumnsInError () 
572                 {
573                         ArrayList dataColumns = new ArrayList ();
574
575                         for (int i = 0; i < columnErrors.Length; i += 1)
576                         {
577                                 if (columnErrors[i] != String.Empty)
578                                         dataColumns.Add (_table.Columns[i]);
579                         }
580
581                         return (DataColumn[])(dataColumns.ToArray ());
582                 }
583
584                 /// <summary>
585                 /// Gets the parent row of a DataRow using the specified DataRelation.
586                 /// </summary>
587                 public DataRow GetParentRow (DataRelation relation) 
588                 {
589                         return GetParentRow (relation, DataRowVersion.Current);
590                 }
591
592                 /// <summary>
593                 /// Gets the parent row of a DataRow using the specified RelationName of a
594                 /// DataRelation.
595                 /// </summary>
596                 public DataRow GetParentRow (string relationName) 
597                 {
598                         return GetParentRow (relationName, DataRowVersion.Current);
599                 }
600
601                 /// <summary>
602                 /// Gets the parent row of a DataRow using the specified DataRelation, and
603                 /// DataRowVersion.
604                 /// </summary>
605                 public DataRow GetParentRow (DataRelation relation, DataRowVersion version) 
606                 {
607                         DataRow[] rows = GetParentRows(relation, version);
608                         if (rows.Length == 0) return null;
609                         return rows[0];
610                 }
611
612                 /// <summary>
613                 /// Gets the parent row of a DataRow using the specified RelationName of a 
614                 /// DataRelation, and DataRowVersion.
615                 /// </summary>
616                 public DataRow GetParentRow (string relationName, DataRowVersion version) 
617                 {
618                         return GetParentRow (Table.DataSet.Relations[relationName], version);
619                 }
620
621                 /// <summary>
622                 /// Gets the parent rows of a DataRow using the specified DataRelation.
623                 /// </summary>
624                 public DataRow[] GetParentRows (DataRelation relation) 
625                 {
626                         return GetParentRows (relation, DataRowVersion.Current);
627                 }
628
629                 /// <summary>
630                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
631                 /// DataRelation.
632                 /// </summary>
633                 public DataRow[] GetParentRows (string relationName) 
634                 {
635                         return GetParentRows (relationName, DataRowVersion.Current);
636                 }
637
638                 /// <summary>
639                 /// Gets the parent rows of a DataRow using the specified DataRelation, and
640                 /// DataRowVersion.
641                 /// </summary>
642                 public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version) 
643                 {
644                         // TODO: Caching for better preformance
645                         ArrayList rows = new ArrayList();
646                         DataColumn[] parentColumns = relation.ParentColumns;
647                         DataColumn[] childColumns = relation.ChildColumns;
648                         int numColumn = parentColumns.Length;
649                         foreach (DataRow row in relation.ParentTable.Rows) {
650                                 bool allColumnsMatch = true;
651                                 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) {
652                                         if (!this[parentColumns[columnCnt], version].Equals(
653                                             row[childColumns[columnCnt], version])) {
654                                                 allColumnsMatch = false;
655                                                 break;
656                                         }
657                                 }
658                                 if (allColumnsMatch) rows.Add(row);
659                         }
660                         return rows.ToArray(typeof(DataRow)) as DataRow[];
661                 }
662
663                 /// <summary>
664                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
665                 /// DataRelation, and DataRowVersion.
666                 /// </summary>
667                 public DataRow[] GetParentRows (string relationName, DataRowVersion version) 
668                 {
669                         return GetParentRows (Table.DataSet.Relations[relationName], version);
670                 }
671
672                 /// <summary>
673                 /// Gets a value indicating whether a specified version exists.
674                 /// </summary>
675                 public bool HasVersion (DataRowVersion version) 
676                 {
677                         switch (version)
678                         {
679                                 case DataRowVersion.Default:
680                                         return true;
681                                 case DataRowVersion.Proposed:
682                                         return (proposed != null);
683                                 case DataRowVersion.Current:
684                                         return (current != null);
685                                 case DataRowVersion.Original:
686                                         return (original != null);
687                         }
688                         return false;
689                 }
690
691                 /// <summary>
692                 /// Gets a value indicating whether the specified DataColumn contains a null value.
693                 /// </summary>
694                 public bool IsNull (DataColumn column) 
695                 {
696                         return (this[column] == null);
697                 }
698
699                 /// <summary>
700                 /// Gets a value indicating whether the column at the specified index contains a null
701                 /// value.
702                 /// </summary>
703                 public bool IsNull (int columnIndex) 
704                 {
705                         return (this[columnIndex] == null);
706                 }
707
708                 /// <summary>
709                 /// Gets a value indicating whether the named column contains a null value.
710                 /// </summary>
711                 public bool IsNull (string columnName) 
712                 {
713                         return (this[columnName] == null);
714                 }
715
716                 /// <summary>
717                 /// Gets a value indicating whether the specified DataColumn and DataRowVersion
718                 /// contains a null value.
719                 /// </summary>
720                 public bool IsNull (DataColumn column, DataRowVersion version) 
721                 {
722                         return (this[column, version] == null);
723                 }
724
725                 /// <summary>
726                 /// Rejects all changes made to the row since AcceptChanges was last called.
727                 /// </summary>
728                 public void RejectChanges () 
729                 {
730                         // If original is null, then nothing has happened since AcceptChanges
731                         // was last called.  We have no "original" to go back to.
732                         if (original != null)
733                         {
734                                 Array.Copy (original, current, _table.Columns.Count);
735                                
736                                 _table.ChangedDataRow (this, DataRowAction.Rollback);
737                                 CancelEdit ();
738                                 switch (rowState)
739                                 {
740                                         case DataRowState.Added:
741                                                 _table.Rows.Remove (this);
742                                                 break;
743                                         case DataRowState.Modified:
744                                                 rowState = DataRowState.Unchanged;
745                                                 break;
746                                         case DataRowState.Deleted:
747                                                 rowState = DataRowState.Unchanged;
748                                                 break;
749                                 } 
750                                 
751                         }                       
752                         else {
753                                 // If rows are just loaded via Xml the original values are null.
754                                 // So in this case we have to remove all columns.
755                                 // FIXME: I'm not realy sure, does this break something else, but
756                                 // if so: FIXME ;)
757                                 
758                                 if ((rowState & DataRowState.Added) > 0)
759                                         _table.Rows.Remove (this);
760                         }
761                 }
762
763                 /// <summary>
764                 /// Sets the error description for a column specified as a DataColumn.
765                 /// </summary>
766                 public void SetColumnError (DataColumn column, string error) 
767                 {
768                         SetColumnError (_table.Columns.IndexOf (column), error);
769                 }
770
771                 /// <summary>
772                 /// Sets the error description for a column specified by index.
773                 /// </summary>
774                 public void SetColumnError (int columnIndex, string error) 
775                 {
776                         if (columnIndex < 0 || columnIndex >= columnErrors.Length)
777                                 throw new IndexOutOfRangeException ();
778                         columnErrors[columnIndex] = error;
779                 }
780
781                 /// <summary>
782                 /// Sets the error description for a column specified by name.
783                 /// </summary>
784                 public void SetColumnError (string columnName, string error) 
785                 {
786                         SetColumnError (_table.Columns.IndexOf (columnName), error);
787                 }
788
789                 /// <summary>
790                 /// Sets the value of the specified DataColumn to a null value.
791                 /// </summary>
792                 protected void SetNull (DataColumn column) 
793                 {
794                         this[column] = DBNull.Value;
795                 }
796
797                 /// <summary>
798                 /// Sets the parent row of a DataRow with specified new parent DataRow.
799                 /// </summary>
800                 [MonoTODO]
801                 public void SetParentRow (DataRow parentRow) 
802                 {
803                         throw new NotImplementedException ();
804                 }
805
806                 /// <summary>
807                 /// Sets the parent row of a DataRow with specified new parent DataRow and
808                 /// DataRelation.
809                 /// </summary>
810                 [MonoTODO]
811                 public void SetParentRow (DataRow parentRow, DataRelation relation) 
812                 {
813                         throw new NotImplementedException ();
814                 }
815
816                 
817                 #endregion // Methods
818         }
819 }