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