* DataColumn.cs (DefaultValue) : Changing null value to DBNull. Checking...
[mono.git] / mcs / class / System.Data / System.Data / DataTable.cs
1 //
2 // System.Data.DataTable.cs
3 //
4 // Author:
5 //   Franklin Wise <gracenote@earthlink.net>
6 //   Christopher Podurgiel (cpodurgiel@msn.com)
7 //   Daniel Morgan <danmorg@sc.rr.com>
8 //   Rodrigo Moya <rodrigo@ximian.com>
9 //   Tim Coleman (tim@timcoleman.com)
10 //   Ville Palo <vi64pa@koti.soon.fi>
11 //
12 // (C) Chris Podurgiel
13 // (C) Ximian, Inc 2002
14 // Copyright (C) Tim Coleman, 2002
15 // Copyright (C) Daniel Morgan, 2002-2003
16 //
17
18 using System;
19 using System.Collections;
20 using System.ComponentModel;
21 using System.Globalization;
22 using System.Runtime.Serialization;
23
24 namespace System.Data {
25         //[Designer]
26         [ToolboxItem (false)]
27         [DefaultEvent ("RowChanging")]
28         [DefaultProperty ("TableName")]
29         [DesignTimeVisible (false)]
30         [Serializable]
31         public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable 
32         {
33                 internal DataSet dataSet;   
34                 
35                 private bool _caseSensitive;
36                 private DataColumnCollection _columnCollection;
37                 private ConstraintCollection _constraintCollection;
38                 private DataView _defaultView;
39
40                 private string _displayExpression;
41                 private PropertyCollection _extendedProperties;
42                 private bool _hasErrors;
43                 private CultureInfo _locale;
44                 private int _minimumCapacity;
45                 private string _nameSpace;
46                 private DataRelationCollection _childRelations; 
47                 private DataRelationCollection _parentRelations;
48                 private string _prefix;
49                 private DataColumn[] _primaryKey;
50                 private DataRowCollection _rows;
51                 private ISite _site;
52                 private string _tableName;
53                 private bool _containsListCollection;
54                 private string _encodedTableName;
55                 internal bool _duringDataLoad;
56                 private bool dataSetPrevEnforceConstraints;
57
58
59                 
60                 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's 
61                 // CaseSensitive property. So when you lost you virginity it's gone for ever
62                 private bool _virginCaseSensitive = true;
63
64                 /// <summary>
65                 /// Initializes a new instance of the DataTable class with no arguments.
66                 /// </summary>
67                 
68                 public DataTable () 
69                 {
70                         dataSet = null;
71                         _columnCollection = new DataColumnCollection(this);
72                         _constraintCollection = new ConstraintCollection(this); 
73                         _extendedProperties = new PropertyCollection();
74                         _tableName = "";
75                         _nameSpace = null;
76                         _caseSensitive = false;         //default value
77                         _displayExpression = null;
78                         _primaryKey = null;
79                         _site = null;
80                         _rows = new DataRowCollection (this);
81                         
82                         //LAMESPEC: spec says 25 impl does 50
83                         _minimumCapacity = 50;
84                         
85                         _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
86                         _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
87
88                 
89                         _defaultView = new DataView(this);
90                 }
91
92                 /// <summary>
93                 /// Intitalizes a new instance of the DataTable class with the specified table name.
94                 /// </summary>
95                 public DataTable (string tableName) : this () 
96                 {
97                         _tableName = tableName;
98                 }
99
100                 /// <summary>
101                 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
102                 /// </summary>
103                 [MonoTODO]
104                 protected DataTable (SerializationInfo info, StreamingContext context)
105                         : this () 
106                 {
107                         //
108                         // TODO: Add constructor logic here
109                         //
110                 }
111
112                 /// <summary>
113                 /// Indicates whether string comparisons within the table are case-sensitive.
114                 /// </summary>
115                 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]        
116                 public bool CaseSensitive {
117                         get { return _caseSensitive; }
118                         set { 
119                                 _virginCaseSensitive = false;
120                                 _caseSensitive = value; 
121                         }
122                 }
123
124                 internal bool VirginCaseSensitive {
125                         get { return _virginCaseSensitive; }
126                         set { _virginCaseSensitive = value; }
127                 }
128
129                 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv) 
130                 {
131                         DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
132                         OnColumnChanged(e);
133                 }
134
135                 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv) 
136                 {
137                         DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
138                         OnColumnChanging (e);
139                 }
140
141                 internal void DeletedDataRow (DataRow dr, DataRowAction action) 
142                 {
143                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
144                         OnRowDeleted (e);
145                 }
146
147                 internal void DeletingDataRow (DataRow dr, DataRowAction action) 
148                 {
149                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
150                         OnRowDeleting(e);
151                 }
152
153                 internal void ChangedDataRow (DataRow dr, DataRowAction action) 
154                 {
155                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
156                         OnRowChanged (e);
157                 }
158
159                 internal void ChangingDataRow (DataRow dr, DataRowAction action) 
160                 {
161                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
162                         OnRowChanging (e);
163                 }
164
165                 /// <summary>
166                 /// Gets the collection of child relations for this DataTable.
167                 /// </summary>
168                 [Browsable (false)]
169                 [DataSysDescription ("Returns the child relations for this table.")]
170                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
171                 public DataRelationCollection ChildRelations {
172                         get {
173                                 return _childRelations;
174                         }
175                 }
176
177                 /// <summary>
178                 /// Gets the collection of columns that belong to this table.
179                 /// </summary>
180                 [DataCategory ("Data")]
181                 [DataSysDescription ("The collection that holds the columns for this table.")]
182                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
183                 public DataColumnCollection Columns {
184                         get { return _columnCollection; }
185                 }
186
187                 /// <summary>
188                 /// Gets the collection of constraints maintained by this table.
189                 /// </summary>
190                 [DataCategory ("Data")] 
191                 [DataSysDescription ("The collection that holds the constraints for this table.")]
192                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
193                 public ConstraintCollection Constraints {
194                         get { return _constraintCollection; }
195                 }
196
197                 /// <summary>
198                 /// Gets the DataSet that this table belongs to.
199                 /// </summary>
200                 [Browsable (false)]
201                 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
202                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
203                 public DataSet DataSet {
204                         get { return dataSet; }
205                 }
206
207                 
208
209                 /// <summary>
210                 /// Gets a customized view of the table which may 
211                 /// include a filtered view, or a cursor position.
212                 /// </summary>
213                 [MonoTODO]      
214                 [Browsable (false)]
215                 [DataSysDescription ("This is the default DataView for the table.")]
216                 public DataView DefaultView {
217                         get { return _defaultView; }
218                 }
219                 
220
221                 /// <summary>
222                 /// Gets or sets the expression that will return 
223                 /// a value used to represent this table in the user interface.
224                 /// </summary>
225                 [DataCategory ("Data")]
226                 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]       
227                 [DefaultValue ("")]
228                 public string DisplayExpression {
229                         get { return "" + _displayExpression; }
230                         set { _displayExpression = value; }
231                 }
232
233                 /// <summary>
234                 /// Gets the collection of customized user information.
235                 /// </summary>
236                 [Browsable (false)]
237                 [DataCategory ("Data")]
238                 [DataSysDescription ("The collection that holds custom user information.")]
239                 public PropertyCollection ExtendedProperties {
240                         get { return _extendedProperties; }
241                 }
242
243                 /// <summary>
244                 /// Gets a value indicating whether there are errors in 
245                 /// any of the_rows in any of the tables of the DataSet to 
246                 /// which the table belongs.
247                 /// </summary>
248                 [Browsable (false)]
249                 [DataSysDescription ("Returns whether the table has errors.")]
250                 public bool HasErrors {
251                         get { 
252                                 // we can not use the _hasError flag because we do not know when to turn it off!
253                                 for (int i = 0; i < _rows.Count; i++)
254                                 {
255                                         if (_rows[i].HasErrors)
256                                                 return true;
257                                 }
258                                 return false;
259                         }
260                 }
261
262                 /// <summary>
263                 /// Gets or sets the locale information used to 
264                 /// compare strings within the table.
265                 /// </summary>
266                 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
267                 public CultureInfo Locale {
268                         get { 
269                                 // if the locale is null, we check for the DataSet locale
270                                 // and if the DataSet is null we return the current culture.
271                                 // this way if DataSet locale is changed, only if there is no locale for 
272                                 // the DataTable it influece the Locale get;
273                                 if (_locale != null)
274                                         return _locale;
275                                 if (DataSet != null)
276                                         return DataSet.Locale;
277                                 return CultureInfo.CurrentCulture;
278                         }
279                         set { 
280                                 if (_locale == null || !_locale.Equals(value))
281                                         _locale = value; 
282                         }
283                 }
284
285                 /// <summary>
286                 /// Gets or sets the initial starting size for this table.
287                 /// </summary>
288                 [DataCategory ("Data")]
289                 [DataSysDescription ("Indicates an initial starting size for this table.")]
290                 [DefaultValue (50)]
291                 public int MinimumCapacity {
292                         get { return _minimumCapacity; }
293                         set { _minimumCapacity = value; }
294                 }
295
296                 /// <summary>
297                 /// Gets or sets the namespace for the XML represenation 
298                 /// of the data stored in the DataTable.
299                 /// </summary>
300                 [DataCategory ("Data")]
301                 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
302                 public string Namespace {
303                         get { return "" + _nameSpace; }
304                         set { _nameSpace = value; }
305                 }
306
307                 /// <summary>
308                 /// Gets the collection of parent relations for 
309                 /// this DataTable.
310                 /// </summary>
311                 [Browsable (false)]
312                 [DataSysDescription ("Returns the parent relations for this table.")]
313                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
314                 public DataRelationCollection ParentRelations {
315                         get {   
316                                 return _parentRelations;
317                         }
318                 }
319
320                 /// <summary>
321                 /// Gets or sets the namespace for the XML represenation
322                 ///  of the data stored in the DataTable.
323                 /// </summary>
324                 [DataCategory ("Data")]
325                 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
326                 [DefaultValue ("")]
327                 public string Prefix {
328                         get { return "" + _prefix; }
329                         set { _prefix = value; }
330                 }
331
332                 /// <summary>
333                 /// Gets or sets an array of columns that function as 
334                 /// primary keys for the data table.
335                 /// </summary>
336                 [DataCategory ("Data")]
337                 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
338                 public DataColumn[] PrimaryKey {
339                         get {
340                                 UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
341                                 if (null == uc) return new DataColumn[] {};
342                                 return uc.Columns;
343                         }
344                         set {
345
346                                 //YUK: msft removes a previous unique constraint if it is flagged as a pk  
347                                 //when a new pk is set 
348                                 
349                                 //clear Primary Key if value == null
350                                 if (null == value) {
351                                         
352                                         RemoveUniqueConstraints ();
353                                         return;
354                                 }
355
356                                 //Does constraint exist for these columns
357                                 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
358                                         this.Constraints, (DataColumn[]) value);
359
360                                 //if constraint doesn't exist for columns
361                                 //create new unique primary key constraint
362                                 if (null == uc) {
363
364                                         RemoveUniqueConstraints ();                                             
365                                         
366                                         foreach (DataColumn Col in (DataColumn[]) value) {
367
368                                                 if (Col.Table == null)
369                                                         break;
370
371                                                 if (Columns.IndexOf (Col) < 0)
372                                                         throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
373                                         }
374
375
376                                         uc = new UniqueConstraint( (DataColumn[]) value, true);
377                                         
378                                         Constraints.Add (uc);
379                                 }
380                                 else { //set existing constraint as the new primary key
381                                         UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
382                                 }
383                                 
384                         }
385                 }
386
387                 /// <summary>
388                 /// Gets the collection of_rows that belong to this table.
389                 /// </summary>
390                 [Browsable (false)]
391                 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]   
392                 public DataRowCollection Rows {
393                         get { return _rows; }
394                 }
395
396                 /// <summary>
397                 /// Gets or sets an System.ComponentModel.ISite 
398                 /// for the DataTable.
399                 /// </summary>
400                 [Browsable (false)]
401                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
402                 public override ISite Site {
403                         get { return _site; }
404                         set { _site = value; }
405                 }
406
407                 /// <summary>
408                 /// Gets or sets the name of the the DataTable.
409                 /// </summary>
410                 [DataCategory ("Data")]
411                 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
412                 [DefaultValue ("")]     
413                 [RefreshProperties (RefreshProperties.All)]
414                 public string TableName {
415                         get { return "" + _tableName; }
416                         set { _tableName = value; }
417                 }
418                 
419                 bool IListSource.ContainsListCollection {
420                         get {
421                                 // the collection is a DataView
422                                 return false;
423                         }
424                 }
425
426                 /// <summary>
427                 /// Commits all the changes made to this table since the 
428                 /// last time AcceptChanges was called.
429                 /// </summary>
430                 public void AcceptChanges () 
431                 {
432                         //FIXME: Do we need to validate anything here or
433                         //try to catch any errors to deal with them?
434                         
435                         // we do not use foreach because if one of the rows is in Delete state
436                         // it will be romeved from Rows and we get an exception.
437                         DataRow myRow;
438                         for (int i = 0; i < Rows.Count; )
439                         {
440                                 myRow = Rows[i];
441                                 myRow.AcceptChanges();
442
443                                 // if the row state is Detached it meens that it was removed from row list (Rows)
444                                 // so we should not increase 'i'.
445                                 if (myRow.RowState != DataRowState.Detached)
446                                         i++;
447                         }
448                 }
449
450                 /// <summary>
451                 /// Begins the initialization of a DataTable that is used 
452                 /// on a form or used by another component. The initialization
453                 /// occurs at runtime.
454                 /// </summary>
455                 public void BeginInit () 
456                 {
457                 }
458
459                 /// <summary>
460                 /// Turns off notifications, index maintenance, and 
461                 /// constraints while loading data.
462                 /// </summary>
463                 [MonoTODO]
464                 public void BeginLoadData () 
465                 {
466                         if (!this._duringDataLoad)
467                         {
468                                 //duringDataLoad is important to EndLoadData and
469                                 //for not throwing unexpected exceptions.
470                                 this._duringDataLoad = true;
471                         
472                                 if (this.dataSet != null)
473                                 {
474                                         //Saving old Enforce constraints state for later
475                                         //use in the EndLoadData.
476                                         this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
477                                         this.dataSet.EnforceConstraints = false;
478                                 }
479                         }
480                         return;
481                 }
482
483                 /// <summary>
484                 /// Clears the DataTable of all data.
485                 /// </summary>
486                 public void Clear () {
487                         // TODO: thow an exception if any rows that 
488                         //       have enforced child relations 
489                         //       that would result in child rows being orphaned
490                         // now we check if any ForeignKeyConstraint is referncing 'table'.
491                         if (DataSet != null)
492                         {
493                                 IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
494                         
495                                 // loop on all tables in dataset
496                                 while (tableEnumerator.MoveNext())
497                                 {
498                                         IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
499                                         // loop on all constrains in the current table
500                                         while (constraintEnumerator.MoveNext())
501                                         {
502                                                 Object o = constraintEnumerator.Current;
503                                                 // we only check ForeignKeyConstraint
504                                                 if (o is ForeignKeyConstraint)
505                                                 {
506                                                         ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
507                                                         if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
508                                                                 throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
509                                                 }
510                                         }
511                                 }
512                         }
513                         
514                         // TODO: throw a NotSupportedException if the DataTable is part
515                         //       of a DataSet that is binded to an XmlDataDocument
516                         
517                         _rows.Clear ();
518                 }
519
520                 /// <summary>
521                 /// Clones the structure of the DataTable, including
522                 ///  all DataTable schemas and constraints.
523                 /// </summary>
524                 [MonoTODO]
525                 public virtual DataTable Clone () 
526                 {
527                         DataTable Copy = new DataTable ();                      
528                         
529                         CopyProperties (Copy);
530                         return Copy;
531                 }
532
533                 /// <summary>
534                 /// Computes the given expression on the current_rows that 
535                 /// pass the filter criteria.
536                 /// </summary>
537                 [MonoTODO]
538                 public object Compute (string expression, string filter) 
539                 {
540                         // TODO: implement this function based
541                         //       on Select (expression)
542                         //
543                         // expression is an aggregate function
544                         // filter is an expression used to limit rows
545
546                         object obj = null;
547
548                         // filter rows
549                         ExpressionElement Expression = new ExpressionMainElement (filter);
550                         
551                         ArrayList List = new ArrayList ();
552                         foreach (DataRow Row in Rows) {
553                                 
554                                 if (Expression.Test (Row))
555                                         List.Add (Row);
556                         }
557                         
558                         DataRow[] rows = (DataRow [])List.ToArray (typeof (DataRow));
559
560                         // TODO: with the filtered rows, execute the aggregate function
561                         //       mentioned in expression
562
563                         return obj;
564                 }
565
566                 /// <summary>
567                 /// Copies both the structure and data for this DataTable.
568                 /// </summary>
569                 [MonoTODO]      
570                 public DataTable Copy () 
571                 {
572                         DataTable Copy = new DataTable ();
573                         CopyProperties (Copy);
574
575                         // we can not simply copy the row values (NewRow [C.ColumnName] = Row [C.ColumnName])
576                         // because if the row state is deleted we get an exception.
577                         foreach (DataRow Row in Rows) {
578                                 DataRow NewRow = Copy.NewRow ();
579                                 Copy.Rows.Add (NewRow);
580                                 Row.CopyValuesToRow(NewRow);
581                         }
582                                         
583                         return Copy;
584                 }
585
586                 [MonoTODO]
587                 private void CopyProperties (DataTable Copy) 
588                 {
589                                         
590                         Copy.CaseSensitive = CaseSensitive;
591                         Copy.VirginCaseSensitive = VirginCaseSensitive;
592
593                         // Copy.ChildRelations
594                         // Copy.Constraints
595                         // Copy.Container
596                         // Copy.DefaultView
597                         // Copy.DesignMode
598                         Copy.DisplayExpression = DisplayExpression;
599                         // Copy.ExtendedProperties
600                         Copy.Locale = Locale;
601                         Copy.MinimumCapacity = MinimumCapacity;
602                         Copy.Namespace = Namespace;
603                         // Copy.ParentRelations
604                         Copy.Prefix = Prefix;
605                         Copy.Site = Site;
606                         Copy.TableName = TableName;
607
608
609
610                         // Copy columns
611                         foreach (DataColumn Column in Columns) {
612                                 
613                                 Copy.Columns.Add (CopyColumn (Column)); 
614                         }
615
616                         CopyConstraints(Copy);
617                         // add primary key to the copy
618                         if (PrimaryKey.Length > 0)
619                         {
620                                 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
621                                 for (int i = 0; i < pColumns.Length; i++)
622                                         pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
623
624                                 Copy.PrimaryKey = pColumns;
625                         }
626                 }
627
628                 private void CopyConstraints(DataTable copy)
629                 {
630                         UniqueConstraint origUc;
631                         UniqueConstraint copyUc;
632                         for (int i = 0; i < this.Constraints.Count; i++)
633                         {
634                                 if (this.Constraints[i] is UniqueConstraint)
635                                 {
636                                         origUc = (UniqueConstraint)this.Constraints[i];
637                                         DataColumn[] columns = new DataColumn[origUc.Columns.Length];
638                                         for (int j = 0; j < columns.Length; j++)
639                                                 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
640                                         
641                                         copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
642                                         copy.Constraints.Add(copyUc);
643                                 }
644                         }
645                 }
646                 /// <summary>
647                 /// Ends the initialization of a DataTable that is used 
648                 /// on a form or used by another component. The 
649                 /// initialization occurs at runtime.
650                 /// </summary>
651                 [MonoTODO]
652                 public void EndInit () 
653                 {
654
655                 }
656
657                 /// <summary>
658                 /// Turns on notifications, index maintenance, and 
659                 /// constraints after loading data.
660                 /// </summary>
661                 [MonoTODO]
662                 public void EndLoadData() 
663                 {
664                         int i = 0;
665                         if (this._duringDataLoad) 
666                         {
667                                 //Returning from loading mode, raising exceptions as usual
668                                 this._duringDataLoad = false;
669                                 
670                                 if (this.dataSet !=null)
671                                 {
672                                         //Getting back to previous EnforceConstraint state
673                                         this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
674                                 }
675                                 for (i=0 ; i<this.Rows.Count ; i++)
676                                 {
677                                         if (this.Rows[i]._nullConstraintViolation )
678                                         {
679                                                 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
680                                         }
681                                                 
682                                 }
683
684                         }
685
686                 }
687
688                 /// <summary>
689                 /// Gets a copy of the DataTable that contains all
690                 ///  changes made to it since it was loaded or 
691                 ///  AcceptChanges was last called.
692                 /// </summary>
693                 [MonoTODO]
694                 public DataTable GetChanges() 
695                 {
696                         return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
697                 }
698
699                 /// <summary>
700                 /// Gets a copy of the DataTable containing all 
701                 /// changes made to it since it was last loaded, or 
702                 /// since AcceptChanges was called, filtered by DataRowState.
703                 /// </summary>
704                 [MonoTODO]      
705                 public DataTable GetChanges(DataRowState rowStates) 
706                 {
707                         DataTable copyTable = null;
708
709                         IEnumerator rowEnumerator = Rows.GetEnumerator();
710                         while (rowEnumerator.MoveNext()) {
711                                 DataRow row = (DataRow)rowEnumerator.Current;
712                                 if (row.IsRowChanged(rowStates)) {
713                                         if (copyTable == null)
714                                                 copyTable = Clone();
715                                         DataRow newRow = copyTable.NewRow();
716                                         copyTable.Rows.Add(newRow);
717                                         row.CopyValuesToRow(newRow);
718                                 }
719                         }
720                          
721                         return copyTable;
722                 }
723
724                 /// <summary>
725                 /// Gets an array of DataRow objects that contain errors.
726                 /// </summary>
727                 [MonoTODO]
728                 public DataRow[] GetErrors () 
729                 {
730                         ArrayList errors = new ArrayList();
731                         for (int i = 0; i < _rows.Count; i++)
732                         {
733                                 if (_rows[i].HasErrors)
734                                         errors.Add(_rows[i]);
735                         }
736                         
737                         return (DataRow[]) errors.ToArray(typeof(DataRow));
738                 }
739         
740                 /// <summary>
741                 /// This member is only meant to support Mono's infrastructure 
742                 /// </summary>
743                 protected virtual DataTable CreateInstance () 
744                 {
745                         return Activator.CreateInstance (this.GetType (), true) as DataTable;
746                 }
747
748                 /// <summary>
749                 /// This member is only meant to support Mono's infrastructure 
750                 /// </summary>
751                 protected virtual Type GetRowType () 
752                 {
753                         return typeof (DataRow);
754                 }
755
756                 /// <summary>
757                 /// This member is only meant to support Mono's infrastructure 
758                 /// 
759                 /// Used for Data Binding between System.Web.UI. controls 
760                 /// like a DataGrid
761                 /// or
762                 /// System.Windows.Forms controls like a DataGrid
763                 /// </summary>
764                 IList IListSource.GetList () 
765                 {
766                         IList list = (IList) _defaultView;
767                         return list;
768                 }
769                                 
770                 /// <summary>
771                 /// Copies a DataRow into a DataTable, preserving any 
772                 /// property settings, as well as original and current values.
773                 /// </summary>
774                 [MonoTODO]
775                 public void ImportRow (DataRow row) 
776                 {
777                         DataRow newRow = NewRow();
778                         Rows.Add(newRow);
779                         row.CopyValuesToRow(newRow);
780                         
781                 }
782
783                 /// <summary>
784                 /// This member is only meant to support Mono's infrastructure          
785                 /// </summary>
786                 [MonoTODO]
787                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) 
788                 {
789                 }
790
791                 /// <summary>
792                 /// Finds and updates a specific row. If no matching row
793                 ///  is found, a new row is created using the given values.
794                 /// </summary>
795                 [MonoTODO]
796                 public DataRow LoadDataRow (object[] values, bool fAcceptChanges) 
797                 {
798                         DataRow row = null;
799                         if (PrimaryKey.Length == 0) {
800                                 row = Rows.Add (values);
801                                 if (fAcceptChanges)
802                                         row.AcceptChanges ();
803                         }
804                         else {
805                                 bool hasPrimaryValues = true;
806                                 // initiate an array that has the values of the primary keys.
807                                 object[] keyValues = new object[PrimaryKey.Length];
808                                 for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
809                                 {
810                                         if(PrimaryKey[i].Ordinal < values.Length)
811                                                 keyValues[i] = values[PrimaryKey[i].Ordinal];
812                                         else
813                                                 hasPrimaryValues = false;
814                                 }
815                                 
816                                 if (hasPrimaryValues){
817                                         // find the row in the table.
818                                         row = Rows.Find(keyValues);
819                                 }
820                                 
821                                 if (row == null)
822                                         row = Rows.Add (values);
823                                 else
824                                         row.ItemArray = values;
825                                 
826                                 if (fAcceptChanges)
827                                         row.AcceptChanges ();
828                         }
829                                 
830                         return row;
831                 }
832
833                 /// <summary>
834                 /// Creates a new DataRow with the same schema as the table.
835                 /// </summary>
836                 public DataRow NewRow () 
837                 {
838                         return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
839                 }
840
841                 /// <summary>
842                 /// This member supports the .NET Framework infrastructure
843                 ///  and is not intended to be used directly from your code.
844                 /// </summary>
845                 protected internal DataRow[] NewRowArray (int size) 
846                 {
847                         return (DataRow[]) Array.CreateInstance (GetRowType (), size);
848                 }
849
850                 /// <summary>
851                 /// Creates a new row from an existing row.
852                 /// </summary>
853                 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder) 
854                 {
855                         return new DataRow (builder);
856                 }
857         
858
859                 /// <summary>
860                 /// Rolls back all changes that have been made to the 
861                 /// table since it was loaded, or the last time AcceptChanges
862                 ///  was called.
863                 /// </summary>
864                 [MonoTODO]
865                 public void RejectChanges () 
866                 {       
867                         for (int i = _rows.Count - 1; i >= 0; i--) {
868                                 DataRow row = _rows [i];
869                                 if (row.RowState != DataRowState.Unchanged)
870                                         _rows [i].RejectChanges ();
871                         }
872                 }
873
874                 /// <summary>
875                 /// Resets the DataTable to its original state.
876                 /// </summary>          
877                 [MonoTODO]
878                 public virtual void Reset () 
879                 {
880                         Clear();
881                         while (ParentRelations.Count > 0)
882                         {
883                                 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
884                                         dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
885                         }
886
887                         while (ChildRelations.Count > 0)
888                         {
889                                 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
890                                         dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
891                         }
892                         Constraints.Clear();
893                         Columns.Clear();
894                 }
895
896                 /// <summary>
897                 /// Gets an array of all DataRow objects.
898                 /// </summary>
899                 public DataRow[] Select () 
900                 {
901                         return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
902                 }
903
904                 /// <summary>
905                 /// Gets an array of all DataRow objects that match 
906                 /// the filter criteria in order of primary key (or 
907                 /// lacking one, order of addition.)
908                 /// </summary>
909                 public DataRow[] Select (string filterExpression) 
910                 {
911                         return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
912                 }
913
914                 /// <summary>
915                 /// Gets an array of all DataRow objects that 
916                 /// match the filter criteria, in the the 
917                 /// specified sort order.
918                 /// </summary>
919                 public DataRow[] Select (string filterExpression, string sort) 
920                 {
921                         return Select(filterExpression, sort, DataViewRowState.CurrentRows);
922                 }
923
924                 /// <summary>
925                 /// Gets an array of all DataRow objects that match
926                 /// the filter in the order of the sort, that match 
927                 /// the specified state.
928                 /// </summary>
929                 [MonoTODO]
930                 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) 
931                 {
932                         DataRow[] dataRows = null;
933                         if (filterExpression == null)
934                                 filterExpression = String.Empty;
935
936                         ExpressionElement Expression = null;
937                         if (filterExpression != null && filterExpression.Length != 0)
938                                 Expression = new ExpressionMainElement(filterExpression);
939
940                         ArrayList List = new ArrayList();
941                         int recordStateFilter = GetRowStateFilter(recordStates);
942                         foreach (DataRow Row in Rows) 
943                         {
944                                 if (((int)Row.RowState & recordStateFilter) != 0)
945                                 {
946                                         if (Expression == null || Expression.Test(Row))
947                                                 List.Add(Row);
948                                 }
949                         }
950
951                         dataRows = (DataRow[])List.ToArray(typeof(DataRow));
952                         
953
954                         if (sort != null && !sort.Equals(String.Empty)) 
955                         {
956                                 SortableColumn[] sortableColumns = null;
957
958                                 sortableColumns = ParseTheSortString (sort);
959                                 if (sortableColumns == null)
960                                         throw new Exception ("sort expression result is null");
961                                 if (sortableColumns.Length == 0)
962                                         throw new Exception("sort expression result is 0");
963
964                                 RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
965                                 dataRows = rowSorter.SortRows ();
966
967                                 sortableColumns = null;
968                                 rowSorter = null;
969                         }
970
971                         
972                         return dataRows;
973                 }
974
975                 private static int GetRowStateFilter(DataViewRowState recordStates)
976                 {
977                         int flag = 0;
978
979                         if ((recordStates & DataViewRowState.Added) != 0)
980                                 flag |= (int)DataRowState.Added;
981                         if ((recordStates & DataViewRowState.Deleted) != 0)
982                                 flag |= (int)DataRowState.Deleted;
983                         if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
984                                 flag |= (int)DataRowState.Modified;
985                         if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
986                                 flag |= (int)DataRowState.Modified;
987                         if ((recordStates & DataViewRowState.Unchanged) != 0)
988                                 flag |= (int)DataRowState.Unchanged;
989
990                         return flag;
991                 }
992
993                 /// <summary>
994                 /// Gets the TableName and DisplayExpression, if 
995                 /// there is one as a concatenated string.
996                 /// </summary>
997                 public override string ToString() 
998                 {
999                         //LAMESPEC: spec says concat the two. impl puts a 
1000                         //plus sign infront of DisplayExpression
1001                         string retVal = TableName;
1002                         if(DisplayExpression != null && DisplayExpression != "")
1003                                 retVal += " + " + DisplayExpression;
1004                         return retVal;
1005                 }
1006
1007                 
1008                 #region Events /////////////////
1009                 
1010                 /// <summary>
1011                 /// Raises the ColumnChanged event.
1012                 /// </summary>
1013                 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
1014                         if (null != ColumnChanged) {
1015                                 ColumnChanged (this, e);
1016                         }
1017                 }
1018
1019                 /// <summary>
1020                 /// Raises the ColumnChanging event.
1021                 /// </summary>
1022                 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
1023                         if (null != ColumnChanging) {
1024                                 ColumnChanging (this, e);
1025                         }
1026                 }
1027
1028                 /// <summary>
1029                 /// Raises the PropertyChanging event.
1030                 /// </summary>
1031                 [MonoTODO]
1032                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
1033                         //      if (null != PropertyChanging)
1034                         //      {
1035                         //              PropertyChanging (this, e);
1036                         //      }
1037                 }
1038
1039                 /// <summary>
1040                 /// Notifies the DataTable that a DataColumn is being removed.
1041                 /// </summary>
1042                 [MonoTODO]
1043                 protected internal virtual void OnRemoveColumn (DataColumn column) {
1044                         //      if (null != RemoveColumn)
1045                         //      {
1046                         //              RemoveColumn(this, e);
1047                         //      }
1048                 }
1049
1050                 /// <summary>
1051                 /// Raises the RowChanged event.
1052                 /// </summary>
1053                 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
1054                         if (null != RowChanged) {
1055                                 RowChanged(this, e);
1056                         }
1057                 }
1058
1059
1060                 /// <summary>
1061                 /// Raises the RowChanging event.
1062                 /// </summary>
1063                 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
1064                         if (null != RowChanging) {
1065                                 RowChanging(this, e);
1066                         }
1067                 }
1068
1069                 /// <summary>
1070                 /// Raises the RowDeleted event.
1071                 /// </summary>
1072                 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
1073                         if (null != RowDeleted) {
1074                                 RowDeleted(this, e);
1075                         }
1076                 }
1077
1078                 /// <summary>
1079                 /// Raises the RowDeleting event.
1080                 /// </summary>
1081                 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
1082                         if (null != RowDeleting) {
1083                                 RowDeleting(this, e);
1084                         }
1085                 }
1086
1087                 [MonoTODO]
1088                 private DataColumn CopyColumn (DataColumn Column) {
1089                         DataColumn Copy = new DataColumn ();
1090
1091                         // Copy all the properties of column
1092                         Copy.AllowDBNull = Column.AllowDBNull;
1093                         Copy.AutoIncrement = Column.AutoIncrement;
1094                         Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
1095                         Copy.AutoIncrementStep = Column.AutoIncrementStep;
1096                         Copy.Caption = Column.Caption;
1097                         Copy.ColumnMapping = Column.ColumnMapping;
1098                         Copy.ColumnName = Column.ColumnName;
1099                         //Copy.Container
1100                         Copy.DataType = Column.DataType;
1101                         Copy.DefaultValue = Column.DefaultValue;                        
1102                         Copy.Expression = Column.Expression;
1103                         //Copy.ExtendedProperties
1104                         Copy.MaxLength = Column.MaxLength;
1105                         Copy.Namespace = Column.Namespace;
1106                         Copy.Prefix = Column.Prefix;
1107                         Copy.ReadOnly = Column.ReadOnly;
1108                         //Copy.Site
1109                         //we do not copy the unique value - it will be copyied when copying the constraints.
1110                         //Copy.Unique = Column.Unique;
1111                         
1112                         return Copy;
1113                 }                       
1114
1115                 /// <summary>
1116                 /// Occurs when after a value has been changed for 
1117                 /// the specified DataColumn in a DataRow.
1118                 /// </summary>
1119                 [DataCategory ("Data")] 
1120                 [DataSysDescription ("Occurs when a value has been changed for this column.")]
1121                 public event DataColumnChangeEventHandler ColumnChanged;
1122
1123                 /// <summary>
1124                 /// Occurs when a value is being changed for the specified 
1125                 /// DataColumn in a DataRow.
1126                 /// </summary>
1127                 [DataCategory ("Data")]
1128                 [DataSysDescription ("Occurs when a value has been submitted for this column. The user can modify the proposed value and should throw an exception to cancel the edit.")]
1129                 public event DataColumnChangeEventHandler ColumnChanging;
1130
1131                 /// <summary>
1132                 /// Occurs after a DataRow has been changed successfully.
1133                 /// </summary>
1134                 [DataCategory ("Data")] 
1135                 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
1136                 public event DataRowChangeEventHandler RowChanged;
1137
1138                 /// <summary>
1139                 /// Occurs when a DataRow is changing.
1140                 /// </summary>
1141                 [DataCategory ("Data")] 
1142                 [DataSysDescription ("Occurs when the row is being changed so that the event handler can modify or cancel the change. The user can modify values in the row and should throw an  exception to cancel the edit.")]
1143                 public event DataRowChangeEventHandler RowChanging;
1144
1145                 /// <summary>
1146                 /// Occurs after a row in the table has been deleted.
1147                 /// </summary>
1148                 [DataCategory ("Data")] 
1149                 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")] 
1150                 public event DataRowChangeEventHandler RowDeleted;
1151
1152                 /// <summary>
1153                 /// Occurs before a row in the table is about to be deleted.
1154                 /// </summary>
1155                 [DataCategory ("Data")] 
1156                 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
1157                 public event DataRowChangeEventHandler RowDeleting;
1158                 
1159                 #endregion // Events
1160
1161                 /// <summary>
1162                 ///  Removes all UniqueConstraints
1163                 /// </summary>
1164                 private void RemoveUniqueConstraints () 
1165                 {
1166                         foreach (Constraint Cons in Constraints) {
1167                                 
1168                                 if (Cons is UniqueConstraint) {
1169                                         Constraints.Remove (Cons);
1170                                         break;
1171                                 }
1172                         }
1173                         
1174                         UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
1175                 }
1176
1177                 // to parse the sort string for DataTable:Select(expression,sort)
1178                 // into sortable columns (think ORDER BY, 
1179                 // such as, "customer ASC, price DESC" )
1180                 private SortableColumn[] ParseTheSortString (string sort) 
1181                 {
1182                         SortableColumn[] sortColumns = null;
1183                         ArrayList columns = null;
1184                 
1185                         if (sort != null && !sort.Equals ("")) {
1186                                 columns = new ArrayList ();
1187                                 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
1188                         
1189                                 for (int c = 0; c < columnExpression.Length; c++) {
1190                                         string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
1191                                 
1192                                         string columnName = columnSortInfo[0].Trim ();
1193                                         string sortOrder = "ASC";
1194                                         if (columnSortInfo.Length > 1) 
1195                                                 sortOrder = columnSortInfo[1].Trim ().ToUpper ();
1196                                         
1197                                         ListSortDirection sortDirection = ListSortDirection.Ascending;
1198                                         switch (sortOrder) {
1199                                         case "ASC":
1200                                                 sortDirection = ListSortDirection.Ascending;
1201                                                 break;
1202                                         case "DESC":
1203                                                 sortDirection = ListSortDirection.Descending;
1204                                                 break;
1205                                         default:
1206                                                 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
1207                                         }
1208                                         Int32 ord = 0;
1209                                         try {
1210                                                 ord = Int32.Parse (columnName);
1211                                         }
1212                                         catch (FormatException) {
1213                                                 ord = -1;
1214                                         }
1215                                         DataColumn dc = null;
1216                                         if (ord == -1)                          
1217                                                 dc = _columnCollection[columnName];
1218                                         else
1219                                                 dc = _columnCollection[ord];
1220                                         SortableColumn sortCol = new SortableColumn (dc,sortDirection);
1221                                         columns.Add (sortCol);
1222                                 }       
1223                                 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
1224                         }               
1225                         return sortColumns;
1226                 }
1227         
1228                 private class SortableColumn 
1229                 {
1230                         private DataColumn col;
1231                         private ListSortDirection dir;
1232
1233                         internal SortableColumn (DataColumn column, 
1234                                                 ListSortDirection direction) 
1235                         {
1236                                 col = column;
1237                                 dir = direction;
1238                         }
1239
1240                         public DataColumn Column {
1241                                 get {
1242                                         return col;
1243                                 }
1244                         }
1245
1246                         public ListSortDirection SortDirection {
1247                                 get {
1248                                         return dir;
1249                                 }
1250                         }
1251                 }
1252
1253                 private class RowSorter : IComparer 
1254                 {
1255                         private SortableColumn[] sortColumns;
1256                         private DataRow[] rowsToSort;
1257                         
1258                         internal RowSorter(DataRow[] unsortedRows, 
1259                                         SortableColumn[] sortColumns) 
1260                         {
1261                                 this.sortColumns = sortColumns;
1262                                 this.rowsToSort = unsortedRows;
1263                         }
1264
1265                         public SortableColumn[] SortColumns {
1266                                 get {
1267                                         return sortColumns;
1268                                 }
1269                         }
1270                         
1271                         public DataRow[] SortRows () 
1272                         {
1273                                 Array.Sort (rowsToSort, this);
1274                                 return rowsToSort;
1275                         }
1276
1277                         int IComparer.Compare (object x, object y) 
1278                         {
1279                                 if(x == null)
1280                                         throw new Exception ("Object to compare is null: x");
1281                                 if(y == null)
1282                                         throw new Exception ("Object to compare is null: y");
1283                                 if(!(x is DataRow))
1284                                         throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
1285                                 if(!(y is DataRow))
1286                                         throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
1287
1288                                 DataRow rowx = (DataRow) x;
1289                                 DataRow rowy = (DataRow) y;
1290
1291                                 for(int i = 0; i < sortColumns.Length; i++) {
1292                                         SortableColumn sortColumn = sortColumns[i];
1293                                         DataColumn dc = sortColumn.Column;
1294
1295                                         IComparable objx = (IComparable) rowx[dc];
1296                                         object objy = rowy[dc];
1297
1298                                         int result = CompareObjects (objx, objy);
1299                                         if (result != 0) {
1300                                                 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
1301                                                         return result;
1302                                                 }
1303                                                 else {
1304                                                         return -result;
1305                                                 }
1306                                         }
1307                                 }
1308                                 return 0;
1309                         }
1310
1311                         private int CompareObjects (object a, object b) 
1312                         {
1313                                 if (a == b)
1314                                         return 0;
1315                                 else if (a == null)
1316                                         return -1;
1317                                 else if (a == DBNull.Value)
1318                                         return -1;
1319                                 else if (b == null)
1320                                         return 1;
1321                                 else if (b == DBNull.Value)
1322                                         return 1;
1323
1324                                 if((a is string) && (b is string)) {
1325                                         a = ((string) a).ToUpper ();
1326                                         b = ((string) b).ToUpper ();                    
1327                                 }
1328
1329                                 if (a is IComparable)
1330                                         return ((a as IComparable).CompareTo (b));
1331                                 else if (b is IComparable)
1332                                         return -((b as IComparable).CompareTo (a));
1333
1334                                 throw new ArgumentException ("Neither a nor b IComparable");
1335                         }
1336                 }
1337         }
1338 }