merging the Mainsoft branch to the trunk
[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 //   Sureshkumar T <tsureshkumar@novell.com>\r
12 //   Konstantin Triger <kostat@mainsoft.com>\r
13 //\r
14 // (C) Chris Podurgiel\r
15 // (C) Ximian, Inc 2002\r
16 // Copyright (C) Tim Coleman, 2002-2003\r
17 // Copyright (C) Daniel Morgan, 2002-2003\r
18 //\r
19 \r
20 //\r
21 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
22 //\r
23 // Permission is hereby granted, free of charge, to any person obtaining\r
24 // a copy of this software and associated documentation files (the\r
25 // "Software"), to deal in the Software without restriction, including\r
26 // without limitation the rights to use, copy, modify, merge, publish,\r
27 // distribute, sublicense, and/or sell copies of the Software, and to\r
28 // permit persons to whom the Software is furnished to do so, subject to\r
29 // the following conditions:\r
30 // \r
31 // The above copyright notice and this permission notice shall be\r
32 // included in all copies or substantial portions of the Software.\r
33 // \r
34 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
35 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
36 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
37 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
38 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
39 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
40 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
41 //\r
42 \r
43 using System;\r
44 using System.Data.Common;\r
45 using System.Collections;\r
46 using System.ComponentModel;\r
47 using System.Globalization;\r
48 using System.IO;\r
49 using System.Runtime.Serialization;\r
50 using System.Xml;\r
51 using System.Xml.Schema;\r
52 using Mono.Data.SqlExpressions;\r
53 \r
54 namespace System.Data {\r
55         //[Designer]\r
56         [ToolboxItem (false)]\r
57         [DefaultEvent ("RowChanging")]\r
58         [DefaultProperty ("TableName")]\r
59         [DesignTimeVisible (false)]\r
60         [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]\r
61         [Serializable]\r
62         public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable \r
63         {\r
64                 #region Fields\r
65 \r
66                 internal DataSet dataSet;   \r
67                 \r
68                 private bool _caseSensitive;\r
69                 private DataColumnCollection _columnCollection;\r
70                 private ConstraintCollection _constraintCollection;\r
71                 // never access it. Use DefaultView.\r
72                 private DataView _defaultView = null;\r
73 \r
74                 private string _displayExpression;\r
75                 private PropertyCollection _extendedProperties;\r
76                 private bool _hasErrors;\r
77                 private CultureInfo _locale;\r
78                 private int _minimumCapacity;\r
79                 private string _nameSpace;\r
80                 private DataRelationCollection _childRelations; \r
81                 private DataRelationCollection _parentRelations;\r
82                 private string _prefix;\r
83                 private UniqueConstraint _primaryKeyConstraint;\r
84                 private DataRowCollection _rows;\r
85                 private ISite _site;\r
86                 private string _tableName;\r
87                 private bool _containsListCollection;\r
88                 private string _encodedTableName;\r
89                 internal bool _duringDataLoad;\r
90                 internal bool _nullConstraintViolationDuringDataLoad;\r
91                 private bool dataSetPrevEnforceConstraints;\r
92                 private bool dataTablePrevEnforceConstraints;\r
93                 private bool enforceConstraints = true;\r
94                 private DataRowBuilder _rowBuilder;\r
95                 private ArrayList _indexes;\r
96                 private RecordCache _recordCache;\r
97                 private int _defaultValuesRowIndex = -1;\r
98                 protected internal bool fInitInProgress;\r
99 \r
100                 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's \r
101                 // CaseSensitive property. So when you lost you virginity it's gone for ever\r
102                 private bool _virginCaseSensitive = true;\r
103                 \r
104                 private PropertyDescriptorCollection _propertyDescriptorsCache;\r
105                 static DataColumn[] _emptyColumnArray = new DataColumn[0];\r
106                 \r
107                 #endregion //Fields\r
108                 \r
109                 /// <summary>\r
110                 /// Initializes a new instance of the DataTable class with no arguments.\r
111                 /// </summary>\r
112                 public DataTable () \r
113                 {\r
114                         dataSet = null;\r
115                         _columnCollection = new DataColumnCollection(this);\r
116                         _constraintCollection = new ConstraintCollection(this); \r
117                         _extendedProperties = new PropertyCollection();\r
118                         _tableName = "";\r
119                         _nameSpace = null;\r
120                         _caseSensitive = false;         //default value\r
121                         _displayExpression = null;\r
122                         _primaryKeyConstraint = null;\r
123                         _site = null;\r
124                         _rows = new DataRowCollection (this);\r
125                         _indexes = new ArrayList();\r
126                         _recordCache = new RecordCache(this);\r
127                         \r
128                         //LAMESPEC: spec says 25 impl does 50\r
129                         _minimumCapacity = 50;\r
130                         \r
131                         _childRelations = new DataRelationCollection.DataTableRelationCollection (this);\r
132                         _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);\r
133                 }\r
134 \r
135                 /// <summary>\r
136                 /// Intitalizes a new instance of the DataTable class with the specified table name.\r
137                 /// </summary>\r
138                 public DataTable (string tableName) : this () \r
139                 {\r
140                         _tableName = tableName;\r
141                 }\r
142 \r
143                 /// <summary>\r
144                 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.\r
145                 /// </summary>\r
146                 [MonoTODO]\r
147                 protected DataTable (SerializationInfo info, StreamingContext context)\r
148                         : this () \r
149                 {\r
150                         string schema = info.GetString ("XmlSchema");\r
151                         string data = info.GetString ("XmlDiffGram");\r
152                         \r
153                         DataSet ds = new DataSet ();\r
154                         ds.ReadXmlSchema (new StringReader (schema));\r
155                         ds.Tables [0].CopyProperties (this);\r
156                         ds = new DataSet ();\r
157                         ds.Tables.Add (this);\r
158                         ds.ReadXml (new StringReader (data), XmlReadMode.DiffGram);\r
159                         ds.Tables.Remove (this);\r
160 /* keeping for a while. With the change above, we shouldn't have to consider \r
161  * DataTable mode in schema inference/read.\r
162                         XmlSchemaMapper mapper = new XmlSchemaMapper (this);\r
163                         XmlTextReader xtr = new XmlTextReader(new StringReader (schema));\r
164                         mapper.Read (xtr);\r
165                         \r
166                         XmlDiffLoader loader = new XmlDiffLoader (this);\r
167                         xtr = new XmlTextReader(new StringReader (data));\r
168                         loader.Load (xtr);\r
169 */\r
170                 }\r
171 \r
172 #if NET_2_0\r
173                 public DataTable (string tableName, string tbNamespace)\r
174                         : this (tableName)\r
175                 {\r
176                         _nameSpace = tbNamespace;\r
177                 }\r
178 #endif\r
179 \r
180                 /// <summary>\r
181                 /// Indicates whether string comparisons within the table are case-sensitive.\r
182                 /// </summary>\r
183                 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]        \r
184                 public bool CaseSensitive {\r
185                         get { \r
186                                 if (_virginCaseSensitive && dataSet != null)\r
187                                         return dataSet.CaseSensitive; \r
188                                 else\r
189                                         return _caseSensitive;\r
190                                 }\r
191                         set {\r
192                                 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {\r
193                                         throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");\r
194                                 }\r
195                                 _virginCaseSensitive = false;\r
196                                 _caseSensitive = value; \r
197                                 ResetCaseSensitiveIndexes();\r
198                         }\r
199                 }\r
200                 \r
201                 internal ArrayList Indexes{\r
202                         get { return _indexes; }\r
203                 }\r
204 \r
205                 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv) \r
206                 {\r
207                         DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);\r
208                         OnColumnChanged(e);\r
209                 }\r
210 \r
211                 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv) \r
212                 {\r
213                         DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);\r
214                         OnColumnChanging (e);\r
215                 }\r
216 \r
217                 internal void DeletedDataRow (DataRow dr, DataRowAction action) \r
218                 {\r
219                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);\r
220                         OnRowDeleted (e);\r
221                 }\r
222 \r
223                 internal void DeletingDataRow (DataRow dr, DataRowAction action) \r
224                 {\r
225                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);\r
226                         OnRowDeleting(e);\r
227                 }\r
228 \r
229                 internal void ChangedDataRow (DataRow dr, DataRowAction action) \r
230                 {\r
231                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);\r
232                         OnRowChanged (e);\r
233                 }\r
234 \r
235                 internal void ChangingDataRow (DataRow dr, DataRowAction action) \r
236                 {\r
237                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);\r
238                         OnRowChanging (e);\r
239                 }\r
240 \r
241                 /// <summary>\r
242                 /// Gets the collection of child relations for this DataTable.\r
243                 /// </summary>\r
244                 [Browsable (false)]\r
245                 [DataSysDescription ("Returns the child relations for this table.")]\r
246                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
247                 public DataRelationCollection ChildRelations {\r
248                         get {\r
249                                 return _childRelations;\r
250                         }\r
251                 }\r
252 \r
253                 /// <summary>\r
254                 /// Gets the collection of columns that belong to this table.\r
255                 /// </summary>\r
256                 [DataCategory ("Data")]\r
257                 [DataSysDescription ("The collection that holds the columns for this table.")]\r
258                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]\r
259                 public DataColumnCollection Columns {\r
260                         get { return _columnCollection; }\r
261                 }\r
262 \r
263                 /// <summary>\r
264                 /// Gets the collection of constraints maintained by this table.\r
265                 /// </summary>\r
266                 [DataCategory ("Data")] \r
267                 [DataSysDescription ("The collection that holds the constraints for this table.")]\r
268                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]\r
269                 public ConstraintCollection Constraints {\r
270                         get { return _constraintCollection; }\r
271                 }\r
272 \r
273                 /// <summary>\r
274                 /// Gets the DataSet that this table belongs to.\r
275                 /// </summary>\r
276                 [Browsable (false)]\r
277                 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]\r
278                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
279                 public DataSet DataSet {\r
280                         get { return dataSet; }\r
281                 }\r
282 \r
283                 \r
284 \r
285                 /// <summary>\r
286                 /// Gets a customized view of the table which may \r
287                 /// include a filtered view, or a cursor position.\r
288                 /// </summary>\r
289                 [MonoTODO]      \r
290                 [Browsable (false)]\r
291                 [DataSysDescription ("This is the default DataView for the table.")]\r
292                 public DataView DefaultView {\r
293                         get {\r
294                                 if (_defaultView == null) {\r
295                                         lock(this){\r
296                                                 if (_defaultView == null){\r
297                                                         if (dataSet != null)\r
298                                                                 _defaultView = dataSet.DefaultViewManager.CreateDataView(this);\r
299                                                         else\r
300                                                                 _defaultView = new DataView(this);\r
301                                                 }\r
302                                         }\r
303                                 }\r
304                                 return _defaultView;\r
305                         }\r
306                 }\r
307                 \r
308 \r
309                 /// <summary>\r
310                 /// Gets or sets the expression that will return \r
311                 /// a value used to represent this table in the user interface.\r
312                 /// </summary>\r
313                 [DataCategory ("Data")]\r
314                 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]       \r
315                 [DefaultValue ("")]\r
316                 public string DisplayExpression {\r
317                         get { return _displayExpression == null ? "" : _displayExpression; }\r
318                         set { _displayExpression = value; }\r
319                 }\r
320 \r
321                 /// <summary>\r
322                 /// Gets the collection of customized user information.\r
323                 /// </summary>\r
324                 [Browsable (false)]\r
325                 [DataCategory ("Data")]\r
326                 [DataSysDescription ("The collection that holds custom user information.")]\r
327                 public PropertyCollection ExtendedProperties {\r
328                         get { return _extendedProperties; }\r
329                 }\r
330 \r
331                 /// <summary>\r
332                 /// Gets a value indicating whether there are errors in \r
333                 /// any of the_rows in any of the tables of the DataSet to \r
334                 /// which the table belongs.\r
335                 /// </summary>\r
336                 [Browsable (false)]\r
337                 [DataSysDescription ("Returns whether the table has errors.")]\r
338                 public bool HasErrors {\r
339                         get { \r
340                                 // we can not use the _hasError flag because we do not know when to turn it off!\r
341                                 for (int i = 0; i < _rows.Count; i++)\r
342                                 {\r
343                                         if (_rows[i].HasErrors)\r
344                                                 return true;\r
345                                 }\r
346                                 return false;\r
347                         }\r
348                 }\r
349 \r
350                 /// <summary>\r
351                 /// Gets or sets the locale information used to \r
352                 /// compare strings within the table.\r
353                 /// </summary>\r
354                 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]\r
355                 public CultureInfo Locale {\r
356                         get { \r
357                                 // if the locale is null, we check for the DataSet locale\r
358                                 // and if the DataSet is null we return the current culture.\r
359                                 // this way if DataSet locale is changed, only if there is no locale for \r
360                                 // the DataTable it influece the Locale get;\r
361                                 if (_locale != null)\r
362                                         return _locale;\r
363                                 if (DataSet != null)\r
364                                         return DataSet.Locale;\r
365                                 return CultureInfo.CurrentCulture;\r
366                         }\r
367                         set { \r
368                                 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {\r
369                                         throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");\r
370                                 }\r
371                                 if (_locale == null || !_locale.Equals(value))\r
372                                         _locale = value; \r
373                         }\r
374                 }\r
375 \r
376                 /// <summary>\r
377                 /// Gets or sets the initial starting size for this table.\r
378                 /// </summary>\r
379                 [DataCategory ("Data")]\r
380                 [DataSysDescription ("Indicates an initial starting size for this table.")]\r
381                 [DefaultValue (50)]\r
382                 public int MinimumCapacity {\r
383                         get { return _minimumCapacity; }\r
384                         set { _minimumCapacity = value; }\r
385                 }\r
386 \r
387                 /// <summary>\r
388                 /// Gets or sets the namespace for the XML represenation \r
389                 /// of the data stored in the DataTable.\r
390                 /// </summary>\r
391                 [DataCategory ("Data")]\r
392                 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]\r
393                 public string Namespace {\r
394                         get\r
395                         {\r
396                                 if (_nameSpace != null)\r
397                                 {\r
398                                         return _nameSpace;\r
399                                 }\r
400                                 if (DataSet != null)\r
401                                 {\r
402                                         return DataSet.Namespace;\r
403                                 }\r
404                                 return String.Empty;\r
405                         }\r
406                         set { _nameSpace = value; }\r
407                 }\r
408 \r
409                 /// <summary>\r
410                 /// Gets the collection of parent relations for \r
411                 /// this DataTable.\r
412                 /// </summary>\r
413                 [Browsable (false)]\r
414                 [DataSysDescription ("Returns the parent relations for this table.")]\r
415                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
416                 public DataRelationCollection ParentRelations {\r
417                         get {   \r
418                                 return _parentRelations;\r
419                         }\r
420                 }\r
421 \r
422                 /// <summary>\r
423                 /// Gets or sets the namespace for the XML represenation\r
424                 ///  of the data stored in the DataTable.\r
425                 /// </summary>\r
426                 [DataCategory ("Data")]\r
427                 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]\r
428                 [DefaultValue ("")]\r
429                 public string Prefix {\r
430                         get { return _prefix == null ? "" : _prefix; }\r
431                         set {\r
432                                 // Prefix cannot contain any special characters other than '_' and ':'\r
433                                 for (int i = 0; i < value.Length; i++) {\r
434                                         if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))\r
435                                                 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");\r
436                                 }\r
437                                 _prefix = value;\r
438                         }\r
439                 }\r
440 \r
441                 /// <summary>\r
442                 /// Gets or sets an array of columns that function as \r
443                 /// primary keys for the data table.\r
444                 /// </summary>\r
445                 [DataCategory ("Data")]\r
446                 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]\r
447                 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]\r
448                 [TypeConverterAttribute ("System.Data.PrimaryKeyTypeConverter, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]\r
449                 public DataColumn[] PrimaryKey {\r
450                         get {\r
451                                 if (_primaryKeyConstraint == null) { \r
452                                         return new DataColumn[] {};\r
453                                 }\r
454                                 return _primaryKeyConstraint.Columns;\r
455                         }\r
456                         set {\r
457                                 UniqueConstraint oldPKConstraint = _primaryKeyConstraint;\r
458                                 \r
459                                 // first check if value is the same as current PK.\r
460                                 if (oldPKConstraint != null && DataColumn.AreColumnSetsTheSame(value, oldPKConstraint.Columns))\r
461                                         return;\r
462 \r
463                                 // remove PK Constraint\r
464                                 if(oldPKConstraint != null) {\r
465                                         _primaryKeyConstraint = null;\r
466                                         Constraints.Remove(oldPKConstraint);\r
467                                 }\r
468                                 \r
469                                 if (value != null) {\r
470                                         //Does constraint exist for these columns\r
471                                         UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(this.Constraints, (DataColumn[]) value);\r
472                                 \r
473                                         //if constraint doesn't exist for columns\r
474                                         //create new unique primary key constraint\r
475                                         if (null == uc) {\r
476                                                 foreach (DataColumn Col in (DataColumn[]) value) {\r
477                                                         if (Col.Table == null)\r
478                                                                 break;\r
479 \r
480                                                         if (Columns.IndexOf (Col) < 0)\r
481                                                                 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");\r
482                                                 }\r
483                                                 // create constraint with primary key indication set to false\r
484                                                 // to avoid recursion\r
485                                                 uc = new UniqueConstraint( (DataColumn[]) value, false);                \r
486                                                 Constraints.Add (uc);\r
487                                         }\r
488 \r
489                                         //set the constraint as the new primary key\r
490                                                 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);\r
491                                         _primaryKeyConstraint = uc;\r
492                                 }                               \r
493                         }\r
494                 }\r
495 \r
496                 internal UniqueConstraint PrimaryKeyConstraint {\r
497                         get{\r
498                                 return _primaryKeyConstraint;\r
499                         }\r
500                 }\r
501 \r
502                 /// <summary>\r
503                 /// Gets the collection of_rows that belong to this table.\r
504                 /// </summary>\r
505                 [Browsable (false)]\r
506                 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]   \r
507                 public DataRowCollection Rows {\r
508                         get { return _rows; }\r
509                 }\r
510 \r
511                 /// <summary>\r
512                 /// Gets or sets an System.ComponentModel.ISite \r
513                 /// for the DataTable.\r
514                 /// </summary>\r
515                 [Browsable (false)]\r
516                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
517                 public override ISite Site {\r
518                         get { return _site; }\r
519                         set { _site = value; }\r
520                 }\r
521 \r
522                 /// <summary>\r
523                 /// Gets or sets the name of the the DataTable.\r
524                 /// </summary>\r
525                 [DataCategory ("Data")]\r
526                 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]\r
527                 [DefaultValue ("")]     \r
528                 [RefreshProperties (RefreshProperties.All)]\r
529                 public string TableName {\r
530                         get { return _tableName == null ? "" : _tableName; }\r
531                         set { _tableName = value; }\r
532                 }\r
533                 \r
534                 bool IListSource.ContainsListCollection {\r
535                         get {\r
536                                 // the collection is a DataView\r
537                                 return false;\r
538                         }\r
539                 }\r
540                 \r
541                 internal RecordCache RecordCache {\r
542                         get {\r
543                                 return _recordCache;\r
544                         }\r
545                 }\r
546                 \r
547                 private DataRowBuilder RowBuilder\r
548                 {\r
549                         get\r
550                         {\r
551                                 // initiate only one row builder.\r
552                                 if (_rowBuilder == null)\r
553                                         _rowBuilder = new DataRowBuilder (this, -1, 0);\r
554                                 else                    \r
555                                         // new row get id -1.\r
556                                         _rowBuilder._rowId = -1;\r
557 \r
558                                 return _rowBuilder;\r
559                         }\r
560                 }\r
561                 \r
562                 private bool EnforceConstraints {\r
563                         get { return enforceConstraints; }\r
564                         set {\r
565                                 if (value != enforceConstraints) {\r
566                                         if (value) {\r
567                                                 // reset indexes since they may be outdated\r
568                                                 ResetIndexes();\r
569 \r
570                                                 bool violatesConstraints = false;\r
571 \r
572                                                 //FIXME: use index for AllowDBNull\r
573                                                 for (int i = 0; i < Columns.Count; i++) {\r
574                                                         DataColumn column = Columns[i];\r
575                                                         if (!column.AllowDBNull) {\r
576                                                                 for (int j = 0; j < Rows.Count; j++){\r
577                                                                         if (Rows[j].IsNull(column)) {\r
578                                                                                 violatesConstraints = true;\r
579                                                                                 Rows[j].RowError = String.Format("Column '{0}' does not allow DBNull.Value.", column.ColumnName);\r
580                                                                         }\r
581                                                                 }\r
582                                                         }\r
583                                                 }\r
584 \r
585                                                 if (violatesConstraints)\r
586                                                         Constraint.ThrowConstraintException();\r
587 \r
588                                                 // assert all constraints\r
589                                                 foreach (Constraint constraint in Constraints) {\r
590                                                         constraint.AssertConstraint();\r
591                                                 }\r
592                                         }\r
593                                         enforceConstraints = value;\r
594                                 }\r
595                         }\r
596                 }\r
597 \r
598                 internal bool RowsExist(DataColumn[] columns, DataColumn[] relatedColumns,DataRow row)\r
599                 {\r
600                         int curIndex = row.IndexFromVersion(DataRowVersion.Default);\r
601                         int tmpRecord = RecordCache.NewRecord();\r
602 \r
603                         try {\r
604                                 for (int i = 0; i < relatedColumns.Length; i++) {\r
605                                         // according to MSDN: the DataType value for both columns must be identical.\r
606                                         columns[i].DataContainer.CopyValue(relatedColumns[i].DataContainer, curIndex, tmpRecord);\r
607                                 }\r
608                                 return RowsExist(columns, tmpRecord);\r
609                         }\r
610                         finally {\r
611                                 RecordCache.DisposeRecord(tmpRecord);\r
612                         }\r
613                 }\r
614 \r
615                 bool RowsExist(DataColumn[] columns, int index)\r
616                 {\r
617                         bool rowsExist = false;\r
618                         Index indx = this.FindIndex(columns);\r
619 \r
620                         if (indx != null) { // lookup for a row in index                        \r
621                                 rowsExist = (indx.Find(index) != -1);\r
622                         } \r
623                         else { \r
624                                 // we have to perform full-table scan\r
625                                 // check that there is a parent for this row.\r
626                                 foreach (DataRow thisRow in this.Rows) {\r
627                                         if (thisRow.RowState != DataRowState.Deleted) {\r
628                                                 bool match = true;\r
629                                                 // check if the values in the columns are equal\r
630                                                 int thisIndex = thisRow.IndexFromVersion(DataRowVersion.Current);\r
631                                                 foreach (DataColumn column in columns) {\r
632                                                         if (column.DataContainer.CompareValues(thisIndex, index) != 0) {\r
633                                                                 match = false;\r
634                                                                 break;\r
635                                                         }       \r
636                                                 }\r
637                                                 if (match) {// there is a row with columns values equals to those supplied.\r
638                                                         rowsExist = true;\r
639                                                         break;\r
640                                                 }\r
641                                         }\r
642                                 }                               \r
643                         }\r
644                         return rowsExist;\r
645                 }\r
646 \r
647                 /// <summary>\r
648                 /// Commits all the changes made to this table since the \r
649                 /// last time AcceptChanges was called.\r
650                 /// </summary>\r
651                 public void AcceptChanges () \r
652                 {\r
653                         //FIXME: Do we need to validate anything here or\r
654                         //try to catch any errors to deal with them?\r
655                         \r
656                         // we do not use foreach because if one of the rows is in Delete state\r
657                         // it will be romeved from Rows and we get an exception.\r
658                         DataRow myRow;\r
659                         for (int i = 0; i < Rows.Count; )\r
660                         {\r
661                                 myRow = Rows[i];\r
662                                 myRow.AcceptChanges();\r
663 \r
664                                 // if the row state is Detached it meens that it was removed from row list (Rows)\r
665                                 // so we should not increase 'i'.\r
666                                 if (myRow.RowState != DataRowState.Detached)\r
667                                         i++;\r
668                         }\r
669                 }\r
670 \r
671                 /// <summary>\r
672                 /// Begins the initialization of a DataTable that is used \r
673                 /// on a form or used by another component. The initialization\r
674                 /// occurs at runtime.\r
675                 /// </summary>\r
676                 public virtual void BeginInit () \r
677                 {\r
678                         fInitInProgress = true;\r
679                 }\r
680 \r
681                 /// <summary>\r
682                 /// Turns off notifications, index maintenance, and \r
683                 /// constraints while loading data.\r
684                 /// </summary>\r
685                 [MonoTODO]\r
686                 public void BeginLoadData () \r
687                 {\r
688                         if (!this._duringDataLoad)\r
689                         {\r
690                                 //duringDataLoad is important to EndLoadData and\r
691                                 //for not throwing unexpected exceptions.\r
692                                 this._duringDataLoad = true;\r
693                                 this._nullConstraintViolationDuringDataLoad = false;\r
694                         \r
695                                 if (this.dataSet != null)\r
696                                 {\r
697                                         //Saving old Enforce constraints state for later\r
698                                         //use in the EndLoadData.\r
699                                         this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;\r
700                                         this.dataSet.EnforceConstraints = false;\r
701                                 }\r
702                                 else {\r
703                                         //if table does not belong to any data set use EnforceConstraints of the table\r
704                                         this.EnforceConstraints = false;\r
705                                 }\r
706                         }\r
707                         return;\r
708                 }\r
709 \r
710                 /// <summary>\r
711                 /// Clears the DataTable of all data.\r
712                 /// </summary>\r
713                 public void Clear () {\r
714                         // Foriegn key constraints are checked in _rows.Clear method\r
715                         _rows.Clear ();\r
716 #if NET_2_0\r
717                         OnTableCleared (new DataTableClearEventArgs (this));\r
718 #endif // NET_2_0\r
719 \r
720                 }\r
721 \r
722                 /// <summary>\r
723                 /// Clones the structure of the DataTable, including\r
724                 ///  all DataTable schemas and constraints.\r
725                 /// </summary>\r
726                 public virtual DataTable Clone () \r
727                 {\r
728                          // Use Activator so we can use non-public constructors.\r
729                         DataTable Copy = (DataTable) Activator.CreateInstance(GetType(), true);                 \r
730                         CopyProperties (Copy);\r
731                         return Copy;\r
732                 }\r
733 \r
734                 /// <summary>\r
735                 /// Computes the given expression on the current_rows that \r
736                 /// pass the filter criteria.\r
737                 /// </summary>\r
738                 [MonoTODO]\r
739                 public object Compute (string expression, string filter) \r
740                 {\r
741                         // expression is an aggregate function\r
742                         // filter is an expression used to limit rows\r
743 \r
744                         DataRow[] rows = Select(filter);\r
745                         \r
746                         if (rows == null || rows.Length == 0)\r
747                                 return DBNull.Value;\r
748                         \r
749                         Parser parser = new Parser (rows);\r
750                         IExpression expr = parser.Compile (expression);\r
751                         object obj = expr.Eval (rows[0]);\r
752                         \r
753                         return obj;\r
754                 }\r
755 \r
756                 /// <summary>\r
757                 /// Copies both the structure and data for this DataTable.\r
758                 /// </summary>\r
759                 public DataTable Copy () \r
760                 {\r
761                         DataTable copy = Clone();\r
762 \r
763                         copy._duringDataLoad = true;\r
764                         foreach (DataRow row in Rows) {\r
765                                 DataRow newRow = copy.NewNotInitializedRow();\r
766                                 copy.Rows.AddInternal(newRow);\r
767                                 CopyRow(row,newRow);\r
768                         }\r
769                         copy._duringDataLoad = false;           \r
770                 \r
771                         // rebuild copy indexes after loading all rows\r
772                         copy.ResetIndexes();\r
773                         return copy;\r
774                 }\r
775 \r
776                 internal void CopyRow(DataRow fromRow,DataRow toRow)\r
777                 {\r
778                         if (fromRow.HasErrors) {\r
779                                 fromRow.CopyErrors(toRow);\r
780                         }\r
781 \r
782                         if (fromRow.HasVersion(DataRowVersion.Original)) {\r
783                                 toRow.Original = toRow.Table.RecordCache.CopyRecord(this,fromRow.Original,-1);\r
784                         }\r
785 \r
786                         if (fromRow.HasVersion(DataRowVersion.Current)) {\r
787                                 if (fromRow.Original != fromRow.Current) {\r
788                                         toRow.Current = toRow.Table.RecordCache.CopyRecord(this,fromRow.Current,-1);\r
789                                 }\r
790                                 else {\r
791                                         toRow.Current = toRow.Original;\r
792                                 }\r
793                         }\r
794                 }\r
795 \r
796                 private void CopyProperties (DataTable Copy) \r
797                 {\r
798                         Copy.CaseSensitive = CaseSensitive;\r
799                         Copy._virginCaseSensitive = _virginCaseSensitive;\r
800 \r
801                         // Copy.ChildRelations\r
802                         // Copy.Constraints\r
803                         // Copy.Container\r
804                         // Copy.DefaultView\r
805                         // Copy.DesignMode\r
806                         Copy.DisplayExpression = DisplayExpression;\r
807                         if(ExtendedProperties.Count > 0) {\r
808                                 //  Cannot copy extended properties directly as the property does not have a set accessor\r
809                                 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);\r
810                                 ExtendedProperties.Keys.CopyTo (tgtArray, 0);\r
811                                 for (int i=0; i < ExtendedProperties.Count; i++)\r
812                                         Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);\r
813                         }\r
814                         Copy.Locale = Locale;\r
815                         Copy.MinimumCapacity = MinimumCapacity;\r
816                         Copy.Namespace = Namespace;\r
817                         // Copy.ParentRelations\r
818                         Copy.Prefix = Prefix;\r
819                         Copy.Site = Site;\r
820                         Copy.TableName = TableName;\r
821 \r
822                         bool isEmpty = Copy.Columns.Count == 0;\r
823 \r
824                         // Copy columns\r
825                         foreach (DataColumn column in Columns) {                        \r
826                                 // When cloning a table, the columns may be added in the default constructor.\r
827                                 if (isEmpty || !Copy.Columns.Contains(column.ColumnName)) {\r
828                                         Copy.Columns.Add (column.Clone());      \r
829                                 }\r
830                         }\r
831 \r
832                         CopyConstraints(Copy);\r
833                         // add primary key to the copy\r
834                         if (PrimaryKey.Length > 0) {\r
835                                 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];\r
836                                 for (int i = 0; i < pColumns.Length; i++)\r
837                                         pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];\r
838 \r
839                                 Copy.PrimaryKey = pColumns;\r
840                         }\r
841                 }\r
842 \r
843                 private void CopyConstraints(DataTable copy)\r
844                 {\r
845                         UniqueConstraint origUc;\r
846                         UniqueConstraint copyUc;\r
847                         for (int i = 0; i < this.Constraints.Count; i++)\r
848                         {\r
849                                 if (this.Constraints[i] is UniqueConstraint)\r
850                                 {\r
851                                         origUc = (UniqueConstraint)this.Constraints[i];\r
852                                         DataColumn[] columns = new DataColumn[origUc.Columns.Length];\r
853                                         for (int j = 0; j < columns.Length; j++)\r
854                                                 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];\r
855                                         \r
856                                         copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);\r
857                                         copy.Constraints.Add(copyUc);\r
858                                 }\r
859                         }\r
860                 }\r
861                 /// <summary>\r
862                 /// Ends the initialization of a DataTable that is used \r
863                 /// on a form or used by another component. The \r
864                 /// initialization occurs at runtime.\r
865                 /// </summary>\r
866                 [MonoTODO]\r
867                 public virtual void EndInit () \r
868                 {\r
869                         fInitInProgress = false;\r
870                         // Add the constraints\r
871                         _constraintCollection.PostEndInit();\r
872                         Columns.PostEndInit();\r
873                 }\r
874 \r
875                 /// <summary>\r
876                 /// Turns on notifications, index maintenance, and \r
877                 /// constraints after loading data.\r
878                 /// </summary>\r
879                 public void EndLoadData() \r
880                 {\r
881                         if (this._duringDataLoad) {\r
882                                 if(this._nullConstraintViolationDuringDataLoad) {\r
883                                         this._nullConstraintViolationDuringDataLoad = false;\r
884                                         throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");\r
885                                 }\r
886                                 \r
887                                 if (this.dataSet !=null) {\r
888                                         //Getting back to previous EnforceConstraint state\r
889                                         this.dataSet.InternalEnforceConstraints(this.dataSetPrevEnforceConstraints,true);\r
890                                 }\r
891                                 else {\r
892                                         //Getting back to the table's previous EnforceConstraint state\r
893                                         this.EnforceConstraints = true;\r
894                                 }\r
895 \r
896                                 //Returning from loading mode, raising exceptions as usual\r
897                                 this._duringDataLoad = false;\r
898                         }\r
899                 }\r
900 \r
901                 /// <summary>\r
902                 /// Gets a copy of the DataTable that contains all\r
903                 ///  changes made to it since it was loaded or \r
904                 ///  AcceptChanges was last called.\r
905                 /// </summary>\r
906                 public DataTable GetChanges() \r
907                 {\r
908                         return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);\r
909                 }\r
910 \r
911                 /// <summary>\r
912                 /// Gets a copy of the DataTable containing all \r
913                 /// changes made to it since it was last loaded, or \r
914                 /// since AcceptChanges was called, filtered by DataRowState.\r
915                 /// </summary>\r
916                 public DataTable GetChanges(DataRowState rowStates) \r
917                 {\r
918                         DataTable copyTable = null;\r
919 \r
920                         IEnumerator rowEnumerator = Rows.GetEnumerator();\r
921                         while (rowEnumerator.MoveNext()) {\r
922                                 DataRow row = (DataRow)rowEnumerator.Current;\r
923                                 // The spec says relationship constraints may cause Unchanged parent rows to be included but\r
924                                 // MS .NET 1.1 does not include Unchanged rows even if their child rows are changed.\r
925                                 if (row.IsRowChanged(rowStates)) {\r
926                                         if (copyTable == null)\r
927                                                 copyTable = Clone();\r
928                                         DataRow newRow = copyTable.NewNotInitializedRow();\r
929                                         row.CopyValuesToRow(newRow);\r
930                                         copyTable.Rows.AddInternal (newRow);\r
931                                 }\r
932                         }\r
933                          \r
934                         return copyTable;\r
935                 }\r
936 \r
937 #if NET_2_0\r
938                 [MonoTODO]\r
939                 public DataTableReader GetDataReader ()\r
940                 {\r
941                         throw new NotImplementedException ();\r
942                 }\r
943 #endif\r
944 \r
945                 /// <summary>\r
946                 /// Gets an array of DataRow objects that contain errors.\r
947                 /// </summary>\r
948                 public DataRow[] GetErrors () \r
949                 {\r
950                         ArrayList errors = new ArrayList();\r
951                         for (int i = 0; i < _rows.Count; i++)\r
952                         {\r
953                                 if (_rows[i].HasErrors)\r
954                                         errors.Add(_rows[i]);\r
955                         }\r
956                         \r
957                         DataRow[] ret = NewRowArray(errors.Count);\r
958                         errors.CopyTo(ret, 0);\r
959                         return ret;\r
960                 }\r
961         \r
962                 /// <summary>\r
963                 /// This member is only meant to support Mono's infrastructure \r
964                 /// </summary>\r
965                 protected virtual DataTable CreateInstance () \r
966                 {\r
967                         return Activator.CreateInstance (this.GetType (), true) as DataTable;\r
968                 }\r
969 \r
970                 /// <summary>\r
971                 /// This member is only meant to support Mono's infrastructure \r
972                 /// </summary>\r
973                 protected virtual Type GetRowType () \r
974                 {\r
975                         return typeof (DataRow);\r
976                 }\r
977 \r
978                 /// <summary>\r
979                 /// This member is only meant to support Mono's infrastructure \r
980                 /// \r
981                 /// Used for Data Binding between System.Web.UI. controls \r
982                 /// like a DataGrid\r
983                 /// or\r
984                 /// System.Windows.Forms controls like a DataGrid\r
985                 /// </summary>\r
986                 IList IListSource.GetList () \r
987                 {\r
988                         IList list = (IList) DefaultView;\r
989                         return list;\r
990                 }\r
991                                 \r
992                 /// <summary>\r
993                 /// Copies a DataRow into a DataTable, preserving any \r
994                 /// property settings, as well as original and current values.\r
995                 /// </summary>\r
996                 public void ImportRow (DataRow row) \r
997                 {\r
998                         DataRow newRow = NewNotInitializedRow();\r
999 \r
1000                         int original = -1;\r
1001                         if (row.HasVersion(DataRowVersion.Original)) {\r
1002                                 original = row.IndexFromVersion(DataRowVersion.Original);\r
1003                                 newRow.Original = RecordCache.NewRecord();\r
1004                                 RecordCache.CopyRecord(row.Table,original,newRow.Original);\r
1005                         }\r
1006 \r
1007                         if (row.HasVersion(DataRowVersion.Current)) {\r
1008                                 int current = row.IndexFromVersion(DataRowVersion.Current);\r
1009                                 if (current == original)\r
1010                                         newRow.Current = newRow.Original;\r
1011                                 else {\r
1012                                         newRow.Current = RecordCache.NewRecord();\r
1013                                         RecordCache.CopyRecord(row.Table,current,newRow.Current);\r
1014                                 }\r
1015                         }\r
1016 \r
1017                         if (EnforceConstraints)\r
1018                                 // we have to check that the new row doesn't colide with existing row\r
1019                                 Rows.ValidateDataRowInternal(newRow);\r
1020 \r
1021                         Rows.AddInternal(newRow);               \r
1022         \r
1023                         if (row.HasErrors) {\r
1024                                 row.CopyErrors(newRow);\r
1025                         }\r
1026                 }\r
1027 \r
1028                 internal int DefaultValuesRowIndex\r
1029                 {\r
1030                         get {\r
1031                                 return _defaultValuesRowIndex;\r
1032                         }       \r
1033                 }\r
1034 \r
1035                 /// <summary>\r
1036                 /// This member is only meant to support Mono's infrastructure          \r
1037                 /// </summary>\r
1038                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) \r
1039                 {\r
1040                         DataSet dset;\r
1041                         if (dataSet != null)\r
1042                                 dset = dataSet;\r
1043                         else {\r
1044                                 dset = new DataSet ("tmpDataSet");\r
1045                                 dset.Tables.Add (this);\r
1046                         }\r
1047                         \r
1048                         StringWriter sw = new StringWriter ();\r
1049                         XmlTextWriter tw = new XmlTextWriter (sw);\r
1050                         tw.Formatting = Formatting.Indented;\r
1051                         dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);\r
1052                         tw.Close ();\r
1053                         \r
1054                         StringWriter sw2 = new StringWriter ();\r
1055                         DataTableCollection tables = new DataTableCollection (dset);\r
1056                         tables.Add (this);\r
1057                         XmlSchemaWriter.WriteXmlSchema (dset, new XmlTextWriter (sw2), tables, null);\r
1058                         sw2.Close ();\r
1059                         \r
1060                         info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));\r
1061                         info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));\r
1062                 }\r
1063 \r
1064 #if NET_2_0\r
1065                 /// <summary>\r
1066                 ///     Loads the table with the values from the reader\r
1067                 /// </summary>\r
1068                 public void Load (IDataReader reader)\r
1069                 {\r
1070                         Load (reader, LoadOption.PreserveChanges);\r
1071                 }\r
1072 \r
1073                 /// <summary>\r
1074                 ///     Loads the table with the values from the reader and the pattern\r
1075                 ///     of the changes to the existing rows or the new rows are based on\r
1076                 ///     the LoadOption passed.\r
1077                 /// </summary>\r
1078                 public void Load (IDataReader reader, LoadOption loadOption)\r
1079                 {\r
1080                         bool prevEnforceConstr = this.EnforceConstraints;\r
1081                         try {\r
1082                                 this.EnforceConstraints = false;\r
1083                                 int [] mapping = DbDataAdapter.BuildSchema (reader, this, SchemaType.Mapped, \r
1084                                                                             MissingSchemaAction.AddWithKey,\r
1085                                                                             MissingMappingAction.Passthrough, \r
1086                                                                             new DataTableMappingCollection ());\r
1087                                 DbDataAdapter.FillFromReader (this,\r
1088                                                               reader,\r
1089                                                               0, // start from\r
1090                                                               0, // all records\r
1091                                                               mapping,\r
1092                                                               loadOption);\r
1093                         } finally {\r
1094                                 this.EnforceConstraints = prevEnforceConstr;\r
1095                         }\r
1096                 }\r
1097 \r
1098                 \r
1099 #endif\r
1100 \r
1101                 /// <summary>\r
1102                 /// Finds and updates a specific row. If no matching row\r
1103                 ///  is found, a new row is created using the given values.\r
1104                 /// </summary>\r
1105                 public DataRow LoadDataRow (object[] values, bool fAcceptChanges) \r
1106                 {\r
1107                         DataRow row = null;\r
1108                         if (PrimaryKey.Length == 0) {\r
1109                                 row = Rows.Add (values);\r
1110                                 if (fAcceptChanges)\r
1111                                         row.AcceptChanges ();\r
1112                         }\r
1113                         else {\r
1114                                 int newRecord = CreateRecord(values);\r
1115                                 int existingRecord = _primaryKeyConstraint.Index.Find(newRecord);\r
1116 \r
1117                                 if (existingRecord < 0) {\r
1118                                         row = NewRowFromBuilder (RowBuilder);\r
1119                                         row.Proposed = newRecord;\r
1120                                         Rows.AddInternal(row);\r
1121                                 }\r
1122                                 else {\r
1123                                         row = RecordCache[existingRecord];\r
1124                                         row.BeginEdit();\r
1125                                         row.ImportRecord(newRecord);\r
1126                                         row.EndEdit();\r
1127                                         \r
1128                                 }\r
1129                                 \r
1130                                 if (fAcceptChanges)\r
1131                                         row.AcceptChanges ();\r
1132                         }\r
1133                                 \r
1134                         return row;\r
1135                 }\r
1136 \r
1137                 internal DataRow LoadDataRow(IDataRecord record, int[] mapping, int length, bool fAcceptChanges)\r
1138                 {\r
1139                         DataRow row = null;\r
1140                                 int tmpRecord = this.RecordCache.NewRecord();\r
1141                                 try {\r
1142                                 RecordCache.ReadIDataRecord(tmpRecord,record,mapping,length);\r
1143                                 if (PrimaryKey.Length != 0) {\r
1144                                         bool hasPrimaryValues = true;\r
1145                                         foreach(DataColumn col in PrimaryKey) {\r
1146                                                 if(!(col.Ordinal < mapping.Length)) {\r
1147                                                         hasPrimaryValues = false;\r
1148                                                         break;\r
1149                                                 }\r
1150                                         }\r
1151                                         \r
1152                                         if (hasPrimaryValues) {\r
1153                                                 // find the row in the table.\r
1154                                                 row = Rows.Find(tmpRecord);\r
1155                                         }\r
1156                                 }\r
1157                                         \r
1158                                 bool shouldUpdateIndex = false;\r
1159                                 if (row == null) {\r
1160                                         row = NewNotInitializedRow();\r
1161                                         row.ImportRecord(tmpRecord);\r
1162                                         Rows.AddInternal (row);\r
1163                                         shouldUpdateIndex = true;\r
1164                                 }\r
1165                                 else {\r
1166                                         // Proposed = tmpRecord\r
1167                                         row.ImportRecord(tmpRecord);\r
1168                                 }\r
1169                                 \r
1170                                 if (fAcceptChanges) {\r
1171                                         row.AcceptChanges();\r
1172                                 }\r
1173                                 \r
1174                                 if (shouldUpdateIndex || !fAcceptChanges) {\r
1175                                         // AcceptChanges not always updates indexes because it calls EndEdit\r
1176                                         foreach(Index index in Indexes) {\r
1177                                                 index.Update(row,tmpRecord);\r
1178                                         }\r
1179                                 }\r
1180 \r
1181                         }\r
1182                         catch(Exception e) {\r
1183                                 this.RecordCache.DisposeRecord(tmpRecord);\r
1184                                 throw e;\r
1185                         }                               \r
1186                         return row;\r
1187                 }\r
1188 \r
1189 #if NET_2_0\r
1190                 /// <summary>\r
1191                 ///     Loads the given values into an existing row if matches or creates\r
1192                 ///     the new row popluated with the values.\r
1193                 /// </summary>\r
1194                 /// <remarks>\r
1195                 ///     This method searches for the values using primary keys and it first\r
1196                 ///     searches using the original values of the rows and if not found, it\r
1197                 ///     searches using the current version of the row.\r
1198                 /// </remarks>\r
1199                 public DataRow LoadDataRow (object [] values, LoadOption loadOption)\r
1200                 {\r
1201                         DataRow row  = null;\r
1202                         bool new_row = false;\r
1203                         \r
1204                         // Find Data DataRow\r
1205                         if (this.PrimaryKey.Length > 0) {\r
1206                                 object [] keyValues = new object [this.PrimaryKey.Length];\r
1207                                 for (int i = 0; i < keyValues.Length; i++)\r
1208                                         keyValues [i] = values [this.PrimaryKey [i].Ordinal];\r
1209                                 row = this.Rows.Find (keyValues, DataRowVersion.Original );\r
1210                                 if (row == null) \r
1211                                         row = this.Rows.Find (keyValues, DataRowVersion.Current);\r
1212                         }\r
1213                                 \r
1214                         // If not found, add new row\r
1215                         if (row == null) {\r
1216                                 row = this.NewRow ();\r
1217                                 new_row = true;\r
1218                         }\r
1219 \r
1220                         bool deleted = row.RowState == DataRowState.Deleted;\r
1221 \r
1222                         if (deleted && loadOption == LoadOption.OverwriteChanges)\r
1223                                 row.RejectChanges ();                        \r
1224 \r
1225                         row.Load (values, loadOption, new_row);\r
1226 \r
1227                         if (deleted && loadOption == LoadOption.Upsert) {\r
1228                                 row = this.NewRow ();\r
1229                                 row.Load (values, loadOption, new_row = true);\r
1230                         }\r
1231 \r
1232                         if (new_row) {\r
1233                                 this.Rows.Add (row);\r
1234                                 if (loadOption == LoadOption.OverwriteChanges ||\r
1235                                     loadOption == LoadOption.PreserveChanges) {\r
1236                                         row.AcceptChanges ();\r
1237                                 }\r
1238                         }\r
1239 \r
1240                         return row;\r
1241                 }\r
1242 \r
1243                 [MonoTODO]\r
1244                 public void Merge (DataTable table)\r
1245                 {\r
1246                         throw new NotImplementedException ();\r
1247                 }\r
1248 \r
1249                 [MonoTODO]\r
1250                 public void Merge (DataTable table, bool preserveChanges)\r
1251                 {\r
1252                         throw new NotImplementedException ();\r
1253                 }\r
1254 \r
1255                 [MonoTODO]\r
1256                 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)\r
1257                 {\r
1258                         throw new NotImplementedException ();\r
1259                 }\r
1260 #endif\r
1261 \r
1262                 /// <summary>\r
1263                 /// Creates a new DataRow with the same schema as the table.\r
1264                 /// </summary>\r
1265                 public DataRow NewRow () \r
1266                 {\r
1267                         EnsureDefaultValueRowIndex();\r
1268 \r
1269                         DataRow newRow = NewRowFromBuilder (RowBuilder);\r
1270 \r
1271                         newRow.Proposed = CreateRecord(null);\r
1272                         return newRow;\r
1273                 }\r
1274 \r
1275                 internal int CreateRecord(object[] values) {\r
1276                         int valCount = values != null ? values.Length : 0;\r
1277                         if (valCount > Columns.Count)\r
1278                                 throw new ArgumentException("Input array is longer than the number of columns in this table.");\r
1279 \r
1280                         int index = RecordCache.NewRecord();\r
1281 \r
1282                         try {\r
1283                                 for (int i = 0; i < valCount; i++) {\r
1284                                         try {\r
1285                                                 Columns[i].DataContainer[index] = values[i];\r
1286                                         }\r
1287                                         catch(Exception e) {\r
1288                                                 throw new ArgumentException(e.Message +\r
1289                                                         String.Format("Couldn't store <{0}> in {1} Column.  Expected type is {2}.",\r
1290                                                         values[i], Columns[i].ColumnName, Columns[i].DataType.Name), e);\r
1291                                         }\r
1292                                 }\r
1293 \r
1294                                 for(int i = valCount; i < Columns.Count; i++) {\r
1295                                         DataColumn column = Columns[i];\r
1296                                         if (column.AutoIncrement)\r
1297                                                 column.DataContainer[index] = column.AutoIncrementValue ();\r
1298                                         else\r
1299                                                 column.DataContainer.CopyValue(DefaultValuesRowIndex, index);\r
1300                                 }\r
1301 \r
1302                                 return index;\r
1303                         }\r
1304                         catch {\r
1305                                 RecordCache.DisposeRecord(index);\r
1306                                 throw;\r
1307                         }\r
1308                 }\r
1309 \r
1310                 private void EnsureDefaultValueRowIndex()\r
1311                 {\r
1312                         // initialize default values row for the first time\r
1313                         if ( _defaultValuesRowIndex == -1 ) {\r
1314                                 _defaultValuesRowIndex = RecordCache.NewRecord();\r
1315                                 foreach(DataColumn column in Columns) {\r
1316                                         column.DataContainer[_defaultValuesRowIndex] = column.DefaultValue;\r
1317                                 }\r
1318                         }\r
1319                 }\r
1320 \r
1321                 /// <summary>\r
1322                 /// This member supports the .NET Framework infrastructure\r
1323                 ///  and is not intended to be used directly from your code.\r
1324                 /// </summary>\r
1325                 protected internal DataRow[] NewRowArray (int size) \r
1326                 {\r
1327                         return (DataRow[]) Array.CreateInstance (GetRowType (), size);\r
1328                 }\r
1329 \r
1330                 /// <summary>\r
1331                 /// Creates a new row from an existing row.\r
1332                 /// </summary>\r
1333                 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder) \r
1334                 {\r
1335                         return new DataRow (builder);\r
1336                 }\r
1337                 \r
1338                 internal DataRow NewNotInitializedRow()\r
1339                 {\r
1340                         EnsureDefaultValueRowIndex();\r
1341 \r
1342                         return NewRowFromBuilder (RowBuilder);\r
1343                 }\r
1344 \r
1345 #if NET_2_0\r
1346                 [MonoTODO]\r
1347                 XmlReadMode ReadXml (Stream stream)\r
1348                 {\r
1349                         throw new NotImplementedException ();\r
1350                 }\r
1351 \r
1352                 public void ReadXmlSchema (Stream stream)\r
1353                 {\r
1354                         ReadXmlSchema (new XmlTextReader (stream));\r
1355                 }\r
1356 \r
1357                 public void ReadXmlSchema (TextReader reader)\r
1358                 {\r
1359                         ReadXmlSchema (new XmlTextReader (reader));\r
1360                 }\r
1361 \r
1362                 public void ReadXmlSchema (string fileName)\r
1363                 {\r
1364                         XmlTextReader reader = null;\r
1365                         try {\r
1366                                 reader = new XmlTextReader (fileName);\r
1367                         ReadXmlSchema (reader);\r
1368                         } finally {\r
1369                                 if (reader != null)\r
1370                         reader.Close ();\r
1371                 }\r
1372                 }\r
1373 \r
1374                 public void ReadXmlSchema (XmlReader reader)\r
1375                 {\r
1376                         DataSet ds = new DataSet ();\r
1377                         new XmlSchemaDataImporter (ds, reader).Process ();\r
1378                         DataTable target = null;\r
1379                         if (TableName == String.Empty) {\r
1380                                 if (ds.Tables.Count > 0)\r
1381                                         target = ds.Tables [0];\r
1382                         }\r
1383                         else {\r
1384                                 target = ds.Tables [TableName];\r
1385                                 if (target == null)\r
1386                                         throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source.", TableName));\r
1387                         }\r
1388                         if (target != null)\r
1389                                 target.CopyProperties (this);\r
1390                 }\r
1391 #endif\r
1392 \r
1393                 /// <summary>\r
1394                 /// Rolls back all changes that have been made to the \r
1395                 /// table since it was loaded, or the last time AcceptChanges\r
1396                 ///  was called.\r
1397                 /// </summary>\r
1398                 public void RejectChanges () \r
1399                 {       \r
1400                         for (int i = _rows.Count - 1; i >= 0; i--) {\r
1401                                 DataRow row = _rows [i];\r
1402                                 if (row.RowState != DataRowState.Unchanged)\r
1403                                         _rows [i].RejectChanges ();\r
1404                         }\r
1405                 }\r
1406 \r
1407                 /// <summary>\r
1408                 /// Resets the DataTable to its original state.\r
1409                 /// </summary>          \r
1410                 public virtual void Reset () \r
1411                 {\r
1412                         Clear();\r
1413                         while (ParentRelations.Count > 0)\r
1414                         {\r
1415                                 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))\r
1416                                         dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);\r
1417                         }\r
1418 \r
1419                         while (ChildRelations.Count > 0)\r
1420                         {\r
1421                                 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))\r
1422                                         dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);\r
1423                         }\r
1424                         Constraints.Clear();\r
1425                         Columns.Clear();\r
1426                 }\r
1427 \r
1428                 /// <summary>\r
1429                 /// Gets an array of all DataRow objects.\r
1430                 /// </summary>\r
1431                 public DataRow[] Select () \r
1432                 {\r
1433                         return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);\r
1434                 }\r
1435 \r
1436                 /// <summary>\r
1437                 /// Gets an array of all DataRow objects that match \r
1438                 /// the filter criteria in order of primary key (or \r
1439                 /// lacking one, order of addition.)\r
1440                 /// </summary>\r
1441                 public DataRow[] Select (string filterExpression) \r
1442                 {\r
1443                         return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);\r
1444                 }\r
1445 \r
1446                 /// <summary>\r
1447                 /// Gets an array of all DataRow objects that \r
1448                 /// match the filter criteria, in the the \r
1449                 /// specified sort order.\r
1450                 /// </summary>\r
1451                 public DataRow[] Select (string filterExpression, string sort) \r
1452                 {\r
1453                         return Select(filterExpression, sort, DataViewRowState.CurrentRows);\r
1454                 }\r
1455 \r
1456                 /// <summary>\r
1457                 /// Gets an array of all DataRow objects that match\r
1458                 /// the filter in the order of the sort, that match \r
1459                 /// the specified state.\r
1460                 /// </summary>\r
1461                 [MonoTODO]\r
1462                 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) \r
1463                 {\r
1464                         if (filterExpression == null)\r
1465                                 filterExpression = String.Empty;\r
1466 \r
1467                         DataColumn[] columns = _emptyColumnArray;\r
1468                         ListSortDirection[] sorts = null;\r
1469                         if (sort != null && !sort.Equals(String.Empty))\r
1470                                 columns = ParseSortString (this, sort, out sorts, false);\r
1471 \r
1472                         IExpression filter = null;\r
1473                         if (filterExpression != String.Empty) {\r
1474                                 Parser parser = new Parser ();\r
1475                                 filter = parser.Compile (filterExpression);\r
1476                         }\r
1477 \r
1478                         Index index = FindIndex(columns, sorts, recordStates, filter);\r
1479                         if (index == null)\r
1480                                 index = new Index(new Key(this,columns,sorts,recordStates,filter));\r
1481 \r
1482                         int[] records = index.GetAll();\r
1483                         DataRow[] dataRows = NewRowArray(index.Size);\r
1484                         for (int i = 0; i < dataRows.Length; i++)\r
1485                                 dataRows[i] = RecordCache[records[i]];\r
1486 \r
1487                         return dataRows;\r
1488                 }\r
1489 \r
1490                 \r
1491                 private void AddIndex (Index index)\r
1492                 {\r
1493                         if (_indexes == null) {\r
1494                                 _indexes = new ArrayList();\r
1495                         }\r
1496 \r
1497                         _indexes.Add (index);\r
1498                 }\r
1499 \r
1500                 /// <summary>\r
1501                 /// Returns index corresponding to columns,sort,row state filter and unique values given.\r
1502                 /// If such an index not exists, creates a new one.\r
1503                 /// </summary>\r
1504                 /// <param name="columns">Columns set of the index to look for.</param>\r
1505                 /// <param name="sort">Columns sort order of the index to look for.</param>\r
1506                 /// <param name="rowState">Rpw state filter of the index to look for.</param>\r
1507                 /// <param name="unique">Uniqueness of the index to look for.</param>\r
1508                 /// <param name="strict">Indicates whenever the index found should correspond in its uniquness to the value of unique parameter specified.</param>\r
1509                 /// <param name="reset">Indicates whenever the already existing index should be forced to reset.</param>\r
1510                 /// <returns></returns>\r
1511                 internal Index GetIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter, bool reset)\r
1512                 {\r
1513                         Index index = FindIndex(columns,sort,rowState,filter);\r
1514                         if (index == null ) {\r
1515                                 index = new Index(new Key(this,columns,sort,rowState,filter));\r
1516 \r
1517                                 AddIndex(index);\r
1518                         }\r
1519                         else if (reset) {\r
1520                                 // reset existing index only if asked for this\r
1521                                 index.Reset();\r
1522                         }\r
1523                         return index;\r
1524                 }\r
1525 \r
1526                 internal Index FindIndex(DataColumn[] columns)\r
1527                 {\r
1528                         return FindIndex(columns,null,DataViewRowState.None, null);\r
1529                 }\r
1530 \r
1531                 internal Index FindIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter)\r
1532                 {\r
1533                         if (Indexes != null) {\r
1534                                 foreach (Index index in Indexes) {\r
1535                                         if (index.Key.Equals(columns,sort,rowState, filter)) {\r
1536                                                 return index;\r
1537                                         }\r
1538                                 }\r
1539                         }\r
1540                         return null;\r
1541                 }\r
1542 \r
1543                 internal void ResetIndexes()\r
1544                 {\r
1545                         foreach(Index index in Indexes) {\r
1546                                 index.Reset();\r
1547                         }\r
1548                 }\r
1549 \r
1550                 internal void ResetCaseSensitiveIndexes()\r
1551                 {\r
1552                         foreach(Index index in Indexes) {\r
1553                                 bool containsStringcolumns = false;\r
1554                                 foreach(DataColumn column in index.Key.Columns) {\r
1555                                         if (column.DataType == typeof(string)) {\r
1556                                                 containsStringcolumns = true;\r
1557                                                 break;\r
1558                                         }\r
1559                                 }\r
1560 \r
1561                                 if (containsStringcolumns) {\r
1562                                         index.Reset();\r
1563                                 }\r
1564                         }\r
1565                 }\r
1566 \r
1567                 internal void DropIndex(Index index)\r
1568                 {\r
1569                         if (index != null && index.RefCount == 0) {     \r
1570                                 _indexes.Remove(index);\r
1571                         }\r
1572                 }\r
1573 \r
1574                 internal void DeleteRowFromIndexes (DataRow row)\r
1575                 {\r
1576                         if (_indexes != null) {\r
1577                                 foreach (Index indx in _indexes) {\r
1578                                         indx.Delete (row);\r
1579                                 }\r
1580                         }\r
1581                 }\r
1582 \r
1583                 /// <summary>\r
1584                 /// Gets the TableName and DisplayExpression, if \r
1585                 /// there is one as a concatenated string.\r
1586                 /// </summary>\r
1587                 public override string ToString() \r
1588                 {\r
1589                         //LAMESPEC: spec says concat the two. impl puts a \r
1590                         //plus sign infront of DisplayExpression\r
1591                         string retVal = TableName;\r
1592                         if(DisplayExpression != null && DisplayExpression != "")\r
1593                                 retVal += " + " + DisplayExpression;\r
1594                         return retVal;\r
1595                 }\r
1596 \r
1597 #if NET_2_0\r
1598                 private XmlWriterSettings GetWriterSettings ()\r
1599                 {\r
1600                         XmlWriterSettings s = new XmlWriterSettings ();\r
1601                         s.Indent = true;\r
1602                         return s;\r
1603                 }\r
1604 \r
1605                 public void WriteXml (Stream stream)\r
1606                 {\r
1607                         WriteXml (stream, XmlWriteMode.IgnoreSchema);\r
1608                 }\r
1609 \r
1610                 public void WriteXml (TextWriter writer)\r
1611                 {\r
1612                         WriteXml (writer, XmlWriteMode.IgnoreSchema);\r
1613                 }\r
1614 \r
1615                 public void WriteXml (XmlWriter writer)\r
1616                 {\r
1617                         WriteXml (writer, XmlWriteMode.IgnoreSchema);\r
1618                 }\r
1619 \r
1620                 public void WriteXml (string fileName)\r
1621                 {\r
1622                         WriteXml (fileName, XmlWriteMode.IgnoreSchema);\r
1623                 }\r
1624 \r
1625                 public void WriteXml (Stream stream, XmlWriteMode mode)\r
1626                 {\r
1627                         WriteXml (XmlWriter.Create (stream, GetWriterSettings ()), mode);\r
1628                 }\r
1629 \r
1630                 public void WriteXml (TextWriter writer, XmlWriteMode mode)\r
1631                 {\r
1632                         WriteXml (XmlWriter.Create (writer, GetWriterSettings ()), mode);\r
1633                 }\r
1634 \r
1635                 [MonoTODO]\r
1636                 public void WriteXml (XmlWriter writer, XmlWriteMode mode)\r
1637                 {\r
1638                         throw new NotImplementedException ();\r
1639                 }\r
1640 \r
1641                 public void WriteXml (string fileName, XmlWriteMode mode)\r
1642                 {\r
1643                         XmlWriter xw = null;\r
1644                         try {\r
1645                                 xw = XmlWriter.Create (fileName, GetWriterSettings ());\r
1646                                 WriteXml (xw, mode);\r
1647                         } finally {\r
1648                                 if (xw != null)\r
1649                                         xw.Close ();\r
1650                         }\r
1651                 }\r
1652 \r
1653                 public void WriteXmlSchema (Stream stream)\r
1654                 {\r
1655                         WriteXmlSchema (XmlWriter.Create (stream, GetWriterSettings ()));\r
1656                 }\r
1657 \r
1658                 public void WriteXmlSchema (TextWriter writer)\r
1659                 {\r
1660                         WriteXmlSchema (XmlWriter.Create (writer, GetWriterSettings ()));\r
1661                 }\r
1662 \r
1663                 public void WriteXmlSchema (XmlWriter writer)\r
1664                 {\r
1665                         DataSet ds = DataSet;\r
1666                         DataSet tmp = null;\r
1667                         try {\r
1668                                 if (ds == null) {\r
1669                                         tmp = ds = new DataSet ();\r
1670                                         ds.Tables.Add (this);\r
1671                                 }\r
1672                                 DataTableCollection col = new DataTableCollection (ds);\r
1673                                 col.Add (this);\r
1674                                 XmlSchemaWriter.WriteXmlSchema (ds, writer, col, null);\r
1675                         } finally {\r
1676                                 if (tmp != null)\r
1677                                         ds.Tables.Remove (this);\r
1678                         }\r
1679                 }\r
1680 \r
1681                 public void WriteXmlSchema (string fileName)\r
1682                 {\r
1683                         XmlWriter xw = null;\r
1684                         try {\r
1685                                 xw = XmlWriter.Create (fileName, GetWriterSettings ());\r
1686                                 WriteXmlSchema (xw);\r
1687                         } finally {\r
1688                                 if (xw != null)\r
1689                                         xw.Close ();\r
1690                         }\r
1691                 }\r
1692 #endif\r
1693                 \r
1694                 #region Events \r
1695                 \r
1696                 /// <summary>\r
1697                 /// Raises the ColumnChanged event.\r
1698                 /// </summary>\r
1699                 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {\r
1700                         if (null != ColumnChanged) {\r
1701                                 ColumnChanged (this, e);\r
1702                         }\r
1703                 }\r
1704 \r
1705                 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e) {\r
1706                         OnColumnChanged(e);\r
1707                 }\r
1708 \r
1709 #if NET_2_0\r
1710                 /// <summary>\r
1711                 /// Raises TableCleared Event and delegates to subscribers\r
1712                 /// </summary>\r
1713                 protected virtual void OnTableCleared (DataTableClearEventArgs e) {\r
1714                         if (TableCleared != null)\r
1715                                 TableCleared (this, e);\r
1716                 }\r
1717 #endif // NET_2_0\r
1718 \r
1719                 /// <summary>\r
1720                 /// Raises the ColumnChanging event.\r
1721                 /// </summary>\r
1722                 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {\r
1723                         if (null != ColumnChanging) {\r
1724                                 ColumnChanging (this, e);\r
1725                         }\r
1726                 }\r
1727 \r
1728                 internal void RaiseOnColumnChanging (DataColumnChangeEventArgs e) {\r
1729                         OnColumnChanging(e);\r
1730                 }\r
1731 \r
1732                 /// <summary>\r
1733                 /// Raises the PropertyChanging event.\r
1734                 /// </summary>\r
1735                 [MonoTODO]\r
1736                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {\r
1737                         //      if (null != PropertyChanging)\r
1738                         //      {\r
1739                         //              PropertyChanging (this, e);\r
1740                         //      }\r
1741                 }\r
1742 \r
1743                 /// <summary>\r
1744                 /// Notifies the DataTable that a DataColumn is being removed.\r
1745                 /// </summary>\r
1746                 [MonoTODO]\r
1747                 protected internal virtual void OnRemoveColumn (DataColumn column) {\r
1748                 }\r
1749 \r
1750 \r
1751                 /// <summary>\r
1752                 /// Raises the RowChanged event.\r
1753                 /// </summary>\r
1754                 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {\r
1755                         if (null != RowChanged) {\r
1756                                 RowChanged(this, e);\r
1757                         }\r
1758                 }\r
1759 \r
1760 \r
1761                 /// <summary>\r
1762                 /// Raises the RowChanging event.\r
1763                 /// </summary>\r
1764                 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {\r
1765                         if (null != RowChanging) {\r
1766                                 RowChanging(this, e);\r
1767                         }\r
1768                 }\r
1769 \r
1770                 /// <summary>\r
1771                 /// Raises the RowDeleted event.\r
1772                 /// </summary>\r
1773                 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {\r
1774                         if (null != RowDeleted) {\r
1775                                 RowDeleted(this, e);\r
1776                         }\r
1777                 }\r
1778 \r
1779                 /// <summary>\r
1780                 /// Raises the RowDeleting event.\r
1781                 /// </summary>\r
1782                 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {\r
1783                         if (null != RowDeleting) {\r
1784                                 RowDeleting(this, e);\r
1785                         }\r
1786                 }\r
1787 \r
1788                 /// <summary>\r
1789                 /// Occurs when after a value has been changed for \r
1790                 /// the specified DataColumn in a DataRow.\r
1791                 /// </summary>\r
1792                 [DataCategory ("Data")] \r
1793                 [DataSysDescription ("Occurs when a value has been changed for this column.")]\r
1794                 public event DataColumnChangeEventHandler ColumnChanged;\r
1795 \r
1796                 /// <summary>\r
1797                 /// Occurs when a value is being changed for the specified \r
1798                 /// DataColumn in a DataRow.\r
1799                 /// </summary>\r
1800                 [DataCategory ("Data")]\r
1801                 [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
1802                 public event DataColumnChangeEventHandler ColumnChanging;\r
1803 \r
1804                 /// <summary>\r
1805                 /// Occurs after a DataRow has been changed successfully.\r
1806                 /// </summary>\r
1807                 [DataCategory ("Data")] \r
1808                 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]\r
1809                 public event DataRowChangeEventHandler RowChanged;\r
1810 \r
1811                 /// <summary>\r
1812                 /// Occurs when a DataRow is changing.\r
1813                 /// </summary>\r
1814                 [DataCategory ("Data")] \r
1815                 [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
1816                 public event DataRowChangeEventHandler RowChanging;\r
1817 \r
1818                 /// <summary>\r
1819                 /// Occurs after a row in the table has been deleted.\r
1820                 /// </summary>\r
1821                 [DataCategory ("Data")] \r
1822                 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")] \r
1823                 public event DataRowChangeEventHandler RowDeleted;\r
1824 \r
1825                 /// <summary>\r
1826                 /// Occurs before a row in the table is about to be deleted.\r
1827                 /// </summary>\r
1828                 [DataCategory ("Data")] \r
1829                 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]\r
1830                 public event DataRowChangeEventHandler RowDeleting;\r
1831 \r
1832 #if NET_2_0\r
1833                 /// <summary>\r
1834                 /// Occurs after the Clear method is called on the datatable.\r
1835                 /// </summary>\r
1836                 [DataCategory ("Data")] \r
1837                 [DataSysDescription ("Occurs when the rows in a table is cleared . Throw an exception to cancel the deletion.")]\r
1838                 public event DataTableClearEventHandler TableCleared;\r
1839 #endif // NET_2_0\r
1840 \r
1841                 #endregion // Events\r
1842 \r
1843                 /// <summary>\r
1844                 ///  Removes all UniqueConstraints\r
1845                 /// </summary>\r
1846                 private void RemoveUniqueConstraints () \r
1847                 {\r
1848                         foreach (Constraint Cons in Constraints) {\r
1849                                 \r
1850                                 if (Cons is UniqueConstraint) {\r
1851                                         Constraints.Remove (Cons);\r
1852                                         break;\r
1853                                 }\r
1854                         }\r
1855                         \r
1856                         UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);\r
1857                 }\r
1858 \r
1859                 internal static DataColumn[] ParseSortString (DataTable table, string sort, out ListSortDirection[] sortDirections, bool rejectNoResult)\r
1860                 {\r
1861                         DataColumn[] sortColumns = _emptyColumnArray;\r
1862                         sortDirections = null;\r
1863                         \r
1864                         ArrayList columns = null;\r
1865                         ArrayList sorts = null;\r
1866                 \r
1867                         if (sort != null && !sort.Equals ("")) {\r
1868                                 columns = new ArrayList ();\r
1869                                 sorts = new ArrayList();\r
1870                                 string[] columnExpression = sort.Trim ().Split (new char[1] {','});\r
1871                         \r
1872                                 for (int c = 0; c < columnExpression.Length; c++) {\r
1873                                         string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});\r
1874                                 \r
1875                                         string columnName = columnSortInfo[0].Trim ();\r
1876                                         string sortOrder = "ASC";\r
1877                                         if (columnSortInfo.Length > 1) \r
1878                                                 sortOrder = columnSortInfo[1].Trim ().ToUpper (table.Locale);\r
1879                                         \r
1880                                         ListSortDirection sortDirection = ListSortDirection.Ascending;\r
1881                                         switch (sortOrder) {\r
1882                                         case "ASC":\r
1883                                                 sortDirection = ListSortDirection.Ascending;\r
1884                                                 break;\r
1885                                         case "DESC":\r
1886                                                 sortDirection = ListSortDirection.Descending;\r
1887                                                 break;\r
1888                                         default:\r
1889                                                 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);\r
1890                                         }\r
1891 \r
1892                                         if (columnName.StartsWith("[") || columnName.EndsWith("]")) {\r
1893                                                 if (columnName.StartsWith("[") && columnName.EndsWith("]"))\r
1894                                                         columnName = columnName.Substring(1, columnName.Length - 2);\r
1895                                                 else\r
1896                                                         throw new ArgumentException(String.Format("{0} isn't a valid Sort string entry.", columnName));\r
1897                                         }\r
1898 \r
1899                                         DataColumn dc = table.Columns[columnName];\r
1900                                         if (dc == null){\r
1901                                                 try {\r
1902                                                         dc = table.Columns[Int32.Parse (columnName)];\r
1903                                         }\r
1904                                         catch (FormatException) {\r
1905                                                         throw new IndexOutOfRangeException("Cannot find column " + columnName);\r
1906                                         }\r
1907                                         }\r
1908 \r
1909                                         columns.Add (dc);\r
1910                                         sorts.Add(sortDirection);\r
1911                                 }       \r
1912                                 sortColumns = (DataColumn[]) columns.ToArray (typeof (DataColumn));\r
1913                                 sortDirections = new ListSortDirection[sorts.Count];\r
1914                                 for (int i = 0; i < sortDirections.Length; i++)\r
1915                                         sortDirections[i] = (ListSortDirection)sorts[i];\r
1916                         }               \r
1917 \r
1918                         if (rejectNoResult) {\r
1919                                 if (sortColumns == null)\r
1920                                         throw new SystemException ("sort expression result is null");\r
1921                                 if (sortColumns.Length == 0)\r
1922                                         throw new SystemException("sort expression result is 0");\r
1923                         }\r
1924 \r
1925                         return sortColumns;\r
1926                 }\r
1927 \r
1928                 private void UpdatePropertyDescriptorsCache()\r
1929                 {\r
1930                         PropertyDescriptor[] descriptors = new PropertyDescriptor[Columns.Count + ChildRelations.Count];\r
1931                         int index = 0;\r
1932                         foreach(DataColumn col in Columns) {\r
1933                                 descriptors[index++] = new DataColumnPropertyDescriptor(col);\r
1934                         }\r
1935 \r
1936                         foreach(DataRelation rel in ChildRelations) {\r
1937                                 descriptors[index++] = new DataRelationPropertyDescriptor(rel);\r
1938                         }\r
1939 \r
1940                         _propertyDescriptorsCache = new PropertyDescriptorCollection(descriptors);\r
1941                 }\r
1942 \r
1943                 internal PropertyDescriptorCollection GetPropertyDescriptorCollection()\r
1944                 {\r
1945                         if (_propertyDescriptorsCache == null) {\r
1946                                 UpdatePropertyDescriptorsCache();\r
1947                         }\r
1948 \r
1949                         return _propertyDescriptorsCache;\r
1950                 }\r
1951 \r
1952                 internal void ResetPropertyDescriptorsCache() {\r
1953                         _propertyDescriptorsCache = null;\r
1954                 }\r
1955         }\r
1956 }\r