Test mailing list
[mono.git] / mcs / class / System.Data / System.Data / DataSet.cs
1 // 
2 // System.Data/DataSet.cs
3 //
4 // Author:
5 //   Christopher Podurgiel <cpodurgiel@msn.com>
6 //   Daniel Morgan <danmorg@sc.rr.com>
7 //   Rodrigo Moya <rodrigo@ximian.com>
8 //   Stuart Caborn <stuart.caborn@virgin.net>
9 //   Tim Coleman (tim@timcoleman.com)
10 //   Ville Palo <vi64pa@koti.soon.fi>
11 //   Atsushi Enomoto <atsushi@ximian.com>
12 //
13 // (C) Ximian, Inc. 2002
14 // Copyright (C) Tim Coleman, 2002, 2003
15 //
16
17 //
18 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
19 //
20 // Permission is hereby granted, free of charge, to any person obtaining
21 // a copy of this software and associated documentation files (the
22 // "Software"), to deal in the Software without restriction, including
23 // without limitation the rights to use, copy, modify, merge, publish,
24 // distribute, sublicense, and/or sell copies of the Software, and to
25 // permit persons to whom the Software is furnished to do so, subject to
26 // the following conditions:
27 // 
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
30 // 
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 //
39
40 using System;
41 using System.Collections;
42 using System.ComponentModel;
43 using System.Globalization;
44 using System.Threading;
45 using System.IO;
46 using System.Runtime.Serialization;
47 using System.Xml;
48 using System.Xml.Schema;
49 using System.Xml.Serialization;
50 using System.Data.Common;
51
52 namespace System.Data {
53
54         [ToolboxItem (false)]
55         [DefaultProperty ("DataSetName")]
56         [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.DataSetDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
57
58         [Serializable]
59         public class DataSet : MarshalByValueComponent, IListSource, 
60                 ISupportInitialize, ISerializable, IXmlSerializable 
61         {
62                 private string dataSetName;
63                 private string _namespace = "";
64                 private string prefix;
65                 private bool caseSensitive;
66                 private bool enforceConstraints = true;
67                 private DataTableCollection tableCollection;
68                 private DataRelationCollection relationCollection;
69                 private PropertyCollection properties;
70                 private DataViewManager defaultView;
71                 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
72                 internal XmlDataDocument _xmlDataDocument = null;
73                 
74                 #region Constructors
75
76                 public DataSet () : this ("NewDataSet") 
77                 {               
78                 }
79                 
80                 public DataSet (string name)
81                 {
82                         dataSetName = name;
83                         tableCollection = new DataTableCollection (this);
84                         relationCollection = new DataRelationCollection.DataSetRelationCollection (this);
85                         properties = new PropertyCollection ();
86                         this.prefix = String.Empty;
87                         
88                         this.Locale = CultureInfo.CurrentCulture;
89                 }
90
91                 protected DataSet (SerializationInfo info, StreamingContext context) : this ()
92                 {
93                         GetSerializationData (info, context);
94                 }
95
96                 #endregion // Constructors
97
98                 #region Public Properties
99
100                 [DataCategory ("Data")]
101                 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
102                 [DefaultValue (false)]
103                 public bool CaseSensitive {
104                         get {
105                                 return caseSensitive;
106                         } 
107                         set {
108                                 caseSensitive = value; 
109                                 if (!caseSensitive) {
110                                         foreach (DataTable table in Tables) {
111                                                 foreach (Constraint c in table.Constraints)
112                                                         c.AssertConstraint ();
113                                         }
114                                 }
115                         }
116                 }
117
118                 [DataCategory ("Data")]
119                 [DataSysDescription ("The name of this DataSet.")]
120                 [DefaultValue ("")]
121                 public string DataSetName {
122                         get { return dataSetName; } 
123                         set { dataSetName = value; }
124                 }
125
126                 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
127                 [Browsable (false)]
128                 public DataViewManager DefaultViewManager {
129                         get {
130                                 if (defaultView == null)
131                                         defaultView = new DataViewManager (this);
132                                 return defaultView;
133                         } 
134                 }
135
136                 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
137                 [DefaultValue (true)]
138                 public bool EnforceConstraints {
139                         get { return enforceConstraints; } 
140                         set { 
141                                 if (value != enforceConstraints) {
142                                         enforceConstraints = value; 
143                                         if (value) {
144                                                 foreach (DataTable table in Tables) {
145                                                         // first assert all unique constraints
146                                                         foreach (UniqueConstraint uc in table.Constraints.UniqueConstraints)
147                                                                 uc.AssertConstraint ();
148                                                         // then assert all foreign keys
149                                                         foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
150                                                                 fk.AssertConstraint ();
151                                                 }
152                                         }
153                                 }
154                         }
155                 }
156
157                 [Browsable (false)]
158                 [DataCategory ("Data")]
159                 [DataSysDescription ("The collection that holds custom user information.")]
160                 public PropertyCollection ExtendedProperties {
161                         get { return properties; }
162                 }
163
164                 [Browsable (false)]
165                 [DataSysDescription ("Indicates that the DataSet has errors.")]
166                 public bool HasErrors {
167                         [MonoTODO]
168                         get {
169                                 for (int i = 0; i < Tables.Count; i++) {
170                                         if (Tables[i].HasErrors)
171                                                 return true;
172                                 }
173                                 return false;
174                         }
175                 }
176
177                 [DataCategory ("Data")]
178                 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
179                 public CultureInfo Locale {
180                         get {
181                                 return locale;
182                         }
183                         set {
184                                 if (locale == null || !locale.Equals (value)) {
185                                         // TODO: check if the new locale is valid
186                                         // TODO: update locale of all tables
187                                         locale = value;
188                                 }
189                         }
190                 }
191
192                 public void Merge (DataRow[] rows)
193                 {
194                         Merge (rows, false, MissingSchemaAction.Add);
195                 }
196                 
197                 public void Merge (DataSet dataSet)
198                 {
199                         Merge (dataSet, false, MissingSchemaAction.Add);
200                 }
201                 
202                 public void Merge (DataTable table)
203                 {
204                         Merge (table, false, MissingSchemaAction.Add);
205                 }
206                 
207                 public void Merge (DataSet dataSet, bool preserveChanges)
208                 {
209                         Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
210                 }
211                 
212                 [MonoTODO]
213                 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
214                 {
215                         if (rows == null)
216                                 throw new ArgumentNullException ("rows");
217                         if (!IsLegalSchemaAction (missingSchemaAction))
218                                 throw new ArgumentOutOfRangeException ("missingSchemaAction");
219                         
220                         MergeManager.Merge (this, rows, preserveChanges, missingSchemaAction);
221                 }
222                 
223                 [MonoTODO]
224                 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
225                 {
226                         if (dataSet == null)
227                                 throw new ArgumentNullException ("dataSet");
228                         if (!IsLegalSchemaAction (missingSchemaAction))
229                                 throw new ArgumentOutOfRangeException ("missingSchemaAction");
230                         
231                         MergeManager.Merge (this, dataSet, preserveChanges, missingSchemaAction);
232                 }
233                 
234                 [MonoTODO]
235                 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
236                 {
237                         if (table == null)
238                                 throw new ArgumentNullException ("table");
239                         if (!IsLegalSchemaAction (missingSchemaAction))
240                                 throw new ArgumentOutOfRangeException ("missingSchemaAction");
241                         
242                         MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);
243                 }
244
245                 private static bool IsLegalSchemaAction (MissingSchemaAction missingSchemaAction)
246                 {
247                         if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
248                                 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
249                                 return true;
250                         return false;
251                 }
252                 
253                 [DataCategory ("Data")]
254                 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
255                 [DefaultValue ("")]
256                 public string Namespace {
257                         get { return _namespace; } 
258                         set {
259                                 //TODO - trigger an event if this happens?
260                                 if (value == null)
261                                         value = String.Empty;
262                                  if (value != this._namespace)
263                                         RaisePropertyChanging ("Namespace");
264                                 _namespace = value;
265                         }
266                 }
267
268                 [DataCategory ("Data")]
269                 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
270                 [DefaultValue ("")]
271                 public string Prefix {
272                         get { return prefix; } 
273                         set {
274                                 if (value == null)
275                                         value = String.Empty;
276                               // Prefix cannot contain any special characters other than '_' and ':'
277                                for (int i = 0; i < value.Length; i++) {
278                                        if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
279                                                throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
280                                }
281
282
283                                 if (value == null)
284                                         value = string.Empty;
285                                 
286                                 if (value != this.prefix) 
287                                         RaisePropertyChanging ("Prefix");
288                                 prefix = value;
289                         }
290                 }
291
292                 [DataCategory ("Data")]
293                 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
294                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
295                 public DataRelationCollection Relations {
296                         get {
297                                 return relationCollection;              
298                         }
299                 }
300
301                 [Browsable (false)]
302                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
303                 public override ISite Site {
304                         [MonoTODO]
305                         get {
306                                 throw new NotImplementedException ();
307                         } 
308                         
309                         [MonoTODO]
310                         set {
311                                 throw new NotImplementedException ();
312                         }
313                 }
314
315                 [DataCategory ("Data")]
316                 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
317                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
318                 public DataTableCollection Tables {
319                         get { return tableCollection; }
320                 }
321
322                 #endregion // Public Properties
323
324                 #region Public Methods
325
326                 [MonoTODO]
327                 public void AcceptChanges ()
328                 {
329                         foreach (DataTable tempTable in tableCollection)
330                                 tempTable.AcceptChanges ();
331                 }
332
333                 /// <summary>
334                 /// Clears all the tables
335                 /// </summary>
336                 public void Clear ()
337                 {
338                         if (_xmlDataDocument != null)
339                                 throw new NotSupportedException ("Clear function on dataset and datatable is not supported when XmlDataDocument is bound to the DataSet.");
340                         bool enforceConstraints = this.EnforceConstraints;
341                         this.EnforceConstraints = false;
342                         for (int t = 0; t < tableCollection.Count; t++) {
343                                 tableCollection[t].Clear ();
344                         }
345                         this.EnforceConstraints = enforceConstraints;
346                 }
347
348                 public virtual DataSet Clone ()
349                 {
350                         // need to return the same type as this...
351                         DataSet Copy = (DataSet) Activator.CreateInstance(GetType(), true);
352         
353                         CopyProperties (Copy);
354
355                         foreach (DataTable Table in Tables) {
356                         // tables are often added in no-args constructor, don't add them
357                         // twice.
358                                 if (!Copy.Tables.Contains(Table.TableName)) {
359                                         Copy.Tables.Add (Table.Clone ());
360                                 }
361                         }
362
363                         //Copy Relationships between tables after existance of tables
364                         //and setting properties correctly
365                         CopyRelations (Copy);
366                         
367                         return Copy;
368                 }
369
370                 // Copies both the structure and data for this DataSet.
371                 public DataSet Copy ()
372                 {
373                         DataSet Copy = new DataSet ();
374                         CopyProperties (Copy);
375
376                         // Copy DatSet's tables
377                         foreach (DataTable Table in Tables) 
378                                 Copy.Tables.Add (Table.Copy ());
379
380                         //Copy Relationships between tables after existance of tables
381                         //and setting properties correctly
382                         CopyRelations (Copy);
383
384                         return Copy;
385                 }
386
387                 private void CopyProperties (DataSet Copy)
388                 {
389                         Copy.CaseSensitive = CaseSensitive;
390                         //Copy.Container = Container
391                         Copy.DataSetName = DataSetName;
392                         //Copy.DefaultViewManager
393                         //Copy.DesignMode
394                         Copy.EnforceConstraints = EnforceConstraints;
395                         if(ExtendedProperties.Count > 0) {
396                                 //  Cannot copy extended properties directly as the property does not have a set accessor
397                 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
398                 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
399                 for (int i=0; i < ExtendedProperties.Count; i++)
400                                         Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
401                         }
402             Copy.Locale = Locale;
403                         Copy.Namespace = Namespace;
404                         Copy.Prefix = Prefix;                   
405                         //Copy.Site = Site; // FIXME : Not sure of this.
406
407                 }
408                 
409                 
410                 private void CopyRelations (DataSet Copy)
411                 {
412
413                         //Creation of the relation contains some of the properties, and the constructor
414                         //demands these values. instead changing the DataRelation constructor and behaviour the
415                         //parameters are pre-configured and sent to the most general constructor
416
417                         foreach (DataRelation MyRelation in this.Relations) {
418                                 string pTable = MyRelation.ParentTable.TableName;
419                                 string cTable = MyRelation.ChildTable.TableName;
420                                 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length]; 
421                                 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
422                                 int i = 0;
423                                 
424                                 foreach (DataColumn DC in MyRelation.ParentColumns) {
425                                         P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
426                                         i++;
427                                 }
428
429                                 i = 0;
430
431                                 foreach (DataColumn DC in MyRelation.ChildColumns) {
432                                         C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
433                                         i++;
434                                 }
435                                 
436                                 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
437                                 //cRel.ChildColumns = MyRelation.ChildColumns;
438                                 //cRel.ChildTable = MyRelation.ChildTable;
439                                 //cRel.ExtendedProperties = cRel.ExtendedProperties; 
440                                 //cRel.Nested = MyRelation.Nested;
441                                 //cRel.ParentColumns = MyRelation.ParentColumns;
442                                 //cRel.ParentTable = MyRelation.ParentTable;
443                                                                 
444                                 Copy.Relations.Add (cRel);
445                         }
446                 }
447
448                 
449
450
451                 public DataSet GetChanges ()
452                 {
453                         return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
454                 }
455
456                 
457                 public DataSet GetChanges (DataRowState rowStates)
458                 {
459                         if (!HasChanges (rowStates))
460                                 return null;
461                         
462                         DataSet copySet = Clone ();
463                         Hashtable addedRows = new Hashtable ();
464
465                         IEnumerator tableEnumerator = Tables.GetEnumerator ();
466                         DataTable origTable;
467                         DataTable copyTable;
468                         while (tableEnumerator.MoveNext ()) {
469                                 origTable = (DataTable)tableEnumerator.Current;
470                                 copyTable = copySet.Tables[origTable.TableName];
471                                 
472                                 // Look for relations that have this table as child
473                                 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
474
475                                 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
476                                 while (rowEnumerator.MoveNext ()) {
477                                         DataRow row = (DataRow)rowEnumerator.Current;
478                                         
479                                         if (row.IsRowChanged (rowStates))
480                                                 AddChangedRow (addedRows, copySet, copyTable, relations, row);
481                                 }
482                         }
483                         return copySet;
484                 }
485                 
486                 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
487                 {
488                         if (addedRows.ContainsKey (row)) return;
489                         
490                         relations.Reset ();
491                         while (relations.MoveNext ()) {
492                                 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
493                                 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
494                                 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
495                                 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
496                         }
497                 
498                         DataRow newRow = copyTable.NewRow ();
499                         row.CopyValuesToRow (newRow);
500                         copyTable.Rows.Add (newRow);
501                         newRow.XmlRowID = row.XmlRowID;
502                         addedRows.Add (row,row);
503                 }
504
505 #if NET_2_0
506                 [MonoTODO]
507                 public DataTableReader GetDataReader (DataTable[] dataTables)
508                 {
509                         throw new NotImplementedException ();
510                 }
511
512                 [MonoTODO]
513                 public DataTableReader GetDataReader ()
514                 {
515                         throw new NotImplementedException ();
516                 }
517 #endif
518                 
519                 public string GetXml ()
520                 {
521                         StringWriter Writer = new StringWriter ();
522                         WriteXml (Writer, XmlWriteMode.IgnoreSchema);
523                         return Writer.ToString ();
524                 }
525
526                 public string GetXmlSchema ()
527                 {
528                         StringWriter Writer = new StringWriter ();
529                         WriteXmlSchema (Writer);
530                         return Writer.ToString ();
531                 }
532
533                 [MonoTODO]
534                 public bool HasChanges ()
535                 {
536                         return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
537                 }
538
539                 [MonoTODO]
540                 public bool HasChanges (DataRowState rowState)
541                 {
542                         if (((int)rowState & 0xffffffe0) != 0)
543                                 throw new ArgumentOutOfRangeException ("rowState");
544
545                         DataTableCollection tableCollection = Tables;
546                         DataTable table;
547                         DataRowCollection rowCollection;
548                         DataRow row;
549
550                         for (int i = 0; i < tableCollection.Count; i++) {
551                                 table = tableCollection[i];
552                                 rowCollection = table.Rows;
553                                 for (int j = 0; j < rowCollection.Count; j++) {
554                                         row = rowCollection[j];
555                                         if ((row.RowState & rowState) != 0)
556                                                 return true;
557                                 }
558                         }
559
560                         return false;           
561                 }
562
563                 public void InferXmlSchema (XmlReader reader, string[] nsArray)
564                 {
565                         if (reader == null)
566                                 return;
567                         XmlDocument doc = new XmlDocument ();
568                         doc.Load (reader);
569                         InferXmlSchema (doc, nsArray);
570                 }
571
572                 private void InferXmlSchema (XmlDocument doc, string [] nsArray)
573                 {
574                         XmlDataInferenceLoader.Infer (this, doc, XmlReadMode.InferSchema, nsArray);
575                 }
576
577                 public void InferXmlSchema (Stream stream, string[] nsArray)
578                 {
579                         InferXmlSchema (new XmlTextReader (stream), nsArray);
580                 }
581
582                 public void InferXmlSchema (TextReader reader, string[] nsArray)
583                 {
584                         InferXmlSchema (new XmlTextReader (reader), nsArray);
585                 }
586
587                 public void InferXmlSchema (string fileName, string[] nsArray)
588                 {
589                         XmlTextReader reader = new XmlTextReader (fileName);
590                         try {
591                                 InferXmlSchema (reader, nsArray);
592                         } finally {
593                                 reader.Close ();
594                         }
595                 }
596
597 #if NET_2_0
598                 [MonoTODO]
599                 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
600                 {
601                         throw new NotImplementedException ();
602                 }
603
604                 [MonoTODO]
605                 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
606                 {
607                         throw new NotImplementedException ();
608                 }
609 #endif
610
611                 public virtual void RejectChanges ()
612                 {
613                         int i;
614                         bool oldEnforceConstraints = this.EnforceConstraints;
615                         this.EnforceConstraints = false;
616                         
617                         for (i = 0; i < this.Tables.Count;i++) 
618                                 this.Tables[i].RejectChanges ();
619
620                         this.EnforceConstraints = oldEnforceConstraints;
621                 }
622
623                 public virtual void Reset ()
624                 {
625                         IEnumerator constraintEnumerator;
626
627                         // first we remove all ForeignKeyConstraints (if we will not do that
628                         // we will get an exception when clearing the tables).
629                         for (int i = 0; i < Tables.Count; i++) {
630                                 ConstraintCollection cc = Tables[i].Constraints;
631                                 for (int j = 0; j < cc.Count; j++) {
632                                         if (cc[j] is ForeignKeyConstraint)
633                                                 cc.Remove (cc[j]);
634                                 }
635                         }
636
637                         Clear ();
638                         Relations.Clear ();
639                         Tables.Clear ();
640                 }
641
642                 public void WriteXml (Stream stream)
643                 {
644                         XmlTextWriter writer = new XmlTextWriter (stream, null);
645                         writer.Formatting = Formatting.Indented;
646                         WriteXml (writer);
647                 }
648
649                 ///<summary>
650                 /// Writes the current data for the DataSet to the specified file.
651                 /// </summary>
652                 /// <param name="filename">Fully qualified filename to write to</param>
653                 public void WriteXml (string fileName)
654                 {
655                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
656                         writer.Formatting = Formatting.Indented;
657                         writer.WriteStartDocument (true);
658                         try {
659                                 WriteXml (writer);
660                         }
661                         finally {
662                                 writer.WriteEndDocument ();
663                                 writer.Close ();
664                         }
665                 }
666
667                 public void WriteXml (TextWriter writer)
668                 {
669                         XmlTextWriter xwriter = new XmlTextWriter (writer);
670                         xwriter.Formatting = Formatting.Indented;
671                         WriteXml (xwriter);
672                 }
673
674                 public void WriteXml (XmlWriter writer)
675                 {
676                         WriteXml (writer, XmlWriteMode.IgnoreSchema);
677                 }
678
679                 public void WriteXml (string filename, XmlWriteMode mode)
680                 {
681                         XmlTextWriter writer = new XmlTextWriter (filename, null);
682                         writer.Formatting = Formatting.Indented;
683                         writer.WriteStartDocument (true);
684                         
685                         try {
686                                 WriteXml (writer, mode);
687                         }
688                         finally {
689                                 writer.WriteEndDocument ();
690                                 writer.Close ();
691                         }
692                 }
693
694                 public void WriteXml (Stream stream, XmlWriteMode mode)
695                 {
696                         XmlTextWriter writer = new XmlTextWriter (stream, null);
697                         writer.Formatting = Formatting.Indented;
698                         WriteXml (writer, mode);
699                 }
700
701                 public void WriteXml (TextWriter writer, XmlWriteMode mode)
702                 {
703                         XmlTextWriter xwriter = new XmlTextWriter (writer);
704                         xwriter.Formatting = Formatting.Indented;
705                         WriteXml (xwriter, mode);
706                 }
707
708                 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
709                 {
710                         if (mode == XmlWriteMode.DiffGram) {
711                                 SetRowsID();
712                                 WriteDiffGramElement(writer);
713                         }
714                         
715                         // It should not write when there is no content to be written
716                         bool shouldOutputContent = (mode != XmlWriteMode.DiffGram);
717                         for (int n = 0; n < tableCollection.Count && !shouldOutputContent; n++)
718                                 shouldOutputContent = tableCollection [n].Rows.Count > 0;
719                                 
720                         if (shouldOutputContent) {
721                                 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
722                                 
723                                 if (mode == XmlWriteMode.WriteSchema)
724                                         DoWriteXmlSchema (writer);
725                                 
726                                 WriteTables (writer, mode, Tables, DataRowVersion.Default);
727                                 writer.WriteEndElement ();
728                         }
729                         
730                         if (mode == XmlWriteMode.DiffGram) {
731                                 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
732
733                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
734                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
735                                         WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
736                                         writer.WriteEndElement ();
737                                 }
738                         }
739                         
740                         if (mode == XmlWriteMode.DiffGram)
741                                 writer.WriteEndElement (); // diffgr:diffgram
742
743                         writer.Flush ();
744                 }
745
746                 public void WriteXmlSchema (Stream stream)
747                 {
748                         XmlTextWriter writer = new XmlTextWriter (stream, null );
749                         writer.Formatting = Formatting.Indented;
750                         WriteXmlSchema (writer);        
751                 }
752
753                 public void WriteXmlSchema (string fileName)
754                 {
755                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
756                         try {
757                                 writer.Formatting = Formatting.Indented;
758                                 writer.WriteStartDocument (true);
759                                 WriteXmlSchema (writer);
760                         } finally {
761                                 writer.WriteEndDocument ();
762                                 writer.Close ();
763                         }
764                 }
765
766                 public void WriteXmlSchema (TextWriter writer)
767                 {
768                         XmlTextWriter xwriter = new XmlTextWriter (writer);
769                         try {
770                                 xwriter.Formatting = Formatting.Indented;
771                                 WriteXmlSchema (xwriter);
772                         } finally {
773                                 xwriter.Close ();
774                         }
775                 }
776
777                 public void WriteXmlSchema (XmlWriter writer)
778                 {
779                         //Create a skeleton doc and then write the schema 
780                         //proper which is common to the WriteXml method in schema mode
781                         DoWriteXmlSchema (writer);
782                 }
783
784                 public void ReadXmlSchema (Stream stream)
785                 {
786                         XmlReader reader = new XmlTextReader (stream, null);
787                         ReadXmlSchema (reader);
788                 }
789
790                 public void ReadXmlSchema (string str)
791                 {
792                         XmlReader reader = new XmlTextReader (str);
793                         try {
794                                 ReadXmlSchema (reader);
795                         }
796                         finally {
797                                 reader.Close ();
798                         }
799                 }
800
801                 public void ReadXmlSchema (TextReader treader)
802                 {
803                         XmlReader reader = new XmlTextReader (treader);
804                         ReadXmlSchema (reader);                 
805                 }
806
807                 public void ReadXmlSchema (XmlReader reader)
808                 {
809 #if true
810                         new XmlSchemaDataImporter (this, reader).Process ();
811 #else
812                         XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
813                         SchemaMapper.Read (reader);
814 #endif
815                 }
816
817                 public XmlReadMode ReadXml (Stream stream)
818                 {
819                         return ReadXml (new XmlTextReader (stream));
820                 }
821
822                 public XmlReadMode ReadXml (string str)
823                 {
824                         XmlTextReader reader = new XmlTextReader (str);
825                         try {
826                                 return ReadXml (reader);
827                         }
828                         finally {
829                                 reader.Close ();
830                         }
831                 }
832
833                 public XmlReadMode ReadXml (TextReader reader)
834                 {
835                         return ReadXml (new XmlTextReader (reader));
836                 }
837
838                 public XmlReadMode ReadXml (XmlReader r)
839                 {
840                         return ReadXml (r, XmlReadMode.Auto);
841                 }
842
843                 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
844                 {
845                         return ReadXml (new XmlTextReader (stream), mode);
846                 }
847
848                 public XmlReadMode ReadXml (string str, XmlReadMode mode)
849                 {
850                         XmlTextReader reader = new XmlTextReader (str);
851                         try {
852                                 return ReadXml (reader, mode);
853                         }
854                         finally {
855                                 reader.Close ();
856                         }
857                 }
858
859                 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
860                 {
861                         return ReadXml (new XmlTextReader (reader), mode);
862                 }
863
864                 // LAMESPEC: XmlReadMode.Fragment is far from presisely
865                 // documented. MS.NET infers schema against this mode.
866                 public XmlReadMode ReadXml (XmlReader input, XmlReadMode mode)
867                 {
868                         switch (input.ReadState) {
869                         case ReadState.EndOfFile:
870                         case ReadState.Error:
871                         case ReadState.Closed:
872                                 return mode;
873                         }
874                         // Skip XML declaration and prolog
875                         input.MoveToContent ();
876                         if (input.EOF)
877                                 return mode;
878
879                         // FIXME: We need more decent code here, but for now
880                         // I don't know the precise MS.NET behavior, I just
881                         // delegate to specific read process.
882                         switch (mode) {
883                         case XmlReadMode.IgnoreSchema:
884                                 return ReadXmlIgnoreSchema (input, mode, true);
885                         case XmlReadMode.ReadSchema:
886                                 return ReadXmlReadSchema (input, mode, true);
887                         }
888                         // remaining modes are: Auto, InferSchema, Fragment, Diffgram
889
890                         XmlReader reader = input;
891
892                         int depth = reader.Depth;
893                         XmlReadMode result = mode;
894                         bool skippedTopLevelElement = false;
895                         string potentialDataSetName = null;
896                         XmlDocument doc = null;
897                         bool shouldReadData = mode != XmlReadMode.DiffGram;
898                         bool shouldNotInfer = Tables.Count > 0;
899
900                         switch (mode) {
901                         case XmlReadMode.Auto:
902                         case XmlReadMode.InferSchema:
903                                 doc = new XmlDocument ();
904                                 do {
905                                         doc.AppendChild (doc.ReadNode (reader));
906                                 } while (!reader.EOF &&
907                                         doc.DocumentElement == null);
908                                 reader = new XmlNodeReader (doc);
909                                 reader.MoveToContent ();
910                                 break;
911                         case XmlReadMode.DiffGram:
912                                 if (!(reader.LocalName == "diffgram" &&
913                                         reader.NamespaceURI == XmlConstants.DiffgrNamespace))
914                                         goto case XmlReadMode.Auto;
915                                 break;
916                         }
917
918                         switch (mode) {
919                         case XmlReadMode.Auto:
920                         case XmlReadMode.InferSchema:
921                         case XmlReadMode.ReadSchema:
922                                 if (!(reader.LocalName == "diffgram" &&
923                                         reader.NamespaceURI == XmlConstants.DiffgrNamespace) &&
924                                         !(reader.LocalName == "schema" &&
925                                         reader.NamespaceURI == XmlSchema.Namespace))
926                                         potentialDataSetName = reader.LocalName;
927                                 goto default;
928                         case XmlReadMode.Fragment:
929                                 break;
930                         default:
931                                 if (!(reader.LocalName == "diffgram" &&
932                                         reader.NamespaceURI == XmlConstants.DiffgrNamespace) &&
933                                         !(reader.LocalName == "schema" &&
934                                         reader.NamespaceURI == XmlSchema.Namespace)) {
935                                         if (!reader.IsEmptyElement) {
936                                                 reader.Read ();
937                                                 reader.MoveToContent ();
938                                                 skippedTopLevelElement = true;
939                                         }
940                                         else {
941                                                 switch (mode) {
942                                                 case XmlReadMode.Auto:
943                                                 case XmlReadMode.InferSchema:
944                                                         DataSetName = reader.LocalName;
945                                                         break;
946                                                 }
947                                                 reader.Read ();
948                                         }
949                                 }
950                                 break;
951                         }
952
953                         // If schema, then read the first element as schema 
954                         if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
955                                 shouldNotInfer = true;
956                                 switch (mode) {
957                                 case XmlReadMode.IgnoreSchema:
958                                 case XmlReadMode.InferSchema:
959                                         reader.Skip ();
960                                         break;
961                                 case XmlReadMode.Fragment:
962                                         ReadXmlSchema (reader);
963                                         break;
964                                 case XmlReadMode.DiffGram:
965                                 case XmlReadMode.Auto:
966                                         if (Tables.Count == 0) {
967                                                 ReadXmlSchema (reader);
968                                                 if (mode == XmlReadMode.Auto)
969                                                         result = XmlReadMode.ReadSchema;
970                                         } else {
971                                         // otherwise just ignore and return IgnoreSchema
972                                                 reader.Skip ();
973                                                 result = XmlReadMode.IgnoreSchema;
974                                         }
975                                         break;
976                                 case XmlReadMode.ReadSchema:
977                                         ReadXmlSchema (reader);
978                                         break;
979                                 }
980                         }
981
982                         // If diffgram, then read the first element as diffgram 
983                         if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
984                                 switch (mode) {
985                                 case XmlReadMode.Auto:
986                                 case XmlReadMode.IgnoreSchema:
987                                 case XmlReadMode.DiffGram:
988                                         XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
989                                         DiffLoader.Load (reader);
990                                         if (mode == XmlReadMode.Auto)
991                                                 result = XmlReadMode.DiffGram;
992                                         shouldReadData = false;
993                                         break;
994                                 case XmlReadMode.Fragment:
995                                         reader.Skip ();
996                                         break;
997                                 default:
998                                         reader.Skip ();
999                                         break;
1000                                 }
1001                         }
1002
1003                         // if schema after diffgram, just skip it.
1004                         if (!shouldReadData && reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
1005                                 shouldNotInfer = true;
1006                                 switch (mode) {
1007                                 default:
1008                                         reader.Skip ();
1009                                         break;
1010                                 case XmlReadMode.ReadSchema:
1011                                 case XmlReadMode.DiffGram:
1012                                         if (Tables.Count == 0)
1013                                                 ReadXmlSchema (reader);
1014                                         break;
1015                                 }
1016                         }
1017
1018                         if (reader.EOF)
1019                                 return result == XmlReadMode.Auto ?
1020                                         potentialDataSetName != null && !shouldNotInfer ?
1021                                         XmlReadMode.InferSchema :
1022                                         XmlReadMode.IgnoreSchema : result;
1023
1024                         // Otherwise, read as dataset... but only when required.
1025                         if (shouldReadData && !shouldNotInfer) {
1026                                 switch (mode) {
1027                                 case XmlReadMode.Auto:
1028                                         if (Tables.Count > 0)
1029                                                 goto case XmlReadMode.IgnoreSchema;
1030                                         else
1031                                                 goto case XmlReadMode.InferSchema;
1032                                 case XmlReadMode.InferSchema:
1033                                         InferXmlSchema (doc, null);
1034                                         if (mode == XmlReadMode.Auto)
1035                                                 result = XmlReadMode.InferSchema;
1036                                         break;
1037                                 case XmlReadMode.IgnoreSchema:
1038                                 case XmlReadMode.Fragment:
1039                                 case XmlReadMode.DiffGram:
1040                                         break;
1041                                 default:
1042                                         shouldReadData = false;
1043                                         break;
1044                                 }
1045                         }
1046
1047                         if (shouldReadData) {
1048                                 XmlReader dataReader = reader;
1049                                 if (doc != null) {
1050                                         dataReader = new XmlNodeReader (doc);
1051                                         dataReader.MoveToContent ();
1052                                 }
1053                                 if (reader.NodeType == XmlNodeType.Element)
1054                                         XmlDataReader.ReadXml (this, dataReader,
1055                                                 mode);
1056                         }
1057
1058                         if (skippedTopLevelElement) {
1059                                 switch (result) {
1060                                 case XmlReadMode.Auto:
1061                                 case XmlReadMode.InferSchema:
1062 //                                      DataSetName = potentialDataSetName;
1063 //                                      result = XmlReadMode.InferSchema;
1064                                         break;
1065                                 }
1066                                 if (reader.NodeType == XmlNodeType.EndElement)
1067                                         reader.ReadEndElement ();
1068                         }
1069 //*
1070                         while (input.Depth > depth)
1071                                 input.Read ();
1072                         if (input.NodeType == XmlNodeType.EndElement)
1073                                 input.Read ();
1074 //*/
1075                         input.MoveToContent ();
1076
1077                         return result == XmlReadMode.Auto ?
1078                                 XmlReadMode.IgnoreSchema : result;
1079                 }
1080                 #endregion // Public Methods
1081
1082                 #region Public Events
1083
1084                 [DataCategory ("Action")]
1085                 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
1086                 public event MergeFailedEventHandler MergeFailed;
1087
1088                 #endregion // Public Events
1089
1090                 #region IListSource methods
1091                 IList IListSource.GetList ()
1092                 {
1093                         return DefaultViewManager;
1094                 }
1095                 
1096                 bool IListSource.ContainsListCollection {
1097                         get {
1098                                 return true;
1099                         }
1100                 }
1101                 #endregion IListSource methods
1102                 
1103                 #region ISupportInitialize methods
1104                 public void BeginInit ()
1105                 {
1106                 }
1107                 
1108                 public void EndInit ()
1109                 {
1110                 }
1111                 #endregion
1112
1113                 #region ISerializable
1114                 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
1115                 {
1116                         StringWriter sw = new StringWriter ();
1117                         XmlTextWriter writer = new XmlTextWriter (sw);
1118                         DoWriteXmlSchema (writer);
1119                         writer.Flush ();
1120                         si.AddValue ("XmlSchema", sw.ToString ());
1121                         
1122                         sw = new StringWriter ();
1123                         writer = new XmlTextWriter (sw);
1124                         WriteXml (writer, XmlWriteMode.DiffGram);
1125                         writer.Flush ();
1126                         si.AddValue ("XmlDiffGram", sw.ToString ());
1127                 }
1128                 #endregion
1129                 
1130                 #region Protected Methods
1131                 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
1132                 {
1133                         string s = info.GetValue ("XmlSchema", typeof (String)) as String;
1134                         XmlTextReader reader = new XmlTextReader (new StringReader (s));
1135                         ReadXmlSchema (reader);
1136                         reader.Close ();
1137                         
1138                         s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
1139                         reader = new XmlTextReader (new StringReader (s));
1140                         ReadXml (reader, XmlReadMode.DiffGram);
1141                         reader.Close ();
1142                 }
1143                 
1144                 
1145                 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
1146                 {
1147                         return null;
1148                 }
1149                 
1150                 protected virtual void ReadXmlSerializable (XmlReader reader)
1151                 {
1152                         ReadXml (reader);
1153                 }
1154
1155                 void IXmlSerializable.ReadXml (XmlReader reader)
1156                 {
1157                         ReadXmlSerializable(reader);
1158                 }
1159                 
1160                 void IXmlSerializable.WriteXml (XmlWriter writer)
1161                 {
1162                         DoWriteXmlSchema (writer);
1163                         WriteXml (writer, XmlWriteMode.DiffGram);
1164                 }
1165
1166                 XmlSchema IXmlSerializable.GetSchema ()
1167                 {
1168                         return GetSchemaSerializable ();
1169                 }
1170
1171                 protected virtual bool ShouldSerializeRelations ()
1172                 {
1173                         return true;
1174                 }
1175                 
1176                 protected virtual bool ShouldSerializeTables ()
1177                 {
1178                         return true;
1179                 }
1180
1181                 [MonoTODO]
1182                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
1183                 {
1184                 }
1185
1186                 [MonoTODO]
1187                 protected virtual void OnRemoveRelation (DataRelation relation)
1188                 {
1189                 }
1190
1191                 [MonoTODO]
1192                 protected virtual void OnRemoveTable (DataTable table)
1193                 {
1194                 }
1195
1196                 internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1197                 {
1198                         if (MergeFailed != null)
1199                                 MergeFailed (this, e);
1200                 }
1201
1202                 [MonoTODO]
1203                 protected internal void RaisePropertyChanging (string name)
1204                 {
1205                 }
1206                 #endregion
1207
1208                 #region Private Methods
1209
1210                 private XmlReadMode ReadXmlIgnoreSchema (XmlReader input, XmlReadMode mode, bool checkRecurse)
1211                 {
1212                         if (input.LocalName == "schema" &&
1213                                 input.NamespaceURI == XmlSchema.Namespace) {
1214                                 input.Skip ();
1215                         }
1216                         else if (input.LocalName == "diffgram" &&
1217                                 input.NamespaceURI == XmlConstants.DiffgrNamespace) {
1218                                 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
1219                                 DiffLoader.Load (input);
1220                         }
1221                         else if (checkRecurse ||
1222                                 input.LocalName == DataSetName &&
1223                                 input.NamespaceURI == Namespace) {
1224                                 XmlDataReader.ReadXml (this, input, mode);
1225                         }
1226                         else if (checkRecurse && !input.IsEmptyElement) {
1227                                 int depth = input.Depth;
1228                                 input.Read ();
1229                                 input.MoveToContent ();
1230                                 ReadXmlIgnoreSchema (input, mode, false);
1231                                 while (input.Depth > depth)
1232                                         input.Skip ();
1233                                 if (input.NodeType == XmlNodeType.EndElement)
1234                                         input.ReadEndElement ();
1235                         }
1236                         input.MoveToContent ();
1237                         return XmlReadMode.IgnoreSchema;
1238                 }
1239
1240                 private XmlReadMode ReadXmlReadSchema (XmlReader input, XmlReadMode mode, bool checkRecurse)
1241                 {
1242                         if (input.LocalName == "schema" &&
1243                                 input.NamespaceURI == XmlSchema.Namespace) {
1244                                 ReadXmlSchema (input);
1245                         }
1246                         else if (checkRecurse && !input.IsEmptyElement) {
1247                                 int depth = input.Depth;
1248                                 input.Read ();
1249                                 input.MoveToContent ();
1250                                 ReadXmlReadSchema (input, mode, false);
1251                                 while (input.Depth > depth)
1252                                         input.Skip ();
1253                                 if (input.NodeType == XmlNodeType.EndElement)
1254                                         input.ReadEndElement ();
1255                         }
1256                         else
1257                                 input.Skip ();
1258                         input.MoveToContent ();
1259                         return XmlReadMode.ReadSchema;
1260                 }
1261
1262                 internal static string WriteObjectXml (object o)
1263                 {
1264                         switch (Type.GetTypeCode (o.GetType ())) {
1265                                 case TypeCode.Boolean:
1266                                         return XmlConvert.ToString ((Boolean) o);
1267                                 case TypeCode.Byte:
1268                                         return XmlConvert.ToString ((Byte) o);
1269                                 case TypeCode.Char:
1270                                         return XmlConvert.ToString ((Char) o);
1271                                 case TypeCode.DateTime:
1272                                         return XmlConvert.ToString ((DateTime) o);
1273                                 case TypeCode.Decimal:
1274                                         return XmlConvert.ToString ((Decimal) o);
1275                                 case TypeCode.Double:
1276                                         return XmlConvert.ToString ((Double) o);
1277                                 case TypeCode.Int16:
1278                                         return XmlConvert.ToString ((Int16) o);
1279                                 case TypeCode.Int32:
1280                                         return XmlConvert.ToString ((Int32) o);
1281                                 case TypeCode.Int64:
1282                                         return XmlConvert.ToString ((Int64) o);
1283                                 case TypeCode.SByte:
1284                                         return XmlConvert.ToString ((SByte) o);
1285                                 case TypeCode.Single:
1286                                         return XmlConvert.ToString ((Single) o);
1287                                 case TypeCode.UInt16:
1288                                         return XmlConvert.ToString ((UInt16) o);
1289                                 case TypeCode.UInt32:
1290                                         return XmlConvert.ToString ((UInt32) o);
1291                                 case TypeCode.UInt64:
1292                                         return XmlConvert.ToString ((UInt64) o);
1293                         }
1294                         if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1295                         if (o is Guid) return XmlConvert.ToString ((Guid) o);
1296                         if (o is byte[]) return Convert.ToBase64String ((byte[])o);
1297                         return o.ToString ();
1298                 }
1299                 
1300                 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1301                 {
1302                         //Write out each table in order, providing it is not
1303                         //part of another table structure via a nested parent relationship
1304                         foreach (DataTable table in tableCollection) {
1305                                 bool isTopLevel = true;
1306                                 /*
1307                                 foreach (DataRelation rel in table.ParentRelations) {
1308                                         if (rel.Nested) {
1309                                                 isTopLevel = false;
1310                                                 break;
1311                                         }
1312                                 }
1313                                 */
1314                                 if (isTopLevel) {
1315                                         WriteTable ( writer, table, mode, version);
1316                                 }
1317                         }
1318                 }
1319
1320                 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1321                 {
1322                         DataRow[] rows = new DataRow [table.Rows.Count];
1323                         table.Rows.CopyTo (rows, 0);
1324                         WriteTable (writer, rows, mode, version, true);
1325                 }
1326
1327                 private void WriteTable (XmlWriter writer,
1328                         DataRow [] rows,
1329                         XmlWriteMode mode,
1330                         DataRowVersion version, bool skipIfNested)
1331                 {
1332                         //The columns can be attributes, hidden, elements, or simple content
1333                         //There can be 0-1 simple content cols or 0-* elements
1334                         System.Collections.ArrayList atts;
1335                         System.Collections.ArrayList elements;
1336                         DataColumn simple = null;
1337
1338                         if (rows.Length == 0) return;
1339                         DataTable table = rows[0].Table;
1340                         SplitColumns (table, out atts, out elements, out simple);
1341                         //sort out the namespacing
1342                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1343                         int relationCount = table.ParentRelations.Count;
1344                         DataRelation oneRel = relationCount == 1 ? table.ParentRelations [0] : null;
1345
1346                         foreach (DataRow row in rows) {
1347                                 if (skipIfNested) {
1348                                         // Skip rows that is a child of any tables.
1349                                         switch (relationCount) {
1350                                         case 0:
1351                                                 break;
1352                                         case 1:
1353                                                 if (!oneRel.Nested)
1354                                                         break;
1355                                                 if (row.GetParentRow (oneRel) != null)
1356                                                         continue;
1357                                                 break;
1358                                         case 2:
1359                                                 bool skip = false;
1360                                                 for (int i = 0; i < table.ParentRelations.Count; i++) {
1361                                                         DataRelation prel = table.ParentRelations [i];
1362                                                         if (!prel.Nested)
1363                                                                 continue;
1364                                                         if (row.GetParentRow (prel) != null) {
1365                                                                 skip = true;
1366                                                                 continue;
1367                                                         }
1368                                                 }
1369                                                 if (skip)
1370                                                         continue;
1371                                                 break;
1372                                         }
1373                                 }
1374
1375                                 if (!row.HasVersion(version) || 
1376                                    (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged 
1377                                       && version == DataRowVersion.Original))
1378                                         continue;
1379                                 
1380                                 // First check are all the rows null. If they are we just write empty element
1381                                 bool AllNulls = true;
1382                                 foreach (DataColumn dc in table.Columns) {
1383                                 
1384                                         if (row [dc.ColumnName, version] != DBNull.Value) {
1385                                                 AllNulls = false;
1386                                                 break;
1387                                         } 
1388                                 }
1389
1390                                 // If all of the columns were null, we have to write empty element
1391                                 if (AllNulls) {
1392                                         writer.WriteElementString (XmlConvert.EncodeLocalName (table.TableName), "");
1393                                         continue;
1394                                 }
1395                                 
1396                                 WriteTableElement (writer, mode, table, row, version);
1397                                 
1398                                 foreach (DataColumn col in atts) {                                      
1399                                         WriteColumnAsAttribute (writer, mode, col, row, version);
1400                                 }
1401                                 
1402                                 if (simple != null) {
1403                                         writer.WriteString (WriteObjectXml (row[simple, version]));
1404                                 }
1405                                 else {                                  
1406                                         foreach (DataColumn col in elements) {
1407                                                 WriteColumnAsElement (writer, mode, col, row, version);
1408                                         }
1409                                 }
1410                                 
1411                                 foreach (DataRelation relation in table.ChildRelations) {
1412                                         if (relation.Nested) {
1413                                                 WriteTable (writer, row.GetChildRows (relation), mode, version, false);
1414                                         }
1415                                 }
1416                                 
1417                                 writer.WriteEndElement ();
1418                         }
1419
1420                 }
1421
1422                 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1423                 {
1424                         string colnspc = null;
1425                         object rowObject = row [col, version];
1426                                                                         
1427                         if (rowObject == null || rowObject == DBNull.Value)
1428                                 return;
1429
1430                         if (col.Namespace != String.Empty)
1431                                 colnspc = col.Namespace;
1432         
1433                         //TODO check if I can get away with write element string
1434                         WriteStartElement (writer, mode, colnspc, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName));
1435                         writer.WriteString (WriteObjectXml (rowObject));
1436                         writer.WriteEndElement ();
1437                 }
1438
1439                 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1440                 {
1441                         if (!row.IsNull (col))
1442                                 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
1443                 }
1444
1445                 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1446                 {
1447                         //sort out the namespacing
1448                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1449
1450                         WriteStartElement (writer, mode, nspc, table.Prefix, XmlConvert.EncodeLocalName (table.TableName));
1451
1452                         if (mode == XmlWriteMode.DiffGram) {
1453                                 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1454                                 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", XmlConvert.ToString (row.XmlRowID));
1455                                 string modeName = null;
1456                                 if (row.RowState == DataRowState.Modified)
1457                                         modeName = "modified";
1458                                 else if (row.RowState == DataRowState.Added)
1459                                         modeName = "inserted";
1460
1461                                 if (version != DataRowVersion.Original && modeName != null)
1462                                         WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1463                         }
1464                 }
1465                     
1466                 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1467                 {
1468                         writer.WriteStartElement (prefix, name, nspc);
1469                 }
1470                 
1471                 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1472                 {
1473                         switch ( mode) {
1474                                 case XmlWriteMode.WriteSchema:
1475                                         writer.WriteAttributeString (prefix, name, nspc);
1476                                         break;
1477                                 case XmlWriteMode.DiffGram:
1478                                         writer.WriteAttributeString (prefix, name, nspc,stringValue);
1479                                         break;
1480                                 default:
1481                                         writer.WriteAttributeString (name, stringValue);
1482                                         break;                                  
1483                         };
1484                 }
1485                 
1486                 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1487                 {
1488                         if (mode == XmlWriteMode.DiffGram) {
1489                                 SetTableRowsID (table);
1490                                 WriteDiffGramElement (writer);
1491                         }
1492                         
1493                         WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1494                         
1495                         WriteTable (writer, table, mode, DataRowVersion.Default);
1496                         
1497                         if (mode == XmlWriteMode.DiffGram) {
1498                                 writer.WriteEndElement (); //DataSet name
1499                                 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1500
1501                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
1502                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1503                                         WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1504                                         writer.WriteEndElement ();
1505                                 }
1506                         }
1507                         writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1508                 }
1509
1510                 private void DoWriteXmlSchema (XmlWriter writer)
1511                 {
1512                         if (writer.WriteState == WriteState.Start)
1513                                 writer.WriteStartDocument ();
1514                         XmlSchemaWriter.WriteXmlSchema (this, writer);
1515                 }
1516                 
1517                 ///<summary>
1518                 /// Helper function to split columns into attributes elements and simple
1519                 /// content
1520                 /// </summary>
1521                 internal static void SplitColumns (DataTable table, 
1522                         out ArrayList atts, 
1523                         out ArrayList elements, 
1524                         out DataColumn simple)
1525                 {
1526                         //The columns can be attributes, hidden, elements, or simple content
1527                         //There can be 0-1 simple content cols or 0-* elements
1528                         atts = new System.Collections.ArrayList ();
1529                         elements = new System.Collections.ArrayList ();
1530                         simple = null;
1531                         
1532                         //Sort out the columns
1533                         foreach (DataColumn col in table.Columns) {
1534                                 switch (col.ColumnMapping) {
1535                                         case MappingType.Attribute:
1536                                                 atts.Add (col);
1537                                                 break;
1538                                         case MappingType.Element:
1539                                                 elements.Add (col);
1540                                                 break;
1541                                         case MappingType.SimpleContent:
1542                                                 if (simple != null) {
1543                                                         throw new System.InvalidOperationException ("There may only be one simple content element");
1544                                                 }
1545                                                 simple = col;
1546                                                 break;
1547                                         default:
1548                                                 //ignore Hidden elements
1549                                                 break;
1550                                 }
1551                         }
1552                 }
1553
1554                 private void WriteDiffGramElement(XmlWriter writer)
1555                 {
1556                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1557                         WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1558                 }
1559
1560                 private void SetRowsID()
1561                 {
1562                         foreach (DataTable Table in Tables)
1563                                 SetTableRowsID (Table);
1564                 }
1565                 
1566                 private void SetTableRowsID (DataTable Table)
1567                 {
1568                         int dataRowID = 0;
1569                         foreach (DataRow Row in Table.Rows) {
1570                                 Row.XmlRowID = dataRowID;
1571                                 dataRowID++;
1572                         }
1573                 }
1574                 #endregion //Private Xml Serialisation
1575         }
1576 }