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