importing messaging-2008 branch to trunk [continued]
[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 #if NET_2_0\r
47 using System.Collections.Generic;\r
48 #endif\r
49 using System.ComponentModel;\r
50 using System.Globalization;\r
51 using System.IO;\r
52 using System.Runtime.Serialization;\r
53 using System.Xml;\r
54 using System.Xml.Schema;\r
55 using System.Xml.Serialization;\r
56 using System.Text.RegularExpressions;\r
57 using Mono.Data.SqlExpressions;\r
58 \r
59 namespace System.Data {\r
60         //[Designer]\r
61         [ToolboxItem (false)]\r
62         [DefaultEvent ("RowChanging")]\r
63         [DefaultProperty ("TableName")]\r
64         [DesignTimeVisible (false)]\r
65         [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]\r
66         [Serializable]\r
67         public partial class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable {\r
68                 #region Fields\r
69 \r
70                 internal DataSet dataSet;\r
71 \r
72                 private bool _caseSensitive;\r
73                 private DataColumnCollection _columnCollection;\r
74                 private ConstraintCollection _constraintCollection;\r
75                 // never access it. Use DefaultView.\r
76                 private DataView _defaultView = null;\r
77 \r
78                 private string _displayExpression;\r
79                 private PropertyCollection _extendedProperties;\r
80                 private bool _hasErrors;\r
81                 private CultureInfo _locale;\r
82                 private int _minimumCapacity;\r
83                 private string _nameSpace;\r
84                 private DataRelationCollection _childRelations;\r
85                 private DataRelationCollection _parentRelations;\r
86                 private string _prefix;\r
87                 private UniqueConstraint _primaryKeyConstraint;\r
88                 private DataRowCollection _rows;\r
89                 private ISite _site;\r
90                 private string _tableName;\r
91                 private bool _containsListCollection;\r
92                 private string _encodedTableName;\r
93                 internal bool _duringDataLoad;\r
94                 internal bool _nullConstraintViolationDuringDataLoad;\r
95                 private bool dataSetPrevEnforceConstraints;\r
96                 private bool dataTablePrevEnforceConstraints;\r
97                 private bool enforceConstraints = true;\r
98                 private DataRowBuilder _rowBuilder;\r
99                 private ArrayList _indexes;\r
100                 private RecordCache _recordCache;\r
101                 private int _defaultValuesRowIndex = -1;\r
102                 protected internal bool fInitInProgress;\r
103 \r
104                 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's\r
105                 // CaseSensitive property. So when you lost you virginity it's gone for ever\r
106                 private bool _virginCaseSensitive = true;\r
107 \r
108                 private PropertyDescriptorCollection _propertyDescriptorsCache;\r
109                 static DataColumn[] _emptyColumnArray = new DataColumn[0];\r
110 \r
111                 // Regex to parse the Sort string.\r
112                 static Regex SortRegex = new Regex ( @"^((\[(?<ColName>.+)\])|(?<ColName>\S+))([ ]+(?<Order>ASC|DESC))?$",\r
113                                                         RegexOptions.IgnoreCase|RegexOptions.ExplicitCapture);\r
114 \r
115 \r
116                 DataColumn [] _latestPrimaryKeyCols;\r
117                 #endregion //Fields\r
118 \r
119                 /// <summary>\r
120                 /// Initializes a new instance of the DataTable class with no arguments.\r
121                 /// </summary>\r
122                 public DataTable ()\r
123                 {\r
124                         dataSet = null;\r
125                         _columnCollection = new DataColumnCollection(this);\r
126                         _constraintCollection = new ConstraintCollection(this);\r
127                         _extendedProperties = new PropertyCollection();\r
128                         _tableName = "";\r
129                         _nameSpace = null;\r
130                         _caseSensitive = false;         //default value\r
131                         _displayExpression = null;\r
132                         _primaryKeyConstraint = null;\r
133                         _site = null;\r
134                         _rows = new DataRowCollection (this);\r
135                         _indexes = new ArrayList();\r
136                         _recordCache = new RecordCache(this);\r
137 \r
138                         //LAMESPEC: spec says 25 impl does 50\r
139                         _minimumCapacity = 50;\r
140 \r
141                         _childRelations = new DataRelationCollection.DataTableRelationCollection (this);\r
142                         _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);\r
143                 }\r
144 \r
145                 /// <summary>\r
146                 /// Intitalizes a new instance of the DataTable class with the specified table name.\r
147                 /// </summary>\r
148                 public DataTable (string tableName)\r
149                         : this ()\r
150                 {\r
151                         _tableName = tableName;\r
152                 }\r
153 \r
154                 /// <summary>\r
155                 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.\r
156                 /// </summary>\r
157                 protected DataTable (SerializationInfo info, StreamingContext context)\r
158                         : this ()\r
159                 {\r
160 #if NET_2_0\r
161                         SerializationInfoEnumerator e = info.GetEnumerator ();\r
162                         SerializationFormat serializationFormat = SerializationFormat.Xml;\r
163 \r
164                         while (e.MoveNext()) {\r
165                                 if (e.ObjectType == typeof(System.Data.SerializationFormat)) {\r
166                                         serializationFormat = (SerializationFormat) e.Value;\r
167                                         break;\r
168                                 }\r
169                         }\r
170                         if (serializationFormat == SerializationFormat.Xml) {\r
171 #endif\r
172                                 string schema = info.GetString ("XmlSchema");\r
173                                 string data = info.GetString ("XmlDiffGram");\r
174 \r
175                                 DataSet ds = new DataSet ();\r
176                                 ds.ReadXmlSchema (new StringReader (schema));\r
177                                 ds.Tables [0].CopyProperties (this);\r
178                                 ds = new DataSet ();\r
179                                 ds.Tables.Add (this);\r
180                                 ds.ReadXml (new StringReader (data), XmlReadMode.DiffGram);\r
181                                 ds.Tables.Remove (this);\r
182                                 /* keeping for a while. With the change above, we shouldn't have to consider\r
183                                  * DataTable mode in schema inference/read.\r
184                                  XmlSchemaMapper mapper = new XmlSchemaMapper (this);\r
185                                  XmlTextReader xtr = new XmlTextReader(new StringReader (schema));\r
186                                  mapper.Read (xtr);\r
187 \r
188                                  XmlDiffLoader loader = new XmlDiffLoader (this);\r
189                                  xtr = new XmlTextReader(new StringReader (data));\r
190                                  loader.Load (xtr);\r
191                                 */\r
192 #if NET_2_0\r
193                         } else /*if (Tables.RemotingFormat == SerializationFormat.Binary)*/ {\r
194                                 BinaryDeserializeTable (info);\r
195                         }\r
196 #endif\r
197                 }\r
198 \r
199                 /// <summary>\r
200                 /// Indicates whether string comparisons within the table are case-sensitive.\r
201                 /// </summary>\r
202 #if !NET_2_0\r
203                 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]\r
204 #endif\r
205                 public bool CaseSensitive {\r
206                         get {\r
207                                 if (_virginCaseSensitive && dataSet != null)\r
208                                         return dataSet.CaseSensitive;\r
209                                 else\r
210                                         return _caseSensitive;\r
211                                 }\r
212                         set {\r
213                                 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {\r
214                                         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
215                                 }\r
216                                 _virginCaseSensitive = false;\r
217                                 _caseSensitive = value;\r
218                                 ResetCaseSensitiveIndexes();\r
219                         }\r
220                 }\r
221 \r
222                 internal ArrayList Indexes {\r
223                         get { return _indexes; }\r
224                 }\r
225 \r
226                 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)\r
227                 {\r
228                         DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);\r
229                         OnColumnChanged (e);\r
230                 }\r
231 \r
232                 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)\r
233                 {\r
234                         DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);\r
235                         OnColumnChanging (e);\r
236                 }\r
237 \r
238                 internal void DeletedDataRow (DataRow dr, DataRowAction action)\r
239                 {\r
240                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);\r
241                         OnRowDeleted (e);\r
242                 }\r
243 \r
244                 internal void DeletingDataRow (DataRow dr, DataRowAction action)\r
245                 {\r
246                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);\r
247                         OnRowDeleting (e);\r
248                 }\r
249 \r
250                 internal void ChangedDataRow (DataRow dr, DataRowAction action)\r
251                 {\r
252                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);\r
253                         OnRowChanged (e);\r
254                 }\r
255 \r
256                 internal void ChangingDataRow (DataRow dr, DataRowAction action)\r
257                 {\r
258                         DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);\r
259                         OnRowChanging (e);\r
260                 }\r
261 \r
262                 /// <summary>\r
263                 /// Gets the collection of child relations for this DataTable.\r
264                 /// </summary>\r
265                 [Browsable (false)]\r
266 #if !NET_2_0\r
267                 [DataSysDescription ("Returns the child relations for this table.")]\r
268 #endif\r
269                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
270                 public DataRelationCollection ChildRelations {\r
271                         get { return _childRelations; }\r
272                 }\r
273 \r
274                 /// <summary>\r
275                 /// Gets the collection of columns that belong to this table.\r
276                 /// </summary>\r
277                 [DataCategory ("Data")]\r
278 #if !NET_2_0\r
279                 [DataSysDescription ("The collection that holds the columns for this table.")]\r
280 #endif\r
281                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]\r
282                 public DataColumnCollection Columns {\r
283                         get { return _columnCollection; }\r
284                 }\r
285 \r
286                 /// <summary>\r
287                 /// Gets the collection of constraints maintained by this table.\r
288                 /// </summary>\r
289                 [DataCategory ("Data")]\r
290 #if !NET_2_0\r
291                 [DataSysDescription ("The collection that holds the constraints for this table.")]\r
292 #endif\r
293                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]\r
294                 public ConstraintCollection Constraints {\r
295                         get { return _constraintCollection; }\r
296 #if NET_2_0\r
297                         internal set { _constraintCollection = value; }\r
298 #endif\r
299                 }\r
300 \r
301                 /// <summary>\r
302                 /// Gets the DataSet that this table belongs to.\r
303                 /// </summary>\r
304                 [Browsable (false)]\r
305 #if !NET_2_0\r
306                 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]\r
307 #endif\r
308                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
309                 public DataSet DataSet {\r
310                         get { return dataSet; }\r
311                 }\r
312 \r
313                 /// <summary>\r
314                 /// Gets a customized view of the table which may\r
315                 /// include a filtered view, or a cursor position.\r
316                 /// </summary>\r
317                 [Browsable (false)]\r
318 #if !NET_2_0\r
319                 [DataSysDescription ("This is the default DataView for the table.")]\r
320 #endif\r
321                 public DataView DefaultView {\r
322                         get {\r
323                                 if (_defaultView == null) {\r
324                                         lock(this){\r
325                                                 if (_defaultView == null){\r
326                                                         if (dataSet != null)\r
327                                                                 _defaultView = dataSet.DefaultViewManager.CreateDataView(this);\r
328                                                         else\r
329                                                                 _defaultView = new DataView(this);\r
330                                                 }\r
331                                         }\r
332                                 }\r
333                                 return _defaultView;\r
334                         }\r
335                 }\r
336 \r
337 \r
338                 /// <summary>\r
339                 /// Gets or sets the expression that will return\r
340                 /// a value used to represent this table in the user interface.\r
341                 /// </summary>\r
342                 [DataCategory ("Data")]\r
343 #if !NET_2_0\r
344                 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]\r
345 #endif\r
346                 [DefaultValue ("")]\r
347                 public string DisplayExpression {\r
348                         get { return _displayExpression == null ? "" : _displayExpression; }\r
349                         set { _displayExpression = value; }\r
350                 }\r
351 \r
352                 /// <summary>\r
353                 /// Gets the collection of customized user information.\r
354                 /// </summary>\r
355                 [Browsable (false)]\r
356                 [DataCategory ("Data")]\r
357 #if !NET_2_0\r
358                 [DataSysDescription ("The collection that holds custom user information.")]\r
359 #endif\r
360                 public PropertyCollection ExtendedProperties {\r
361                         get { return _extendedProperties; }\r
362                 }\r
363 \r
364                 /// <summary>\r
365                 /// Gets a value indicating whether there are errors in\r
366                 /// any of the_rows in any of the tables of the DataSet to\r
367                 /// which the table belongs.\r
368                 /// </summary>\r
369                 [Browsable (false)]\r
370 #if !NET_2_0\r
371                 [DataSysDescription ("Returns whether the table has errors.")]\r
372 #endif\r
373                 public bool HasErrors {\r
374                         get {\r
375                                 // we can not use the _hasError flag because we do not know when to turn it off!\r
376                                 for (int i = 0; i < _rows.Count; i++) {\r
377                                         if (_rows[i].HasErrors)\r
378                                                 return true;\r
379                                 }\r
380                                 return false;\r
381                         }\r
382                 }\r
383 \r
384                 /// <summary>\r
385                 /// Gets or sets the locale information used to\r
386                 /// compare strings within the table.\r
387                 /// </summary>\r
388 #if !NET_2_0\r
389                 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]\r
390 #endif\r
391                 public CultureInfo Locale {\r
392                         get {\r
393                                 // if the locale is null, we check for the DataSet locale\r
394                                 // and if the DataSet is null we return the current culture.\r
395                                 // this way if DataSet locale is changed, only if there is no locale for\r
396                                 // the DataTable it influece the Locale get;\r
397                                 if (_locale != null)\r
398                                         return _locale;\r
399                                 if (DataSet != null)\r
400                                         return DataSet.Locale;\r
401                                 return CultureInfo.CurrentCulture;\r
402                         }\r
403                         set {\r
404                                 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {\r
405                                         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
406                                 }\r
407                                 if (_locale == null || !_locale.Equals(value))\r
408                                         _locale = value;\r
409                         }\r
410                 }\r
411 \r
412                 internal bool LocaleSpecified {\r
413                         get { return _locale != null; }\r
414                 }\r
415 \r
416                 /// <summary>\r
417                 /// Gets or sets the initial starting size for this table.\r
418                 /// </summary>\r
419                 [DataCategory ("Data")]\r
420 #if !NET_2_0\r
421                 [DataSysDescription ("Indicates an initial starting size for this table.")]\r
422 #endif\r
423                 [DefaultValue (50)]\r
424                 public int MinimumCapacity {\r
425                         get { return _minimumCapacity; }\r
426                         set { _minimumCapacity = value; }\r
427                 }\r
428 \r
429                 /// <summary>\r
430                 /// Gets or sets the namespace for the XML represenation\r
431                 /// of the data stored in the DataTable.\r
432                 /// </summary>\r
433                 [DataCategory ("Data")]\r
434 #if !NET_2_0\r
435                 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]\r
436 #endif\r
437                 public string Namespace {\r
438                         get {\r
439                                 if (_nameSpace != null)\r
440                                         return _nameSpace;\r
441                                 if (DataSet != null)\r
442                                         return DataSet.Namespace;\r
443                                 return String.Empty;\r
444                         }\r
445                         set { _nameSpace = value; }\r
446                 }\r
447 \r
448                 /// <summary>\r
449                 /// Gets the collection of parent relations for\r
450                 /// this DataTable.\r
451                 /// </summary>\r
452                 [Browsable (false)]\r
453 #if !NET_2_0\r
454                 [DataSysDescription ("Returns the parent relations for this table.")]\r
455 #endif\r
456                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
457                 public DataRelationCollection ParentRelations {\r
458                         get { return _parentRelations; }\r
459                 }\r
460 \r
461                 /// <summary>\r
462                 /// Gets or sets the namespace for the XML represenation\r
463                 ///  of the data stored in the DataTable.\r
464                 /// </summary>\r
465                 [DataCategory ("Data")]\r
466 #if !NET_2_0\r
467                 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]\r
468 #endif\r
469                 [DefaultValue ("")]\r
470                 public string Prefix {\r
471                         get { return _prefix == null ? "" : _prefix; }\r
472                         set {\r
473                                 // Prefix cannot contain any special characters other than '_' and ':'\r
474                                 for (int i = 0; i < value.Length; i++) {\r
475                                         if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))\r
476                                                 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");\r
477                                 }\r
478                                 _prefix = value;\r
479                         }\r
480                 }\r
481 \r
482                 /// <summary>\r
483                 /// Gets or sets an array of columns that function as\r
484                 /// primary keys for the data table.\r
485                 /// </summary>\r
486                 [DataCategory ("Data")]\r
487 #if !NET_2_0\r
488                 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]\r
489 #endif\r
490                 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]\r
491                 [TypeConverterAttribute ("System.Data.PrimaryKeyTypeConverter, " + Consts.AssemblySystem_Data)]\r
492                 public DataColumn[] PrimaryKey {\r
493                         get {\r
494                                 if (_primaryKeyConstraint == null)\r
495                                         return new DataColumn[] {};\r
496                                 return _primaryKeyConstraint.Columns;\r
497                         }\r
498                         set {\r
499                                 if (value == null || value.Length == 0) {\r
500                                         if (_primaryKeyConstraint != null) {\r
501                                                 _primaryKeyConstraint.SetIsPrimaryKey (false);\r
502                                                 Constraints.Remove(_primaryKeyConstraint);\r
503                                                 _primaryKeyConstraint = null;\r
504                                         }\r
505                                         return;\r
506                                 }\r
507 \r
508                                 if (InitInProgress) {\r
509                                         _latestPrimaryKeyCols = value;\r
510                                         return;\r
511                                 }\r
512 \r
513                                 // first check if value is the same as current PK.\r
514                                 if (_primaryKeyConstraint != null &&\r
515                                     DataColumn.AreColumnSetsTheSame (value, _primaryKeyConstraint.Columns))\r
516                                         return;\r
517 \r
518                                 //Does constraint exist for these columns\r
519                                 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet (this.Constraints, (DataColumn[]) value);\r
520 \r
521                                 //if constraint doesn't exist for columns\r
522                                 //create new unique primary key constraint\r
523                                 if (null == uc) {\r
524                                         foreach (DataColumn Col in (DataColumn []) value) {\r
525                                                 if (Col.Table == null)\r
526                                                         break;\r
527 \r
528                                                 if (Columns.IndexOf (Col) < 0)\r
529                                                         throw new ArgumentException ("PrimaryKey columns do not belong to this table.");\r
530                                         }\r
531                                         // create constraint with primary key indication set to false\r
532                                         // to avoid recursion\r
533                                         uc = new UniqueConstraint ((DataColumn []) value, false);\r
534                                         Constraints.Add (uc);\r
535                                 }\r
536 \r
537                                 //Remove the existing primary key\r
538                                 if (_primaryKeyConstraint != null) {\r
539                                         _primaryKeyConstraint.SetIsPrimaryKey (false);\r
540                                         Constraints.Remove (_primaryKeyConstraint);\r
541                                         _primaryKeyConstraint = null;\r
542                                 }\r
543 \r
544                                 //set the constraint as the new primary key\r
545                                 UniqueConstraint.SetAsPrimaryKey (Constraints, uc);\r
546                                 _primaryKeyConstraint = uc;\r
547 \r
548                                 for (int j = 0; j < uc.Columns.Length; ++j)\r
549                                         uc.Columns [j].AllowDBNull = false;\r
550                         }\r
551                 }\r
552 \r
553                 internal UniqueConstraint PrimaryKeyConstraint {\r
554                         get { return _primaryKeyConstraint; }\r
555                 }\r
556 \r
557                 /// <summary>\r
558                 /// Gets the collection of_rows that belong to this table.\r
559                 /// </summary>\r
560                 [Browsable (false)]\r
561 #if !NET_2_0\r
562                 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]\r
563 #endif\r
564                 public DataRowCollection Rows {\r
565                         get { return _rows; }\r
566                 }\r
567 \r
568                 /// <summary>\r
569                 /// Gets or sets an System.ComponentModel.ISite\r
570                 /// for the DataTable.\r
571                 /// </summary>\r
572                 [Browsable (false)]\r
573                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]\r
574                 public override ISite Site {\r
575                         get { return _site; }\r
576                         set { _site = value; }\r
577                 }\r
578 \r
579                 /// <summary>\r
580                 /// Gets or sets the name of the the DataTable.\r
581                 /// </summary>\r
582                 [DataCategory ("Data")]\r
583 #if !NET_2_0\r
584                 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]\r
585 #endif\r
586                 [DefaultValue ("")]\r
587                 [RefreshProperties (RefreshProperties.All)]\r
588                 public string TableName {\r
589                         get { return _tableName == null ? "" : _tableName; }\r
590                         set { _tableName = value; }\r
591                 }\r
592 \r
593                 bool IListSource.ContainsListCollection {\r
594                         // the collection is a DataView\r
595                         get { return false; }\r
596                 }\r
597 \r
598                 internal RecordCache RecordCache {\r
599                         get { return _recordCache; }\r
600                 }\r
601 \r
602                 private DataRowBuilder RowBuilder {\r
603                         get {\r
604                                 // initiate only one row builder.\r
605                                 if (_rowBuilder == null)\r
606                                         _rowBuilder = new DataRowBuilder (this, -1, 0);\r
607                                 else\r
608                                         // new row get id -1.\r
609                                         _rowBuilder._rowId = -1;\r
610 \r
611                                 return _rowBuilder;\r
612                         }\r
613                 }\r
614 \r
615                 internal bool EnforceConstraints {\r
616                         get { return enforceConstraints; }\r
617                         set {\r
618                                 if (value == enforceConstraints)\r
619                                         return;\r
620 \r
621                                 if (value) {\r
622                                         // reset indexes since they may be outdated\r
623                                         ResetIndexes();\r
624 \r
625                                         // assert all constraints\r
626                                         foreach (Constraint constraint in Constraints)\r
627                                                 constraint.AssertConstraint ();\r
628 \r
629                                         AssertNotNullConstraints ();\r
630 \r
631                                         if (HasErrors)\r
632                                                 Constraint.ThrowConstraintException ();\r
633                                 }\r
634                                 enforceConstraints = value;\r
635                         }\r
636                 }\r
637 \r
638                 internal void AssertNotNullConstraints ()\r
639                 {\r
640                         if (_duringDataLoad && !_nullConstraintViolationDuringDataLoad)\r
641                                 return;\r
642 \r
643                         bool seen = false;\r
644                         for (int i = 0; i < Columns.Count; i++) {\r
645                                 DataColumn column = Columns [i];\r
646                                 if (column.AllowDBNull)\r
647                                         continue;\r
648                                 for (int j = 0; j < Rows.Count; j++) {\r
649                                         if (Rows [j].HasVersion (DataRowVersion.Default) && Rows[j].IsNull (column)) {\r
650                                                 seen = true;\r
651                                                 string errMsg = String.Format ("Column '{0}' does not allow DBNull.Value.",\r
652                                                                                column.ColumnName);\r
653                                                 Rows [j].SetColumnError (i, errMsg);\r
654                                                 Rows [j].RowError = errMsg;\r
655                                         }\r
656                                 }\r
657                         }\r
658                         _nullConstraintViolationDuringDataLoad = seen;\r
659                 }\r
660 \r
661                 internal bool RowsExist (DataColumn [] columns, DataColumn [] relatedColumns, DataRow row)\r
662                 {\r
663                         int curIndex = row.IndexFromVersion (DataRowVersion.Default);\r
664                         int tmpRecord = RecordCache.NewRecord ();\r
665 \r
666                         try {\r
667                                 for (int i = 0; i < relatedColumns.Length; i++)\r
668                                         // according to MSDN: the DataType value for both columns must be identical.\r
669                                         columns [i].DataContainer.CopyValue (relatedColumns [i].DataContainer, curIndex, tmpRecord);\r
670                                 return RowsExist (columns, tmpRecord);\r
671                         } finally {\r
672                                 RecordCache.DisposeRecord (tmpRecord);\r
673                         }\r
674                 }\r
675 \r
676                 bool RowsExist (DataColumn [] columns, int index)\r
677                 {\r
678                         Index indx = this.FindIndex (columns);\r
679 \r
680                         if (indx != null)\r
681                                 return indx.Find (index) != -1;\r
682 \r
683                         // we have to perform full-table scan\r
684                         // check that there is a parent for this row.\r
685                         foreach (DataRow thisRow in this.Rows) {\r
686                                 if (thisRow.RowState == DataRowState.Deleted)\r
687                                         continue;\r
688                                 // check if the values in the columns are equal\r
689                                 int thisIndex = thisRow.IndexFromVersion (\r
690                                         thisRow.RowState == DataRowState.Modified ? DataRowVersion.Original : DataRowVersion.Current);\r
691                                 bool match = true;\r
692                                 foreach (DataColumn column in columns) {\r
693                                         if (column.DataContainer.CompareValues (thisIndex, index) != 0) {\r
694                                                 match = false;\r
695                                                 break;\r
696                                         }\r
697                                 }\r
698                                 if (match)\r
699                                         return true;\r
700                         }\r
701                         return false;\r
702                 }\r
703 \r
704                 /// <summary>\r
705                 /// Commits all the changes made to this table since the\r
706                 /// last time AcceptChanges was called.\r
707                 /// </summary>\r
708                 public void AcceptChanges ()\r
709                 {\r
710                         //FIXME: Do we need to validate anything here or\r
711                         //try to catch any errors to deal with them?\r
712 \r
713                         // we do not use foreach because if one of the rows is in Delete state\r
714                         // it will be romeved from Rows and we get an exception.\r
715                         DataRow myRow;\r
716                         for (int i = 0; i < Rows.Count; ) {\r
717                                 myRow = Rows [i];\r
718                                 myRow.AcceptChanges ();\r
719 \r
720                                 // if the row state is Detached it meens that it was removed from row list (Rows)\r
721                                 // so we should not increase 'i'.\r
722                                 if (myRow.RowState != DataRowState.Detached)\r
723                                         i++;\r
724                         }\r
725                         _rows.OnListChanged (this, new ListChangedEventArgs (ListChangedType.Reset, -1, -1));\r
726                 }\r
727 \r
728                 /// <summary>\r
729                 /// Begins the initialization of a DataTable that is used\r
730                 /// on a form or used by another component. The initialization\r
731                 /// occurs at runtime.\r
732                 /// </summary>\r
733                 public\r
734 #if NET_2_0\r
735                 virtual\r
736 #endif\r
737                 void BeginInit ()\r
738                 {\r
739                         InitInProgress = true;\r
740 #if NET_2_0\r
741                         tableInitialized = false;\r
742 #endif\r
743                 }\r
744 \r
745                 /// <summary>\r
746                 /// Turns off notifications, index maintenance, and\r
747                 /// constraints while loading data.\r
748                 /// </summary>\r
749                 public void BeginLoadData ()\r
750                 {\r
751                         if (this._duringDataLoad)\r
752                                 return;\r
753 \r
754                         //duringDataLoad is important to EndLoadData and\r
755                         //for not throwing unexpected exceptions.\r
756                         this._duringDataLoad = true;\r
757                         this._nullConstraintViolationDuringDataLoad = false;\r
758 \r
759                         if (this.dataSet != null) {\r
760                                 //Saving old Enforce constraints state for later\r
761                                 //use in the EndLoadData.\r
762                                 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;\r
763                                 this.dataSet.EnforceConstraints = false;\r
764                         } else {\r
765                                 //if table does not belong to any data set use EnforceConstraints of the table\r
766                                 this.EnforceConstraints = false;\r
767                         }\r
768                         return;\r
769                 }\r
770 \r
771                 /// <summary>\r
772                 /// Clears the DataTable of all data.\r
773                 /// </summary>\r
774                 public void Clear ()\r
775                 {\r
776                         DataTableClearing ();\r
777                         // Foriegn key constraints are checked in _rows.Clear method\r
778                         _rows.Clear ();\r
779                         foreach (Index index in Indexes)\r
780                                 index.Reset ();\r
781                         DataTableCleared ();\r
782                 }\r
783 \r
784                 // defined in the NET_2_0 profile\r
785                 partial void DataTableClearing ();\r
786                 partial void DataTableCleared ();\r
787 \r
788                 /// <summary>\r
789                 /// Clones the structure of the DataTable, including\r
790                 ///  all DataTable schemas and constraints.\r
791                 /// </summary>\r
792                 public virtual DataTable Clone ()\r
793                 {\r
794                          // Use Activator so we can use non-public constructors.\r
795                         DataTable Copy = (DataTable) Activator.CreateInstance (GetType (), true);\r
796                         CopyProperties (Copy);\r
797                         return Copy;\r
798                 }\r
799 \r
800                 /// <summary>\r
801                 /// Computes the given expression on the current_rows that\r
802                 /// pass the filter criteria.\r
803                 /// </summary>\r
804                 public object Compute (string expression, string filter)\r
805                 {\r
806                         // expression is an aggregate function\r
807                         // filter is an expression used to limit rows\r
808 \r
809                         DataRow [] rows = Select (filter);\r
810 \r
811                         if (rows == null || rows.Length == 0)\r
812                                 return DBNull.Value;\r
813 \r
814                         Parser parser = new Parser (rows);\r
815                         IExpression expr = parser.Compile (expression);\r
816                         object obj = expr.Eval (rows [0]);\r
817 \r
818                         return obj;\r
819                 }\r
820 \r
821                 /// <summary>\r
822                 /// Copies both the structure and data for this DataTable.\r
823                 /// </summary>\r
824                 public DataTable Copy ()\r
825                 {\r
826                         DataTable copy = Clone ();\r
827 \r
828                         copy._duringDataLoad = true;\r
829                         foreach (DataRow row in Rows) {\r
830                                 DataRow newRow = copy.NewNotInitializedRow ();\r
831                                 copy.Rows.AddInternal (newRow);\r
832                                 CopyRow (row, newRow);\r
833                         }\r
834                         copy._duringDataLoad = false;\r
835 \r
836                         // rebuild copy indexes after loading all rows\r
837                         copy.ResetIndexes ();\r
838                         return copy;\r
839                 }\r
840 \r
841                 internal void CopyRow (DataRow fromRow, DataRow toRow)\r
842                 {\r
843                         if (fromRow.HasErrors)\r
844                                 fromRow.CopyErrors (toRow);\r
845 \r
846                         if (fromRow.HasVersion (DataRowVersion.Original))\r
847                                 toRow.Original = toRow.Table.RecordCache.CopyRecord (this, fromRow.Original, -1);\r
848 \r
849                         if (fromRow.HasVersion (DataRowVersion.Current)) {\r
850                                 if (fromRow.Original != fromRow.Current)\r
851                                         toRow.Current = toRow.Table.RecordCache.CopyRecord (this, fromRow.Current, -1);\r
852                                 else\r
853                                         toRow.Current = toRow.Original;\r
854                         }\r
855                 }\r
856 \r
857                 private void CopyProperties (DataTable Copy)\r
858                 {\r
859                         Copy.CaseSensitive = CaseSensitive;\r
860                         Copy._virginCaseSensitive = _virginCaseSensitive;\r
861 \r
862                         // Copy.ChildRelations\r
863                         // Copy.Constraints\r
864                         // Copy.Container\r
865                         // Copy.DefaultView\r
866                         // Copy.DesignMode\r
867                         Copy.DisplayExpression = DisplayExpression;\r
868                         if (ExtendedProperties.Count > 0) {\r
869                                 //  Cannot copy extended properties directly as the property does not have a set accessor\r
870                                 Array tgtArray = Array.CreateInstance (typeof (object), ExtendedProperties.Count);\r
871                                 ExtendedProperties.Keys.CopyTo (tgtArray, 0);\r
872                                 for (int i=0; i < ExtendedProperties.Count; i++)\r
873                                         Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);\r
874                         }\r
875                         Copy._locale = _locale;\r
876                         Copy.MinimumCapacity = MinimumCapacity;\r
877                         Copy.Namespace = Namespace;\r
878                         // Copy.ParentRelations\r
879                         Copy.Prefix = Prefix;\r
880                         Copy.Site = Site;\r
881                         Copy.TableName = TableName;\r
882 \r
883                         bool isEmpty = Copy.Columns.Count == 0;\r
884 \r
885                         // Copy columns\r
886                         foreach (DataColumn column in Columns) {\r
887                                 // When cloning a table, the columns may be added in the default constructor.\r
888                                 if (isEmpty || !Copy.Columns.Contains (column.ColumnName))\r
889                                         Copy.Columns.Add (column.Clone ());\r
890                         }\r
891 \r
892                         CopyConstraints (Copy);\r
893                         // add primary key to the copy\r
894                         if (PrimaryKey.Length > 0) {\r
895                                 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];\r
896                                 for (int i = 0; i < pColumns.Length; i++)\r
897                                         pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];\r
898 \r
899                                 Copy.PrimaryKey = pColumns;\r
900                         }\r
901                 }\r
902 \r
903                 private void CopyConstraints (DataTable copy)\r
904                 {\r
905                         UniqueConstraint origUc;\r
906                         UniqueConstraint copyUc;\r
907                         for (int i = 0; i < this.Constraints.Count; i++) {\r
908                                 if (this.Constraints[i] is UniqueConstraint) {\r
909                                         // typed ds can already contain the constraints\r
910                                         if (copy.Constraints.Contains (this.Constraints [i].ConstraintName))\r
911                                                 continue;\r
912 \r
913                                         origUc = (UniqueConstraint) this.Constraints [i];\r
914                                         DataColumn [] columns = new DataColumn [origUc.Columns.Length];\r
915                                         for (int j = 0; j < columns.Length; j++)\r
916                                                 columns[j] = copy.Columns [origUc.Columns [j].ColumnName];\r
917 \r
918                                         copyUc = new UniqueConstraint (origUc.ConstraintName, columns, origUc.IsPrimaryKey);\r
919                                         copy.Constraints.Add (copyUc);\r
920                                 }\r
921                         }\r
922                 }\r
923                 /// <summary>\r
924                 /// Ends the initialization of a DataTable that is used\r
925                 /// on a form or used by another component. The\r
926                 /// initialization occurs at runtime.\r
927                 /// </summary>\r
928                 public\r
929 #if NET_2_0\r
930                 virtual\r
931 #endif\r
932                 void EndInit ()\r
933                 {\r
934                         InitInProgress = false;\r
935                         DataTableInitialized ();\r
936                         FinishInit ();\r
937                 }\r
938 \r
939                 // defined in NET_2_0 profile\r
940                 partial void DataTableInitialized ();\r
941 \r
942                 internal bool InitInProgress {\r
943                         get { return fInitInProgress; }\r
944                         set { fInitInProgress = value; }\r
945                 }\r
946 \r
947                 internal void FinishInit ()\r
948                 {\r
949                         UniqueConstraint oldPK = _primaryKeyConstraint;\r
950 \r
951                         // Columns shud be added 'before' the constraints\r
952                         Columns.PostAddRange ();\r
953 \r
954                         // Add the constraints\r
955                         _constraintCollection.PostAddRange ();\r
956 \r
957                         // ms.net behavior : If a PrimaryKey (UniqueConstraint) is added thru AddRange,\r
958                         // then it takes precedence over an direct assignment of PrimaryKey\r
959                         if (_primaryKeyConstraint == oldPK)\r
960                                 PrimaryKey = _latestPrimaryKeyCols;\r
961                 }\r
962 \r
963                 /// <summary>\r
964                 /// Turns on notifications, index maintenance, and\r
965                 /// constraints after loading data.\r
966                 /// </summary>\r
967                 public void EndLoadData ()\r
968                 {\r
969                         if (this._duringDataLoad) {\r
970                                 //Getting back to previous EnforceConstraint state\r
971                                 if (this.dataSet != null)\r
972                                         this.dataSet.InternalEnforceConstraints (this.dataSetPrevEnforceConstraints, true);\r
973                                 else\r
974                                         this.EnforceConstraints = true;\r
975 \r
976                                 this._duringDataLoad = false;\r
977                         }\r
978                 }\r
979 \r
980                 /// <summary>\r
981                 /// Gets a copy of the DataTable that contains all\r
982                 ///  changes made to it since it was loaded or\r
983                 ///  AcceptChanges was last called.\r
984                 /// </summary>\r
985                 public DataTable GetChanges ()\r
986                 {\r
987                         return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);\r
988                 }\r
989 \r
990                 /// <summary>\r
991                 /// Gets a copy of the DataTable containing all\r
992                 /// changes made to it since it was last loaded, or\r
993                 /// since AcceptChanges was called, filtered by DataRowState.\r
994                 /// </summary>\r
995                 public DataTable GetChanges (DataRowState rowStates)\r
996                 {\r
997                         DataTable copyTable = null;\r
998 \r
999                         foreach (DataRow row in Rows) {\r
1000                                 // The spec says relationship constraints may cause Unchanged parent rows to be included but\r
1001                                 // MS .NET 1.1 does not include Unchanged rows even if their child rows are changed.\r
1002                                 if (!row.IsRowChanged (rowStates))\r
1003                                         continue;\r
1004                                 if (copyTable == null)\r
1005                                         copyTable = Clone ();\r
1006                                 DataRow newRow = copyTable.NewNotInitializedRow ();\r
1007                                 row.CopyValuesToRow (newRow);\r
1008 #if NET_2_0\r
1009                                 newRow.XmlRowID = row.XmlRowID;\r
1010 #endif\r
1011                                 copyTable.Rows.AddInternal (newRow);\r
1012                         }\r
1013 \r
1014                         return copyTable;\r
1015                 }\r
1016 \r
1017                 /// <summary>\r
1018                 /// Gets an array of DataRow objects that contain errors.\r
1019                 /// </summary>\r
1020                 public DataRow [] GetErrors ()\r
1021                 {\r
1022                         ArrayList errors = new ArrayList();\r
1023                         for (int i = 0; i < _rows.Count; i++) {\r
1024                                 if (_rows[i].HasErrors)\r
1025                                         errors.Add (_rows[i]);\r
1026                         }\r
1027 \r
1028                         DataRow[] ret = NewRowArray (errors.Count);\r
1029                         errors.CopyTo (ret, 0);\r
1030                         return ret;\r
1031                 }\r
1032 \r
1033                 /// <summary>\r
1034                 /// This member is only meant to support Mono's infrastructure\r
1035                 /// </summary>\r
1036                 protected virtual DataTable CreateInstance ()\r
1037                 {\r
1038                         return Activator.CreateInstance (this.GetType (), true) as DataTable;\r
1039                 }\r
1040 \r
1041                 /// <summary>\r
1042                 /// This member is only meant to support Mono's infrastructure\r
1043                 /// </summary>\r
1044                 protected virtual Type GetRowType ()\r
1045                 {\r
1046                         return typeof (DataRow);\r
1047                 }\r
1048 \r
1049                 /// <summary>\r
1050                 /// This member is only meant to support Mono's infrastructure\r
1051                 ///\r
1052                 /// Used for Data Binding between System.Web.UI. controls\r
1053                 /// like a DataGrid\r
1054                 /// or\r
1055                 /// System.Windows.Forms controls like a DataGrid\r
1056                 /// </summary>\r
1057                 IList IListSource.GetList ()\r
1058                 {\r
1059                         IList list = (IList) DefaultView;\r
1060                         return list;\r
1061                 }\r
1062 \r
1063                 /// <summary>\r
1064                 /// Copies a DataRow into a DataTable, preserving any\r
1065                 /// property settings, as well as original and current values.\r
1066                 /// </summary>\r
1067                 public void ImportRow (DataRow row)\r
1068                 {\r
1069                         if (row.RowState == DataRowState.Detached)\r
1070                                 return;\r
1071 \r
1072                         DataRow newRow = NewNotInitializedRow ();\r
1073 \r
1074                         int original = -1;\r
1075                         if (row.HasVersion (DataRowVersion.Original)) {\r
1076                                 original = row.IndexFromVersion (DataRowVersion.Original);\r
1077                                 newRow.Original = RecordCache.NewRecord ();\r
1078                                 RecordCache.CopyRecord (row.Table, original, newRow.Original);\r
1079                         }\r
1080 \r
1081                         if (row.HasVersion (DataRowVersion.Current)) {\r
1082                                 int current = row.IndexFromVersion (DataRowVersion.Current);\r
1083                                 if (current == original) {\r
1084                                         newRow.Current = newRow.Original;\r
1085                                 } else {\r
1086                                         newRow.Current = RecordCache.NewRecord ();\r
1087                                         RecordCache.CopyRecord (row.Table, current, newRow.Current);\r
1088                                 }\r
1089                         }\r
1090 \r
1091                         //Import the row only if RowState is not detached\r
1092                         //Validation for Deleted Rows happens during Accept/RejectChanges\r
1093                         if (row.RowState != DataRowState.Deleted)\r
1094                                 newRow.Validate ();\r
1095                         else\r
1096                                 AddRowToIndexes (newRow);\r
1097                         Rows.AddInternal(newRow);\r
1098 \r
1099                         if (row.HasErrors)\r
1100                                 row.CopyErrors (newRow);\r
1101                 }\r
1102 \r
1103                 internal int DefaultValuesRowIndex {\r
1104                         get { return _defaultValuesRowIndex; }\r
1105                 }\r
1106 \r
1107                 /// <summary>\r
1108                 /// This member is only meant to support Mono's infrastructure\r
1109                 /// </summary>\r
1110 #if NET_2_0\r
1111                 public virtual\r
1112 #endif\r
1113                 void\r
1114 #if !NET_2_0\r
1115                 ISerializable.\r
1116 #endif\r
1117                 GetObjectData (SerializationInfo info, StreamingContext context)\r
1118                 {\r
1119 #if NET_2_0\r
1120                         if (RemotingFormat == SerializationFormat.Xml) {\r
1121 #endif\r
1122                                 DataSet dset;\r
1123                                 if (dataSet != null)\r
1124                                         dset = dataSet;\r
1125                                 else {\r
1126                                         dset = new DataSet ("tmpDataSet");\r
1127                                         dset.Tables.Add (this);\r
1128                                 }\r
1129 \r
1130                                 StringWriter sw = new StringWriter ();\r
1131                                 XmlTextWriter tw = new XmlTextWriter (sw);\r
1132                                 tw.Formatting = Formatting.Indented;\r
1133                                 dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);\r
1134                                 tw.Close ();\r
1135 \r
1136                                 StringWriter sw2 = new StringWriter ();\r
1137                                 DataTableCollection tables = new DataTableCollection (dset);\r
1138                                 tables.Add (this);\r
1139                                 XmlSchemaWriter.WriteXmlSchema (dset, new XmlTextWriter (sw2), tables, null);\r
1140                                 sw2.Close ();\r
1141 \r
1142                                 info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));\r
1143                                 info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));\r
1144 #if NET_2_0\r
1145                         } else /*if (RemotingFormat == SerializationFormat.Binary)*/ {\r
1146                                 BinarySerializeProperty (info);\r
1147                                 if (dataSet == null) {\r
1148                                         for (int i = 0; i < Columns.Count; i++) {\r
1149                                                 info.AddValue ("DataTable.DataColumn_" + i + ".Expression",\r
1150                                                                Columns[i].Expression);\r
1151                                         }\r
1152                                         BinarySerialize (info, "DataTable_0.");\r
1153                                 }\r
1154                         }\r
1155 #endif\r
1156                 }\r
1157 \r
1158                 /// <summary>\r
1159                 /// Finds and updates a specific row. If no matching row\r
1160                 ///  is found, a new row is created using the given values.\r
1161                 /// </summary>\r
1162                 public DataRow LoadDataRow (object [] values, bool fAcceptChanges)\r
1163                 {\r
1164                         DataRow row = null;\r
1165                         if (PrimaryKey.Length == 0) {\r
1166                                 row = Rows.Add (values);\r
1167                         } else {\r
1168                                 EnsureDefaultValueRowIndex ();\r
1169                                 int newRecord = CreateRecord (values);\r
1170                                 int existingRecord = _primaryKeyConstraint.Index.Find (newRecord);\r
1171 \r
1172                                 if (existingRecord < 0) {\r
1173                                         row = NewRowFromBuilder (RowBuilder);\r
1174                                         row.Proposed = newRecord;\r
1175                                         Rows.AddInternal(row);\r
1176                                         if (!_duringDataLoad)\r
1177                                                 AddRowToIndexes (row);\r
1178                                 } else {\r
1179                                         row = RecordCache [existingRecord];\r
1180                                         row.BeginEdit ();\r
1181                                         row.ImportRecord (newRecord);\r
1182                                         row.EndEdit ();\r
1183                                 }\r
1184                         }\r
1185 \r
1186                         if (fAcceptChanges)\r
1187                                 row.AcceptChanges ();\r
1188 \r
1189                         return row;\r
1190                 }\r
1191 \r
1192                 internal DataRow LoadDataRow (IDataRecord record, int[] mapping, int length, bool fAcceptChanges)\r
1193                 {\r
1194                         DataRow row = null;\r
1195                         int tmpRecord = this.RecordCache.NewRecord ();\r
1196                         try {\r
1197                                 RecordCache.ReadIDataRecord (tmpRecord,record,mapping,length);\r
1198                                 if (PrimaryKey.Length != 0) {\r
1199                                         bool hasPrimaryValues = true;\r
1200                                         foreach(DataColumn col in PrimaryKey) {\r
1201                                                 if(!(col.Ordinal < mapping.Length)) {\r
1202                                                         hasPrimaryValues = false;\r
1203                                                         break;\r
1204                                                 }\r
1205                                         }\r
1206 \r
1207                                         if (hasPrimaryValues) {\r
1208                                                 int existingRecord = _primaryKeyConstraint.Index.Find (tmpRecord);\r
1209                                                 if (existingRecord != -1)\r
1210                                                         row  = RecordCache [existingRecord];\r
1211                                         }\r
1212                                 }\r
1213 \r
1214                                 if (row == null) {\r
1215                                         row = NewNotInitializedRow ();\r
1216                                         row.Proposed = tmpRecord;\r
1217                                         Rows.AddInternal (row);\r
1218                                 } else {\r
1219                                         row.BeginEdit ();\r
1220                                         row.ImportRecord (tmpRecord);\r
1221                                         row.EndEdit ();\r
1222                                 }\r
1223 \r
1224                                 if (fAcceptChanges)\r
1225                                         row.AcceptChanges ();\r
1226 \r
1227                         } catch {\r
1228                                 this.RecordCache.DisposeRecord (tmpRecord);\r
1229                                 throw;\r
1230                         }\r
1231                         return row;\r
1232                 }\r
1233 \r
1234                 /// <summary>\r
1235                 /// Creates a new DataRow with the same schema as the table.\r
1236                 /// </summary>\r
1237                 public DataRow NewRow ()\r
1238                 {\r
1239                         EnsureDefaultValueRowIndex();\r
1240 \r
1241                         DataRow newRow = NewRowFromBuilder (RowBuilder);\r
1242                         newRow.Proposed = CreateRecord (null);\r
1243                         NewRowAdded (newRow);\r
1244                         return newRow;\r
1245                 }\r
1246 \r
1247                 // defined in the NET_2_0 profile\r
1248                 partial void NewRowAdded (DataRow dr);\r
1249 \r
1250                 internal int CreateRecord (object [] values)\r
1251                 {\r
1252                         int valCount = values != null ? values.Length : 0;\r
1253                         if (valCount > Columns.Count)\r
1254                                 throw new ArgumentException ("Input array is longer than the number of columns in this table.");\r
1255 \r
1256                         int index = RecordCache.NewRecord ();\r
1257 \r
1258                         try {\r
1259                                 for (int i = 0; i < valCount; i++) {\r
1260                                         object value = values[i];\r
1261                                         if (value == null)\r
1262                                                 Columns [i].SetDefaultValue (index);\r
1263                                         else\r
1264                                                 Columns [i][index] = values [i];\r
1265                                 }\r
1266 \r
1267                                 for(int i = valCount; i < Columns.Count; i++)\r
1268                                         Columns [i].SetDefaultValue (index);\r
1269 \r
1270                                 return index;\r
1271                         } catch {\r
1272                                 RecordCache.DisposeRecord (index);\r
1273                                 throw;\r
1274                         }\r
1275                 }\r
1276 \r
1277                 private void EnsureDefaultValueRowIndex ()\r
1278                 {\r
1279                         // initialize default values row for the first time\r
1280                         if (_defaultValuesRowIndex == -1) {\r
1281                                 _defaultValuesRowIndex = RecordCache.NewRecord();\r
1282                                 for (int i = 0; i < Columns.Count; ++i) {\r
1283                                         DataColumn column = Columns [i];\r
1284                                         column.DataContainer [_defaultValuesRowIndex] = column.DefaultValue;\r
1285                                 }\r
1286                         }\r
1287                 }\r
1288 \r
1289                 /// <summary>\r
1290                 /// This member supports the .NET Framework infrastructure\r
1291                 ///  and is not intended to be used directly from your code.\r
1292                 /// </summary>\r
1293                 DataRow [] empty_rows;\r
1294                 protected internal DataRow [] NewRowArray (int size)\r
1295                 {\r
1296                         if (size == 0 && empty_rows != null)\r
1297                                 return empty_rows;\r
1298                         Type t = GetRowType ();\r
1299                         /* Avoid reflection if possible */\r
1300                         DataRow [] rows = t == typeof (DataRow) ? new DataRow [size] : (DataRow []) Array.CreateInstance (t, size);\r
1301                         if (size == 0)\r
1302                                 empty_rows = rows;\r
1303                         return rows;\r
1304                 }\r
1305 \r
1306                 /// <summary>\r
1307                 /// Creates a new row from an existing row.\r
1308                 /// </summary>\r
1309                 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)\r
1310                 {\r
1311                         return new DataRow (builder);\r
1312                 }\r
1313 \r
1314                 internal DataRow NewNotInitializedRow ()\r
1315                 {\r
1316                         EnsureDefaultValueRowIndex ();\r
1317 \r
1318                         return NewRowFromBuilder (RowBuilder);\r
1319                 }\r
1320 \r
1321                 /// <summary>\r
1322                 /// Rolls back all changes that have been made to the\r
1323                 /// table since it was loaded, or the last time AcceptChanges\r
1324                 ///  was called.\r
1325                 /// </summary>\r
1326                 public void RejectChanges ()\r
1327                 {\r
1328                         for (int i = _rows.Count - 1; i >= 0; i--) {\r
1329                                 DataRow row = _rows [i];\r
1330                                 if (row.RowState != DataRowState.Unchanged)\r
1331                                         _rows [i].RejectChanges ();\r
1332                         }\r
1333                 }\r
1334 \r
1335                 /// <summary>\r
1336                 /// Resets the DataTable to its original state.\r
1337                 /// </summary>\r
1338                 public virtual void Reset ()\r
1339                 {\r
1340                         Clear ();\r
1341                         while (ParentRelations.Count > 0) {\r
1342                                 if (dataSet.Relations.Contains (ParentRelations [ParentRelations.Count - 1].RelationName))\r
1343                                         dataSet.Relations.Remove (ParentRelations [ParentRelations.Count - 1]);\r
1344                         }\r
1345 \r
1346                         while (ChildRelations.Count > 0) {\r
1347                                 if (dataSet.Relations.Contains (ChildRelations [ChildRelations.Count - 1].RelationName))\r
1348                                         dataSet.Relations.Remove (ChildRelations [ChildRelations.Count - 1]);\r
1349                         }\r
1350                         Constraints.Clear ();\r
1351                         Columns.Clear ();\r
1352                 }\r
1353 \r
1354                 /// <summary>\r
1355                 /// Gets an array of all DataRow objects.\r
1356                 /// </summary>\r
1357                 public DataRow[] Select ()\r
1358                 {\r
1359                         return Select (String.Empty, String.Empty, DataViewRowState.CurrentRows);\r
1360                 }\r
1361 \r
1362                 /// <summary>\r
1363                 /// Gets an array of all DataRow objects that match\r
1364                 /// the filter criteria in order of primary key (or\r
1365                 /// lacking one, order of addition.)\r
1366                 /// </summary>\r
1367                 public DataRow[] Select (string filterExpression)\r
1368                 {\r
1369                         return Select (filterExpression, String.Empty, DataViewRowState.CurrentRows);\r
1370                 }\r
1371 \r
1372                 /// <summary>\r
1373                 /// Gets an array of all DataRow objects that\r
1374                 /// match the filter criteria, in the the\r
1375                 /// specified sort order.\r
1376                 /// </summary>\r
1377                 public DataRow[] Select (string filterExpression, string sort)\r
1378                 {\r
1379                         return Select (filterExpression, sort, DataViewRowState.CurrentRows);\r
1380                 }\r
1381 \r
1382                 /// <summary>\r
1383                 /// Gets an array of all DataRow objects that match\r
1384                 /// the filter in the order of the sort, that match\r
1385                 /// the specified state.\r
1386                 /// </summary>\r
1387                 public DataRow [] Select (string filterExpression, string sort, DataViewRowState recordStates)\r
1388                 {\r
1389                         if (filterExpression == null)\r
1390                                 filterExpression = String.Empty;\r
1391 \r
1392                         IExpression filter = null;\r
1393                         if (filterExpression != String.Empty) {\r
1394                                 Parser parser = new Parser ();\r
1395                                 filter = parser.Compile (filterExpression);\r
1396                         }\r
1397 \r
1398                         DataColumn [] columns = _emptyColumnArray;\r
1399                         ListSortDirection [] sorts = null;\r
1400 \r
1401                         if (sort != null && !sort.Equals(String.Empty))\r
1402                                 columns = ParseSortString (this, sort, out sorts, false);\r
1403 \r
1404                         if (Rows.Count == 0)\r
1405                                 return NewRowArray (0);\r
1406 \r
1407                         //if sort order is not given, sort it in Ascending order of the\r
1408                         //columns involved in the filter\r
1409                         if (columns.Length == 0 && filter != null) {\r
1410                                 ArrayList list = new ArrayList ();\r
1411                                 for (int i = 0; i < Columns.Count; ++i) {\r
1412                                         if (!filter.DependsOn (Columns [i]))\r
1413                                                 continue;\r
1414                                         list.Add (Columns [i]);\r
1415                                 }\r
1416                                 columns = (DataColumn []) list.ToArray (typeof (DataColumn));\r
1417                         }\r
1418 \r
1419                         bool addIndex = true;\r
1420                         if (filterExpression != String.Empty)\r
1421                                 addIndex = false;\r
1422                         Index index = GetIndex (columns, sorts, recordStates, filter, false, addIndex);\r
1423 \r
1424                         int [] records = index.GetAll ();\r
1425                         DataRow [] dataRows = NewRowArray (index.Size);\r
1426                         for (int i = 0; i < dataRows.Length; i++)\r
1427                                 dataRows [i] = RecordCache [records [i]];\r
1428 \r
1429                         return dataRows;\r
1430                 }\r
1431 \r
1432                 private void AddIndex (Index index)\r
1433                 {\r
1434                         if (_indexes == null)\r
1435                                 _indexes = new ArrayList();\r
1436                         _indexes.Add (index);\r
1437                 }\r
1438 \r
1439                 /// <summary>\r
1440                 /// Returns index corresponding to columns,sort,row state filter and unique values given.\r
1441                 /// If such an index not exists, creates a new one.\r
1442                 /// </summary>\r
1443                 /// <param name="columns">Columns set of the index to look for.</param>\r
1444                 /// <param name="sort">Columns sort order of the index to look for.</param>\r
1445                 /// <param name="rowState">Rpw state filter of the index to look for.</param>\r
1446                 /// <param name="unique">Uniqueness of the index to look for.</param>\r
1447                 /// <param name="strict">Indicates whenever the index found should correspond in its uniquness to the value of unique parameter specified.</param>\r
1448                 /// <param name="reset">Indicates whenever the already existing index should be forced to reset.</param>\r
1449                 /// <returns></returns>\r
1450                 internal Index GetIndex (DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter, bool reset)\r
1451                 {\r
1452                         return GetIndex (columns, sort, rowState, filter, reset, true);\r
1453                 }\r
1454 \r
1455                 internal Index GetIndex (DataColumn[] columns, ListSortDirection[] sort,\r
1456                                          DataViewRowState rowState, IExpression filter,\r
1457                                          bool reset, bool addIndex)\r
1458                 {\r
1459                         Index index = FindIndex(columns, sort, rowState, filter);\r
1460                         if (index == null) {\r
1461                                 index = new Index(new Key (this, columns, sort, rowState, filter));\r
1462 \r
1463                                 if (addIndex)\r
1464                                         AddIndex (index);\r
1465                         } else if (reset) {\r
1466                                 // reset existing index only if asked for this\r
1467                                 index.Reset ();\r
1468                         }\r
1469                         return index;\r
1470                 }\r
1471 \r
1472                 internal Index FindIndex (DataColumn[] columns)\r
1473                 {\r
1474                         return FindIndex (columns, null, DataViewRowState.None, null);\r
1475                 }\r
1476 \r
1477                 internal Index FindIndex (DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter)\r
1478                 {\r
1479                         if (Indexes != null) {\r
1480                                 foreach (Index index in Indexes) {\r
1481                                         if (index.Key.Equals (columns,sort,rowState, filter))\r
1482                                                 return index;\r
1483                                 }\r
1484                         }\r
1485                         return null;\r
1486                 }\r
1487 \r
1488                 internal void ResetIndexes ()\r
1489                 {\r
1490                         foreach(Index index in Indexes)\r
1491                                 index.Reset ();\r
1492                 }\r
1493 \r
1494                 internal void ResetCaseSensitiveIndexes ()\r
1495                 {\r
1496                         foreach (Index index in Indexes) {\r
1497                                 bool containsStringcolumns = false;\r
1498                                 foreach(DataColumn column in index.Key.Columns) {\r
1499                                         if (column.DataType == typeof(string)) {\r
1500                                                 containsStringcolumns = true;\r
1501                                                 break;\r
1502                                         }\r
1503                                 }\r
1504 \r
1505                                 if (!containsStringcolumns && index.Key.HasFilter) {\r
1506                                         foreach (DataColumn column in Columns) {\r
1507                                                 if ((column.DataType == DbTypes.TypeOfString) && (index.Key.DependsOn (column))) {\r
1508                                                         containsStringcolumns = true;\r
1509                                                         break;\r
1510                                                 }\r
1511                                         }\r
1512                                 }\r
1513 \r
1514                                 if (containsStringcolumns)\r
1515                                         index.Reset ();\r
1516                         }\r
1517                 }\r
1518 \r
1519                 internal void DropIndex (Index index)\r
1520                 {\r
1521                         if (index != null && index.RefCount == 0) {\r
1522                                 _indexes.Remove (index);\r
1523                         }\r
1524                 }\r
1525 \r
1526                 internal void DropReferencedIndexes (DataColumn column)\r
1527                 {\r
1528                         if (_indexes != null)\r
1529                                 for (int i = _indexes.Count - 1; i >= 0; i--) {\r
1530                                         Index indx = (Index)_indexes [i];\r
1531                                         if (indx.Key.DependsOn (column))\r
1532                                                 _indexes.Remove (indx);\r
1533                                 }\r
1534                 }\r
1535 \r
1536                 internal void AddRowToIndexes (DataRow row)\r
1537                 {\r
1538                         if (_indexes != null) {\r
1539                                 for (int i = 0; i < _indexes.Count; ++i)\r
1540                                         ((Index)_indexes [i]).Add (row);\r
1541                         }\r
1542                 }\r
1543 \r
1544                 internal void DeleteRowFromIndexes (DataRow row)\r
1545                 {\r
1546                         if (_indexes != null) {\r
1547                                 foreach (Index indx in _indexes)\r
1548                                         indx.Delete (row);\r
1549                         }\r
1550                 }\r
1551 \r
1552                 /// <summary>\r
1553                 /// Gets the TableName and DisplayExpression, if\r
1554                 /// there is one as a concatenated string.\r
1555                 /// </summary>\r
1556                 public override string ToString ()\r
1557                 {\r
1558                         //LAMESPEC: spec says concat the two. impl puts a\r
1559                         //plus sign infront of DisplayExpression\r
1560                         string retVal = TableName;\r
1561                         if(DisplayExpression != null && DisplayExpression != "")\r
1562                                 retVal += " + " + DisplayExpression;\r
1563                         return retVal;\r
1564                 }\r
1565 \r
1566                 #region Events\r
1567 \r
1568                 /// <summary>\r
1569                 /// Raises the ColumnChanged event.\r
1570                 /// </summary>\r
1571                 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e)\r
1572                 {\r
1573                         if (null != ColumnChanged)\r
1574                                 ColumnChanged (this, e);\r
1575                 }\r
1576 \r
1577                 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e)\r
1578                 {\r
1579                         OnColumnChanged (e);\r
1580                 }\r
1581 \r
1582                 /// <summary>\r
1583                 /// Raises the ColumnChanging event.\r
1584                 /// </summary>\r
1585                 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e)\r
1586                 {\r
1587                         if (null != ColumnChanging)\r
1588                                 ColumnChanging (this, e);\r
1589                 }\r
1590 \r
1591                 internal void RaiseOnColumnChanging (DataColumnChangeEventArgs e)\r
1592                 {\r
1593                         OnColumnChanging(e);\r
1594                 }\r
1595 \r
1596                 /// <summary>\r
1597                 /// Raises the PropertyChanging event.\r
1598                 /// </summary>\r
1599                 [MonoTODO]\r
1600                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)\r
1601                 {\r
1602                         //if (null != PropertyChanging)\r
1603                         //{\r
1604                         //      PropertyChanging (this, pcevent);\r
1605                         //}\r
1606                         throw new NotImplementedException ();\r
1607                 }\r
1608 \r
1609                 /// <summary>\r
1610                 /// Notifies the DataTable that a DataColumn is being removed.\r
1611                 /// </summary>\r
1612                 protected internal virtual void OnRemoveColumn (DataColumn column)\r
1613                 {\r
1614                         DropReferencedIndexes (column);\r
1615                 }\r
1616 \r
1617                 /// <summary>\r
1618                 /// Raises the RowChanged event.\r
1619                 /// </summary>\r
1620                 protected virtual void OnRowChanged (DataRowChangeEventArgs e)\r
1621                 {\r
1622                         if (null != RowChanged)\r
1623                                 RowChanged (this, e);\r
1624                 }\r
1625 \r
1626 \r
1627                 /// <summary>\r
1628                 /// Raises the RowChanging event.\r
1629                 /// </summary>\r
1630                 protected virtual void OnRowChanging (DataRowChangeEventArgs e)\r
1631                 {\r
1632                         if (null != RowChanging)\r
1633                                 RowChanging (this, e);\r
1634                 }\r
1635 \r
1636                 /// <summary>\r
1637                 /// Raises the RowDeleted event.\r
1638                 /// </summary>\r
1639                 protected virtual void OnRowDeleted (DataRowChangeEventArgs e)\r
1640                 {\r
1641                         if (null != RowDeleted)\r
1642                                 RowDeleted (this, e);\r
1643                 }\r
1644 \r
1645                 /// <summary>\r
1646                 /// Raises the RowDeleting event.\r
1647                 /// </summary>\r
1648                 protected virtual void OnRowDeleting (DataRowChangeEventArgs e)\r
1649                 {\r
1650                         if (null != RowDeleting)\r
1651                                 RowDeleting (this, e);\r
1652                 }\r
1653 \r
1654                 /// <summary>\r
1655                 /// Occurs when after a value has been changed for\r
1656                 /// the specified DataColumn in a DataRow.\r
1657                 /// </summary>\r
1658                 [DataCategory ("Data")]\r
1659 #if !NET_2_0\r
1660                 [DataSysDescription ("Occurs when a value has been changed for this column.")]\r
1661 #endif\r
1662                 public event DataColumnChangeEventHandler ColumnChanged;\r
1663 \r
1664                 /// <summary>\r
1665                 /// Occurs when a value is being changed for the specified\r
1666                 /// DataColumn in a DataRow.\r
1667                 /// </summary>\r
1668                 [DataCategory ("Data")]\r
1669 #if !NET_2_0\r
1670                 [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
1671 #endif\r
1672                 public event DataColumnChangeEventHandler ColumnChanging;\r
1673 \r
1674                 /// <summary>\r
1675                 /// Occurs after a DataRow has been changed successfully.\r
1676                 /// </summary>\r
1677                 [DataCategory ("Data")]\r
1678 #if !NET_2_0\r
1679                 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]\r
1680 #endif\r
1681                 public event DataRowChangeEventHandler RowChanged;\r
1682 \r
1683                 /// <summary>\r
1684                 /// Occurs when a DataRow is changing.\r
1685                 /// </summary>\r
1686                 [DataCategory ("Data")]\r
1687 #if !NET_2_0\r
1688                 [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
1689 #endif\r
1690                 public event DataRowChangeEventHandler RowChanging;\r
1691 \r
1692                 /// <summary>\r
1693                 /// Occurs after a row in the table has been deleted.\r
1694                 /// </summary>\r
1695                 [DataCategory ("Data")]\r
1696 #if !NET_2_0\r
1697                 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]\r
1698 #endif\r
1699                 public event DataRowChangeEventHandler RowDeleted;\r
1700 \r
1701                 /// <summary>\r
1702                 /// Occurs before a row in the table is about to be deleted.\r
1703                 /// </summary>\r
1704                 [DataCategory ("Data")]\r
1705 #if !NET_2_0\r
1706                 [DataSysDescription ("Occurs when a row in the table marked for deletion.  Throw an exception to cancel the deletion.")]\r
1707 #endif\r
1708                 public event DataRowChangeEventHandler RowDeleting;\r
1709 \r
1710                 #endregion // Events\r
1711 \r
1712                 internal static DataColumn[] ParseSortString (DataTable table, string sort, out ListSortDirection[] sortDirections, bool rejectNoResult)\r
1713                 {\r
1714                         DataColumn[] sortColumns = _emptyColumnArray;\r
1715                         sortDirections = null;\r
1716 \r
1717                         ArrayList columns = null;\r
1718                         ArrayList sorts = null;\r
1719 \r
1720                         if (sort != null && !sort.Equals ("")) {\r
1721                                 columns = new ArrayList ();\r
1722                                 sorts = new ArrayList();\r
1723                                 string[] columnExpression = sort.Trim ().Split (new char[1] {','});\r
1724 \r
1725                                 for (int c = 0; c < columnExpression.Length; c++) {\r
1726                                         string rawColumnName = columnExpression[c].Trim ();\r
1727 \r
1728                                         Match match = SortRegex.Match (rawColumnName);\r
1729                                         Group g = match.Groups["ColName"] ;\r
1730                                         if (!g.Success)\r
1731                                                 throw new IndexOutOfRangeException ("Could not find column: " + rawColumnName);\r
1732 \r
1733                                         string columnName = g.Value;\r
1734                                         DataColumn dc = table.Columns[columnName];\r
1735                                         if (dc == null){\r
1736                                                 try {\r
1737                                                         dc = table.Columns[Int32.Parse (columnName)];\r
1738                                                 } catch (FormatException) {\r
1739                                                         throw new IndexOutOfRangeException("Cannot find column " + columnName);\r
1740                                                 }\r
1741                                         }\r
1742                                         columns.Add (dc);\r
1743 \r
1744                                         g = match.Groups["Order"];\r
1745                                         if (!g.Success || String.Compare (g.Value, "ASC", true, CultureInfo.InvariantCulture) == 0)\r
1746                                                 sorts.Add(ListSortDirection.Ascending);\r
1747                                         else\r
1748                                                 sorts.Add (ListSortDirection.Descending);\r
1749                                 }\r
1750 \r
1751                                 sortColumns = (DataColumn[]) columns.ToArray (typeof (DataColumn));\r
1752                                 sortDirections = new ListSortDirection[sorts.Count];\r
1753                                 for (int i = 0; i < sortDirections.Length; i++)\r
1754                                         sortDirections[i] = (ListSortDirection)sorts[i];\r
1755                         }\r
1756 \r
1757                         if (rejectNoResult) {\r
1758                                 if (sortColumns == null)\r
1759                                         throw new SystemException ("sort expression result is null");\r
1760                                 if (sortColumns.Length == 0)\r
1761                                         throw new SystemException("sort expression result is 0");\r
1762                         }\r
1763 \r
1764                         return sortColumns;\r
1765                 }\r
1766 \r
1767                 private void UpdatePropertyDescriptorsCache ()\r
1768                 {\r
1769                         PropertyDescriptor[] descriptors = new PropertyDescriptor[Columns.Count + ChildRelations.Count];\r
1770                         int index = 0;\r
1771                         foreach (DataColumn col in Columns)\r
1772                                 descriptors [index++] = new DataColumnPropertyDescriptor (col);\r
1773 \r
1774                         foreach (DataRelation rel in ChildRelations)\r
1775                                 descriptors [index++] = new DataRelationPropertyDescriptor (rel);\r
1776 \r
1777                         _propertyDescriptorsCache = new PropertyDescriptorCollection (descriptors);\r
1778                 }\r
1779 \r
1780                 internal PropertyDescriptorCollection GetPropertyDescriptorCollection()\r
1781                 {\r
1782                         if (_propertyDescriptorsCache == null)\r
1783                                 UpdatePropertyDescriptorsCache ();\r
1784                         return _propertyDescriptorsCache;\r
1785                 }\r
1786 \r
1787                 internal void ResetPropertyDescriptorsCache ()\r
1788                 {\r
1789                         _propertyDescriptorsCache = null;\r
1790                 }\r
1791 \r
1792                 internal void SetRowsID()\r
1793                 {\r
1794                         int dataRowID = 0;\r
1795                         foreach (DataRow row in Rows) {\r
1796                                 row.XmlRowID = dataRowID;\r
1797                                 dataRowID++;\r
1798                         }\r
1799                 }\r
1800         }\r
1801 \r
1802 #if NET_2_0\r
1803         [XmlSchemaProvider ("GetDataTableSchema")]\r
1804         partial class DataTable : IXmlSerializable {\r
1805                 [MonoNotSupported ("")]\r
1806                 XmlSchema IXmlSerializable.GetSchema ()\r
1807                 {\r
1808                         return GetSchema ();\r
1809                 }\r
1810 \r
1811                 void IXmlSerializable.ReadXml (XmlReader reader)\r
1812                 {\r
1813                         ReadXml_internal (reader, true);\r
1814                 }\r
1815 \r
1816                 void IXmlSerializable.WriteXml (XmlWriter writer)\r
1817                 {\r
1818                         DataSet dset = dataSet;\r
1819                         bool isPartOfDataSet = true;\r
1820 \r
1821                         if (dataSet == null) {\r
1822                                 dset = new DataSet ();\r
1823                                 dset.Tables.Add (this);\r
1824                                 isPartOfDataSet = false;\r
1825                         }\r
1826 \r
1827                         XmlSchemaWriter.WriteXmlSchema (writer, new DataTable [] { this },\r
1828                                                                                         null, TableName, dset.DataSetName, LocaleSpecified ? Locale : dset.LocaleSpecified ? dset.Locale : null);\r
1829                         dset.WriteIndividualTableContent (writer, this, XmlWriteMode.DiffGram);\r
1830                         writer.Flush ();\r
1831 \r
1832                         if (!isPartOfDataSet)\r
1833                                 dataSet.Tables.Remove(this);\r
1834                 }\r
1835 \r
1836                 [MonoTODO]\r
1837                 protected virtual XmlSchema GetSchema ()\r
1838                 {\r
1839                         throw new NotImplementedException ();\r
1840                 }\r
1841 \r
1842                 public static XmlSchemaComplexType GetDataTableSchema (XmlSchemaSet schemaSet)\r
1843                 {\r
1844                         return new XmlSchemaComplexType ();\r
1845                 }\r
1846 \r
1847                 public XmlReadMode ReadXml (Stream stream)\r
1848                 {\r
1849                         return ReadXml (new XmlTextReader(stream, null));\r
1850                 }\r
1851 \r
1852                 public XmlReadMode ReadXml (string fileName)\r
1853                 {\r
1854                         XmlReader reader = new XmlTextReader (fileName);\r
1855                         try {\r
1856                                 return ReadXml (reader);\r
1857                         } finally {\r
1858                                 reader.Close();\r
1859                         }\r
1860                 }\r
1861 \r
1862                 public XmlReadMode ReadXml (TextReader reader)\r
1863                 {\r
1864                         return ReadXml (new XmlTextReader (reader));\r
1865                 }\r
1866 \r
1867                 public XmlReadMode ReadXml (XmlReader reader)\r
1868                 {\r
1869                         return ReadXml_internal (reader, false);\r
1870                 }\r
1871 \r
1872                 public XmlReadMode ReadXml_internal (XmlReader reader, bool serializable)\r
1873                 {\r
1874                         // The documentation from MS for this method is rather\r
1875                         // poor.  The following cases have been observed\r
1876                         // during testing:\r
1877                         //\r
1878                         //     Reading a table from XML may create a DataSet to\r
1879                         //     store child tables.\r
1880                         //\r
1881                         //     If the table has at least one column present,\r
1882                         //     we do not require the schema to be present in\r
1883                         //     the xml.  If the table has no columns, neither\r
1884                         //     regular data nor diffgrams will be read, but\r
1885                         //     will throw an error indicating that schema\r
1886                         //     will not be inferred.\r
1887                         //\r
1888                         //     We will likely need to take advantage of the\r
1889                         //     msdata:MainDataTable attribute added to the\r
1890                         //     schema info to load into the appropriate\r
1891                         //     locations.\r
1892                         bool isPartOfDataSet = true;\r
1893                         bool isTableNameBlank = false;\r
1894                         XmlReadMode mode = XmlReadMode.ReadSchema;\r
1895                         DataSet dataSet = null;\r
1896                         DataSet ds = new DataSet ();\r
1897 \r
1898                         reader.MoveToContent ();\r
1899                         if (Columns.Count > 0 && reader.LocalName != "diffgram" || serializable)\r
1900                                 mode = ds.ReadXml (reader);\r
1901                         else if (Columns.Count > 0 && reader.LocalName == "diffgram") {\r
1902                                   try {\r
1903                                         if (TableName == String.Empty)\r
1904                                                 isTableNameBlank = true;\r
1905                                         if (DataSet == null) {\r
1906                                                 isPartOfDataSet = false;\r
1907                                                 ds.Tables.Add (this);\r
1908                                                 mode = ds.ReadXml (reader);\r
1909                                         } else\r
1910                                                 mode = DataSet.ReadXml (reader);\r
1911                                   } catch (DataException) {\r
1912                                         mode = XmlReadMode.DiffGram;\r
1913                                         if (isTableNameBlank)\r
1914                                                 TableName = String.Empty;\r
1915                                   } finally {\r
1916                                         if (!isPartOfDataSet)\r
1917                                                 ds.Tables.Remove (this);\r
1918                                   }\r
1919                                   return mode;\r
1920                         } else {\r
1921                                 mode = ds.ReadXml (reader, XmlReadMode.ReadSchema);\r
1922                         }\r
1923                         if (mode == XmlReadMode.InferSchema)\r
1924                                 mode = XmlReadMode.IgnoreSchema;\r
1925                         if (DataSet == null) {\r
1926                                   isPartOfDataSet = false;\r
1927                                   dataSet = new DataSet ();\r
1928                                   if (TableName == String.Empty)\r
1929                                         isTableNameBlank = true;\r
1930                                   dataSet.Tables.Add (this);\r
1931                         }\r
1932 \r
1933                         DenyXmlResolving (this, ds, mode, isTableNameBlank, isPartOfDataSet);\r
1934                         if (Columns.Count > 0 && TableName != ds.Tables [0].TableName) {\r
1935                                 if (isPartOfDataSet == false)\r
1936                                         dataSet.Tables.Remove (this);\r
1937 \r
1938                                 if (isTableNameBlank && isPartOfDataSet == false)\r
1939                                         TableName = String.Empty;\r
1940                                 return mode;\r
1941                         }\r
1942                         else {\r
1943                                 TableName = ds.Tables [0].TableName;\r
1944                         }\r
1945 \r
1946                         if (!isPartOfDataSet) {\r
1947                                 if (Columns.Count > 0) {\r
1948                                         dataSet.Merge (ds, true, MissingSchemaAction.Ignore);\r
1949                                 } else {\r
1950                                         dataSet.Merge (ds, true, MissingSchemaAction.AddWithKey);\r
1951                                 }\r
1952                                 if (ChildRelations.Count == 0) {\r
1953                                         dataSet.Tables.Remove (this);\r
1954                                 } else {\r
1955                                         dataSet.DataSetName = ds.DataSetName;\r
1956                                 }\r
1957                         } else {\r
1958                                 if (Columns.Count > 0) {\r
1959                                         DataSet.Merge (ds, true, MissingSchemaAction.Ignore);\r
1960                                 } else {\r
1961                                         DataSet.Merge (ds, true, MissingSchemaAction.AddWithKey);\r
1962                                 }\r
1963                         }\r
1964                         return mode;\r
1965                 }\r
1966 \r
1967                 private void DenyXmlResolving (DataTable table, DataSet ds, XmlReadMode mode, bool isTableNameBlank, bool isPartOfDataSet)\r
1968                 {\r
1969                         if (ds.Tables.Count == 0 && table.Columns.Count == 0)\r
1970                                 throw new InvalidOperationException ("DataTable does not support schema inference from XML");\r
1971 \r
1972                         if (table.Columns.Count == 0 && ds.Tables [0].TableName != table.TableName && isTableNameBlank == false)\r
1973                                 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source",\r
1974                                                                             table.TableName));\r
1975 \r
1976                         if (table.Columns.Count > 0 && ds.Tables [0].TableName != table.TableName &&\r
1977                             isTableNameBlank == false && mode == XmlReadMode.ReadSchema &&\r
1978                             isPartOfDataSet == false)\r
1979                                 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source",\r
1980                                                                             table.TableName));\r
1981 \r
1982                         if (isPartOfDataSet == true && table.Columns.Count > 0 &&\r
1983                             mode == XmlReadMode.ReadSchema && table.TableName != ds.Tables [0].TableName)\r
1984                                 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source",\r
1985                                                                             table.TableName));\r
1986                 }\r
1987 \r
1988                 public void ReadXmlSchema (Stream stream)\r
1989                 {\r
1990                         ReadXmlSchema (new XmlTextReader (stream));\r
1991                 }\r
1992 \r
1993                 public void ReadXmlSchema (TextReader reader)\r
1994                 {\r
1995                         ReadXmlSchema (new XmlTextReader (reader));\r
1996                 }\r
1997 \r
1998                 public void ReadXmlSchema (string fileName)\r
1999                 {\r
2000                         XmlTextReader reader = null;\r
2001                         try {\r
2002                                 reader = new XmlTextReader (fileName);\r
2003                                 ReadXmlSchema (reader);\r
2004                         } finally {\r
2005                                 if (reader != null)\r
2006                                         reader.Close ();\r
2007                         }\r
2008                 }\r
2009 \r
2010                 public void ReadXmlSchema (XmlReader reader)\r
2011                 {\r
2012                         if (this.Columns.Count > 0)\r
2013                                 return;\r
2014 \r
2015                         DataSet ds = new DataSet ();\r
2016                         new XmlSchemaDataImporter (ds, reader, false).Process ();\r
2017                         DataTable target = null;\r
2018                         if (TableName == String.Empty) {\r
2019                                 if (ds.Tables.Count > 0)\r
2020                                         target = ds.Tables [0];\r
2021                         }\r
2022                         else {\r
2023                                 target = ds.Tables [TableName];\r
2024                                 if (target == null)\r
2025                                         throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source.", TableName));\r
2026                         }\r
2027                         if (target != null)\r
2028                                 target.CopyProperties (this);\r
2029                 }\r
2030 \r
2031                 [MonoNotSupported ("")]\r
2032                 protected virtual void ReadXmlSerializable (XmlReader reader)\r
2033                 {\r
2034                         throw new NotImplementedException ();\r
2035                 }\r
2036 \r
2037                 private XmlWriterSettings GetWriterSettings ()\r
2038                 {\r
2039                         XmlWriterSettings s = new XmlWriterSettings ();\r
2040                         s.Indent = true;\r
2041                         s.OmitXmlDeclaration = true;\r
2042                         return s;\r
2043                 }\r
2044 \r
2045                 public void WriteXml (Stream stream)\r
2046                 {\r
2047                         WriteXml (stream, XmlWriteMode.IgnoreSchema, false);\r
2048                 }\r
2049 \r
2050                 public void WriteXml (TextWriter writer)\r
2051                 {\r
2052                         WriteXml (writer, XmlWriteMode.IgnoreSchema, false);\r
2053                 }\r
2054 \r
2055                 public void WriteXml (XmlWriter writer)\r
2056                 {\r
2057                         WriteXml (writer, XmlWriteMode.IgnoreSchema, false);\r
2058                 }\r
2059 \r
2060                 public void WriteXml (string fileName)\r
2061                 {\r
2062                         WriteXml (fileName, XmlWriteMode.IgnoreSchema, false);\r
2063                 }\r
2064 \r
2065                 public void WriteXml (Stream stream, XmlWriteMode mode)\r
2066                 {\r
2067                         WriteXml (stream, mode, false);\r
2068                 }\r
2069 \r
2070                 public void WriteXml (TextWriter writer, XmlWriteMode mode)\r
2071                 {\r
2072                         WriteXml (writer, mode, false);\r
2073                 }\r
2074 \r
2075                 public void WriteXml (XmlWriter writer, XmlWriteMode mode)\r
2076                 {\r
2077                         WriteXml (writer, mode, false);\r
2078                 }\r
2079 \r
2080                 public void WriteXml (string fileName, XmlWriteMode mode)\r
2081                 {\r
2082                         WriteXml (fileName, mode, false);\r
2083                 }\r
2084 \r
2085                 public void WriteXml (Stream stream, bool writeHierarchy)\r
2086                 {\r
2087                         WriteXml (stream, XmlWriteMode.IgnoreSchema, writeHierarchy);\r
2088                 }\r
2089 \r
2090                 public void WriteXml (string fileName, bool writeHierarchy)\r
2091                 {\r
2092                         WriteXml (fileName, XmlWriteMode.IgnoreSchema, writeHierarchy);\r
2093                 }\r
2094 \r
2095                 public void WriteXml (TextWriter writer, bool writeHierarchy)\r
2096                 {\r
2097                         WriteXml (writer, XmlWriteMode.IgnoreSchema, writeHierarchy);\r
2098                 }\r
2099 \r
2100                 public void WriteXml (XmlWriter writer, bool writeHierarchy)\r
2101                 {\r
2102                         WriteXml (writer, XmlWriteMode.IgnoreSchema, writeHierarchy);\r
2103                 }\r
2104 \r
2105                 public void WriteXml (Stream stream, XmlWriteMode mode, bool writeHierarchy)\r
2106                 {\r
2107                         WriteXml (XmlWriter.Create (stream, GetWriterSettings ()), mode, writeHierarchy);\r
2108                 }\r
2109 \r
2110                 public void WriteXml (string fileName, XmlWriteMode mode, bool writeHierarchy)\r
2111                 {\r
2112                         XmlWriter xw = null;\r
2113                         try {\r
2114                                 xw = XmlWriter.Create (fileName, GetWriterSettings ());\r
2115                                 WriteXml (xw, mode, writeHierarchy);\r
2116                         } finally {\r
2117                                 if (xw != null)\r
2118                                         xw.Close ();\r
2119                         }\r
2120                 }\r
2121 \r
2122                 public void WriteXml (TextWriter writer, XmlWriteMode mode, bool writeHierarchy)\r
2123                 {\r
2124                         WriteXml (XmlWriter.Create (writer, GetWriterSettings ()), mode, writeHierarchy);\r
2125                 }\r
2126 \r
2127                 public void WriteXml (XmlWriter writer, XmlWriteMode mode, bool writeHierarchy)\r
2128                 {\r
2129                         // If we're in mode XmlWriteMode.WriteSchema, we need to output an extra\r
2130                         // msdata:MainDataTable attribute that wouldn't normally be part of the\r
2131                         // DataSet WriteXml output.\r
2132                         //\r
2133                         // For the writeHierarchy == true case, we write what would be output by\r
2134                         // a DataSet write, but we limit ourselves to our table and its descendants.\r
2135                         //\r
2136                         // For the writeHierarchy == false case, we write what would be output by\r
2137                         // a DataSet write, but we limit ourselves to this table.\r
2138                         //\r
2139                         // If the table is not in a DataSet, we follow the following behaviour:\r
2140                         //   For WriteSchema cases, we do a write as if there is a wrapper\r
2141                         //   dataset called NewDataSet.\r
2142                         //   For IgnoreSchema or DiffGram cases, we do a write as if there\r
2143                         //   is a wrapper dataset called DocumentElement.\r
2144 \r
2145                         // Generate a list of tables to write.\r
2146                         List <DataTable> tables = new List <DataTable> ();\r
2147                         if (writeHierarchy == false)\r
2148                                 tables.Add (this);\r
2149                         else\r
2150                                 FindAllChildren (tables, this);\r
2151 \r
2152                         // If we're in a DataSet, generate a list of relations to write.\r
2153                         List <DataRelation> relations = new List <DataRelation> ();\r
2154                         if (DataSet != null) {\r
2155                                 foreach (DataRelation relation in DataSet.Relations) {\r
2156                                         if (tables.Contains (relation.ParentTable) &&\r
2157                                            tables.Contains (relation.ChildTable))\r
2158                                                 relations.Add (relation);\r
2159                                 }\r
2160                         }\r
2161 \r
2162                         // Add the msdata:MainDataTable info if we're writing schema data.\r
2163                         string mainDataTable = null;\r
2164                         if (mode == XmlWriteMode.WriteSchema)\r
2165                                 mainDataTable = this.TableName;\r
2166 \r
2167                         // Figure out the DataSet name.\r
2168                         string dataSetName = null;\r
2169                         if (DataSet != null)\r
2170                                 dataSetName = DataSet.DataSetName;\r
2171                         else if (DataSet == null && mode == XmlWriteMode.WriteSchema)\r
2172                                 dataSetName = "NewDataSet";\r
2173                         else\r
2174                                 dataSetName = "DocumentElement";\r
2175 \r
2176                         XmlTableWriter.WriteTables(writer, mode, tables, relations, mainDataTable, dataSetName);\r
2177                 }\r
2178 \r
2179                 private void FindAllChildren(List<DataTable> list, DataTable root)\r
2180                 {\r
2181                         if (!list.Contains(root))\r
2182                         {\r
2183                                 list.Add(root);\r
2184                                 foreach (DataRelation relation in root.ChildRelations)\r
2185                                 {\r
2186                                         FindAllChildren(list, relation.ChildTable);\r
2187                                 }\r
2188                         }\r
2189                 }\r
2190 \r
2191                 public void WriteXmlSchema (Stream stream)\r
2192                 {\r
2193                         if (TableName == "") {\r
2194                                 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");\r
2195                         }\r
2196                         XmlWriterSettings s = GetWriterSettings ();\r
2197                         s.OmitXmlDeclaration = false;\r
2198                         WriteXmlSchema (XmlWriter.Create (stream, s));\r
2199                 }\r
2200 \r
2201                 public void WriteXmlSchema (TextWriter writer)\r
2202                 {\r
2203                         if (TableName == "") {\r
2204                                 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");\r
2205                         }\r
2206                         XmlWriterSettings s = GetWriterSettings ();\r
2207                         s.OmitXmlDeclaration = false;\r
2208                         WriteXmlSchema (XmlWriter.Create (writer, s));\r
2209                 }\r
2210 \r
2211                 public void WriteXmlSchema (XmlWriter writer)\r
2212                 {\r
2213                         WriteXmlSchema (writer, false);\r
2214 /*\r
2215                         if (TableName == "") {\r
2216                                 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");\r
2217                         }\r
2218                         DataSet ds = DataSet;\r
2219                         DataSet tmp = null;\r
2220                         try {\r
2221                                 if (ds == null) {\r
2222                                         tmp = ds = new DataSet ();\r
2223                                         ds.Tables.Add (this);\r
2224                                 }\r
2225                                 writer.WriteStartDocument ();\r
2226                                 DataTableCollection col = new DataTableCollection (ds);\r
2227                                 col.Add (this);\r
2228                                 DataTable [] tables = new DataTable [col.Count];\r
2229                                 for (int i = 0; i < col.Count; i++) tables[i] = col[i];\r
2230                                 string tableName;\r
2231                                 if (ds.Namespace == "")\r
2232                                         tableName = TableName;\r
2233                                 else\r
2234                                         tableName = ds.Namespace + "_x003A_" + TableName;\r
2235                                 XmlSchemaWriter.WriteXmlSchema (writer, tables, null, tableName, ds.DataSetName);\r
2236                         } finally {\r
2237                                 if (tmp != null)\r
2238                                         ds.Tables.Remove (this);\r
2239                         }\r
2240 */\r
2241                 }\r
2242 \r
2243                 public void WriteXmlSchema (string fileName)\r
2244                 {\r
2245                         if (fileName == "") {\r
2246                                 throw new ArgumentException ("Empty path name is not legal.");\r
2247                         }\r
2248                         if (TableName == "") {\r
2249                                 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");\r
2250                         }\r
2251                         XmlTextWriter writer = null;\r
2252                         try {\r
2253                                 XmlWriterSettings s = GetWriterSettings ();\r
2254                                 s.OmitXmlDeclaration = false;\r
2255                                 writer = new XmlTextWriter (fileName, null);\r
2256                                 WriteXmlSchema (writer);\r
2257                         } finally {\r
2258                                 if (writer != null) {\r
2259                                         writer.Close ();\r
2260                                 }\r
2261                         }\r
2262                 }\r
2263 \r
2264                 public void WriteXmlSchema (Stream stream, bool writeHierarchy)\r
2265                 {\r
2266                         if (TableName == "") {\r
2267                                 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");\r
2268                         }\r
2269                         XmlWriterSettings s = GetWriterSettings ();\r
2270                         s.OmitXmlDeclaration = false;\r
2271                         WriteXmlSchema (XmlWriter.Create (stream, s), writeHierarchy);\r
2272                 }\r
2273 \r
2274                 public void WriteXmlSchema (TextWriter writer, bool writeHierarchy)\r
2275                 {\r
2276                         if (TableName == "") {\r
2277                                 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");\r
2278                         }\r
2279                         XmlWriterSettings s = GetWriterSettings ();\r
2280                         s.OmitXmlDeclaration = false;\r
2281                         WriteXmlSchema (XmlWriter.Create (writer, s), writeHierarchy);\r
2282                 }\r
2283 \r
2284                 public void WriteXmlSchema (XmlWriter writer, bool writeHierarchy)\r
2285                 {\r
2286                         if (TableName == "") {\r
2287                                 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");\r
2288                         }\r
2289 //                      if (writeHierarchy == false) {\r
2290 //                              WriteXmlSchema (writer);\r
2291 //                      }\r
2292 //                      else {\r
2293                                 DataSet ds = DataSet;\r
2294                                 DataSet tmp = null;\r
2295                                 try {\r
2296                                         if (ds == null) {\r
2297                                                 tmp = ds = new DataSet ();\r
2298                                                 ds.Tables.Add (this);\r
2299                                         }\r
2300                                         writer.WriteStartDocument ();\r
2301                                         //XmlSchemaWriter.WriteXmlSchema (ds, writer);\r
2302                                         //DataTable [] tables = new DataTable [ds.Tables.Count];\r
2303                                         DataTable [] tables = null;\r
2304                                         //DataRelation [] relations =  new DataRelation [ds.Relations.Count];\r
2305                                         //for (int i = 0; i < ds.Relations.Count; i++) {\r
2306                                         //      relations[i] = ds.Relations[i];\r
2307                                         //}\r
2308                                         DataRelation [] relations = null;\r
2309                                         if (writeHierarchy && ChildRelations.Count > 0) {\r
2310                                                 relations = new DataRelation [ChildRelations.Count];\r
2311                                                 for (int i = 0; i < ChildRelations.Count; i++) {\r
2312                                                         relations [i] = ChildRelations [i];\r
2313                                                 }\r
2314                                                 tables = new DataTable [ds.Tables.Count];\r
2315                                                 for (int i = 0; i < ds.Tables.Count; i++) tables [i] = ds.Tables [i];\r
2316                                         } else {\r
2317                                                 tables = new DataTable [1];\r
2318                                                 tables [0] = this;\r
2319                                         }\r
2320 \r
2321                                         string tableName;\r
2322                                         if (ds.Namespace == "")\r
2323                                                 tableName = TableName;\r
2324                                         else\r
2325                                                 tableName = ds.Namespace + "_x003A_" + TableName;\r
2326                                         XmlSchemaWriter.WriteXmlSchema (writer, tables, relations, tableName, ds.DataSetName, LocaleSpecified ? Locale : ds.LocaleSpecified ? ds.Locale : null);\r
2327                                 } finally {\r
2328                                         if (tmp != null)\r
2329                                                 ds.Tables.Remove (this);\r
2330                                 }\r
2331 //                      }\r
2332                 }\r
2333 \r
2334                 public void WriteXmlSchema (string fileName, bool writeHierarchy)\r
2335                 {\r
2336                         if (fileName == "") {\r
2337                                 throw new ArgumentException ("Empty path name is not legal.");\r
2338                         }\r
2339                         if (TableName == "") {\r
2340                                 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");\r
2341                         }\r
2342                         XmlTextWriter writer = null;\r
2343                         try {\r
2344                                 XmlWriterSettings s = GetWriterSettings ();\r
2345                                 s.OmitXmlDeclaration = false;\r
2346                                 writer = new XmlTextWriter (fileName, null);\r
2347                                 WriteXmlSchema (writer, writeHierarchy);\r
2348                         } finally {\r
2349                                 if (writer != null) {\r
2350                                         writer.Close ();\r
2351                                 }\r
2352                         }\r
2353                 }\r
2354         }\r
2355 \r
2356         partial class DataTable : ISupportInitializeNotification {\r
2357                 private bool tableInitialized = true;\r
2358 \r
2359                 [Browsable (false)]\r
2360                 public bool IsInitialized {\r
2361                         get { return tableInitialized; }\r
2362                 }\r
2363 \r
2364                 public event EventHandler Initialized;\r
2365 \r
2366                 private void OnTableInitialized (EventArgs e)\r
2367                 {\r
2368                         if (null != Initialized)\r
2369                                 Initialized (this, e);\r
2370                 }\r
2371 \r
2372                 partial void DataTableInitialized ()\r
2373                 {\r
2374                         tableInitialized = true;\r
2375                         OnTableInitialized (new EventArgs ());\r
2376                 }\r
2377         }\r
2378 \r
2379         partial class DataTable {\r
2380                 public DataTable (string tableName, string tbNamespace)\r
2381                         : this (tableName)\r
2382                 {\r
2383                         _nameSpace = tbNamespace;\r
2384                 }\r
2385 \r
2386                 SerializationFormat remotingFormat = SerializationFormat.Xml;\r
2387                 [DefaultValue (SerializationFormat.Xml)]\r
2388                 public SerializationFormat RemotingFormat {\r
2389                         get {\r
2390                                 if (dataSet != null)\r
2391                                         remotingFormat = dataSet.RemotingFormat;\r
2392                                 return remotingFormat;\r
2393                         }\r
2394                         set {\r
2395                                 if (dataSet != null)\r
2396                                         throw new ArgumentException ("Cannot have different remoting format property value for DataSet and DataTable");\r
2397                                 remotingFormat = value;\r
2398                         }\r
2399                 }\r
2400 \r
2401                 internal void DeserializeConstraints (ArrayList arrayList)\r
2402                 {\r
2403                         bool fKeyNavigate = false;\r
2404                         for (int i = 0; i < arrayList.Count; i++) {\r
2405                                 ArrayList tmpArrayList = arrayList [i] as ArrayList;\r
2406                                 if (tmpArrayList == null)\r
2407                                         continue;\r
2408                                 if ((string) tmpArrayList [0] == "F") {\r
2409                                         int [] constraintsArray = tmpArrayList [2] as int [];\r
2410 \r
2411                                         if (constraintsArray == null)\r
2412                                                 continue;\r
2413                                         ArrayList parentColumns = new ArrayList ();\r
2414                                         DataTable dt = dataSet.Tables [constraintsArray [0]];\r
2415                                         for (int j = 0; j < constraintsArray.Length - 1; j++) {\r
2416                                                 parentColumns.Add (dt.Columns [constraintsArray [j + 1]]);\r
2417                                         }\r
2418 \r
2419                                         constraintsArray = tmpArrayList [3] as int [];\r
2420 \r
2421                                         if (constraintsArray == null)\r
2422                                                 continue;\r
2423                                         ArrayList childColumns  = new ArrayList ();\r
2424                                         dt = dataSet.Tables [constraintsArray [0]];\r
2425                                         for (int j = 0; j < constraintsArray.Length - 1; j++) {\r
2426                                                 childColumns.Add (dt.Columns [constraintsArray [j + 1]]);\r
2427                                         }\r
2428 \r
2429                                         ForeignKeyConstraint fKeyConstraint = new\r
2430                                           ForeignKeyConstraint ((string) tmpArrayList [1],\r
2431                                                                 (DataColumn []) parentColumns.ToArray (typeof (DataColumn)),\r
2432                                                                 (DataColumn []) childColumns.ToArray (typeof (DataColumn)));\r
2433                                         Array ruleArray = (Array) tmpArrayList [4];\r
2434                                         fKeyConstraint.AcceptRejectRule = (AcceptRejectRule) ruleArray.GetValue (0);\r
2435                                         fKeyConstraint.UpdateRule = (Rule) ruleArray.GetValue (1);\r
2436                                         fKeyConstraint.DeleteRule = (Rule) ruleArray.GetValue (2);\r
2437                                         // FIXME: refactor this deserialization code out to ForeighKeyConstraint\r
2438                                         fKeyConstraint.SetExtendedProperties ((PropertyCollection) tmpArrayList [5]);\r
2439                                         Constraints.Add (fKeyConstraint);\r
2440                                         fKeyNavigate = true;\r
2441                                 } else if (fKeyNavigate == false &&\r
2442                                            (string) tmpArrayList [0] == "U") {\r
2443                                         UniqueConstraint unique = null;\r
2444                                         ArrayList uniqueDataColumns = new ArrayList ();\r
2445                                         int [] constraintsArray = tmpArrayList [2] as int [];\r
2446 \r
2447                                         if (constraintsArray == null)\r
2448                                                 continue;\r
2449 \r
2450                                         for (int j = 0; j < constraintsArray.Length; j++) {\r
2451                                                 uniqueDataColumns.Add (Columns [constraintsArray [j]]);\r
2452                                         }\r
2453                                         unique = new UniqueConstraint ((string) tmpArrayList[1],\r
2454                                                                        (DataColumn[]) uniqueDataColumns.\r
2455                                                                        ToArray (typeof (DataColumn)),\r
2456                                                                        (bool) tmpArrayList [3]);\r
2457                                         /*\r
2458                                           If UniqueConstraint already exist, don't add them\r
2459                                         */\r
2460                                         if (Constraints.IndexOf (unique) != -1 ||\r
2461                                             Constraints.IndexOf ((string) tmpArrayList[1]) != -1) {\r
2462                                                 continue;\r
2463                                         }\r
2464                                         // FIXME: refactor this deserialization code out to UniqueConstraint\r
2465                                         unique.SetExtendedProperties ((PropertyCollection) tmpArrayList [4]);\r
2466                                         Constraints.Add (unique);\r
2467                                 } else {\r
2468                                         fKeyNavigate = false;\r
2469                                 }\r
2470                         }\r
2471                 }\r
2472 \r
2473                 DataRowState GetCurrentRowState (BitArray rowStateBitArray, int i)\r
2474                 {\r
2475                         DataRowState currentRowState;\r
2476                         if (rowStateBitArray[i] == false &&\r
2477                             rowStateBitArray[i+1] == false &&\r
2478                             rowStateBitArray[i+2] == true)\r
2479                                 currentRowState = DataRowState.Detached;\r
2480                         else if (rowStateBitArray[i] == false &&\r
2481                                  rowStateBitArray[i+1] == false &&\r
2482                                  rowStateBitArray[i+2] == false)\r
2483                                 currentRowState = DataRowState.Unchanged;\r
2484                         else if (rowStateBitArray[i] == false &&\r
2485                                  rowStateBitArray[i+1] == true &&\r
2486                                  rowStateBitArray[i+2] == false)\r
2487                                 currentRowState = DataRowState.Added;\r
2488                         else if (rowStateBitArray[i] == true &&\r
2489                                  rowStateBitArray[i+1] == true &&\r
2490                                  rowStateBitArray[i+2] == false)\r
2491                                 currentRowState = DataRowState.Deleted;\r
2492                         else\r
2493                                 currentRowState = DataRowState.Modified;\r
2494                         return currentRowState;\r
2495                 }\r
2496 \r
2497                 internal void DeserializeRecords (ArrayList arrayList, ArrayList nullBits, BitArray rowStateBitArray)\r
2498                 {\r
2499                         BitArray  nullBit = null;\r
2500                         if (arrayList == null || arrayList.Count < 1)\r
2501                                 return;\r
2502                         int len = ((Array) arrayList [0]).Length;\r
2503                         object [] tmpArray = new object [arrayList.Count];\r
2504                         int k = 0;\r
2505                         DataRowState currentRowState;\r
2506                         for (int l = 0; l < len; l++) { // Columns\r
2507                                 currentRowState = GetCurrentRowState (rowStateBitArray, k *3);\r
2508                                 for (int j = 0; j < arrayList.Count; j++) { // Rows\r
2509                                         Array array = (Array) arrayList [j];\r
2510                                         nullBit = (BitArray) nullBits [j];\r
2511                                         if (nullBit [l] == false) {\r
2512                                                 tmpArray [j] = array.GetValue (l);\r
2513                                         } else {\r
2514                                                 tmpArray [j] = null;\r
2515                                         }\r
2516                                 }\r
2517                                 LoadDataRow (tmpArray, false);\r
2518                                 if (currentRowState == DataRowState.Modified) {\r
2519                                         Rows[k].AcceptChanges ();\r
2520                                         l++;\r
2521                                         for (int j = 0; j < arrayList.Count; j++) {\r
2522                                                 Array array = (Array) arrayList [j];\r
2523                                                 nullBit = (BitArray) nullBits[j];\r
2524                                                 if (nullBit [l] == false) {\r
2525                                                         Rows [k][j] = array.GetValue (l);\r
2526                                                 } else {\r
2527                                                         Rows [k][j] = null;\r
2528                                                 }\r
2529                                         }\r
2530                                 } else if (currentRowState == DataRowState.Unchanged) {\r
2531                                         Rows[k].AcceptChanges ();\r
2532                                 } else if (currentRowState == DataRowState.Deleted) {\r
2533                                         Rows[k].AcceptChanges ();\r
2534                                         Rows[k].Delete ();\r
2535                                 }\r
2536                                 k++;\r
2537                         }\r
2538                 }\r
2539 \r
2540                 void BinaryDeserializeTable (SerializationInfo info)\r
2541                 {\r
2542                         ArrayList arrayList = null;\r
2543 \r
2544                         TableName = info.GetString ("DataTable.TableName");\r
2545                         Namespace = info.GetString ("DataTable.Namespace");\r
2546                         Prefix = info.GetString ("DataTable.Prefix");\r
2547                         CaseSensitive = info.GetBoolean ("DataTable.CaseSensitive");\r
2548                         /*\r
2549                           FIXME: Private variable available in SerializationInfo\r
2550                           this.caseSensitiveAmbientCaseSensitive = info.GetBoolean("DataTable.caseSensitiveAmbientCaseSensitive");\r
2551                           this.NestedInDataSet = info.GetBoolean("DataTable.NestedInDataSet");\r
2552                           this.RepeatableElement = info.GetBoolean("DataTable.RepeatableElement");\r
2553                           this.RemotingVersion = (System.Version) info.GetValue("DataTable.RemotingVersion",\r
2554                           typeof(System.Version));\r
2555                         */\r
2556                         Locale = new CultureInfo (info.GetInt32 ("DataTable.LocaleLCID"));\r
2557                         _extendedProperties = (PropertyCollection) info.GetValue ("DataTable.ExtendedProperties",\r
2558                                                                                  typeof (PropertyCollection));\r
2559                         MinimumCapacity = info.GetInt32 ("DataTable.MinimumCapacity");\r
2560                         int columnsCount = info.GetInt32 ("DataTable.Columns.Count");\r
2561 \r
2562                         for (int i = 0; i < columnsCount; i++) {\r
2563                                 Columns.Add ();\r
2564                                 string prefix = "DataTable.DataColumn_" + i + ".";\r
2565                                 Columns[i].ColumnName = info.GetString (prefix + "ColumnName");\r
2566                                 Columns[i].Namespace = info.GetString (prefix + "Namespace");\r
2567                                 Columns[i].Caption = info.GetString (prefix + "Caption");\r
2568                                 Columns[i].Prefix = info.GetString (prefix + "Prefix");\r
2569                                 Columns[i].DataType = (Type) info.GetValue (prefix + "DataType",\r
2570                                                                             typeof (Type));\r
2571                                 Columns[i].DefaultValue = info.GetValue (prefix + "DefaultValue",\r
2572                                                                                   typeof (Object));\r
2573                                 Columns[i].AllowDBNull = info.GetBoolean (prefix + "AllowDBNull");\r
2574                                 Columns[i].AutoIncrement = info.GetBoolean (prefix + "AutoIncrement");\r
2575                                 Columns[i].AutoIncrementStep = info.GetInt64 (prefix + "AutoIncrementStep");\r
2576                                 Columns[i].AutoIncrementSeed = info.GetInt64(prefix + "AutoIncrementSeed");\r
2577                                 Columns[i].ReadOnly = info.GetBoolean (prefix + "ReadOnly");\r
2578                                 Columns[i].MaxLength = info.GetInt32 (prefix + "MaxLength");\r
2579                                 /*\r
2580                                   FIXME: Private variable available in SerializationInfo\r
2581                                   this.Columns[i].SimpleType = info.GetString("DataTable.DataColumn_" +\r
2582                                   i + ".SimpleType");\r
2583                                   this.Columns[i].AutoIncrementCurrent = info.GetInt64("DataTable.DataColumn_" +\r
2584                                   i + ".AutoIncrementCurrent");\r
2585                                   this.Columns[i].XmlDataType = info.GetString("DataTable.DataColumn_" +\r
2586                                   i + ".XmlDataType");\r
2587                                 */\r
2588                                 Columns[i].ExtendedProperties = (PropertyCollection) info.GetValue (prefix + "ExtendedProperties",\r
2589                                                                                                     typeof (PropertyCollection));\r
2590                                 if (Columns[i].DataType == typeof (DataSetDateTime)) {\r
2591                                         Columns[i].DateTimeMode = (DataSetDateTime) info.GetValue (prefix + "DateTimeMode",\r
2592                                                                                                    typeof (DataSetDateTime));\r
2593                                 }\r
2594                                 Columns[i].ColumnMapping = (MappingType) info.GetValue (prefix + "ColumnMapping",\r
2595                                                                                         typeof (MappingType));\r
2596                                 try {\r
2597                                         Columns[i].Expression = info.GetString (prefix + "Expression");\r
2598 \r
2599                                         prefix = "DataTable_0.";\r
2600 \r
2601                                         arrayList = (ArrayList) info.GetValue (prefix + "Constraints",\r
2602                                                                                typeof (ArrayList));\r
2603                                         if (Constraints == null)\r
2604                                                 Constraints = new ConstraintCollection (this);\r
2605                                         DeserializeConstraints (arrayList);\r
2606                                 } catch (SerializationException) {\r
2607                                 }\r
2608                         }\r
2609                         try {\r
2610                                 String prefix = "DataTable_0.";\r
2611                                 ArrayList nullBits = (ArrayList) info.GetValue (prefix + "NullBits",\r
2612                                                                                 typeof (ArrayList));\r
2613                                 arrayList = (ArrayList) info.GetValue (prefix + "Records",\r
2614                                                                        typeof (ArrayList));\r
2615                                 BitArray rowStateBitArray = (BitArray) info.GetValue (prefix + "RowStates",\r
2616                                                                                       typeof (BitArray));\r
2617                                 Hashtable htRowErrors = (Hashtable) info.GetValue (prefix + "RowErrors",\r
2618                                                                                   typeof (Hashtable));\r
2619                                 DeserializeRecords (arrayList, nullBits, rowStateBitArray);\r
2620                         } catch (SerializationException) {\r
2621                         }\r
2622                 }\r
2623 \r
2624                 internal void BinarySerializeProperty (SerializationInfo info)\r
2625                 {\r
2626                         Version vr = new Version (2, 0);\r
2627                         info.AddValue ("DataTable.RemotingVersion", vr);\r
2628                         info.AddValue ("DataTable.RemotingFormat", RemotingFormat);\r
2629                         info.AddValue ("DataTable.TableName", TableName);\r
2630                         info.AddValue ("DataTable.Namespace", Namespace);\r
2631                         info.AddValue ("DataTable.Prefix", Prefix);\r
2632                         info.AddValue ("DataTable.CaseSensitive", CaseSensitive);\r
2633                         /*\r
2634                           FIXME: Required by MS.NET\r
2635                           caseSensitiveAmbient, NestedInDataSet, RepeatableElement\r
2636                         */\r
2637                         info.AddValue ("DataTable.caseSensitiveAmbient", true);\r
2638                         info.AddValue ("DataTable.NestedInDataSet", true);\r
2639                         info.AddValue ("DataTable.RepeatableElement", false);\r
2640                         info.AddValue ("DataTable.LocaleLCID", Locale.LCID);\r
2641                         info.AddValue ("DataTable.MinimumCapacity", MinimumCapacity);\r
2642                         info.AddValue ("DataTable.Columns.Count", Columns.Count);\r
2643                         info.AddValue ("DataTable.ExtendedProperties", _extendedProperties);\r
2644                         for (int i = 0; i < Columns.Count; i++) {\r
2645                                 info.AddValue ("DataTable.DataColumn_" + i + ".ColumnName",\r
2646                                                Columns[i].ColumnName);\r
2647                                 info.AddValue ("DataTable.DataColumn_" + i + ".Namespace",\r
2648                                                Columns[i].Namespace);\r
2649                                 info.AddValue ("DataTable.DataColumn_" + i + ".Caption",\r
2650                                                Columns[i].Caption);\r
2651                                 info.AddValue ("DataTable.DataColumn_" + i + ".Prefix",\r
2652                                                Columns[i].Prefix);\r
2653                                 info.AddValue ("DataTable.DataColumn_" + i + ".DataType",\r
2654                                                Columns[i].DataType, typeof (Type));\r
2655                                 info.AddValue ("DataTable.DataColumn_" + i + ".DefaultValue",\r
2656                                                Columns[i].DefaultValue, typeof (DBNull));\r
2657                                 info.AddValue ("DataTable.DataColumn_" + i + ".AllowDBNull",\r
2658                                                Columns[i].AllowDBNull);\r
2659                                 info.AddValue ("DataTable.DataColumn_" + i + ".AutoIncrement",\r
2660                                                Columns[i].AutoIncrement);\r
2661                                 info.AddValue ("DataTable.DataColumn_" + i + ".AutoIncrementStep",\r
2662                                                Columns[i].AutoIncrementStep);\r
2663                                 info.AddValue ("DataTable.DataColumn_" + i + ".AutoIncrementSeed",\r
2664                                                Columns[i].AutoIncrementSeed);\r
2665                                 info.AddValue ("DataTable.DataColumn_" + i + ".ReadOnly",\r
2666                                                Columns[i].ReadOnly);\r
2667                                 info.AddValue ("DataTable.DataColumn_" + i + ".MaxLength",\r
2668                                                Columns[i].MaxLength);\r
2669                                 info.AddValue ("DataTable.DataColumn_" + i + ".ExtendedProperties",\r
2670                                                Columns[i].ExtendedProperties);\r
2671                                 info.AddValue ("DataTable.DataColumn_" + i + ".DateTimeMode",\r
2672                                                Columns[i].DateTimeMode);\r
2673                                 info.AddValue ("DataTable.DataColumn_" + i + ".ColumnMapping",\r
2674                                                Columns[i].ColumnMapping, typeof (MappingType));\r
2675                                 /*\r
2676                                   FIXME: Required by MS.NET\r
2677                                   SimpleType, AutoIncrementCurrent, XmlDataType\r
2678                                 */\r
2679                                 info.AddValue ("DataTable.DataColumn_" + i + ".SimpleType",\r
2680                                                null, typeof (string));\r
2681                                 info.AddValue ("DataTable.DataColumn_" + i + ".AutoIncrementCurrent",\r
2682                                                Columns[i].AutoIncrementValue());\r
2683                                 info.AddValue ("DataTable.DataColumn_" + i + ".XmlDataType",\r
2684                                                null, typeof (string));\r
2685                         }\r
2686                         /*\r
2687                           FIXME: Required by MS.NET\r
2688                           TypeName\r
2689                         */\r
2690                         info.AddValue ("DataTable.TypeName", null, typeof (string));\r
2691                 }\r
2692 \r
2693                 internal void SerializeConstraints (SerializationInfo info, string prefix)\r
2694                 {\r
2695                         ArrayList constraintArrayList = new ArrayList ();\r
2696                         for (int j = 0; j < Constraints.Count; j++) {\r
2697                                 ArrayList constraintList = new ArrayList ();\r
2698                                 if (Constraints[j] is UniqueConstraint) {\r
2699                                         constraintList.Add ("U");\r
2700                                         UniqueConstraint unique = (UniqueConstraint) Constraints [j];\r
2701                                         constraintList.Add (unique.ConstraintName);\r
2702                                         DataColumn [] columns = unique.Columns;\r
2703                                         int [] tmpArray = new int [columns.Length];\r
2704                                         for (int k = 0; k < columns.Length; k++)\r
2705                                                 tmpArray [k] = unique.Table.Columns.IndexOf (unique.Columns [k]);\r
2706                                         constraintList.Add (tmpArray);\r
2707                                         constraintList.Add (unique.IsPrimaryKey);\r
2708                                         constraintList.Add (unique.ExtendedProperties);\r
2709                                 } else if (Constraints[j] is ForeignKeyConstraint) {\r
2710                                         constraintList.Add ("F");\r
2711                                         ForeignKeyConstraint fKey = (ForeignKeyConstraint) Constraints [j];\r
2712                                         constraintList.Add (fKey.ConstraintName);\r
2713 \r
2714                                         int [] tmpArray = new int [fKey.RelatedColumns.Length + 1];\r
2715                                         tmpArray [0] = DataSet.Tables.IndexOf (fKey.RelatedTable);\r
2716                                         for (int i = 0; i < fKey.Columns.Length; i++) {\r
2717                                                 tmpArray [i + 1] = fKey.RelatedColumns [i].Ordinal;\r
2718                                         }\r
2719                                         constraintList.Add (tmpArray);\r
2720 \r
2721                                         tmpArray = new int [fKey.Columns.Length + 1];\r
2722                                         tmpArray [0] = DataSet.Tables.IndexOf (fKey.Table);\r
2723                                         for (int i = 0; i < fKey.Columns.Length; i++) {\r
2724                                                 tmpArray [i + 1] = fKey.Columns [i].Ordinal;\r
2725                                         }\r
2726                                         constraintList.Add (tmpArray);\r
2727 \r
2728                                         tmpArray = new int [3];\r
2729                                         tmpArray [0] = (int) fKey.AcceptRejectRule;\r
2730                                         tmpArray [1] = (int) fKey.UpdateRule;\r
2731                                         tmpArray [2] = (int) fKey.DeleteRule;\r
2732                                         constraintList.Add (tmpArray);\r
2733 \r
2734                                         constraintList.Add (fKey.ExtendedProperties);\r
2735                                 }\r
2736                                 else\r
2737                                         continue;\r
2738                                 constraintArrayList.Add (constraintList);\r
2739                         }\r
2740                         info.AddValue (prefix, constraintArrayList, typeof (ArrayList));\r
2741                 }\r
2742 \r
2743                 internal void BinarySerialize (SerializationInfo info, string prefix)\r
2744                 {\r
2745                         int columnsCount = Columns.Count;\r
2746                         int rowsCount = Rows.Count;\r
2747                         int recordsCount = Rows.Count;\r
2748                         ArrayList recordList = new ArrayList ();\r
2749                         ArrayList bitArrayList = new ArrayList ();\r
2750                         BitArray rowStateBitArray = new BitArray (rowsCount * 3);\r
2751                         for (int k = 0; k < Rows.Count; k++) {\r
2752                                 if (Rows[k].RowState == DataRowState.Modified)\r
2753                                         recordsCount++;\r
2754                         }\r
2755                         SerializeConstraints (info, prefix + "Constraints");\r
2756                         for (int j = 0; j < columnsCount; j++) {\r
2757                                 if (rowsCount == 0)\r
2758                                         continue;\r
2759                                 BitArray nullBits = new BitArray (rowsCount);\r
2760                                 Array recordArray = Array.CreateInstance (Rows[0][j].GetType (), recordsCount);\r
2761                                 DataColumn column = Columns [j];\r
2762                                 for (int k = 0, l = 0; k < Rows.Count; k++, l++) {\r
2763                                         DataRowVersion version;\r
2764                                         DataRow dr = Rows[k];\r
2765                                         if (dr.RowState == DataRowState.Modified) {\r
2766                                                 version = DataRowVersion.Default;\r
2767                                                 nullBits.Length = nullBits.Length + 1;\r
2768                                                 if (dr.IsNull (column, version) == false) {\r
2769                                                         nullBits [l] = false;\r
2770                                                         recordArray.SetValue (dr [j, version], l);\r
2771                                                 } else {\r
2772                                                         nullBits [l] = true;\r
2773                                                 }\r
2774                                                 l++;\r
2775                                                 version = DataRowVersion.Current;\r
2776                                         } else if (dr.RowState == DataRowState.Deleted) {\r
2777                                                 version = DataRowVersion.Original;\r
2778                                         } else {\r
2779                                                 version = DataRowVersion.Default;\r
2780                                         }\r
2781                                         if (dr.IsNull (column, version) == false) {\r
2782                                                 nullBits [l] = false;\r
2783                                                 recordArray.SetValue (dr [j, version], l);\r
2784                                         } else {\r
2785                                                 nullBits [l] = true;\r
2786                                         }\r
2787                                 }\r
2788                                 recordList.Add (recordArray);\r
2789                                 bitArrayList.Add (nullBits);\r
2790                         }\r
2791                         for (int j = 0; j < Rows.Count; j++) {\r
2792                                 int l = j * 3;\r
2793                                 DataRowState rowState = Rows [j].RowState;\r
2794                                 if (rowState == DataRowState.Detached) {\r
2795                                         rowStateBitArray [l] = false;\r
2796                                         rowStateBitArray [l + 1] = false;\r
2797                                         rowStateBitArray [l + 2] = true;\r
2798                                 } else if (rowState == DataRowState.Unchanged) {\r
2799                                         rowStateBitArray [l] = false;\r
2800                                         rowStateBitArray [l + 1] = false;\r
2801                                         rowStateBitArray [l + 2] = false;\r
2802                                 } else if (rowState == DataRowState.Added) {\r
2803                                         rowStateBitArray [l] = false;\r
2804                                         rowStateBitArray [l + 1] = true;\r
2805                                         rowStateBitArray [l + 2] = false;\r
2806                                 } else if (rowState == DataRowState.Deleted) {\r
2807                                         rowStateBitArray [l] = true;\r
2808                                         rowStateBitArray [l + 1] = true;\r
2809                                         rowStateBitArray [l + 2] = false;\r
2810                                 } else {\r
2811                                         rowStateBitArray [l] = true;\r
2812                                         rowStateBitArray [l + 1] = false;\r
2813                                         rowStateBitArray [l + 2] = false;\r
2814                                 }\r
2815                         }\r
2816                         info.AddValue (prefix + "Rows.Count", Rows.Count);\r
2817                         info.AddValue (prefix + "Records.Count", recordsCount);\r
2818                         info.AddValue (prefix + "Records", recordList, typeof (ArrayList));\r
2819                         info.AddValue (prefix + "NullBits", bitArrayList, typeof (ArrayList));\r
2820                         info.AddValue (prefix + "RowStates",\r
2821                                        rowStateBitArray, typeof (BitArray));\r
2822                         // FIXME: To get row errors\r
2823                         Hashtable htRowErrors = new Hashtable ();\r
2824                         info.AddValue (prefix + "RowErrors",\r
2825                                        htRowErrors, typeof (Hashtable));\r
2826                         // FIXME: To get column errors\r
2827                         Hashtable htColumnErrors = new Hashtable ();\r
2828                         info.AddValue (prefix + "ColumnErrors",\r
2829                                        htColumnErrors, typeof (Hashtable));\r
2830                 }\r
2831 \r
2832                 public DataTableReader CreateDataReader ()\r
2833                 {\r
2834                         return new DataTableReader (this);\r
2835                 }\r
2836 \r
2837                 /// <summary>\r
2838                 ///     Loads the table with the values from the reader\r
2839                 /// </summary>\r
2840                 public void Load (IDataReader reader)\r
2841                 {\r
2842                         if (reader == null)\r
2843                                 throw new ArgumentNullException ("Value cannot be null. Parameter name: reader");\r
2844                         Load (reader, LoadOption.PreserveChanges);\r
2845                 }\r
2846 \r
2847                 /// <summary>\r
2848                 ///     Loads the table with the values from the reader and the pattern\r
2849                 ///     of the changes to the existing rows or the new rows are based on\r
2850                 ///     the LoadOption passed.\r
2851                 /// </summary>\r
2852                 public void Load (IDataReader reader, LoadOption loadOption)\r
2853                 {\r
2854                         if (reader == null)\r
2855                                 throw new ArgumentNullException ("Value cannot be null. Parameter name: reader");\r
2856                         bool prevEnforceConstr = this.EnforceConstraints;\r
2857                         try {\r
2858                                 this.EnforceConstraints = false;\r
2859                                 int [] mapping = DbDataAdapter.BuildSchema (reader, this, SchemaType.Mapped,\r
2860                                                                             MissingSchemaAction.AddWithKey,\r
2861                                                                             MissingMappingAction.Passthrough,\r
2862                                                                             new DataTableMappingCollection ());\r
2863                                 DbDataAdapter.FillFromReader (this,\r
2864                                                               reader,\r
2865                                                               0, // start from\r
2866                                                               0, // all records\r
2867                                                               mapping,\r
2868                                                               loadOption);\r
2869                         } finally {\r
2870                                 this.EnforceConstraints = prevEnforceConstr;\r
2871                         }\r
2872                 }\r
2873 \r
2874                 public virtual void Load (IDataReader reader, LoadOption loadOption, FillErrorEventHandler errorHandler)\r
2875                 {\r
2876                         if (reader == null)\r
2877                                 throw new ArgumentNullException ("Value cannot be null. Parameter name: reader");\r
2878 \r
2879                         bool prevEnforceConstr = this.EnforceConstraints;\r
2880                         try {\r
2881                                 this.EnforceConstraints = false;\r
2882 \r
2883                                 int [] mapping = DbDataAdapter.BuildSchema (reader, this, SchemaType.Mapped,\r
2884                                                                             MissingSchemaAction.AddWithKey,\r
2885                                                                             MissingMappingAction.Passthrough,\r
2886                                                                             new DataTableMappingCollection ());\r
2887                                 DbDataAdapter.FillFromReader (this,\r
2888                                                               reader,\r
2889                                                               0, // start from\r
2890                                                               0, // all records\r
2891                                                               mapping,\r
2892                                                               loadOption,\r
2893                                                               errorHandler);\r
2894                         } finally {\r
2895                                 this.EnforceConstraints = prevEnforceConstr;\r
2896                         }\r
2897                 }\r
2898 \r
2899                 /// <summary>\r
2900                 ///     Loads the given values into an existing row if matches or creates\r
2901                 ///     the new row popluated with the values.\r
2902                 /// </summary>\r
2903                 /// <remarks>\r
2904                 ///     This method searches for the values using primary keys and it first\r
2905                 ///     searches using the original values of the rows and if not found, it\r
2906                 ///     searches using the current version of the row.\r
2907                 /// </remarks>\r
2908                 public DataRow LoadDataRow (object [] values, LoadOption loadOption)\r
2909                 {\r
2910                         DataRow row = null;\r
2911 \r
2912                         // Find Data DataRow\r
2913                         if (this.PrimaryKey.Length > 0) {\r
2914                                 object [] keys = new object [PrimaryKey.Length];\r
2915                                 for (int i = 0; i < PrimaryKey.Length; i++)\r
2916                                         keys [i] = values [PrimaryKey [i].Ordinal];\r
2917 \r
2918                                 row = Rows.Find (keys, DataViewRowState.OriginalRows);\r
2919                                 if (row == null)\r
2920                                         row = Rows.Find (keys);\r
2921                         }\r
2922 \r
2923                         // If not found, add new row\r
2924                         if (row == null ||\r
2925                             (row.RowState == DataRowState.Deleted && loadOption == LoadOption.Upsert)) {\r
2926                                 row = NewNotInitializedRow ();\r
2927                                 row.ImportRecord (CreateRecord (values));\r
2928 \r
2929                                 row.Validate (); // this adds to index ;-)\r
2930 \r
2931                                 if (loadOption == LoadOption.OverwriteChanges ||\r
2932                                     loadOption == LoadOption.PreserveChanges)\r
2933                                         Rows.AddInternal (row, DataRowAction.ChangeCurrentAndOriginal);\r
2934                                 else\r
2935                                         Rows.AddInternal(row);\r
2936                                 return row;\r
2937                         }\r
2938 \r
2939                         row.Load (values, loadOption);\r
2940 \r
2941                         return row;\r
2942                 }\r
2943 \r
2944                 public void Merge (DataTable table)\r
2945                 {\r
2946                         Merge (table, false, MissingSchemaAction.Add);\r
2947                 }\r
2948 \r
2949                 public void Merge (DataTable table, bool preserveChanges)\r
2950                 {\r
2951                         Merge (table, preserveChanges, MissingSchemaAction.Add);\r
2952                 }\r
2953 \r
2954                 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)\r
2955                 {\r
2956                         MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);\r
2957                 }\r
2958 \r
2959                 internal int CompareRecords (int x, int y)\r
2960                 {\r
2961                         for (int col = 0; col < Columns.Count; col++) {\r
2962                                 int res = Columns[col].DataContainer.CompareValues (x, y);\r
2963                                 if (res != 0)\r
2964                                         return res;\r
2965                         }\r
2966 \r
2967                         return 0;\r
2968                 }\r
2969 \r
2970                 /// <summary>\r
2971                 /// Occurs after the Clear method is called on the datatable.\r
2972                 /// </summary>\r
2973                 [DataCategory ("Data")]\r
2974                 public event DataTableClearEventHandler TableCleared;\r
2975 \r
2976                 [DataCategory ("Data")]\r
2977                 public event DataTableClearEventHandler TableClearing;\r
2978 \r
2979                 public event DataTableNewRowEventHandler TableNewRow;\r
2980 \r
2981                 /// <summary>\r
2982                 /// Raises TableCleared Event and delegates to subscribers\r
2983                 /// </summary>\r
2984                 protected virtual void OnTableCleared (DataTableClearEventArgs e)\r
2985                 {\r
2986                         if (TableCleared != null)\r
2987                                 TableCleared (this, e);\r
2988                 }\r
2989 \r
2990                 partial void DataTableCleared ()\r
2991                 {\r
2992                         OnTableCleared (new DataTableClearEventArgs (this));\r
2993                 }\r
2994 \r
2995                 protected virtual void OnTableClearing (DataTableClearEventArgs e)\r
2996                 {\r
2997                         if (TableClearing != null)\r
2998                                 TableClearing (this, e);\r
2999                 }\r
3000 \r
3001                 partial void DataTableClearing ()\r
3002                 {\r
3003                         OnTableClearing (new DataTableClearEventArgs (this));\r
3004                 }\r
3005 \r
3006                 protected virtual void OnTableNewRow (DataTableNewRowEventArgs e)\r
3007                 {\r
3008                         if (null != TableNewRow)\r
3009                                 TableNewRow (this, e);\r
3010                 }\r
3011 \r
3012                 partial void NewRowAdded (DataRow dr)\r
3013                 {\r
3014                         OnTableNewRow (new DataTableNewRowEventArgs (dr));\r
3015                 }\r
3016         }\r
3017 #endif\r
3018 }\r