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