Switch to compiler-tester
[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                         \r
1203                         // Find Data DataRow\r
1204                         if (this.PrimaryKey.Length > 0) {\r
1205                                 object [] keys = new object [PrimaryKey.Length];\r
1206                                 for (int i=0; i < PrimaryKey.Length; i++)\r
1207                                         keys [i] = values [PrimaryKey [i].Ordinal];\r
1208 \r
1209                                 row = Rows.Find(keys, DataViewRowState.OriginalRows);\r
1210                                 if (row == null)\r
1211                                         row = Rows.Find (keys);\r
1212                         }\r
1213                                 \r
1214                         // If not found, add new row\r
1215                         if (row == null \r
1216                             || (row.RowState == DataRowState.Deleted\r
1217                                 && loadOption == LoadOption.Upsert)) {\r
1218                                 row = NewNotInitializedRow ();\r
1219                                 row.ImportRecord (CreateRecord(values));\r
1220 \r
1221                                 if (EnforceConstraints) \r
1222                                         // we have to check that the new row doesn't colide with existing row\r
1223                                         Rows.ValidateDataRowInternal(row); // this adds to index ;-)\r
1224                                      \r
1225                                 Rows.AddInternal(row);          \r
1226         \r
1227                                 if (loadOption == LoadOption.OverwriteChanges ||\r
1228                                     loadOption == LoadOption.PreserveChanges) {\r
1229                                         row.AcceptChanges ();\r
1230                                 }\r
1231                                 return row;\r
1232                         }\r
1233 \r
1234                         bool deleted = row.RowState == DataRowState.Deleted;\r
1235 \r
1236                         if (deleted && loadOption == LoadOption.OverwriteChanges)\r
1237                                 row.RejectChanges ();                        \r
1238 \r
1239                         row.Load (values, loadOption);\r
1240 \r
1241                         return row;\r
1242                 }\r
1243 \r
1244                 [MonoTODO]\r
1245                 public void Merge (DataTable table)\r
1246                 {\r
1247                         throw new NotImplementedException ();\r
1248                 }\r
1249 \r
1250                 [MonoTODO]\r
1251                 public void Merge (DataTable table, bool preserveChanges)\r
1252                 {\r
1253                         throw new NotImplementedException ();\r
1254                 }\r
1255 \r
1256                 [MonoTODO]\r
1257                 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)\r
1258                 {\r
1259                         throw new NotImplementedException ();\r
1260                 }\r
1261 #endif\r
1262 \r
1263                 /// <summary>\r
1264                 /// Creates a new DataRow with the same schema as the table.\r
1265                 /// </summary>\r
1266                 public DataRow NewRow () \r
1267                 {\r
1268                         EnsureDefaultValueRowIndex();\r
1269 \r
1270                         DataRow newRow = NewRowFromBuilder (RowBuilder);\r
1271 \r
1272                         newRow.Proposed = CreateRecord(null);\r
1273                         return newRow;\r
1274                 }\r
1275 \r
1276                 internal int CreateRecord(object[] values) {\r
1277                         int valCount = values != null ? values.Length : 0;\r
1278                         if (valCount > Columns.Count)\r
1279                                 throw new ArgumentException("Input array is longer than the number of columns in this table.");\r
1280 \r
1281                         int index = RecordCache.NewRecord();\r
1282 \r
1283                         try {\r
1284                                 \r
1285                                 for (int i = 0; i < valCount; i++) {\r
1286                                         object value = values[i];\r
1287                                         if (value == null)\r
1288                                                 Columns[i].SetDefaultValue(index);\r
1289                                         else\r
1290                                                 Columns[i][index] = values[i];\r
1291                                 }\r
1292 \r
1293                                 for(int i = valCount; i < Columns.Count; i++) {\r
1294                                         Columns[i].SetDefaultValue(index);\r
1295                                 }\r
1296 \r
1297                                 return index;\r
1298                         }\r
1299                         catch {\r
1300                                 RecordCache.DisposeRecord(index);\r
1301                                 throw;\r
1302                         }\r
1303                 }\r
1304 \r
1305                 private void EnsureDefaultValueRowIndex()\r
1306                 {\r
1307                         // initialize default values row for the first time\r
1308                         if ( _defaultValuesRowIndex == -1 ) {\r
1309                                 _defaultValuesRowIndex = RecordCache.NewRecord();\r
1310                                 foreach(DataColumn column in Columns) {\r
1311                                         column.DataContainer[_defaultValuesRowIndex] = column.DefaultValue;\r
1312                                 }\r
1313                         }\r
1314                 }\r
1315 \r
1316 #if NET_2_0\r
1317                 internal int CompareRecords(int x, int y) {\r
1318                         for (int col = 0; col < Columns.Count; col++) {\r
1319                                 int res = Columns[col].DataContainer.CompareValues (x, y);\r
1320                                 if (res != 0)\r
1321                                         return res;\r
1322                         }\r
1323 \r
1324                         return 0;\r
1325                 }\r
1326 #endif\r
1327 \r
1328                 /// <summary>\r
1329                 /// This member supports the .NET Framework infrastructure\r
1330                 ///  and is not intended to be used directly from your code.\r
1331                 /// </summary>\r
1332                 protected internal DataRow[] NewRowArray (int size) \r
1333                 {\r
1334                         return (DataRow[]) Array.CreateInstance (GetRowType (), size);\r
1335                 }\r
1336 \r
1337                 /// <summary>\r
1338                 /// Creates a new row from an existing row.\r
1339                 /// </summary>\r
1340                 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder) \r
1341                 {\r
1342                         return new DataRow (builder);\r
1343                 }\r
1344                 \r
1345                 internal DataRow NewNotInitializedRow()\r
1346                 {\r
1347                         EnsureDefaultValueRowIndex();\r
1348 \r
1349                         return NewRowFromBuilder (RowBuilder);\r
1350                 }\r
1351 \r
1352 #if NET_2_0\r
1353                 [MonoTODO]\r
1354                 XmlReadMode ReadXml (Stream stream)\r
1355                 {\r
1356                         throw new NotImplementedException ();\r
1357                 }\r
1358 \r
1359                 public void ReadXmlSchema (Stream stream)\r
1360                 {\r
1361                         ReadXmlSchema (new XmlTextReader (stream));\r
1362                 }\r
1363 \r
1364                 public void ReadXmlSchema (TextReader reader)\r
1365                 {\r
1366                         ReadXmlSchema (new XmlTextReader (reader));\r
1367                 }\r
1368 \r
1369                 public void ReadXmlSchema (string fileName)\r
1370                 {\r
1371                         XmlTextReader reader = null;\r
1372                         try {\r
1373                                 reader = new XmlTextReader (fileName);\r
1374                         ReadXmlSchema (reader);\r
1375                         } finally {\r
1376                                 if (reader != null)\r
1377                         reader.Close ();\r
1378                 }\r
1379                 }\r
1380 \r
1381                 public void ReadXmlSchema (XmlReader reader)\r
1382                 {\r
1383                         DataSet ds = new DataSet ();\r
1384                         new XmlSchemaDataImporter (ds, reader).Process ();\r
1385                         DataTable target = null;\r
1386                         if (TableName == String.Empty) {\r
1387                                 if (ds.Tables.Count > 0)\r
1388                                         target = ds.Tables [0];\r
1389                         }\r
1390                         else {\r
1391                                 target = ds.Tables [TableName];\r
1392                                 if (target == null)\r
1393                                         throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source.", TableName));\r
1394                         }\r
1395                         if (target != null)\r
1396                                 target.CopyProperties (this);\r
1397                 }\r
1398 #endif\r
1399 \r
1400                 /// <summary>\r
1401                 /// Rolls back all changes that have been made to the \r
1402                 /// table since it was loaded, or the last time AcceptChanges\r
1403                 ///  was called.\r
1404                 /// </summary>\r
1405                 public void RejectChanges () \r
1406                 {       \r
1407                         for (int i = _rows.Count - 1; i >= 0; i--) {\r
1408                                 DataRow row = _rows [i];\r
1409                                 if (row.RowState != DataRowState.Unchanged)\r
1410                                         _rows [i].RejectChanges ();\r
1411                         }\r
1412                 }\r
1413 \r
1414                 /// <summary>\r
1415                 /// Resets the DataTable to its original state.\r
1416                 /// </summary>          \r
1417                 public virtual void Reset () \r
1418                 {\r
1419                         Clear();\r
1420                         while (ParentRelations.Count > 0)\r
1421                         {\r
1422                                 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))\r
1423                                         dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);\r
1424                         }\r
1425 \r
1426                         while (ChildRelations.Count > 0)\r
1427                         {\r
1428                                 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))\r
1429                                         dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);\r
1430                         }\r
1431                         Constraints.Clear();\r
1432                         Columns.Clear();\r
1433                 }\r
1434 \r
1435                 /// <summary>\r
1436                 /// Gets an array of all DataRow objects.\r
1437                 /// </summary>\r
1438                 public DataRow[] Select () \r
1439                 {\r
1440                         return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);\r
1441                 }\r
1442 \r
1443                 /// <summary>\r
1444                 /// Gets an array of all DataRow objects that match \r
1445                 /// the filter criteria in order of primary key (or \r
1446                 /// lacking one, order of addition.)\r
1447                 /// </summary>\r
1448                 public DataRow[] Select (string filterExpression) \r
1449                 {\r
1450                         return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);\r
1451                 }\r
1452 \r
1453                 /// <summary>\r
1454                 /// Gets an array of all DataRow objects that \r
1455                 /// match the filter criteria, in the the \r
1456                 /// specified sort order.\r
1457                 /// </summary>\r
1458                 public DataRow[] Select (string filterExpression, string sort) \r
1459                 {\r
1460                         return Select(filterExpression, sort, DataViewRowState.CurrentRows);\r
1461                 }\r
1462 \r
1463                 /// <summary>\r
1464                 /// Gets an array of all DataRow objects that match\r
1465                 /// the filter in the order of the sort, that match \r
1466                 /// the specified state.\r
1467                 /// </summary>\r
1468                 [MonoTODO]\r
1469                 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) \r
1470                 {\r
1471                         if (filterExpression == null)\r
1472                                 filterExpression = String.Empty;\r
1473 \r
1474                         DataColumn[] columns = _emptyColumnArray;\r
1475                         ListSortDirection[] sorts = null;\r
1476                         if (sort != null && !sort.Equals(String.Empty))\r
1477                                 columns = ParseSortString (this, sort, out sorts, false);\r
1478 \r
1479                         IExpression filter = null;\r
1480                         if (filterExpression != String.Empty) {\r
1481                                 Parser parser = new Parser ();\r
1482                                 filter = parser.Compile (filterExpression);\r
1483                         }\r
1484 \r
1485                         Index index = FindIndex(columns, sorts, recordStates, filter);\r
1486                         if (index == null)\r
1487                                 index = new Index(new Key(this,columns,sorts,recordStates,filter));\r
1488 \r
1489                         int[] records = index.GetAll();\r
1490                         DataRow[] dataRows = NewRowArray(index.Size);\r
1491                         for (int i = 0; i < dataRows.Length; i++)\r
1492                                 dataRows[i] = RecordCache[records[i]];\r
1493 \r
1494                         return dataRows;\r
1495                 }\r
1496 \r
1497                 \r
1498                 private void AddIndex (Index index)\r
1499                 {\r
1500                         if (_indexes == null) {\r
1501                                 _indexes = new ArrayList();\r
1502                         }\r
1503 \r
1504                         _indexes.Add (index);\r
1505                 }\r
1506 \r
1507                 /// <summary>\r
1508                 /// Returns index corresponding to columns,sort,row state filter and unique values given.\r
1509                 /// If such an index not exists, creates a new one.\r
1510                 /// </summary>\r
1511                 /// <param name="columns">Columns set of the index to look for.</param>\r
1512                 /// <param name="sort">Columns sort order of the index to look for.</param>\r
1513                 /// <param name="rowState">Rpw state filter of the index to look for.</param>\r
1514                 /// <param name="unique">Uniqueness of the index to look for.</param>\r
1515                 /// <param name="strict">Indicates whenever the index found should correspond in its uniquness to the value of unique parameter specified.</param>\r
1516                 /// <param name="reset">Indicates whenever the already existing index should be forced to reset.</param>\r
1517                 /// <returns></returns>\r
1518                 internal Index GetIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter, bool reset)\r
1519                 {\r
1520                         Index index = FindIndex(columns,sort,rowState,filter);\r
1521                         if (index == null ) {\r
1522                                 index = new Index(new Key(this,columns,sort,rowState,filter));\r
1523 \r
1524                                 AddIndex(index);\r
1525                         }\r
1526                         else if (reset) {\r
1527                                 // reset existing index only if asked for this\r
1528                                 index.Reset();\r
1529                         }\r
1530                         return index;\r
1531                 }\r
1532 \r
1533                 internal Index FindIndex(DataColumn[] columns)\r
1534                 {\r
1535                         return FindIndex(columns,null,DataViewRowState.None, null);\r
1536                 }\r
1537 \r
1538                 internal Index FindIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter)\r
1539                 {\r
1540                         if (Indexes != null) {\r
1541                                 foreach (Index index in Indexes) {\r
1542                                         if (index.Key.Equals(columns,sort,rowState, filter)) {\r
1543                                                 return index;\r
1544                                         }\r
1545                                 }\r
1546                         }\r
1547                         return null;\r
1548                 }\r
1549 \r
1550                 internal void ResetIndexes()\r
1551                 {\r
1552                         foreach(Index index in Indexes) {\r
1553                                 index.Reset();\r
1554                         }\r
1555                 }\r
1556 \r
1557                 internal void ResetCaseSensitiveIndexes()\r
1558                 {\r
1559                         foreach(Index index in Indexes) {\r
1560                                 bool containsStringcolumns = false;\r
1561                                 foreach(DataColumn column in index.Key.Columns) {\r
1562                                         if (column.DataType == typeof(string)) {\r
1563                                                 containsStringcolumns = true;\r
1564                                                 break;\r
1565                                         }\r
1566                                 }\r
1567 \r
1568                                 if (containsStringcolumns) {\r
1569                                         index.Reset();\r
1570                                 }\r
1571                         }\r
1572                 }\r
1573 \r
1574                 internal void DropIndex(Index index)\r
1575                 {\r
1576                         if (index != null && index.RefCount == 0) {     \r
1577                                 _indexes.Remove(index);\r
1578                         }\r
1579                 }\r
1580 \r
1581                 internal void DeleteRowFromIndexes (DataRow row)\r
1582                 {\r
1583                         if (_indexes != null) {\r
1584                                 foreach (Index indx in _indexes) {\r
1585                                         indx.Delete (row);\r
1586                                 }\r
1587                         }\r
1588                 }\r
1589 \r
1590                 /// <summary>\r
1591                 /// Gets the TableName and DisplayExpression, if \r
1592                 /// there is one as a concatenated string.\r
1593                 /// </summary>\r
1594                 public override string ToString() \r
1595                 {\r
1596                         //LAMESPEC: spec says concat the two. impl puts a \r
1597                         //plus sign infront of DisplayExpression\r
1598                         string retVal = TableName;\r
1599                         if(DisplayExpression != null && DisplayExpression != "")\r
1600                                 retVal += " + " + DisplayExpression;\r
1601                         return retVal;\r
1602                 }\r
1603 \r
1604 #if NET_2_0\r
1605                 private XmlWriterSettings GetWriterSettings ()\r
1606                 {\r
1607                         XmlWriterSettings s = new XmlWriterSettings ();\r
1608                         s.Indent = true;\r
1609                         return s;\r
1610                 }\r
1611 \r
1612                 public void WriteXml (Stream stream)\r
1613                 {\r
1614                         WriteXml (stream, XmlWriteMode.IgnoreSchema);\r
1615                 }\r
1616 \r
1617                 public void WriteXml (TextWriter writer)\r
1618                 {\r
1619                         WriteXml (writer, XmlWriteMode.IgnoreSchema);\r
1620                 }\r
1621 \r
1622                 public void WriteXml (XmlWriter writer)\r
1623                 {\r
1624                         WriteXml (writer, XmlWriteMode.IgnoreSchema);\r
1625                 }\r
1626 \r
1627                 public void WriteXml (string fileName)\r
1628                 {\r
1629                         WriteXml (fileName, XmlWriteMode.IgnoreSchema);\r
1630                 }\r
1631 \r
1632                 public void WriteXml (Stream stream, XmlWriteMode mode)\r
1633                 {\r
1634                         WriteXml (XmlWriter.Create (stream, GetWriterSettings ()), mode);\r
1635                 }\r
1636 \r
1637                 public void WriteXml (TextWriter writer, XmlWriteMode mode)\r
1638                 {\r
1639                         WriteXml (XmlWriter.Create (writer, GetWriterSettings ()), mode);\r
1640                 }\r
1641 \r
1642                 [MonoTODO]\r
1643                 public void WriteXml (XmlWriter writer, XmlWriteMode mode)\r
1644                 {\r
1645                         throw new NotImplementedException ();\r
1646                 }\r
1647 \r
1648                 public void WriteXml (string fileName, XmlWriteMode mode)\r
1649                 {\r
1650                         XmlWriter xw = null;\r
1651                         try {\r
1652                                 xw = XmlWriter.Create (fileName, GetWriterSettings ());\r
1653                                 WriteXml (xw, mode);\r
1654                         } finally {\r
1655                                 if (xw != null)\r
1656                                         xw.Close ();\r
1657                         }\r
1658                 }\r
1659 \r
1660                 public void WriteXmlSchema (Stream stream)\r
1661                 {\r
1662                         WriteXmlSchema (XmlWriter.Create (stream, GetWriterSettings ()));\r
1663                 }\r
1664 \r
1665                 public void WriteXmlSchema (TextWriter writer)\r
1666                 {\r
1667                         WriteXmlSchema (XmlWriter.Create (writer, GetWriterSettings ()));\r
1668                 }\r
1669 \r
1670                 public void WriteXmlSchema (XmlWriter writer)\r
1671                 {\r
1672                         DataSet ds = DataSet;\r
1673                         DataSet tmp = null;\r
1674                         try {\r
1675                                 if (ds == null) {\r
1676                                         tmp = ds = new DataSet ();\r
1677                                         ds.Tables.Add (this);\r
1678                                 }\r
1679                                 DataTableCollection col = new DataTableCollection (ds);\r
1680                                 col.Add (this);\r
1681                                 XmlSchemaWriter.WriteXmlSchema (ds, writer, col, null);\r
1682                         } finally {\r
1683                                 if (tmp != null)\r
1684                                         ds.Tables.Remove (this);\r
1685                         }\r
1686                 }\r
1687 \r
1688                 public void WriteXmlSchema (string fileName)\r
1689                 {\r
1690                         XmlWriter xw = null;\r
1691                         try {\r
1692                                 xw = XmlWriter.Create (fileName, GetWriterSettings ());\r
1693                                 WriteXmlSchema (xw);\r
1694                         } finally {\r
1695                                 if (xw != null)\r
1696                                         xw.Close ();\r
1697                         }\r
1698                 }\r
1699 #endif\r
1700                 \r
1701                 #region Events \r
1702                 \r
1703                 /// <summary>\r
1704                 /// Raises the ColumnChanged event.\r
1705                 /// </summary>\r
1706                 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {\r
1707                         if (null != ColumnChanged) {\r
1708                                 ColumnChanged (this, e);\r
1709                         }\r
1710                 }\r
1711 \r
1712                 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e) {\r
1713                         OnColumnChanged(e);\r
1714                 }\r
1715 \r
1716 #if NET_2_0\r
1717                 /// <summary>\r
1718                 /// Raises TableCleared Event and delegates to subscribers\r
1719                 /// </summary>\r
1720                 protected virtual void OnTableCleared (DataTableClearEventArgs e) {\r
1721                         if (TableCleared != null)\r
1722                                 TableCleared (this, e);\r
1723                 }\r
1724 #endif // NET_2_0\r
1725 \r
1726                 /// <summary>\r
1727                 /// Raises the ColumnChanging event.\r
1728                 /// </summary>\r
1729                 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {\r
1730                         if (null != ColumnChanging) {\r
1731                                 ColumnChanging (this, e);\r
1732                         }\r
1733                 }\r
1734 \r
1735                 internal void RaiseOnColumnChanging (DataColumnChangeEventArgs e) {\r
1736                         OnColumnChanging(e);\r
1737                 }\r
1738 \r
1739                 /// <summary>\r
1740                 /// Raises the PropertyChanging event.\r
1741                 /// </summary>\r
1742                 [MonoTODO]\r
1743                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {\r
1744                         //      if (null != PropertyChanging)\r
1745                         //      {\r
1746                         //              PropertyChanging (this, e);\r
1747                         //      }\r
1748                 }\r
1749 \r
1750                 /// <summary>\r
1751                 /// Notifies the DataTable that a DataColumn is being removed.\r
1752                 /// </summary>\r
1753                 [MonoTODO]\r
1754                 protected internal virtual void OnRemoveColumn (DataColumn column) {\r
1755                 }\r
1756 \r
1757 \r
1758                 /// <summary>\r
1759                 /// Raises the RowChanged event.\r
1760                 /// </summary>\r
1761                 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {\r
1762                         if (null != RowChanged) {\r
1763                                 RowChanged(this, e);\r
1764                         }\r
1765                 }\r
1766 \r
1767 \r
1768                 /// <summary>\r
1769                 /// Raises the RowChanging event.\r
1770                 /// </summary>\r
1771                 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {\r
1772                         if (null != RowChanging) {\r
1773                                 RowChanging(this, e);\r
1774                         }\r
1775                 }\r
1776 \r
1777                 /// <summary>\r
1778                 /// Raises the RowDeleted event.\r
1779                 /// </summary>\r
1780                 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {\r
1781                         if (null != RowDeleted) {\r
1782                                 RowDeleted(this, e);\r
1783                         }\r
1784                 }\r
1785 \r
1786                 /// <summary>\r
1787                 /// Raises the RowDeleting event.\r
1788                 /// </summary>\r
1789                 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {\r
1790                         if (null != RowDeleting) {\r
1791                                 RowDeleting(this, e);\r
1792                         }\r
1793                 }\r
1794 \r
1795                 /// <summary>\r
1796                 /// Occurs when after a value has been changed for \r
1797                 /// the specified DataColumn in a DataRow.\r
1798                 /// </summary>\r
1799                 [DataCategory ("Data")] \r
1800                 [DataSysDescription ("Occurs when a value has been changed for this column.")]\r
1801                 public event DataColumnChangeEventHandler ColumnChanged;\r
1802 \r
1803                 /// <summary>\r
1804                 /// Occurs when a value is being changed for the specified \r
1805                 /// DataColumn in a DataRow.\r
1806                 /// </summary>\r
1807                 [DataCategory ("Data")]\r
1808                 [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
1809                 public event DataColumnChangeEventHandler ColumnChanging;\r
1810 \r
1811                 /// <summary>\r
1812                 /// Occurs after a DataRow has been changed successfully.\r
1813                 /// </summary>\r
1814                 [DataCategory ("Data")] \r
1815                 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]\r
1816                 public event DataRowChangeEventHandler RowChanged;\r
1817 \r
1818                 /// <summary>\r
1819                 /// Occurs when a DataRow is changing.\r
1820                 /// </summary>\r
1821                 [DataCategory ("Data")] \r
1822                 [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
1823                 public event DataRowChangeEventHandler RowChanging;\r
1824 \r
1825                 /// <summary>\r
1826                 /// Occurs after a row in the table has been deleted.\r
1827                 /// </summary>\r
1828                 [DataCategory ("Data")] \r
1829                 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")] \r
1830                 public event DataRowChangeEventHandler RowDeleted;\r
1831 \r
1832                 /// <summary>\r
1833                 /// Occurs before a row in the table is about to be deleted.\r
1834                 /// </summary>\r
1835                 [DataCategory ("Data")] \r
1836                 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]\r
1837                 public event DataRowChangeEventHandler RowDeleting;\r
1838 \r
1839 #if NET_2_0\r
1840                 /// <summary>\r
1841                 /// Occurs after the Clear method is called on the datatable.\r
1842                 /// </summary>\r
1843                 [DataCategory ("Data")] \r
1844                 [DataSysDescription ("Occurs when the rows in a table is cleared . Throw an exception to cancel the deletion.")]\r
1845                 public event DataTableClearEventHandler TableCleared;\r
1846 #endif // NET_2_0\r
1847 \r
1848                 #endregion // Events\r
1849 \r
1850                 /// <summary>\r
1851                 ///  Removes all UniqueConstraints\r
1852                 /// </summary>\r
1853                 private void RemoveUniqueConstraints () \r
1854                 {\r
1855                         foreach (Constraint Cons in Constraints) {\r
1856                                 \r
1857                                 if (Cons is UniqueConstraint) {\r
1858                                         Constraints.Remove (Cons);\r
1859                                         break;\r
1860                                 }\r
1861                         }\r
1862                         \r
1863                         UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);\r
1864                 }\r
1865 \r
1866                 internal static DataColumn[] ParseSortString (DataTable table, string sort, out ListSortDirection[] sortDirections, bool rejectNoResult)\r
1867                 {\r
1868                         DataColumn[] sortColumns = _emptyColumnArray;\r
1869                         sortDirections = null;\r
1870                         \r
1871                         ArrayList columns = null;\r
1872                         ArrayList sorts = null;\r
1873                 \r
1874                         if (sort != null && !sort.Equals ("")) {\r
1875                                 columns = new ArrayList ();\r
1876                                 sorts = new ArrayList();\r
1877                                 string[] columnExpression = sort.Trim ().Split (new char[1] {','});\r
1878                         \r
1879                                 for (int c = 0; c < columnExpression.Length; c++) {\r
1880                                         string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});\r
1881                                 \r
1882                                         string columnName = columnSortInfo[0].Trim ();\r
1883                                         string sortOrder = "ASC";\r
1884                                         if (columnSortInfo.Length > 1) \r
1885                                                 sortOrder = columnSortInfo[1].Trim ().ToUpper (table.Locale);\r
1886                                         \r
1887                                         ListSortDirection sortDirection = ListSortDirection.Ascending;\r
1888                                         switch (sortOrder) {\r
1889                                         case "ASC":\r
1890                                                 sortDirection = ListSortDirection.Ascending;\r
1891                                                 break;\r
1892                                         case "DESC":\r
1893                                                 sortDirection = ListSortDirection.Descending;\r
1894                                                 break;\r
1895                                         default:\r
1896                                                 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);\r
1897                                         }\r
1898 \r
1899                                         if (columnName.StartsWith("[") || columnName.EndsWith("]")) {\r
1900                                                 if (columnName.StartsWith("[") && columnName.EndsWith("]"))\r
1901                                                         columnName = columnName.Substring(1, columnName.Length - 2);\r
1902                                                 else\r
1903                                                         throw new ArgumentException(String.Format("{0} isn't a valid Sort string entry.", columnName));\r
1904                                         }\r
1905 \r
1906                                         DataColumn dc = table.Columns[columnName];\r
1907                                         if (dc == null){\r
1908                                                 try {\r
1909                                                         dc = table.Columns[Int32.Parse (columnName)];\r
1910                                         }\r
1911                                         catch (FormatException) {\r
1912                                                         throw new IndexOutOfRangeException("Cannot find column " + columnName);\r
1913                                         }\r
1914                                         }\r
1915 \r
1916                                         columns.Add (dc);\r
1917                                         sorts.Add(sortDirection);\r
1918                                 }       \r
1919                                 sortColumns = (DataColumn[]) columns.ToArray (typeof (DataColumn));\r
1920                                 sortDirections = new ListSortDirection[sorts.Count];\r
1921                                 for (int i = 0; i < sortDirections.Length; i++)\r
1922                                         sortDirections[i] = (ListSortDirection)sorts[i];\r
1923                         }               \r
1924 \r
1925                         if (rejectNoResult) {\r
1926                                 if (sortColumns == null)\r
1927                                         throw new SystemException ("sort expression result is null");\r
1928                                 if (sortColumns.Length == 0)\r
1929                                         throw new SystemException("sort expression result is 0");\r
1930                         }\r
1931 \r
1932                         return sortColumns;\r
1933                 }\r
1934 \r
1935                 private void UpdatePropertyDescriptorsCache()\r
1936                 {\r
1937                         PropertyDescriptor[] descriptors = new PropertyDescriptor[Columns.Count + ChildRelations.Count];\r
1938                         int index = 0;\r
1939                         foreach(DataColumn col in Columns) {\r
1940                                 descriptors[index++] = new DataColumnPropertyDescriptor(col);\r
1941                         }\r
1942 \r
1943                         foreach(DataRelation rel in ChildRelations) {\r
1944                                 descriptors[index++] = new DataRelationPropertyDescriptor(rel);\r
1945                         }\r
1946 \r
1947                         _propertyDescriptorsCache = new PropertyDescriptorCollection(descriptors);\r
1948                 }\r
1949 \r
1950                 internal PropertyDescriptorCollection GetPropertyDescriptorCollection()\r
1951                 {\r
1952                         if (_propertyDescriptorsCache == null) {\r
1953                                 UpdatePropertyDescriptorsCache();\r
1954                         }\r
1955 \r
1956                         return _propertyDescriptorsCache;\r
1957                 }\r
1958 \r
1959                 internal void ResetPropertyDescriptorsCache() {\r
1960                         _propertyDescriptorsCache = null;\r
1961                 }\r
1962         }\r
1963 }\r