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