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