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