c8f06e7ec0bd6f00383bb1d1d3f8f70a9e64cfaf
[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-2003
15 // Copyright (C) Daniel Morgan, 2002-2003
16 //
17
18 //
19 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
20 //
21 // Permission is hereby granted, free of charge, to any person obtaining
22 // a copy of this software and associated documentation files (the
23 // "Software"), to deal in the Software without restriction, including
24 // without limitation the rights to use, copy, modify, merge, publish,
25 // distribute, sublicense, and/or sell copies of the Software, and to
26 // permit persons to whom the Software is furnished to do so, subject to
27 // the following conditions:
28 // 
29 // The above copyright notice and this permission notice shall be
30 // included in all copies or substantial portions of the Software.
31 // 
32 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 //
40
41 using System;
42 using System.Data.Common;
43 using System.Collections;
44 using System.ComponentModel;
45 using System.Globalization;
46 using System.IO;
47 using System.Runtime.Serialization;
48 using System.Xml;
49 using System.Xml.Schema;
50 using Mono.Data.SqlExpressions;
51
52 namespace System.Data {
53         //[Designer]
54         [ToolboxItem (false)]
55         [DefaultEvent ("RowChanging")]
56         [DefaultProperty ("TableName")]
57         [DesignTimeVisible (false)]
58         [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
59         [Serializable]
60         public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable 
61         {
62                 #region Fields
63
64                 internal DataSet dataSet;   
65                 
66                 private bool _caseSensitive;
67                 private DataColumnCollection _columnCollection;
68                 private ConstraintCollection _constraintCollection;
69                 private DataView _defaultView;
70
71                 private string _displayExpression;
72                 private PropertyCollection _extendedProperties;
73                 private bool _hasErrors;
74                 private CultureInfo _locale;
75                 private int _minimumCapacity;
76                 private string _nameSpace;
77                 private DataRelationCollection _childRelations; 
78                 private DataRelationCollection _parentRelations;
79                 private string _prefix;
80                 private DataColumn[] _primaryKey;
81                 private DataRowCollection _rows;
82                 private ISite _site;
83                 private string _tableName;
84                 private bool _containsListCollection;
85                 private string _encodedTableName;
86                 internal bool _duringDataLoad;
87                 internal bool _nullConstraintViolationDuringDataLoad;
88                 private bool dataSetPrevEnforceConstraints;
89                 private bool dataTablePrevEnforceConstraints;
90                 private bool enforceConstraints = true;
91                 private DataRowBuilder _rowBuilder;
92                 private ArrayList _indexes;
93                 private RecordCache _recordCache;
94                 private int _defaultValuesRowIndex = -1;
95                 protected internal bool fInitInProgress;
96
97                 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's 
98                 // CaseSensitive property. So when you lost you virginity it's gone for ever
99                 private bool _virginCaseSensitive = true;
100                 
101                 #endregion //Fields
102                 
103                 private delegate void PostEndInit();
104
105                 /// <summary>
106                 /// Initializes a new instance of the DataTable class with no arguments.
107                 /// </summary>
108                 public DataTable () 
109                 {
110                         dataSet = null;
111                         _columnCollection = new DataColumnCollection(this);
112                         _constraintCollection = new ConstraintCollection(this); 
113                         _extendedProperties = new PropertyCollection();
114                         _tableName = "";
115                         _nameSpace = null;
116                         _caseSensitive = false;         //default value
117                         _displayExpression = null;
118                         _primaryKey = null;
119                         _site = null;
120                         _rows = new DataRowCollection (this);
121                         _indexes = new ArrayList();
122                         _recordCache = new RecordCache(this);
123                         
124                         //LAMESPEC: spec says 25 impl does 50
125                         _minimumCapacity = 50;
126                         
127                         _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
128                         _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
129
130                         _defaultView = new DataView(this);
131                 }
132
133                 /// <summary>
134                 /// Intitalizes a new instance of the DataTable class with the specified table name.
135                 /// </summary>
136                 public DataTable (string tableName) : this () 
137                 {
138                         _tableName = tableName;
139                 }
140
141                 /// <summary>
142                 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
143                 /// </summary>
144                 [MonoTODO]
145                 protected DataTable (SerializationInfo info, StreamingContext context)
146                         : this () 
147                 {
148                         string schema = info.GetString ("XmlSchema");
149                         string data = info.GetString ("XmlDiffGram");
150                         
151                         DataSet ds = new DataSet ();
152                         ds.ReadXmlSchema (new StringReader (schema));
153                         ds.Tables [0].CopyProperties (this);
154                         ds = new DataSet ();
155                         ds.Tables.Add (this);
156                         ds.ReadXml (new StringReader (data), XmlReadMode.DiffGram);
157                         ds.Tables.Remove (this);
158 /* keeping for a while. With the change above, we shouldn't have to consider 
159  * DataTable mode in schema inference/read.
160                         XmlSchemaMapper mapper = new XmlSchemaMapper (this);
161                         XmlTextReader xtr = new XmlTextReader(new StringReader (schema));
162                         mapper.Read (xtr);
163                         
164                         XmlDiffLoader loader = new XmlDiffLoader (this);
165                         xtr = new XmlTextReader(new StringReader (data));
166                         loader.Load (xtr);
167 */
168                 }
169
170 #if NET_2_0
171                 public DataTable (string tableName, string tbNamespace)
172                         : this (tableName)
173                 {
174                         _nameSpace = tbNamespace;
175                 }
176 #endif
177
178                 /// <summary>
179                 /// Indicates whether string comparisons within the table are case-sensitive.
180                 /// </summary>
181                 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]        
182                 public bool CaseSensitive {
183                         get { 
184                                 if (_virginCaseSensitive && dataSet != null)
185                                         return dataSet.CaseSensitive; 
186                                 else
187                                         return _caseSensitive;
188                                 }
189                         set {
190                                 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
191                                         throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");
192                                 }
193                                 _virginCaseSensitive = false;
194                                 _caseSensitive = value; 
195                         }
196                 }
197
198                 internal bool VirginCaseSensitive {
199                         get { return _virginCaseSensitive; }
200                         set { _virginCaseSensitive = value; }
201                 }
202
203                 internal ArrayList Indexes{
204                         get { return _indexes; }
205                 }
206
207                 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv) 
208                 {
209                         DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
210                         OnColumnChanged(e);
211                 }
212
213                 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv) 
214                 {
215                         DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
216                         OnColumnChanging (e);
217                 }
218
219                 internal void DeletedDataRow (DataRow dr, DataRowAction action) 
220                 {
221                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
222                         OnRowDeleted (e);
223                 }
224
225                 internal void DeletingDataRow (DataRow dr, DataRowAction action) 
226                 {
227                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
228                         OnRowDeleting(e);
229                 }
230
231                 internal void ChangedDataRow (DataRow dr, DataRowAction action) 
232                 {
233                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
234                         OnRowChanged (e);
235                 }
236
237                 internal void ChangingDataRow (DataRow dr, DataRowAction action) 
238                 {
239                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
240                         OnRowChanging (e);
241                 }
242
243                 /// <summary>
244                 /// Gets the collection of child relations for this DataTable.
245                 /// </summary>
246                 [Browsable (false)]
247                 [DataSysDescription ("Returns the child relations for this table.")]
248                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
249                 public DataRelationCollection ChildRelations {
250                         get {
251                                 return _childRelations;
252                         }
253                 }
254
255                 /// <summary>
256                 /// Gets the collection of columns that belong to this table.
257                 /// </summary>
258                 [DataCategory ("Data")]
259                 [DataSysDescription ("The collection that holds the columns for this table.")]
260                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
261                 public DataColumnCollection Columns {
262                         get { return _columnCollection; }
263                 }
264
265                 /// <summary>
266                 /// Gets the collection of constraints maintained by this table.
267                 /// </summary>
268                 [DataCategory ("Data")] 
269                 [DataSysDescription ("The collection that holds the constraints for this table.")]
270                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
271                 public ConstraintCollection Constraints {
272                         get { return _constraintCollection; }
273                 }
274
275                 /// <summary>
276                 /// Gets the DataSet that this table belongs to.
277                 /// </summary>
278                 [Browsable (false)]
279                 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
280                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
281                 public DataSet DataSet {
282                         get { return dataSet; }
283                 }
284
285                 
286
287                 /// <summary>
288                 /// Gets a customized view of the table which may 
289                 /// include a filtered view, or a cursor position.
290                 /// </summary>
291                 [MonoTODO]      
292                 [Browsable (false)]
293                 [DataSysDescription ("This is the default DataView for the table.")]
294                 public DataView DefaultView {
295                         get { return _defaultView; }
296                 }
297                 
298
299                 /// <summary>
300                 /// Gets or sets the expression that will return 
301                 /// a value used to represent this table in the user interface.
302                 /// </summary>
303                 [DataCategory ("Data")]
304                 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]       
305                 [DefaultValue ("")]
306                 public string DisplayExpression {
307                         get { return _displayExpression == null ? "" : _displayExpression; }
308                         set { _displayExpression = value; }
309                 }
310
311                 /// <summary>
312                 /// Gets the collection of customized user information.
313                 /// </summary>
314                 [Browsable (false)]
315                 [DataCategory ("Data")]
316                 [DataSysDescription ("The collection that holds custom user information.")]
317                 public PropertyCollection ExtendedProperties {
318                         get { return _extendedProperties; }
319                 }
320
321                 /// <summary>
322                 /// Gets a value indicating whether there are errors in 
323                 /// any of the_rows in any of the tables of the DataSet to 
324                 /// which the table belongs.
325                 /// </summary>
326                 [Browsable (false)]
327                 [DataSysDescription ("Returns whether the table has errors.")]
328                 public bool HasErrors {
329                         get { 
330                                 // we can not use the _hasError flag because we do not know when to turn it off!
331                                 for (int i = 0; i < _rows.Count; i++)
332                                 {
333                                         if (_rows[i].HasErrors)
334                                                 return true;
335                                 }
336                                 return false;
337                         }
338                 }
339
340                 /// <summary>
341                 /// Gets or sets the locale information used to 
342                 /// compare strings within the table.
343                 /// </summary>
344                 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
345                 public CultureInfo Locale {
346                         get { 
347                                 // if the locale is null, we check for the DataSet locale
348                                 // and if the DataSet is null we return the current culture.
349                                 // this way if DataSet locale is changed, only if there is no locale for 
350                                 // the DataTable it influece the Locale get;
351                                 if (_locale != null)
352                                         return _locale;
353                                 if (DataSet != null)
354                                         return DataSet.Locale;
355                                 return CultureInfo.CurrentCulture;
356                         }
357                         set { 
358                                 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
359                                         throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");
360                                 }
361                                 if (_locale == null || !_locale.Equals(value))
362                                         _locale = value; 
363                         }
364                 }
365
366                 /// <summary>
367                 /// Gets or sets the initial starting size for this table.
368                 /// </summary>
369                 [DataCategory ("Data")]
370                 [DataSysDescription ("Indicates an initial starting size for this table.")]
371                 [DefaultValue (50)]
372                 public int MinimumCapacity {
373                         get { return _minimumCapacity; }
374                         set { _minimumCapacity = value; }
375                 }
376
377                 /// <summary>
378                 /// Gets or sets the namespace for the XML represenation 
379                 /// of the data stored in the DataTable.
380                 /// </summary>
381                 [DataCategory ("Data")]
382                 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
383                 public string Namespace {
384                         get {
385                                 if (_nameSpace != null)
386                                         return _nameSpace;
387                                 else if (dataSet != null)
388                                         return dataSet.Namespace;
389                                 return String.Empty;
390                         }
391                         set { _nameSpace = value; }
392                 }
393
394                 /// <summary>
395                 /// Gets the collection of parent relations for 
396                 /// this DataTable.
397                 /// </summary>
398                 [Browsable (false)]
399                 [DataSysDescription ("Returns the parent relations for this table.")]
400                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
401                 public DataRelationCollection ParentRelations {
402                         get {   
403                                 return _parentRelations;
404                         }
405                 }
406
407                 /// <summary>
408                 /// Gets or sets the namespace for the XML represenation
409                 ///  of the data stored in the DataTable.
410                 /// </summary>
411                 [DataCategory ("Data")]
412                 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
413                 [DefaultValue ("")]
414                 public string Prefix {
415                         get { return _prefix == null ? "" : _prefix; }
416                         set {
417                                 // Prefix cannot contain any special characters other than '_' and ':'
418                                 for (int i = 0; i < value.Length; i++) {
419                                         if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
420                                                 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
421                                 }
422                                 _prefix = value;
423                         }
424                 }
425
426                 /// <summary>
427                 /// Gets or sets an array of columns that function as 
428                 /// primary keys for the data table.
429                 /// </summary>
430                 [DataCategory ("Data")]
431                 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
432                 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
433                 [TypeConverterAttribute ("System.Data.PrimaryKeyTypeConverter, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
434                 public DataColumn[] PrimaryKey {
435                         get {
436                                 UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
437                                 if (null == uc) return new DataColumn[] {};
438                                 return uc.Columns;
439                         }
440                         set {
441                                 UniqueConstraint oldPKConstraint = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
442                                 
443                                 // first check if value is the same as current PK.
444                                 if (oldPKConstraint != null && DataColumn.AreColumnSetsTheSame(value, oldPKConstraint.Columns))
445                                         return;
446
447                                 // remove PK Constraint
448                                 if(oldPKConstraint != null) {
449                                         Constraints.Remove(oldPKConstraint);
450                                 }
451                                 
452                                 if (value != null) {
453                                         //Does constraint exist for these columns
454                                         UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
455                                                 this.Constraints, (DataColumn[]) value);
456                                 
457                                         //if constraint doesn't exist for columns
458                                         //create new unique primary key constraint
459                                         if (null == uc) {
460                                                 foreach (DataColumn Col in (DataColumn[]) value) {
461
462                                                         if (Col.Table == null)
463                                                                 break;
464
465                                                         if (Columns.IndexOf (Col) < 0)
466                                                                 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
467                                                 }
468
469
470                                                 uc = new UniqueConstraint( (DataColumn[]) value, true);
471                                         
472                                                 Constraints.Add (uc);
473                                         }
474                                         else { 
475                                                 //set existing constraint as the new primary key
476                                                 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
477                                         }
478
479                                         // if we really supplied some columns fo primary key - 
480                                         // rebuild indexes fo all foreign key constraints
481                                         if(value.Length > 0) {
482                                                 foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
483                                                         constraint.AssertConstraint();
484                                                 }
485                                         }
486                                 }
487                                 else {
488                                         // if primary key is null now - drop all the indexes for foreign key constraints
489                                         foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
490                                                 Index index = constraint.Index;
491                                                 constraint.Index = null;
492                                                 this.DropIndex(index);
493                                         }
494                                 }
495                                 
496                         }
497                 }
498
499                 /// <summary>
500                 /// Gets the collection of_rows that belong to this table.
501                 /// </summary>
502                 [Browsable (false)]
503                 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]   
504                 public DataRowCollection Rows {
505                         get { return _rows; }
506                 }
507
508                 /// <summary>
509                 /// Gets or sets an System.ComponentModel.ISite 
510                 /// for the DataTable.
511                 /// </summary>
512                 [Browsable (false)]
513                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
514                 public override ISite Site {
515                         get { return _site; }
516                         set { _site = value; }
517                 }
518
519                 /// <summary>
520                 /// Gets or sets the name of the the DataTable.
521                 /// </summary>
522                 [DataCategory ("Data")]
523                 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
524                 [DefaultValue ("")]     
525                 [RefreshProperties (RefreshProperties.All)]
526                 public string TableName {
527                         get { return _tableName == null ? "" : _tableName; }
528                         set { _tableName = value; }
529                 }
530                 
531                 bool IListSource.ContainsListCollection {
532                         get {
533                                 // the collection is a DataView
534                                 return false;
535                         }
536                 }
537                 
538                 internal RecordCache RecordCache {
539                         get {
540                                 return _recordCache;
541                         }
542                 }
543                 
544                 private bool EnforceConstraints {
545                         get { return enforceConstraints; }
546                         set {
547                                 if (value != enforceConstraints) {
548                                         if (value) {
549                                                 // first assert all unique constraints
550                                                 foreach (UniqueConstraint uc in this.Constraints.UniqueConstraints)
551                                                 uc.AssertConstraint ();
552                                                 // then assert all foreign keys
553                                                 foreach (ForeignKeyConstraint fk in this.Constraints.ForeignKeyConstraints)
554                                                         fk.AssertConstraint ();
555                                         }
556                                         enforceConstraints = value;
557                                 }
558                         }
559                 }
560
561                 internal bool RowsExist(DataColumn[] columns, DataColumn[] relatedColumns,DataRow row)
562                 {
563                         int curIndex = row.IndexFromVersion(DataRowVersion.Default);
564                         int tmpRecord = RecordCache.NewRecord();
565
566                         try {
567                                 for (int i = 0; i < relatedColumns.Length; i++) {
568                                         // according to MSDN: the DataType value for both columns must be identical.
569                                         columns[i].DataContainer.CopyValue(relatedColumns[i].DataContainer, curIndex, tmpRecord);
570                                 }
571
572                                 return RowsExist(columns, tmpRecord, relatedColumns.Length);
573                         }
574                         finally {
575                                 RecordCache.DisposeRecord(tmpRecord);
576                         }
577                 }
578
579                 bool RowsExist(DataColumn[] columns, int index, int length)
580                 {
581                         bool rowsExist = false;
582                         Index indx = this.GetIndexByColumns (columns);
583
584                         if (indx != null) { // lookup for a row in index                        
585                                 rowsExist = (indx.FindSimple (index, length, false) != null);
586                         } 
587                         
588                         if(indx == null || rowsExist == false) { 
589                                 // no index or rowExist= false, we have to perform full-table scan
590                                 // check that there is a parent for this row.
591                                 foreach (DataRow thisRow in this.Rows) {
592                                         if (thisRow.RowState != DataRowState.Deleted) {
593                                                 bool match = true;
594                                                 // check if the values in the columns are equal
595                                                 int thisIndex = thisRow.IndexFromVersion(DataRowVersion.Current);
596                                                 foreach (DataColumn column in columns) {
597                                                         if (column.DataContainer.CompareValues(thisIndex, index) != 0) {
598                                                                 match = false;
599                                                                 break;
600                                                         }       
601                                                 }
602                                                 if (match) {// there is a row with columns values equals to those supplied.
603                                                         rowsExist = true;
604                                                         break;
605                                                 }
606                                         }
607                                 }                               
608                         }
609                         return rowsExist;
610                 }
611
612                 /// <summary>
613                 /// Commits all the changes made to this table since the 
614                 /// last time AcceptChanges was called.
615                 /// </summary>
616                 public void AcceptChanges () 
617                 {
618                         //FIXME: Do we need to validate anything here or
619                         //try to catch any errors to deal with them?
620                         
621                         // we do not use foreach because if one of the rows is in Delete state
622                         // it will be romeved from Rows and we get an exception.
623                         DataRow myRow;
624                         for (int i = 0; i < Rows.Count; )
625                         {
626                                 myRow = Rows[i];
627                                 myRow.AcceptChanges();
628
629                                 // if the row state is Detached it meens that it was removed from row list (Rows)
630                                 // so we should not increase 'i'.
631                                 if (myRow.RowState != DataRowState.Detached)
632                                         i++;
633                         }
634                 }
635
636                 /// <summary>
637                 /// Begins the initialization of a DataTable that is used 
638                 /// on a form or used by another component. The initialization
639                 /// occurs at runtime.
640                 /// </summary>
641                 public virtual void BeginInit () 
642                 {
643                         fInitInProgress = true;
644                 }
645
646                 /// <summary>
647                 /// Turns off notifications, index maintenance, and 
648                 /// constraints while loading data.
649                 /// </summary>
650                 [MonoTODO]
651                 public void BeginLoadData () 
652                 {
653                         if (!this._duringDataLoad)
654                         {
655                                 //duringDataLoad is important to EndLoadData and
656                                 //for not throwing unexpected exceptions.
657                                 this._duringDataLoad = true;
658                                 this._nullConstraintViolationDuringDataLoad = false;
659                         
660                                 if (this.dataSet != null)
661                                 {
662                                         //Saving old Enforce constraints state for later
663                                         //use in the EndLoadData.
664                                         this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
665                                         this.dataSet.EnforceConstraints = false;
666                                 }
667                                 else {
668                                         //if table does not belong to any data set use EnforceConstraints of the table
669                                         this.dataTablePrevEnforceConstraints = this.EnforceConstraints;
670                                         this.EnforceConstraints = false;
671                                 }
672                         }
673                         return;
674                 }
675
676                 /// <summary>
677                 /// Clears the DataTable of all data.
678                 /// </summary>
679                 public void Clear () {
680                         // TODO: thow an exception if any rows that 
681                         //       have enforced child relations 
682                         //       that would result in child rows being orphaned
683                         // now we check if any ForeignKeyConstraint is referncing 'table'.
684                         if (DataSet != null)
685                         {
686                                 if (DataSet._xmlDataDocument != null) 
687                                         throw new NotSupportedException ("Clear function on dataset and datatable is not supported on XmlDataDocument.");
688
689                                 IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
690                         
691                                 // loop on all tables in dataset
692                                 while (tableEnumerator.MoveNext())
693                                 {
694                                         IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
695                                         // loop on all constrains in the current table
696                                         while (constraintEnumerator.MoveNext())
697                                         {
698                                                 Object o = constraintEnumerator.Current;
699                                                 // we only check ForeignKeyConstraint
700                                                 if (o is ForeignKeyConstraint)
701                                                 {
702                                                         ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
703                                                         if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
704                                                                 throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
705                                                 }
706                                         }
707                                 }
708                         }
709                         
710                         
711                         _rows.Clear ();
712                 }
713
714                 /// <summary>
715                 /// Clones the structure of the DataTable, including
716                 ///  all DataTable schemas and constraints.
717                 /// </summary>
718                 public virtual DataTable Clone () 
719                 {
720                         DataTable Copy = new DataTable ();                      
721                         
722                         CopyProperties (Copy);
723                         return Copy;
724                 }
725
726                 /// <summary>
727                 /// Computes the given expression on the current_rows that 
728                 /// pass the filter criteria.
729                 /// </summary>
730                 [MonoTODO]
731                 public object Compute (string expression, string filter) 
732                 {
733                         // expression is an aggregate function
734                         // filter is an expression used to limit rows
735
736                         DataRow[] rows = Select(filter);
737                         
738                         Parser parser = new Parser (rows);
739                         IExpression expr = parser.Compile (expression);
740                         object obj = expr.Eval (rows[0]);
741                         
742                         return obj;
743                 }
744
745                 /// <summary>
746                 /// Copies both the structure and data for this DataTable.
747                 /// </summary>
748                 public DataTable Copy () 
749                 {
750                         DataTable copy = Clone();
751
752                         copy._duringDataLoad = true;
753                         foreach (DataRow row in Rows) {
754                                 DataRow newRow = copy.NewNotInitializedRow();
755                                 copy.Rows.Add(newRow);
756                                 CopyRow(row,newRow);
757                         }
758                         copy._duringDataLoad = false;           
759                         return copy;
760                 }
761
762                 internal void CopyRow(DataRow fromRow,DataRow toRow)
763                 {
764                         fromRow.CopyState(toRow);
765
766                         if (fromRow.HasVersion(DataRowVersion.Original)) {
767                                 toRow._original = toRow.Table.RecordCache.CopyRecord(this,fromRow._original,-1);
768                         }
769
770                         if (fromRow.HasVersion(DataRowVersion.Current)) {
771                                 toRow._current = toRow.Table.RecordCache.CopyRecord(this,fromRow._current,-1);
772                         }
773                 }
774
775                 private void CopyProperties (DataTable Copy) 
776                 {
777                                         
778                         Copy.CaseSensitive = CaseSensitive;
779                         Copy.VirginCaseSensitive = VirginCaseSensitive;
780
781                         // Copy.ChildRelations
782                         // Copy.Constraints
783                         // Copy.Container
784                         // Copy.DefaultView
785                         // Copy.DesignMode
786                         Copy.DisplayExpression = DisplayExpression;
787                         if(ExtendedProperties.Count > 0) {
788                                 //  Cannot copy extended properties directly as the property does not have a set accessor
789                                 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
790                                 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
791                                 for (int i=0; i < ExtendedProperties.Count; i++)
792                                         Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
793                         }
794                         Copy.Locale = Locale;
795                         Copy.MinimumCapacity = MinimumCapacity;
796                         Copy.Namespace = Namespace;
797                         // Copy.ParentRelations
798                         Copy.Prefix = Prefix;
799                         Copy.Site = Site;
800                         Copy.TableName = TableName;
801
802
803
804                         // Copy columns
805                         foreach (DataColumn Column in Columns) {
806                                 
807                                 Copy.Columns.Add (CopyColumn (Column)); 
808                         }
809
810                         CopyConstraints(Copy);
811                         // add primary key to the copy
812                         if (PrimaryKey.Length > 0)
813                         {
814                                 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
815                                 for (int i = 0; i < pColumns.Length; i++)
816                                         pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
817
818                                 Copy.PrimaryKey = pColumns;
819                         }
820                 }
821
822                 private void CopyConstraints(DataTable copy)
823                 {
824                         UniqueConstraint origUc;
825                         UniqueConstraint copyUc;
826                         for (int i = 0; i < this.Constraints.Count; i++)
827                         {
828                                 if (this.Constraints[i] is UniqueConstraint)
829                                 {
830                                         origUc = (UniqueConstraint)this.Constraints[i];
831                                         DataColumn[] columns = new DataColumn[origUc.Columns.Length];
832                                         for (int j = 0; j < columns.Length; j++)
833                                                 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
834                                         
835                                         copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
836                                         copy.Constraints.Add(copyUc);
837                                 }
838                         }
839                 }
840                 /// <summary>
841                 /// Ends the initialization of a DataTable that is used 
842                 /// on a form or used by another component. The 
843                 /// initialization occurs at runtime.
844                 /// </summary>
845                 [MonoTODO]
846                 public virtual void EndInit () 
847                 {
848                         fInitInProgress = false;
849                         // Add the constraints
850                         PostEndInit _postEndInit = new PostEndInit (_constraintCollection.PostEndInit);
851                         _postEndInit();
852                 }
853
854                 /// <summary>
855                 /// Turns on notifications, index maintenance, and 
856                 /// constraints after loading data.
857                 /// </summary>
858                 public void EndLoadData() 
859                 {
860                         int i = 0;
861                         if (this._duringDataLoad) 
862                         {
863                                 
864                                 if (this.dataSet !=null)
865                                 {
866                                         //Getting back to previous EnforceConstraint state
867                                         this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
868                                 }
869                                 else {
870                                         //Getting back to the table's previous EnforceConstraint state
871                                         this.EnforceConstraints = this.dataTablePrevEnforceConstraints;
872                                 }
873
874                                 if(this._nullConstraintViolationDuringDataLoad) {
875                                         this._nullConstraintViolationDuringDataLoad = false;
876                                         throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
877                                 }
878
879                                 //Returning from loading mode, raising exceptions as usual
880                                 this._duringDataLoad = false;
881
882                         }
883
884                 }
885
886                 /// <summary>
887                 /// Gets a copy of the DataTable that contains all
888                 ///  changes made to it since it was loaded or 
889                 ///  AcceptChanges was last called.
890                 /// </summary>
891                 public DataTable GetChanges() 
892                 {
893                         return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
894                 }
895
896                 /// <summary>
897                 /// Gets a copy of the DataTable containing all 
898                 /// changes made to it since it was last loaded, or 
899                 /// since AcceptChanges was called, filtered by DataRowState.
900                 /// </summary>
901                 public DataTable GetChanges(DataRowState rowStates) 
902                 {
903                         DataTable copyTable = null;
904
905                         IEnumerator rowEnumerator = Rows.GetEnumerator();
906                         while (rowEnumerator.MoveNext()) {
907                                 DataRow row = (DataRow)rowEnumerator.Current;
908                                 // The spec says relationship constraints may cause Unchanged parent rows to be included but
909                                 // MS .NET 1.1 does not include Unchanged rows even if their child rows are changed.
910                                 if (row.IsRowChanged(rowStates)) {
911                                         if (copyTable == null)
912                                                 copyTable = Clone();
913                                         DataRow newRow = copyTable.NewRow();
914                                         row.CopyValuesToRow(newRow);
915                                         copyTable.Rows.Add (newRow);
916                                 }
917                         }
918                          
919                         return copyTable;
920                 }
921
922 #if NET_2_0
923                 [MonoTODO]
924                 public DataTableReader GetDataReader ()
925                 {
926                         throw new NotImplementedException ();
927                 }
928 #endif
929
930                 /// <summary>
931                 /// Gets an array of DataRow objects that contain errors.
932                 /// </summary>
933                 public DataRow[] GetErrors () 
934                 {
935                         ArrayList errors = new ArrayList();
936                         for (int i = 0; i < _rows.Count; i++)
937                         {
938                                 if (_rows[i].HasErrors)
939                                         errors.Add(_rows[i]);
940                         }
941                         
942                         return (DataRow[]) errors.ToArray(typeof(DataRow));
943                 }
944         
945                 /// <summary>
946                 /// This member is only meant to support Mono's infrastructure 
947                 /// </summary>
948                 protected virtual DataTable CreateInstance () 
949                 {
950                         return Activator.CreateInstance (this.GetType (), true) as DataTable;
951                 }
952
953                 /// <summary>
954                 /// This member is only meant to support Mono's infrastructure 
955                 /// </summary>
956                 protected virtual Type GetRowType () 
957                 {
958                         return typeof (DataRow);
959                 }
960
961                 /// <summary>
962                 /// This member is only meant to support Mono's infrastructure 
963                 /// 
964                 /// Used for Data Binding between System.Web.UI. controls 
965                 /// like a DataGrid
966                 /// or
967                 /// System.Windows.Forms controls like a DataGrid
968                 /// </summary>
969                 IList IListSource.GetList () 
970                 {
971                         IList list = (IList) _defaultView;
972                         return list;
973                 }
974                                 
975                 /// <summary>
976                 /// Copies a DataRow into a DataTable, preserving any 
977                 /// property settings, as well as original and current values.
978                 /// </summary>
979                 public void ImportRow (DataRow row) 
980                 {
981                         DataRow newRow = NewRow();
982                         Rows.Add(newRow);
983                         row.CopyValuesToRow(newRow);
984                         
985                 }
986
987                 internal int DefaultValuesRowIndex
988                 {
989                         get {
990                                 return _defaultValuesRowIndex;
991                         }       
992                 }
993
994                 /// <summary>
995                 /// This member is only meant to support Mono's infrastructure          
996                 /// </summary>
997                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) 
998                 {
999                         DataSet dset;
1000                         if (dataSet != null)
1001                                 dset = dataSet;
1002                         else {
1003                                 dset = new DataSet ("tmpDataSet");
1004                                 dset.Tables.Add (this);
1005                         }
1006                         
1007                         StringWriter sw = new StringWriter ();
1008                         XmlTextWriter tw = new XmlTextWriter (sw);
1009                         dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);
1010                         tw.Close ();
1011                         
1012                         StringWriter sw2 = new StringWriter ();
1013                         DataTableCollection tables = new DataTableCollection (dset);
1014                         tables.Add (this);
1015                         XmlSchema schema = dset.BuildSchema (tables, null);
1016                         schema.Write (sw2);
1017                         sw2.Close ();
1018                         
1019                         info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));
1020                         info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));
1021                 }
1022
1023 #if NET_2_0
1024                 [MonoTODO]
1025                 public void Load (IDataReader reader)
1026                 {
1027                         throw new NotImplementedException ();
1028                 }
1029
1030                 [MonoTODO]
1031                 public void Load (IDataReader reader, LoadOption loadOption)
1032                 {
1033                         throw new NotImplementedException ();
1034                 }
1035 #endif
1036
1037                 /// <summary>
1038                 /// Finds and updates a specific row. If no matching row
1039                 ///  is found, a new row is created using the given values.
1040                 /// </summary>
1041                 public DataRow LoadDataRow (object[] values, bool fAcceptChanges) 
1042                 {
1043                         DataRow row = null;
1044                         if (PrimaryKey.Length == 0) {
1045                                 row = Rows.Add (values);
1046                                 if (fAcceptChanges)
1047                                         row.AcceptChanges ();
1048                         }
1049                         else {
1050                                 bool hasPrimaryValues = true;
1051                                 // initiate an array that has the values of the primary keys.
1052                                 object[] keyValues = new object[PrimaryKey.Length];
1053                                 for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
1054                                 {
1055                                         if(PrimaryKey[i].Ordinal < values.Length)
1056                                                 keyValues[i] = values[PrimaryKey[i].Ordinal];
1057                                         else
1058                                                 hasPrimaryValues = false;
1059                                 }
1060                                 
1061                                 if (hasPrimaryValues){
1062                                         // find the row in the table.
1063                                         row = Rows.Find(keyValues);
1064                                 }
1065                                 
1066                                 if (row == null)
1067                                         row = Rows.Add (values);
1068                                 else
1069                                         row.ItemArray = values;
1070                                 
1071                                 if (fAcceptChanges)
1072                                         row.AcceptChanges ();
1073                         }
1074                                 
1075                         return row;
1076                 }
1077
1078                 internal DataRow LoadDataRow(IDataRecord record, int[] mapping, bool fAcceptChanges)
1079                 {
1080                         DataRow row = null;
1081                         if (PrimaryKey.Length == 0) {
1082                                 row = NewRow();
1083                                 row.SetValuesFromDataRecord(record, mapping);
1084                                 Rows.Add (row);
1085
1086                                 if (fAcceptChanges) {
1087                                         row.AcceptChanges();
1088                                 }
1089                         }
1090                         else {
1091                                 bool hasPrimaryValues = true;
1092                                 int tmpRecord = this.RecordCache.NewRecord();
1093                                 try {
1094                                         for (int i = 0; i < PrimaryKey.Length && hasPrimaryValues; i++) {
1095                                                 DataColumn primaryKeyColumn = PrimaryKey[i];
1096                                                 int ordinal = primaryKeyColumn.Ordinal;
1097                                                 if(ordinal < mapping.Length) {
1098                                                         primaryKeyColumn.DataContainer.SetItemFromDataRecord(tmpRecord,record,mapping[ordinal]);
1099                                                 }
1100                                                 else {
1101                                                         hasPrimaryValues = false;
1102                                                 }
1103                                         }
1104                                         
1105                                         if (hasPrimaryValues) {
1106                                                 // find the row in the table.
1107                                                 row = Rows.Find(tmpRecord,PrimaryKey.Length);
1108                                         }
1109                                 }
1110                                 finally {
1111                                         this.RecordCache.DisposeRecord(tmpRecord);
1112                                 }
1113
1114                                 if (row == null) {
1115                                         row = NewRow();
1116                                         row.SetValuesFromDataRecord(record, mapping);
1117                                         Rows.Add (row);
1118                                 }
1119                                 else {
1120                                         row.SetValuesFromDataRecord(record, mapping);
1121                                 }
1122                                 
1123                                 if (fAcceptChanges) {
1124                                         row.AcceptChanges();
1125                                 }
1126                         }                               
1127                         return row;
1128                 }
1129
1130 #if NET_2_0
1131                 [MonoTODO]
1132                 public DataRow LoadDataRow (object[] values, LoadOption loadOption)
1133                 {
1134                         throw new NotImplementedException ();
1135                 }
1136
1137                 [MonoTODO]
1138                 public void Merge (DataTable table)
1139                 {
1140                         throw new NotImplementedException ();
1141                 }
1142
1143                 [MonoTODO]
1144                 public void Merge (DataTable table, bool preserveChanges)
1145                 {
1146                         throw new NotImplementedException ();
1147                 }
1148
1149                 [MonoTODO]
1150                 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
1151                 {
1152                         throw new NotImplementedException ();
1153                 }
1154 #endif
1155
1156                 /// <summary>
1157                 /// Creates a new DataRow with the same schema as the table.
1158                 /// </summary>
1159                 public DataRow NewRow () 
1160                 {
1161                         // initiate only one row builder.
1162                         if (_rowBuilder == null)
1163                                 _rowBuilder = new DataRowBuilder (this, 0, 0);
1164                         
1165                         // new row get id -1.
1166                         _rowBuilder._rowId = -1;
1167
1168                         // initialize default values row for the first time
1169                         if ( _defaultValuesRowIndex == -1 ) {
1170                                 _defaultValuesRowIndex = RecordCache.NewRecord();
1171                                 foreach(DataColumn column in Columns) {
1172                                         column.DataContainer[_defaultValuesRowIndex] = column.DefaultValue;
1173                                 }
1174                         }
1175
1176                         return this.NewRowFromBuilder (_rowBuilder);
1177                 }
1178
1179                 /// <summary>
1180                 /// This member supports the .NET Framework infrastructure
1181                 ///  and is not intended to be used directly from your code.
1182                 /// </summary>
1183                 protected internal DataRow[] NewRowArray (int size) 
1184                 {
1185                         return (DataRow[]) Array.CreateInstance (GetRowType (), size);
1186                 }
1187
1188                 /// <summary>
1189                 /// Creates a new row from an existing row.
1190                 /// </summary>
1191                 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder) 
1192                 {
1193                         return new DataRow (builder);
1194                 }
1195                 
1196                 internal DataRow NewNotInitializedRow()
1197                 {
1198                         return new DataRow(this,-1);
1199                 }
1200
1201 #if NET_2_0
1202                 [MonoTODO]
1203                 XmlReadMode ReadXml (Stream stream)
1204                 {
1205                         throw new NotImplementedException ();
1206                 }
1207
1208                 public void ReadXmlSchema (Stream stream)
1209                 {
1210                         XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1211                         mapper.Read (new XmlTextReader(stream));
1212                 }
1213
1214                 public void ReadXmlSchema (TextReader reader)
1215                 {
1216                         XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1217                         mapper.Read (new XmlTextReader(reader));
1218                 }
1219
1220                 public void ReadXmlSchema (string fileName)
1221                 {
1222                         StreamReader reader = new StreamReader (fileName);
1223                         ReadXmlSchema (reader);
1224                         reader.Close ();
1225                 }
1226
1227                 public void ReadXmlSchema (XmlReader reader)
1228                 {
1229                         XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1230                         mapper.Read (reader);
1231                 }
1232 #endif
1233
1234                 /// <summary>
1235                 /// Rolls back all changes that have been made to the 
1236                 /// table since it was loaded, or the last time AcceptChanges
1237                 ///  was called.
1238                 /// </summary>
1239                 public void RejectChanges () 
1240                 {       
1241                         for (int i = _rows.Count - 1; i >= 0; i--) {
1242                                 DataRow row = _rows [i];
1243                                 if (row.RowState != DataRowState.Unchanged)
1244                                         _rows [i].RejectChanges ();
1245                         }
1246                 }
1247
1248                 /// <summary>
1249                 /// Resets the DataTable to its original state.
1250                 /// </summary>          
1251                 public virtual void Reset () 
1252                 {
1253                         Clear();
1254                         while (ParentRelations.Count > 0)
1255                         {
1256                                 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
1257                                         dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
1258                         }
1259
1260                         while (ChildRelations.Count > 0)
1261                         {
1262                                 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
1263                                         dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
1264                         }
1265                         Constraints.Clear();
1266                         Columns.Clear();
1267                 }
1268
1269                 /// <summary>
1270                 /// Gets an array of all DataRow objects.
1271                 /// </summary>
1272                 public DataRow[] Select () 
1273                 {
1274                         return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
1275                 }
1276
1277                 /// <summary>
1278                 /// Gets an array of all DataRow objects that match 
1279                 /// the filter criteria in order of primary key (or 
1280                 /// lacking one, order of addition.)
1281                 /// </summary>
1282                 public DataRow[] Select (string filterExpression) 
1283                 {
1284                         return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
1285                 }
1286
1287                 /// <summary>
1288                 /// Gets an array of all DataRow objects that 
1289                 /// match the filter criteria, in the the 
1290                 /// specified sort order.
1291                 /// </summary>
1292                 public DataRow[] Select (string filterExpression, string sort) 
1293                 {
1294                         return Select(filterExpression, sort, DataViewRowState.CurrentRows);
1295                 }
1296
1297                 /// <summary>
1298                 /// Gets an array of all DataRow objects that match
1299                 /// the filter in the order of the sort, that match 
1300                 /// the specified state.
1301                 /// </summary>
1302                 [MonoTODO]
1303                 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) 
1304                 {
1305                         if (filterExpression == null)
1306                                 filterExpression = String.Empty;
1307
1308                         IExpression filter = null;
1309                         if (filterExpression != String.Empty) {
1310                                 Parser parser = new Parser ();
1311                                 filter = parser.Compile (filterExpression);
1312                         }
1313
1314                         ArrayList rowList = new ArrayList();
1315                         int recordStateFilter = GetRowStateFilter(recordStates);
1316                         foreach (DataRow row in Rows) {
1317                                 if (((int)row.RowState & recordStateFilter) != 0) {
1318                                         if (filter != null && !(bool)filter.Eval (row))
1319                                                 continue;
1320                                         rowList.Add (row);
1321                                 }
1322                         }
1323
1324                         DataRow[] dataRows = (DataRow[])rowList.ToArray(typeof(DataRow));
1325
1326                         if (sort != null && !sort.Equals(String.Empty)) 
1327                         {
1328                                 SortableColumn[] sortableColumns = null;
1329
1330                                 sortableColumns = ParseTheSortString (sort);
1331                                 if (sortableColumns == null)
1332                                         throw new Exception ("sort expression result is null");
1333                                 if (sortableColumns.Length == 0)
1334                                         throw new Exception("sort expression result is 0");
1335
1336                                 RowSorter rowSorter = new RowSorter (this, dataRows, sortableColumns);
1337                                 dataRows = rowSorter.SortRows ();
1338
1339                                 sortableColumns = null;
1340                                 rowSorter = null;
1341                         }
1342
1343                         
1344                         return dataRows;
1345                 }
1346
1347                 internal void AddIndex (Index index)
1348                 {
1349                         if (_indexes == null)
1350                                 _indexes = new ArrayList();
1351
1352                         _indexes.Add (index);
1353                 }
1354
1355                 internal void RemoveIndex (Index indx)
1356                 {
1357                         _indexes.Remove (indx);
1358                 }
1359
1360                 internal Index GetIndexByColumns (DataColumn[] columns)
1361                 {
1362                         return GetIndexByColumns(columns,false,false);
1363                 }
1364
1365 //              internal Index GetIndexByColumnsExtended(DataColumn[] columns)
1366 //              {
1367 //                      DataColumn[] pkColumns = this.PrimaryKey;
1368 //                      if((pkColumns != null) && (pkColumns.Length > 0)) {
1369 //                              DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];                                  
1370 //                              Array.Copy(columns,0,cols,0,columns.Length);
1371 //                              Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1372 //
1373 //                              return _getIndexByColumns(cols,false,false);
1374 //                      } else {
1375 //                              return null;
1376 //                      }
1377 //              }
1378
1379                 internal Index GetIndexByColumns (DataColumn[] columns, bool unique)
1380                 {
1381                         return GetIndexByColumns(columns,unique,true);
1382                 }
1383
1384 //              internal Index GetIndexByColumnsExtended(DataColumn[] columns, bool unique)
1385 //              {
1386 //                      DataColumn[] pkColumns = this.PrimaryKey;
1387 //                      if((pkColumns != null) && (pkColumns.Length > 0)) {
1388 //                              DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];                                  
1389 //                              Array.Copy(columns,0,cols,0,columns.Length);
1390 //                              Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1391 //
1392 //                              return _getIndexByColumns(cols,unique,true);
1393 //                      } else  {
1394 //                              return null;
1395 //                      }
1396 //              }
1397
1398                 internal Index GetIndexByColumns(DataColumn[] columns, bool unique, bool useUnique)
1399                 {
1400                         if (_indexes != null) {
1401                                 foreach (Index indx in _indexes) {
1402                                         bool found = false;
1403                                         if ((!useUnique) || ((useUnique)&& (indx.IsUnique))) {
1404                                                 found = DataColumn.AreColumnSetsTheSame (indx.Columns, columns);
1405                                         }
1406                                         if (found)
1407                                                 return indx;
1408                                 }
1409                         }
1410
1411                         return null;
1412                 }
1413
1414                 internal void DeleteRowFromIndexes (DataRow row)
1415                 {
1416                         if (_indexes != null) {
1417                                 foreach (Index indx in _indexes) {
1418                                         indx.Delete (row);
1419                                 }
1420                         }
1421                 }
1422
1423                 private static int GetRowStateFilter(DataViewRowState recordStates)
1424                 {
1425                         int flag = 0;
1426
1427                         if ((recordStates & DataViewRowState.Added) != 0)
1428                                 flag |= (int)DataRowState.Added;
1429                         if ((recordStates & DataViewRowState.Deleted) != 0)
1430                                 flag |= (int)DataRowState.Deleted;
1431                         if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
1432                                 flag |= (int)DataRowState.Modified;
1433                         if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
1434                                 flag |= (int)DataRowState.Modified;
1435                         if ((recordStates & DataViewRowState.Unchanged) != 0)
1436                                 flag |= (int)DataRowState.Unchanged;
1437
1438                         return flag;
1439                 }
1440
1441                 /// <summary>
1442                 /// Gets the TableName and DisplayExpression, if 
1443                 /// there is one as a concatenated string.
1444                 /// </summary>
1445                 public override string ToString() 
1446                 {
1447                         //LAMESPEC: spec says concat the two. impl puts a 
1448                         //plus sign infront of DisplayExpression
1449                         string retVal = TableName;
1450                         if(DisplayExpression != null && DisplayExpression != "")
1451                                 retVal += " + " + DisplayExpression;
1452                         return retVal;
1453                 }
1454
1455 #if NET_2_0
1456                 [MonoTODO]
1457                 public void WriteXml (Stream stream)
1458                 {
1459                         throw new NotImplementedException ();
1460                 }
1461
1462                 [MonoTODO]
1463                 public void WriteXml (TextWriter writer)
1464                 {
1465                         throw new NotImplementedException ();
1466                 }
1467
1468                 [MonoTODO]
1469                 public void WriteXml (XmlWriter writer)
1470                 {
1471                         throw new NotImplementedException ();
1472                 }
1473
1474                 [MonoTODO]
1475                 public void WriteXml (string fileName)
1476                 {
1477                         throw new NotImplementedException ();
1478                 }
1479
1480                 [MonoTODO]
1481                 public void WriteXml (Stream stream, XmlWriteMode mode)
1482                 {
1483                         throw new NotImplementedException ();
1484                 }
1485
1486                 [MonoTODO]
1487                 public void WriteXml (TextWriter writer, XmlWriteMode mode)
1488                 {
1489                         throw new NotImplementedException ();
1490                 }
1491
1492                 [MonoTODO]
1493                 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
1494                 {
1495                         throw new NotImplementedException ();
1496                 }
1497
1498                 [MonoTODO]
1499                 public void WriteXml (string fileName, XmlWriteMode mode)
1500                 {
1501                         throw new NotImplementedException ();
1502                 }
1503
1504                 [MonoTODO]
1505                 public void WriteXmlSchema (Stream stream)
1506                 {
1507                         throw new NotImplementedException ();
1508                 }
1509
1510                 [MonoTODO]
1511                 public void WriteXmlSchema (TextWriter writer)
1512                 {
1513                         throw new NotImplementedException ();
1514                 }
1515
1516                 [MonoTODO]
1517                 public void WriteXmlSchema (XmlWriter writer)
1518                 {
1519                         throw new NotImplementedException ();
1520                 }
1521
1522                 [MonoTODO]
1523                 public void WriteXmlSchema (string fileName)
1524                 {
1525                         throw new NotImplementedException ();
1526                 }
1527 #endif
1528                 
1529                 #region Events 
1530                 
1531                 /// <summary>
1532                 /// Raises the ColumnChanged event.
1533                 /// </summary>
1534                 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
1535                         if (null != ColumnChanged) {
1536                                 ColumnChanged (this, e);
1537                         }
1538                 }
1539
1540                 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e) {
1541                         OnColumnChanged(e);
1542                 }
1543
1544                 /// <summary>
1545                 /// Raises the ColumnChanging event.
1546                 /// </summary>
1547                 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
1548                         if (null != ColumnChanging) {
1549                                 ColumnChanging (this, e);
1550                         }
1551                 }
1552
1553                 /// <summary>
1554                 /// Raises the PropertyChanging event.
1555                 /// </summary>
1556                 [MonoTODO]
1557                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
1558                         //      if (null != PropertyChanging)
1559                         //      {
1560                         //              PropertyChanging (this, e);
1561                         //      }
1562                 }
1563
1564                 /// <summary>
1565                 /// Notifies the DataTable that a DataColumn is being removed.
1566                 /// </summary>
1567                 [MonoTODO]
1568                 protected internal virtual void OnRemoveColumn (DataColumn column) {
1569                 }
1570
1571
1572                 /// <summary>
1573                 /// Raises the RowChanged event.
1574                 /// </summary>
1575                 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
1576                         if (null != RowChanged) {
1577                                 RowChanged(this, e);
1578                         }
1579                 }
1580
1581
1582                 /// <summary>
1583                 /// Raises the RowChanging event.
1584                 /// </summary>
1585                 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
1586                         if (null != RowChanging) {
1587                                 RowChanging(this, e);
1588                         }
1589                 }
1590
1591                 /// <summary>
1592                 /// Raises the RowDeleted event.
1593                 /// </summary>
1594                 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
1595                         if (null != RowDeleted) {
1596                                 RowDeleted(this, e);
1597                         }
1598                 }
1599
1600                 /// <summary>
1601                 /// Raises the RowDeleting event.
1602                 /// </summary>
1603                 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
1604                         if (null != RowDeleting) {
1605                                 RowDeleting(this, e);
1606                         }
1607                 }
1608
1609                 [MonoTODO]
1610                 private DataColumn CopyColumn (DataColumn Column) {
1611                         DataColumn Copy = new DataColumn ();
1612
1613                         // Copy all the properties of column
1614                         Copy.AllowDBNull = Column.AllowDBNull;
1615                         Copy.AutoIncrement = Column.AutoIncrement;
1616                         Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
1617                         Copy.AutoIncrementStep = Column.AutoIncrementStep;
1618                         Copy.Caption = Column.Caption;
1619                         Copy.ColumnMapping = Column.ColumnMapping;
1620                         Copy.ColumnName = Column.ColumnName;
1621                         //Copy.Container
1622                         Copy.DataType = Column.DataType;
1623                         Copy.DefaultValue = Column.DefaultValue;                        
1624                         Copy.Expression = Column.Expression;
1625                         //Copy.ExtendedProperties
1626                         Copy.MaxLength = Column.MaxLength;
1627                         Copy.Namespace = Column.Namespace;
1628                         Copy.Prefix = Column.Prefix;
1629                         Copy.ReadOnly = Column.ReadOnly;
1630                         //Copy.Site
1631                         //we do not copy the unique value - it will be copyied when copying the constraints.
1632                         //Copy.Unique = Column.Unique;
1633                         
1634                         return Copy;
1635                 }                       
1636
1637                 /// <summary>
1638                 /// Occurs when after a value has been changed for 
1639                 /// the specified DataColumn in a DataRow.
1640                 /// </summary>
1641                 [DataCategory ("Data")] 
1642                 [DataSysDescription ("Occurs when a value has been changed for this column.")]
1643                 public event DataColumnChangeEventHandler ColumnChanged;
1644
1645                 /// <summary>
1646                 /// Occurs when a value is being changed for the specified 
1647                 /// DataColumn in a DataRow.
1648                 /// </summary>
1649                 [DataCategory ("Data")]
1650                 [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.")]
1651                 public event DataColumnChangeEventHandler ColumnChanging;
1652
1653                 /// <summary>
1654                 /// Occurs after a DataRow has been changed successfully.
1655                 /// </summary>
1656                 [DataCategory ("Data")] 
1657                 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
1658                 public event DataRowChangeEventHandler RowChanged;
1659
1660                 /// <summary>
1661                 /// Occurs when a DataRow is changing.
1662                 /// </summary>
1663                 [DataCategory ("Data")] 
1664                 [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.")]
1665                 public event DataRowChangeEventHandler RowChanging;
1666
1667                 /// <summary>
1668                 /// Occurs after a row in the table has been deleted.
1669                 /// </summary>
1670                 [DataCategory ("Data")] 
1671                 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")] 
1672                 public event DataRowChangeEventHandler RowDeleted;
1673
1674                 /// <summary>
1675                 /// Occurs before a row in the table is about to be deleted.
1676                 /// </summary>
1677                 [DataCategory ("Data")] 
1678                 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
1679                 public event DataRowChangeEventHandler RowDeleting;
1680                 
1681                 #endregion // Events
1682
1683                 /// <summary>
1684                 ///  Removes all UniqueConstraints
1685                 /// </summary>
1686                 private void RemoveUniqueConstraints () 
1687                 {
1688                         foreach (Constraint Cons in Constraints) {
1689                                 
1690                                 if (Cons is UniqueConstraint) {
1691                                         Constraints.Remove (Cons);
1692                                         break;
1693                                 }
1694                         }
1695                         
1696                         UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
1697                 }
1698
1699                 // to parse the sort string for DataTable:Select(expression,sort)
1700                 // into sortable columns (think ORDER BY, 
1701                 // such as, "customer ASC, price DESC" )
1702                 private SortableColumn[] ParseTheSortString (string sort) 
1703                 {
1704                         SortableColumn[] sortColumns = null;
1705                         ArrayList columns = null;
1706                 
1707                         if (sort != null && !sort.Equals ("")) {
1708                                 columns = new ArrayList ();
1709                                 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
1710                         
1711                                 for (int c = 0; c < columnExpression.Length; c++) {
1712                                         string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
1713                                 
1714                                         string columnName = columnSortInfo[0].Trim ();
1715                                         string sortOrder = "ASC";
1716                                         if (columnSortInfo.Length > 1) 
1717                                                 sortOrder = columnSortInfo[1].Trim ().ToUpper (Locale);
1718                                         
1719                                         ListSortDirection sortDirection = ListSortDirection.Ascending;
1720                                         switch (sortOrder) {
1721                                         case "ASC":
1722                                                 sortDirection = ListSortDirection.Ascending;
1723                                                 break;
1724                                         case "DESC":
1725                                                 sortDirection = ListSortDirection.Descending;
1726                                                 break;
1727                                         default:
1728                                                 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
1729                                         }
1730                                         Int32 ord = 0;
1731                                         try {
1732                                                 ord = Int32.Parse (columnName);
1733                                         }
1734                                         catch (FormatException) {
1735                                                 ord = -1;
1736                                         }
1737                                         DataColumn dc = null;
1738                                         if (ord == -1)                          
1739                                                 dc = _columnCollection[columnName];
1740                                         else
1741                                                 dc = _columnCollection[ord];
1742                                         SortableColumn sortCol = new SortableColumn (dc,sortDirection);
1743                                         columns.Add (sortCol);
1744                                 }       
1745                                 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
1746                         }               
1747                         return sortColumns;
1748                 }
1749         
1750                 private class SortableColumn 
1751                 {
1752                         private DataColumn col;
1753                         private ListSortDirection dir;
1754
1755                         internal SortableColumn (DataColumn column, 
1756                                                 ListSortDirection direction) 
1757                         {
1758                                 col = column;
1759                                 dir = direction;
1760                         }
1761
1762                         public DataColumn Column {
1763                                 get {
1764                                         return col;
1765                                 }
1766                         }
1767
1768                         public ListSortDirection SortDirection {
1769                                 get {
1770                                         return dir;
1771                                 }
1772                         }
1773                 }
1774
1775                 private class RowSorter : IComparer 
1776                 {
1777                         private DataTable table;
1778                         private SortableColumn[] sortColumns;
1779                         private DataRow[] rowsToSort;
1780                         
1781                         internal RowSorter(DataTable table,
1782                                         DataRow[] unsortedRows, 
1783                                         SortableColumn[] sortColumns) 
1784                         {
1785                                 this.table = table;
1786                                 this.sortColumns = sortColumns;
1787                                 this.rowsToSort = unsortedRows;
1788                         }
1789
1790                         public SortableColumn[] SortColumns {
1791                                 get {
1792                                         return sortColumns;
1793                                 }
1794                         }
1795                         
1796                         public DataRow[] SortRows () 
1797                         {
1798                                 Array.Sort (rowsToSort, this);
1799                                 return rowsToSort;
1800                         }
1801
1802                         int IComparer.Compare (object x, object y) 
1803                         {
1804                                 if(x == null)
1805                                         throw new Exception ("Object to compare is null: x");
1806                                 if(y == null)
1807                                         throw new Exception ("Object to compare is null: y");
1808                                 if(!(x is DataRow))
1809                                         throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
1810                                 if(!(y is DataRow))
1811                                         throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
1812
1813                                 DataRow rowx = (DataRow) x;
1814                                 DataRow rowy = (DataRow) y;
1815
1816                                 for(int i = 0; i < sortColumns.Length; i++) {
1817                                         SortableColumn sortColumn = sortColumns[i];
1818                                         DataColumn dc = sortColumn.Column;
1819
1820                                         int result = dc.CompareValues(
1821                                                 rowx.IndexFromVersion(DataRowVersion.Default),
1822                                                 rowy.IndexFromVersion(DataRowVersion.Default));
1823
1824                                         if (result != 0) {
1825                                                 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
1826                                                         return result;
1827                                                 }
1828                                                 else {
1829                                                         return -result;
1830                                                 }
1831                                         }
1832                                 }
1833                                 return 0;
1834                         }
1835                 }
1836
1837                 /// <summary>
1838                 /// Creates new index for a table
1839                 /// </summary>
1840                 internal Index CreateIndex(string name, DataColumn[] columns, bool unique)
1841                 {
1842                         // first check whenever index exists on the columns
1843                         Index idx = this.GetIndexByColumns(columns);
1844                         if(idx != null) {
1845                         // if index on this columns already exists - return it
1846                                 return idx;
1847                         }
1848
1849                         // create new index
1850                         Index newIndex = new Index(name,this,columns,unique);
1851
1852                         //InitializeIndex (newIndex);                   
1853
1854                         // add new index to table indexes
1855                         this.AddIndex(newIndex);
1856                         return newIndex;
1857                 }
1858
1859 //              /// <summary>
1860 //              /// Creates new extended index for a table
1861 //              /// </summary>
1862 //              internal Index CreateIndexExtended(string name, DataColumn[] columns, bool unique)
1863 //              {
1864 //                      // first check whenever extended index exists on the columns
1865 //                      Index idx = this.GetIndexByColumnsExtended(columns);
1866 //                      if(idx != null) {
1867 //                              // if extended index on this columns already exists - return it
1868 //                              return idx;
1869 //                      }
1870 //
1871 //                      DataColumn[] pkColumns = this.PrimaryKey;
1872 //                      if((pkColumns != null) && (pkColumns.Length > 0)) {
1873 //                              DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];                                  
1874 //                              Array.Copy(columns,0,cols,0,columns.Length);
1875 //                              Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1876 //                              return this.CreateIndex(name, cols, unique);
1877 //                      } 
1878 //                      else {
1879 //                              throw new InvalidOperationException("Can not create extended index if the primary key is null or primary key does not contains any row");
1880 //                      }
1881 //              }
1882
1883 //              /// <summary>
1884 //              /// Drops extended index if it is not referenced anymore
1885 //              /// by any of table constraints
1886 //              /// </summary>
1887 //              internal void DropIndexExtended(DataColumn[] columns)
1888 //              {
1889 //                      // first check whenever extended index exists on the columns
1890 //                      Index index = this.GetIndexByColumnsExtended(columns);
1891 //                      if(index == null) {
1892 //                              // if no extended index on this columns exists - do nothing
1893 //                              return;
1894 //                      }
1895 //                      this.DropIndex(index);
1896 //              }
1897
1898                 /// <summary>
1899                 /// Drops index specified by columns if it is not referenced anymore
1900                 /// by any of table constraints
1901                 /// </summary>
1902                 internal void DropIndex(DataColumn[] columns)
1903                 {
1904                         // first check whenever index exists for the columns
1905                         Index index = this.GetIndexByColumns(columns);
1906                         if(index == null) {
1907                         // if no index on this columns already exists - do nothing
1908                                 return;
1909                         }
1910                         this.DropIndex(index);
1911                 }
1912
1913                 internal void DropIndex(Index index)
1914                 {
1915                         // loop through table constraints and checks 
1916                         foreach(Constraint constraint in Constraints) {
1917                                 // if we found another reference to the index we do not remove the index.
1918                                 if (index == constraint.Index)
1919                                         return; 
1920                         }
1921                         
1922                         this.RemoveIndex(index);
1923                 }
1924
1925                 internal void InitializeIndex (Index indx)
1926                 {
1927                         DataRow[] rows = new DataRow[this.Rows.Count];
1928                         this.Rows.CopyTo (rows, 0);
1929                         indx.Root = null;
1930                         // fill index with table rows
1931                         foreach(DataRow row in this.Rows) {
1932                                 if(row.RowState != DataRowState.Deleted) {
1933                                         indx.Insert(new Node(row), DataRowVersion.Default);
1934                                 }
1935                         }
1936                 }
1937         }
1938 }