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