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