2003-03-27 Ville Palo <vi64pa@kolumbus.fi>
[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(); 
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                                         UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
317                                         return;
318                                 }
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                                         uc = new UniqueConstraint( (DataColumn[]) value, true);
329                                 }
330                                 else { //set existing constraint as the new primary key
331                                         UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
332                                 }
333                                 
334                         }
335                 }
336
337                 /// <summary>
338                 /// Gets the collection of_rows that belong to this table.
339                 /// </summary>
340                 [Browsable (false)]
341                 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]   
342                 public DataRowCollection Rows {
343                         get { return _rows; }
344                 }
345
346                 /// <summary>
347                 /// Gets or sets an System.ComponentModel.ISite 
348                 /// for the DataTable.
349                 /// </summary>
350                 [Browsable (false)]
351                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
352                 public override ISite Site {
353                         get { return _site; }
354                         set { _site = value; }
355                 }
356
357                 /// <summary>
358                 /// Gets or sets the name of the the DataTable.
359                 /// </summary>
360                 [DataCategory ("Data")]
361                 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
362                 [DefaultValue ("")]     
363                 [RefreshProperties (RefreshProperties.All)]
364                 public string TableName {
365                         get { return "" + _tableName; }
366                         set { _tableName = value; }
367                 }
368                 
369                 bool IListSource.ContainsListCollection {
370                         get {
371                                 // the collection is a DataView
372                                 return false;
373                         }
374                 }
375
376                 /// <summary>
377                 /// Commits all the changes made to this table since the 
378                 /// last time AcceptChanges was called.
379                 /// </summary>
380                 public void AcceptChanges () 
381                 {
382                         //FIXME: Do we need to validate anything here or
383                         //try to catch any errors to deal with them?
384
385                         foreach(DataRow myRow in _rows) {
386                                 myRow.AcceptChanges();
387                         }
388                 }
389
390                 /// <summary>
391                 /// Begins the initialization of a DataTable that is used 
392                 /// on a form or used by another component. The initialization
393                 /// occurs at runtime.
394                 /// </summary>
395                 public void BeginInit () 
396                 {
397                 }
398
399                 /// <summary>
400                 /// Turns off notifications, index maintenance, and 
401                 /// constraints while loading data.
402                 /// </summary>
403                 [MonoTODO]
404                 public void BeginLoadData () 
405                 {
406                 }
407
408                 /// <summary>
409                 /// Clears the DataTable of all data.
410                 /// </summary>
411                 public void Clear () {
412                         // TODO: thow an exception if any rows that 
413                         //       have enforced child relations 
414                         //       that would result in child rows being orphaned
415                         // TODO: throw a NotSupportedException if the DataTable is part
416                         //       of a DataSet that is binded to an XmlDataDocument
417
418                         _rows.Clear ();
419                 }
420
421                 /// <summary>
422                 /// Clones the structure of the DataTable, including
423                 ///  all DataTable schemas and constraints.
424                 /// </summary>
425                 [MonoTODO]
426                 public virtual DataTable Clone () 
427                 {
428                         DataTable Copy = new DataTable ();                      
429                         CopyProperties (Copy);
430                         return Copy;
431                 }
432
433                 /// <summary>
434                 /// Computes the given expression on the current_rows that 
435                 /// pass the filter criteria.
436                 /// </summary>
437                 [MonoTODO]
438                 public object Compute (string expression, string filter) 
439                 {
440                         // TODO: implement this function based
441                         //       on Select (expression)
442                         //
443                         // expression is an aggregate function
444                         // filter is an expression used to limit rows
445
446                         object obj = null;
447
448                         // filter rows
449                         ExpressionElement Expression = new ExpressionMainElement (filter);
450                         
451                         ArrayList List = new ArrayList ();
452                         foreach (DataRow Row in Rows) {
453                                 
454                                 if (Expression.Test (Row))
455                                         List.Add (Row);
456                         }
457                         
458                         DataRow[] rows = (DataRow [])List.ToArray (typeof (DataRow));
459
460                         // TODO: with the filtered rows, execute the aggregate function
461                         //       mentioned in expression
462
463                         return obj;
464                 }
465
466                 /// <summary>
467                 /// Copies both the structure and data for this DataTable.
468                 /// </summary>
469                 [MonoTODO]      
470                 public DataTable Copy () 
471                 {
472                         DataTable Copy = new DataTable ();
473                         CopyProperties (Copy);
474
475                         foreach (DataRow Row in Rows) {
476                                 DataRow NewRow = Copy.NewRow ();
477                                 NewRow.RowError = Row.RowError;
478                                 foreach (DataColumn C in Copy.Columns) {
479                                         NewRow [C.ColumnName] = Row [C.ColumnName];
480                                 }
481                                 Copy.Rows.Add (NewRow);
482                         }
483                                         
484                         return Copy;
485                 }
486
487                 [MonoTODO]
488                 private void CopyProperties (DataTable Copy) 
489                 {
490                         Copy.CaseSensitive = CaseSensitive;
491                         Copy.VirginCaseSensitive = VirginCaseSensitive;
492
493                         // Copy.ChildRelations
494                         // Copy.Constraints
495                         // Copy.Container
496                         // Copy.DefaultView
497                         // Copy.DesignMode
498                         Copy.DisplayExpression = DisplayExpression;
499                         // Copy.ExtendedProperties
500                         Copy.Locale = Locale;
501                         Copy.MinimumCapacity = MinimumCapacity;
502                         Copy.Namespace = Namespace;
503                         // Copy.ParentRelations
504                         Copy.Prefix = Prefix;
505                         //Copy.PrimaryKey = PrimaryKey;
506                         Copy.Site = Site;
507                         Copy.TableName = TableName;
508
509                         // Copy columns
510                         foreach (DataColumn Column in Columns) {
511                                 Copy.Columns.Add (CopyColumn (Column));                         
512                         }
513
514                 }
515                 /// <summary>
516                 /// Ends the initialization of a DataTable that is used 
517                 /// on a form or used by another component. The 
518                 /// initialization occurs at runtime.
519                 /// </summary>
520                 [MonoTODO]
521                 public void EndInit () 
522                 {
523
524                 }
525
526                 /// <summary>
527                 /// Turns on notifications, index maintenance, and 
528                 /// constraints after loading data.
529                 /// </summary>
530                 [MonoTODO]
531                 public void EndLoadData() 
532                 {
533                 }
534
535                 /// <summary>
536                 /// Gets a copy of the DataTable that contains all
537                 ///  changes made to it since it was loaded or 
538                 ///  AcceptChanges was last called.
539                 /// </summary>
540                 [MonoTODO]
541                 public DataTable GetChanges() 
542                 {
543                         //TODO:
544                         return this;
545                 }
546
547                 /// <summary>
548                 /// Gets a copy of the DataTable containing all 
549                 /// changes made to it since it was last loaded, or 
550                 /// since AcceptChanges was called, filtered by DataRowState.
551                 /// </summary>
552                 [MonoTODO]      
553                 public DataTable GetChanges(DataRowState rowStates) 
554                 {
555                         //TODO:
556                         return this;
557                 }
558
559                 /// <summary>
560                 /// Gets an array of DataRow objects that contain errors.
561                 /// </summary>
562                 [MonoTODO]
563                 public DataRow[] GetErrors () 
564                 {
565                         throw new NotImplementedException ();
566                 }
567         
568                 /// <summary>
569                 /// This member is only meant to support Mono's infrastructure 
570                 /// </summary>
571                 protected virtual DataTable CreateInstance () 
572                 {
573                         return Activator.CreateInstance (this.GetType (), true) as DataTable;
574                 }
575
576                 /// <summary>
577                 /// This member is only meant to support Mono's infrastructure 
578                 /// </summary>
579                 protected virtual Type GetRowType () 
580                 {
581                         return typeof (DataRow);
582                 }
583
584                 /// <summary>
585                 /// This member is only meant to support Mono's infrastructure 
586                 /// 
587                 /// Used for Data Binding between System.Web.UI. controls 
588                 /// like a DataGrid
589                 /// or
590                 /// System.Windows.Forms controls like a DataGrid
591                 /// </summary>
592                 IList IListSource.GetList () 
593                 {
594                         IList list = (IList) _defaultView;
595                         return list;
596                 }
597                                 
598                 /// <summary>
599                 /// Copies a DataRow into a DataTable, preserving any 
600                 /// property settings, as well as original and current values.
601                 /// </summary>
602                 [MonoTODO]
603                 public void ImportRow (DataRow row) 
604                 {
605                 }
606
607                 /// <summary>
608                 /// This member is only meant to support Mono's infrastructure          
609                 /// </summary>
610                 [MonoTODO]
611                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) 
612                 {
613                 }
614
615                 /// <summary>
616                 /// Finds and updates a specific row. If no matching row
617                 ///  is found, a new row is created using the given values.
618                 /// </summary>
619                 [MonoTODO]
620                 public DataRow LoadDataRow (object[] values, bool fAcceptChanges) 
621                 {
622                         DataRow row = null;
623                         if (PrimaryKey.Length == 0) {
624                                 row = Rows.Add (values);
625                                 if (fAcceptChanges)
626                                         row.AcceptChanges ();
627                         }
628                         else
629                                 throw new NotImplementedException ();
630                         return row;
631                 }
632
633                 /// <summary>
634                 /// Creates a new DataRow with the same schema as the table.
635                 /// </summary>
636                 public DataRow NewRow () 
637                 {
638                         return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
639                 }
640
641                 /// <summary>
642                 /// This member supports the .NET Framework infrastructure
643                 ///  and is not intended to be used directly from your code.
644                 /// </summary>
645                 protected internal DataRow[] NewRowArray (int size) 
646                 {
647                         return (DataRow[]) Array.CreateInstance (GetRowType (), size);
648                 }
649
650                 /// <summary>
651                 /// Creates a new row from an existing row.
652                 /// </summary>
653                 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder) 
654                 {
655                         return new DataRow (builder);
656                 }
657         
658
659                 /// <summary>
660                 /// Rolls back all changes that have been made to the 
661                 /// table since it was loaded, or the last time AcceptChanges
662                 ///  was called.
663                 /// </summary>
664                 [MonoTODO]
665                 public void RejectChanges () 
666                 {       
667                         //foreach(DataRow myRow in _rows)
668                         //{
669                         for (int i = _rows.Count - 1; i >= 0; i--) {
670                                 DataRow row = _rows [i];
671                                 if (row.RowState != DataRowState.Unchanged)
672                                         _rows [i].RejectChanges ();
673                         }
674                 }
675
676                 /// <summary>
677                 /// Resets the DataTable to its original state.
678                 /// </summary>          
679                 [MonoTODO]
680                 public virtual void Reset () 
681                 {
682                 }
683
684                 /// <summary>
685                 /// Gets an array of all DataRow objects.
686                 /// </summary>
687                 public DataRow[] Select () 
688                 {
689                         DataRow[] dataRows = new DataRow[_rows.Count];
690                         _rows.CopyTo (dataRows, 0);
691                         return dataRows;
692                 }
693
694                 /// <summary>
695                 /// Gets an array of all DataRow objects that match 
696                 /// the filter criteria in order of primary key (or 
697                 /// lacking one, order of addition.)
698                 /// </summary>
699                 public DataRow[] Select (string filterExpression) 
700                 {
701                         ExpressionElement Expression = new ExpressionMainElement (filterExpression);
702
703                         ArrayList List = new ArrayList ();
704                         foreach (DataRow Row in Rows) {
705                                 
706                                 if (Expression.Test (Row))
707                                         List.Add (Row);
708                         }
709                         
710                         return (DataRow [])List.ToArray (typeof (DataRow));
711                 }
712
713                 /// <summary>
714                 /// Gets an array of all DataRow objects that 
715                 /// match the filter criteria, in the the 
716                 /// specified sort order.
717                 /// </summary>
718                 public DataRow[] Select (string filterExpression, string sort) 
719                 {
720                         DataRow[] dataRows = null;
721
722                         if (filterExpression != null && filterExpression.Equals (String.Empty) == false)
723                                 dataRows = Select (filterExpression);
724                         else
725                                 dataRows = Select ();
726
727                         if (sort != null && !sort.Equals (String.Empty)) {
728                                 SortableColumn[] sortableColumns = null;\r
729 \r
730                                 sortableColumns = ParseTheSortString (sort);\r
731                                 if (sortableColumns == null)\r
732                                         throw new Exception ("sort expression result is null");\r
733                                 if (sortableColumns.Length == 0)\r
734                                         throw new Exception("sort expression result is 0");\r
735
736                                 RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);\r
737                                 dataRows = rowSorter.SortRows ();
738                         
739                                 sortableColumns = null;
740                                 rowSorter = null;
741                         }
742
743                         return dataRows;
744                 }
745
746                 /// <summary>
747                 /// Gets an array of all DataRow objects that match
748                 /// the filter in the order of the sort, that match 
749                 /// the specified state.
750                 /// </summary>
751                 [MonoTODO]
752                 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) 
753                 {
754                         DataRow[] dataRows = null;
755
756                         // TODO: do something with recordStates
757                         dataRows = Select (filterExpression, sort);
758
759                         return dataRows;
760                 }
761
762                 /// <summary>
763                 /// Gets the TableName and DisplayExpression, if 
764                 /// there is one as a concatenated string.
765                 /// </summary>
766                 public override string ToString() 
767                 {
768                         //LAMESPEC: spec says concat the two. impl puts a 
769                         //plus sign infront of DisplayExpression
770                         return TableName + " + " + DisplayExpression;
771                 }
772
773                 
774                 #region Events /////////////////
775                 
776                 /// <summary>
777                 /// Raises the ColumnChanged event.
778                 /// </summary>
779                 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
780                         if (null != ColumnChanged) {
781                                 ColumnChanged (this, e);
782                         }
783                 }
784
785                 /// <summary>
786                 /// Raises the ColumnChanging event.
787                 /// </summary>
788                 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
789                         if (null != ColumnChanging) {
790                                 ColumnChanging (this, e);
791                         }
792                 }
793
794                 /// <summary>
795                 /// Raises the PropertyChanging event.
796                 /// </summary>
797                 [MonoTODO]
798                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
799                         //      if (null != PropertyChanging)
800                         //      {
801                         //              PropertyChanging (this, e);
802                         //      }
803                 }
804
805                 /// <summary>
806                 /// Notifies the DataTable that a DataColumn is being removed.
807                 /// </summary>
808                 [MonoTODO]
809                 protected internal virtual void OnRemoveColumn (DataColumn column) {
810                         //      if (null != RemoveColumn)
811                         //      {
812                         //              RemoveColumn(this, e);
813                         //      }
814                 }
815
816                 /// <summary>
817                 /// Raises the RowChanged event.
818                 /// </summary>
819                 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
820                         if (null != RowChanged) {
821                                 RowChanged(this, e);
822                         }
823                 }
824
825
826                 /// <summary>
827                 /// Raises the RowChanging event.
828                 /// </summary>
829                 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
830                         if (null != RowChanging) {
831                                 RowChanging(this, e);
832                         }
833                 }
834
835                 /// <summary>
836                 /// Raises the RowDeleted event.
837                 /// </summary>
838                 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
839                         if (null != RowDeleted) {
840                                 RowDeleted(this, e);
841                         }
842                 }
843
844                 /// <summary>
845                 /// Raises the RowDeleting event.
846                 /// </summary>
847                 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
848                         if (null != RowDeleting) {
849                                 RowDeleting(this, e);
850                         }
851                 }
852
853                 [MonoTODO]
854                 private DataColumn CopyColumn (DataColumn Column) {
855                         DataColumn Copy = new DataColumn ();
856
857                         // Copy all the properties of column
858                         Copy.AllowDBNull = Column.AllowDBNull;
859                         Copy.AutoIncrement = Column.AutoIncrement;
860                         Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
861                         Copy.AutoIncrementStep = Column.AutoIncrementStep;
862                         Copy.Caption = Column.Caption;
863                         Copy.ColumnMapping = Column.ColumnMapping;
864                         Copy.ColumnName = Column.ColumnName;
865                         // Copy.Container
866                         // Copy.DataType
867                         // Copy.DefaultValue                    
868                         Copy.Expression = Column.Expression;
869                         //Copy.ExtendedProperties
870                         Copy.MaxLength = Column.MaxLength;
871                         Copy.Namespace = Column.Namespace;
872                         Copy.Prefix = Column.Prefix;
873                         Copy.ReadOnly = Column.ReadOnly;
874                         //Copy.Site
875                         Copy.Unique = Column.Unique;
876                         
877                         return Copy;
878                 }                       
879
880                 /// <summary>
881                 /// Occurs when after a value has been changed for 
882                 /// the specified DataColumn in a DataRow.
883                 /// </summary>
884                 [DataCategory ("Data")] 
885                 [DataSysDescription ("Occurs when a value has been changed for this column.")]
886                 public event DataColumnChangeEventHandler ColumnChanged;
887
888                 /// <summary>
889                 /// Occurs when a value is being changed for the specified 
890                 /// DataColumn in a DataRow.
891                 /// </summary>
892                 [DataCategory ("Data")]
893                 [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.")]
894                 public event DataColumnChangeEventHandler ColumnChanging;
895
896                 /// <summary>
897                 /// Occurs after a DataRow has been changed successfully.
898                 /// </summary>
899                 [DataCategory ("Data")] 
900                 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
901                 public event DataRowChangeEventHandler RowChanged;
902
903                 /// <summary>
904                 /// Occurs when a DataRow is changing.
905                 /// </summary>
906                 [DataCategory ("Data")] 
907                 [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.")]
908                 public event DataRowChangeEventHandler RowChanging;
909
910                 /// <summary>
911                 /// Occurs after a row in the table has been deleted.
912                 /// </summary>
913                 [DataCategory ("Data")] 
914                 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")] 
915                 public event DataRowChangeEventHandler RowDeleted;
916
917                 /// <summary>
918                 /// Occurs before a row in the table is about to be deleted.
919                 /// </summary>
920                 [DataCategory ("Data")] 
921                 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
922                 public event DataRowChangeEventHandler RowDeleting;
923                 
924                 #endregion // Events
925
926                 // to parse the sort string for DataTable:Select(expression,sort)
927                 // into sortable columns (think ORDER BY, 
928                 // such as, "customer ASC, price DESC" )
929                 private SortableColumn[] ParseTheSortString (string sort) \r
930                 {\r
931                         SortableColumn[] sortColumns = null;\r
932                         ArrayList columns = null;\r
933                 \r
934                         if (sort != null && !sort.Equals ("")) {\r
935                                 columns = new ArrayList ();\r
936                                 string[] columnExpression = sort.Trim ().Split (new char[1] {','});\r
937                         \r
938                                 for (int c = 0; c < columnExpression.Length; c++) {\r
939                                         string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});\r
940                                 \r
941                                         string columnName = columnSortInfo[0].Trim ();\r
942                                         string sortOrder = "ASC";\r
943                                         if (columnSortInfo.Length > 1) \r
944                                                 sortOrder = columnSortInfo[1].Trim ().ToUpper ();\r
945                                         \r
946                                         ListSortDirection sortDirection = ListSortDirection.Ascending;\r
947                                         switch (sortOrder) {\r
948                                         case "ASC":\r
949                                                 sortDirection = ListSortDirection.Ascending;\r
950                                                 break;\r
951                                         case "DESC":\r
952                                                 sortDirection = ListSortDirection.Descending;\r
953                                                 break;\r
954                                         default:\r
955                                                 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);\r
956                                         }\r
957                                         Int32 ord = 0;\r
958                                         try {\r
959                                                 ord = Int32.Parse (columnName);\r
960                                         }\r
961                                         catch (FormatException) {\r
962                                                 ord = -1;\r
963                                         }\r
964                                         DataColumn dc = null;\r
965                                         if (ord == -1)                          \r
966                                                 dc = _columnCollection[columnName];\r
967                                         else\r
968                                                 dc = _columnCollection[ord];\r
969                                         SortableColumn sortCol = new SortableColumn (dc,sortDirection);\r
970                                         columns.Add (sortCol);\r
971                                 }       \r
972                                 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));\r
973                         }               \r
974                         return sortColumns;\r
975                 }
976         
977                 private class SortableColumn \r
978                 {\r
979                         private DataColumn col;\r
980                         private ListSortDirection dir;\r
981 \r
982                         internal SortableColumn (DataColumn column, \r
983                                                 ListSortDirection direction) \r
984                         {\r
985                                 col = column;\r
986                                 dir = direction;\r
987                         }\r
988 \r
989                         public DataColumn Column {\r
990                                 get {\r
991                                         return col;\r
992                                 }\r
993                         }\r
994 \r
995                         public ListSortDirection SortDirection {\r
996                                 get {\r
997                                         return dir;\r
998                                 }\r
999                         }\r
1000                 }\r
1001 \r
1002                 private class RowSorter : IComparer \r
1003                 {\r
1004                         private SortableColumn[] sortColumns;\r
1005                         private DataRow[] rowsToSort;\r
1006                         \r
1007                         internal RowSorter(DataRow[] unsortedRows, \r
1008                                         SortableColumn[] sortColumns) \r
1009                         {\r
1010                                 this.sortColumns = sortColumns;\r
1011                                 this.rowsToSort = unsortedRows;\r
1012                         }\r
1013 \r
1014                         public SortableColumn[] SortColumns {\r
1015                                 get {\r
1016                                         return sortColumns;\r
1017                                 }\r
1018                         }\r
1019                         \r
1020                         public DataRow[] SortRows () \r
1021                         {\r
1022                                 Array.Sort (rowsToSort, this);\r
1023                                 return rowsToSort;\r
1024                         }\r
1025 \r
1026                         int IComparer.Compare (object x, object y) \r
1027                         {\r
1028                                 if(x == null)\r
1029                                         throw new Exception ("Object to compare is null: x");\r
1030                                 if(y == null)\r
1031                                         throw new Exception ("Object to compare is null: y");\r
1032                                 if(!(x is DataRow))\r
1033                                         throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());\r
1034                                 if(!(y is DataRow))\r
1035                                         throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());\r
1036 \r
1037                                 DataRow rowx = (DataRow) x;\r
1038                                 DataRow rowy = (DataRow) y;\r
1039 \r
1040                                 for(int i = 0; i < sortColumns.Length; i++) {\r
1041                                         SortableColumn sortColumn = sortColumns[i];\r
1042                                         DataColumn dc = sortColumn.Column;\r
1043 \r
1044                                         IComparable objx = (IComparable) rowx[dc];\r
1045                                         object objy = rowy[dc];\r
1046 \r
1047                                         int result = CompareObjects (objx, objy);\r
1048                                         if (result != 0) {\r
1049                                                 if (sortColumn.SortDirection == ListSortDirection.Ascending) {\r
1050                                                         return result;\r
1051                                                 }\r
1052                                                 else {\r
1053                                                         return -result;\r
1054                                                 }\r
1055                                         }\r
1056                                 }\r
1057                                 return 0;\r
1058                         }\r
1059 \r
1060                         private int CompareObjects (object a, object b) \r
1061                         {\r
1062                                 if (a == b)
1063                                         return 0;
1064                                 else if (a == null)
1065                                         return -1;
1066                                 else if (a == DBNull.Value)
1067                                         return -1;
1068                                 else if (b == null)
1069                                         return 1;
1070                                 else if (b == DBNull.Value)
1071                                         return 1;
1072
1073                                 if((a is string) && (b is string)) {
1074                                         a = ((string) a).ToUpper ();
1075                                         b = ((string) b).ToUpper ();                    
1076                                 }
1077
1078                                 if (a is IComparable)
1079                                         return ((a as IComparable).CompareTo (b));
1080                                 else if (b is IComparable)
1081                                         return -((b as IComparable).CompareTo (a));
1082
1083                                 throw new ArgumentException ("Neither a nor b IComparable");
1084                         }\r
1085                 }
1086         }
1087 }