This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[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                         DataSet Copy = new DataSet ();
351                         CopyProperties (Copy);
352
353                         foreach (DataTable Table in Tables) {
354                                 Copy.Tables.Add (Table.Clone ());
355                         }
356
357                         //Copy Relationships between tables after existance of tables
358                         //and setting properties correctly
359                         CopyRelations (Copy);
360                         
361                         return Copy;
362                 }
363
364                 // Copies both the structure and data for this DataSet.
365                 public DataSet Copy ()
366                 {
367                         DataSet Copy = new DataSet ();
368                         CopyProperties (Copy);
369
370                         // Copy DatSet's tables
371                         foreach (DataTable Table in Tables) 
372                                 Copy.Tables.Add (Table.Copy ());
373
374                         //Copy Relationships between tables after existance of tables
375                         //and setting properties correctly
376                         CopyRelations (Copy);
377
378                         return Copy;
379                 }
380
381                 private void CopyProperties (DataSet Copy)
382                 {
383                         Copy.CaseSensitive = CaseSensitive;
384                         //Copy.Container = Container
385                         Copy.DataSetName = DataSetName;
386                         //Copy.DefaultViewManager
387                         //Copy.DesignMode
388                         Copy.EnforceConstraints = EnforceConstraints;
389                         if(ExtendedProperties.Count > 0) {
390                                 //  Cannot copy extended properties directly as the property does not have a set accessor
391                 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
392                 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
393                 for (int i=0; i < ExtendedProperties.Count; i++)
394                                         Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
395                         }
396             Copy.Locale = Locale;
397                         Copy.Namespace = Namespace;
398                         Copy.Prefix = Prefix;                   
399                         //Copy.Site = Site; // FIXME : Not sure of this.
400
401                 }
402                 
403                 
404                 private void CopyRelations (DataSet Copy)
405                 {
406
407                         //Creation of the relation contains some of the properties, and the constructor
408                         //demands these values. instead changing the DataRelation constructor and behaviour the
409                         //parameters are pre-configured and sent to the most general constructor
410
411                         foreach (DataRelation MyRelation in this.Relations) {
412                                 string pTable = MyRelation.ParentTable.TableName;
413                                 string cTable = MyRelation.ChildTable.TableName;
414                                 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length]; 
415                                 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
416                                 int i = 0;
417                                 
418                                 foreach (DataColumn DC in MyRelation.ParentColumns) {
419                                         P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
420                                         i++;
421                                 }
422
423                                 i = 0;
424
425                                 foreach (DataColumn DC in MyRelation.ChildColumns) {
426                                         C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
427                                         i++;
428                                 }
429                                 
430                                 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
431                                 //cRel.ChildColumns = MyRelation.ChildColumns;
432                                 //cRel.ChildTable = MyRelation.ChildTable;
433                                 //cRel.ExtendedProperties = cRel.ExtendedProperties; 
434                                 //cRel.Nested = MyRelation.Nested;
435                                 //cRel.ParentColumns = MyRelation.ParentColumns;
436                                 //cRel.ParentTable = MyRelation.ParentTable;
437                                                                 
438                                 Copy.Relations.Add (cRel);
439                         }
440                 }
441
442                 
443
444
445                 public DataSet GetChanges ()
446                 {
447                         return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
448                 }
449
450                 
451                 public DataSet GetChanges (DataRowState rowStates)
452                 {
453                         if (!HasChanges (rowStates))
454                                 return null;
455                         
456                         DataSet copySet = Clone ();
457                         Hashtable addedRows = new Hashtable ();
458
459                         IEnumerator tableEnumerator = Tables.GetEnumerator ();
460                         DataTable origTable;
461                         DataTable copyTable;
462                         while (tableEnumerator.MoveNext ()) {
463                                 origTable = (DataTable)tableEnumerator.Current;
464                                 copyTable = copySet.Tables[origTable.TableName];
465                                 
466                                 // Look for relations that have this table as child
467                                 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
468
469                                 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
470                                 while (rowEnumerator.MoveNext ()) {
471                                         DataRow row = (DataRow)rowEnumerator.Current;
472                                         
473                                         if (row.IsRowChanged (rowStates))
474                                                 AddChangedRow (addedRows, copySet, copyTable, relations, row);
475                                 }
476                         }
477                         return copySet;
478                 }
479                 
480                 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
481                 {
482                         if (addedRows.ContainsKey (row)) return;
483                         
484                         relations.Reset ();
485                         while (relations.MoveNext ()) {
486                                 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
487                                 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
488                                 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
489                                 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
490                         }
491                 
492                         DataRow newRow = copyTable.NewRow ();
493                         copyTable.Rows.Add (newRow);
494                         row.CopyValuesToRow (newRow);
495                         newRow.XmlRowID = row.XmlRowID;
496                         addedRows.Add (row,row);
497                 }
498
499 #if NET_2_0
500                 [MonoTODO]
501                 public DataTableReader GetDataReader (DataTable[] dataTables)
502                 {
503                         throw new NotImplementedException ();
504                 }
505
506                 [MonoTODO]
507                 public DataTableReader GetDataReader ()
508                 {
509                         throw new NotImplementedException ();
510                 }
511 #endif
512                 
513                 public string GetXml ()
514                 {
515                         StringWriter Writer = new StringWriter ();
516                         WriteXml (Writer, XmlWriteMode.IgnoreSchema);
517                         return Writer.ToString ();
518                 }
519
520                 public string GetXmlSchema ()
521                 {
522                         StringWriter Writer = new StringWriter ();
523                         WriteXmlSchema (Writer);
524                         return Writer.ToString ();
525                 }
526
527                 [MonoTODO]
528                 public bool HasChanges ()
529                 {
530                         return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
531                 }
532
533                 [MonoTODO]
534                 public bool HasChanges (DataRowState rowState)
535                 {
536                         if (((int)rowState & 0xffffffe0) != 0)
537                                 throw new ArgumentOutOfRangeException ("rowState");
538
539                         DataTableCollection tableCollection = Tables;
540                         DataTable table;
541                         DataRowCollection rowCollection;
542                         DataRow row;
543
544                         for (int i = 0; i < tableCollection.Count; i++) {
545                                 table = tableCollection[i];
546                                 rowCollection = table.Rows;
547                                 for (int j = 0; j < rowCollection.Count; j++) {
548                                         row = rowCollection[j];
549                                         if ((row.RowState & rowState) != 0)
550                                                 return true;
551                                 }
552                         }
553
554                         return false;           
555                 }
556
557                 public void InferXmlSchema (XmlReader reader, string[] nsArray)
558                 {
559                         if (reader == null)
560                                 return;
561                         XmlDocument doc = new XmlDocument ();
562                         doc.Load (reader);
563                         InferXmlSchema (doc, nsArray);
564                 }
565
566                 private void InferXmlSchema (XmlDocument doc, string [] nsArray)
567                 {
568                         XmlDataInferenceLoader.Infer (this, doc, XmlReadMode.InferSchema, nsArray);
569                 }
570
571                 public void InferXmlSchema (Stream stream, string[] nsArray)
572                 {
573                         InferXmlSchema (new XmlTextReader (stream), nsArray);
574                 }
575
576                 public void InferXmlSchema (TextReader reader, string[] nsArray)
577                 {
578                         InferXmlSchema (new XmlTextReader (reader), nsArray);
579                 }
580
581                 public void InferXmlSchema (string fileName, string[] nsArray)
582                 {
583                         XmlTextReader reader = new XmlTextReader (fileName);
584                         try {
585                                 InferXmlSchema (reader, nsArray);
586                         } finally {
587                                 reader.Close ();
588                         }
589                 }
590
591 #if NET_2_0
592                 [MonoTODO]
593                 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
594                 {
595                         throw new NotImplementedException ();
596                 }
597
598                 [MonoTODO]
599                 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
600                 {
601                         throw new NotImplementedException ();
602                 }
603 #endif
604
605                 public virtual void RejectChanges ()
606                 {
607                         int i;
608                         bool oldEnforceConstraints = this.EnforceConstraints;
609                         this.EnforceConstraints = false;
610                         
611                         for (i = 0; i < this.Tables.Count;i++) 
612                                 this.Tables[i].RejectChanges ();
613
614                         this.EnforceConstraints = oldEnforceConstraints;
615                 }
616
617                 public virtual void Reset ()
618                 {
619                         IEnumerator constraintEnumerator;
620
621                         // first we remove all ForeignKeyConstraints (if we will not do that
622                         // we will get an exception when clearing the tables).
623                         for (int i = 0; i < Tables.Count; i++) {
624                                 ConstraintCollection cc = Tables[i].Constraints;
625                                 for (int j = 0; j < cc.Count; j++) {
626                                         if (cc[j] is ForeignKeyConstraint)
627                                                 cc.Remove (cc[j]);
628                                 }
629                         }
630
631                         Clear ();
632                         Relations.Clear ();
633                         Tables.Clear ();
634                 }
635
636                 public void WriteXml (Stream stream)
637                 {
638                         XmlTextWriter writer = new XmlTextWriter (stream, null);
639                         writer.Formatting = Formatting.Indented;
640                         WriteXml (writer);
641                 }
642
643                 ///<summary>
644                 /// Writes the current data for the DataSet to the specified file.
645                 /// </summary>
646                 /// <param name="filename">Fully qualified filename to write to</param>
647                 public void WriteXml (string fileName)
648                 {
649                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
650                         writer.Formatting = Formatting.Indented;
651                         writer.WriteStartDocument (true);
652                         try {
653                                 WriteXml (writer);
654                         }
655                         finally {
656                                 writer.WriteEndDocument ();
657                                 writer.Close ();
658                         }
659                 }
660
661                 public void WriteXml (TextWriter writer)
662                 {
663                         XmlTextWriter xwriter = new XmlTextWriter (writer);
664                         xwriter.Formatting = Formatting.Indented;
665                         WriteXml (xwriter);
666                 }
667
668                 public void WriteXml (XmlWriter writer)
669                 {
670                         WriteXml (writer, XmlWriteMode.IgnoreSchema);
671                 }
672
673                 public void WriteXml (string filename, XmlWriteMode mode)
674                 {
675                         XmlTextWriter writer = new XmlTextWriter (filename, null);
676                         writer.Formatting = Formatting.Indented;
677                         writer.WriteStartDocument (true);
678                         
679                         try {
680                                 WriteXml (writer, mode);
681                         }
682                         finally {
683                                 writer.WriteEndDocument ();
684                                 writer.Close ();
685                         }
686                 }
687
688                 public void WriteXml (Stream stream, XmlWriteMode mode)
689                 {
690                         XmlTextWriter writer = new XmlTextWriter (stream, null);
691                         writer.Formatting = Formatting.Indented;
692                         WriteXml (writer, mode);
693                 }
694
695                 public void WriteXml (TextWriter writer, XmlWriteMode mode)
696                 {
697                         XmlTextWriter xwriter = new XmlTextWriter (writer);
698                         xwriter.Formatting = Formatting.Indented;
699                         WriteXml (xwriter, mode);
700                 }
701
702                 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
703                 {
704                         if (mode == XmlWriteMode.DiffGram) {
705                                 SetRowsID();
706                                 WriteDiffGramElement(writer);
707                         }
708                         
709                         // It should not write when there is no content to be written
710                         bool shouldOutputContent = (mode != XmlWriteMode.DiffGram);
711                         for (int n = 0; n < tableCollection.Count && !shouldOutputContent; n++)
712                                 shouldOutputContent = tableCollection [n].Rows.Count > 0;
713                                 
714                         if (shouldOutputContent) {
715                                 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
716                                 
717                                 if (mode == XmlWriteMode.WriteSchema)
718                                         DoWriteXmlSchema (writer);
719                                 
720                                 WriteTables (writer, mode, Tables, DataRowVersion.Default);
721                                 writer.WriteEndElement ();
722                         }
723                         
724                         if (mode == XmlWriteMode.DiffGram) {
725                                 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
726
727                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
728                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
729                                         WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
730                                         writer.WriteEndElement ();
731                                 }
732                         }
733                         
734                         if (mode == XmlWriteMode.DiffGram)
735                                 writer.WriteEndElement (); // diffgr:diffgram
736
737                         writer.Flush ();
738                 }
739
740                 public void WriteXmlSchema (Stream stream)
741                 {
742                         XmlTextWriter writer = new XmlTextWriter (stream, null );
743                         writer.Formatting = Formatting.Indented;
744                         WriteXmlSchema (writer);        
745                 }
746
747                 public void WriteXmlSchema (string fileName)
748                 {
749                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
750                         try {
751                                 writer.Formatting = Formatting.Indented;
752                                 writer.WriteStartDocument (true);
753                                 WriteXmlSchema (writer);
754                         } finally {
755                                 writer.WriteEndDocument ();
756                                 writer.Close ();
757                         }
758                 }
759
760                 public void WriteXmlSchema (TextWriter writer)
761                 {
762                         XmlTextWriter xwriter = new XmlTextWriter (writer);
763                         try {
764                                 xwriter.Formatting = Formatting.Indented;
765 //                              xwriter.WriteStartDocument ();
766                                 WriteXmlSchema (xwriter);
767                         } finally {
768 //                              xwriter.WriteEndDocument ();
769                                 xwriter.Close ();
770                         }
771                 }
772
773                 public void WriteXmlSchema (XmlWriter writer)
774                 {
775                         //Create a skeleton doc and then write the schema 
776                         //proper which is common to the WriteXml method in schema mode
777                         DoWriteXmlSchema (writer);
778                 }
779
780                 public void ReadXmlSchema (Stream stream)
781                 {
782                         XmlReader reader = new XmlTextReader (stream, null);
783                         ReadXmlSchema (reader);
784                 }
785
786                 public void ReadXmlSchema (string str)
787                 {
788                         XmlReader reader = new XmlTextReader (str);
789                         try {
790                                 ReadXmlSchema (reader);
791                         }
792                         finally {
793                                 reader.Close ();
794                         }
795                 }
796
797                 public void ReadXmlSchema (TextReader treader)
798                 {
799                         XmlReader reader = new XmlTextReader (treader);
800                         ReadXmlSchema (reader);                 
801                 }
802
803                 public void ReadXmlSchema (XmlReader reader)
804                 {
805 #if true
806                         new XmlSchemaDataImporter (this, reader).Process ();
807 #else
808                         XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
809                         SchemaMapper.Read (reader);
810 #endif
811                 }
812
813                 public XmlReadMode ReadXml (Stream stream)
814                 {
815                         return ReadXml (new XmlTextReader (stream));
816                 }
817
818                 public XmlReadMode ReadXml (string str)
819                 {
820                         XmlTextReader reader = new XmlTextReader (str);
821                         try {
822                                 return ReadXml (reader);
823                         }
824                         finally {
825                                 reader.Close ();
826                         }
827                 }
828
829                 public XmlReadMode ReadXml (TextReader reader)
830                 {
831                         return ReadXml (new XmlTextReader (reader));
832                 }
833
834                 public XmlReadMode ReadXml (XmlReader r)
835                 {
836                         return ReadXml (r, XmlReadMode.Auto);
837                 }
838
839                 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
840                 {
841                         return ReadXml (new XmlTextReader (stream), mode);
842                 }
843
844                 public XmlReadMode ReadXml (string str, XmlReadMode mode)
845                 {
846                         XmlTextReader reader = new XmlTextReader (str);
847                         try {
848                                 return ReadXml (reader, mode);
849                         }
850                         finally {
851                                 reader.Close ();
852                         }
853                 }
854
855                 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
856                 {
857                         return ReadXml (new XmlTextReader (reader), mode);
858                 }
859
860                 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
861                 {
862                         switch (reader.ReadState) {
863                         case ReadState.EndOfFile:
864                         case ReadState.Error:
865                         case ReadState.Closed:
866                                 return mode;
867                         }
868                         // Skip XML declaration and prolog
869                         reader.MoveToContent();
870                         if (reader.EOF)
871                                 return mode;
872
873                         XmlReadMode Result = mode;
874
875                         // If diffgram, then read the first element as diffgram 
876                         if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
877                                 switch (mode) {
878                                 case XmlReadMode.Auto:
879                                 case XmlReadMode.DiffGram:
880                                         XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
881                                         DiffLoader.Load (reader);
882                                         // (and leave rest of the reader as is)
883                                         return  XmlReadMode.DiffGram;
884                                 case XmlReadMode.Fragment:
885                                         reader.Skip ();
886                                         // (and continue to read)
887                                         break;
888                                 default:
889                                         reader.Skip ();
890                                         // (and leave rest of the reader as is)
891                                         return mode;
892                                 }
893                         }
894                         // If schema, then read the first element as schema 
895                         if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
896                                 switch (mode) {
897                                 case XmlReadMode.IgnoreSchema:
898                                 case XmlReadMode.InferSchema:
899                                         reader.Skip ();
900                                         // (and break up read)
901                                         return mode;
902                                 case XmlReadMode.Fragment:
903                                         ReadXmlSchema (reader);
904                                         // (and continue to read)
905                                         break;
906                                 case XmlReadMode.Auto:
907                                         if (Tables.Count == 0) {
908                                                 ReadXmlSchema (reader);
909                                                 return XmlReadMode.ReadSchema;
910                                         } else {
911                                         // otherwise just ignore and return IgnoreSchema
912                                                 reader.Skip ();
913                                                 return XmlReadMode.IgnoreSchema;
914                                         }
915                                 default:
916                                         ReadXmlSchema (reader);
917                                         // (and leave rest of the reader as is)
918                                         return mode; // When DiffGram, return DiffGram
919                                 }
920                         }
921                         // Otherwise, read as dataset... but only when required.
922                         XmlReadMode explicitReturnMode = XmlReadMode.Auto;
923                         XmlDocument doc;
924                         switch (mode) {
925                         case XmlReadMode.Auto:
926                                 if (Tables.Count > 0)
927                                         goto case XmlReadMode.IgnoreSchema;
928                                 else
929                                         goto case XmlReadMode.InferSchema;
930                         case XmlReadMode.InferSchema:
931                                 doc = new XmlDocument ();
932                                 do {
933                                         doc.AppendChild (doc.ReadNode (reader));
934                                         reader.MoveToContent ();
935                                         if (doc.DocumentElement != null)
936                                                 break;
937                                 } while (!reader.EOF);
938                                 InferXmlSchema (doc, null);
939                                 reader = new XmlNodeReader (doc);
940                                 explicitReturnMode = XmlReadMode.InferSchema;
941                                 break;
942                         case XmlReadMode.ReadSchema:
943                                 doc = new XmlDocument ();
944                                 do {
945                                         doc.AppendChild (doc.ReadNode (reader));
946                                         reader.MoveToContent ();
947                                         if (doc.DocumentElement != null)
948                                                 break;
949                                 } while (!reader.EOF);
950                                 if (doc.DocumentElement != null) {
951                                         XmlElement schema = doc.DocumentElement ["schema", XmlSchema.Namespace] as XmlElement;
952                                         if (schema != null) {
953                                                 ReadXmlSchema (new XmlNodeReader (schema));
954                                                 explicitReturnMode = XmlReadMode.ReadSchema;
955                                         }
956                                 }
957                                 reader = new XmlNodeReader (doc);
958                                 break;
959                         case XmlReadMode.IgnoreSchema:
960                         case XmlReadMode.Fragment:
961                                 break;
962                         default:
963                                 reader.Skip ();
964                                 return mode;
965                         }
966
967                         XmlDataReader.ReadXml (this, reader, mode);
968                         if (explicitReturnMode != XmlReadMode.Auto)
969                                 return explicitReturnMode;
970                         return mode == XmlReadMode.Auto ? XmlReadMode.IgnoreSchema : mode;
971                 }
972                 #endregion // Public Methods
973
974                 #region Public Events
975
976                 [DataCategory ("Action")]
977                 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
978                 public event MergeFailedEventHandler MergeFailed;
979
980                 #endregion // Public Events
981
982                 #region IListSource methods
983                 IList IListSource.GetList ()
984                 {
985                         return DefaultViewManager;
986                 }
987                 
988                 bool IListSource.ContainsListCollection {
989                         get {
990                                 return true;
991                         }
992                 }
993                 #endregion IListSource methods
994                 
995                 #region ISupportInitialize methods
996                 public void BeginInit ()
997                 {
998                 }
999                 
1000                 public void EndInit ()
1001                 {
1002                 }
1003                 #endregion
1004
1005                 #region ISerializable
1006                 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
1007                 {
1008                         StringWriter sw = new StringWriter ();
1009                         XmlTextWriter writer = new XmlTextWriter (sw);
1010                         DoWriteXmlSchema (writer);
1011                         writer.Flush ();
1012                         si.AddValue ("XmlSchema", sw.ToString ());
1013                         
1014                         sw = new StringWriter ();
1015                         writer = new XmlTextWriter (sw);
1016                         WriteXml (writer, XmlWriteMode.DiffGram);
1017                         writer.Flush ();
1018                         si.AddValue ("XmlDiffGram", sw.ToString ());
1019                 }
1020                 #endregion
1021                 
1022                 #region Protected Methods
1023                 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
1024                 {
1025                         string s = info.GetValue ("XmlSchema", typeof (String)) as String;
1026                         XmlTextReader reader = new XmlTextReader (new StringReader (s));
1027                         ReadXmlSchema (reader);
1028                         reader.Close ();
1029                         
1030                         s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
1031                         reader = new XmlTextReader (new StringReader (s));
1032                         ReadXml (reader, XmlReadMode.DiffGram);
1033                         reader.Close ();
1034                 }
1035                 
1036                 
1037                 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
1038                 {
1039                         return null;
1040                 }
1041                 
1042                 protected virtual void ReadXmlSerializable (XmlReader reader)
1043                 {
1044                         reader.MoveToContent ();
1045                         reader.ReadStartElement ();
1046                         reader.MoveToContent ();
1047                         ReadXmlSchema (reader);
1048                         reader.MoveToContent ();
1049                         ReadXml (reader, XmlReadMode.DiffGram);
1050                         reader.MoveToContent ();
1051                         reader.ReadEndElement ();
1052                 }
1053
1054                 void IXmlSerializable.ReadXml (XmlReader reader)
1055                 {
1056                         ReadXmlSerializable(reader);
1057                 }
1058                 
1059                 void IXmlSerializable.WriteXml (XmlWriter writer)
1060                 {
1061                         DoWriteXmlSchema (writer);
1062                         WriteXml (writer, XmlWriteMode.DiffGram);
1063                 }
1064
1065                 XmlSchema IXmlSerializable.GetSchema ()
1066                 {
1067                         return BuildSchema ();
1068                 }
1069
1070                 protected virtual bool ShouldSerializeRelations ()
1071                 {
1072                         return true;
1073                 }
1074                 
1075                 protected virtual bool ShouldSerializeTables ()
1076                 {
1077                         return true;
1078                 }
1079
1080                 [MonoTODO]
1081                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
1082                 {
1083                 }
1084
1085                 [MonoTODO]
1086                 protected virtual void OnRemoveRelation (DataRelation relation)
1087                 {
1088                 }
1089
1090                 [MonoTODO]
1091                 protected virtual void OnRemoveTable (DataTable table)
1092                 {
1093                 }
1094
1095                 internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1096                 {
1097                         if (MergeFailed != null)
1098                                 MergeFailed (this, e);
1099                 }
1100
1101                 [MonoTODO]
1102                 protected internal void RaisePropertyChanging (string name)
1103                 {
1104                 }
1105                 #endregion
1106
1107                 #region Private Xml Serialisation
1108
1109                 private string WriteObjectXml (object o)
1110                 {
1111                         switch (Type.GetTypeCode (o.GetType ())) {
1112                                 case TypeCode.Boolean:
1113                                         return XmlConvert.ToString ((Boolean) o);
1114                                 case TypeCode.Byte:
1115                                         return XmlConvert.ToString ((Byte) o);
1116                                 case TypeCode.Char:
1117                                         return XmlConvert.ToString ((Char) o);
1118                                 case TypeCode.DateTime:
1119                                         return XmlConvert.ToString ((DateTime) o);
1120                                 case TypeCode.Decimal:
1121                                         return XmlConvert.ToString ((Decimal) o);
1122                                 case TypeCode.Double:
1123                                         return XmlConvert.ToString ((Double) o);
1124                                 case TypeCode.Int16:
1125                                         return XmlConvert.ToString ((Int16) o);
1126                                 case TypeCode.Int32:
1127                                         return XmlConvert.ToString ((Int32) o);
1128                                 case TypeCode.Int64:
1129                                         return XmlConvert.ToString ((Int64) o);
1130                                 case TypeCode.SByte:
1131                                         return XmlConvert.ToString ((SByte) o);
1132                                 case TypeCode.Single:
1133                                         return XmlConvert.ToString ((Single) o);
1134                                 case TypeCode.UInt16:
1135                                         return XmlConvert.ToString ((UInt16) o);
1136                                 case TypeCode.UInt32:
1137                                         return XmlConvert.ToString ((UInt32) o);
1138                                 case TypeCode.UInt64:
1139                                         return XmlConvert.ToString ((UInt64) o);
1140                         }
1141                         if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1142                         if (o is Guid) return XmlConvert.ToString ((Guid) o);
1143                         if (o is byte[]) return Convert.ToBase64String ((byte[])o);
1144                         return o.ToString ();
1145                 }
1146                 
1147                 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1148                 {
1149                         //Write out each table in order, providing it is not
1150                         //part of another table structure via a nested parent relationship
1151                         foreach (DataTable table in tableCollection) {
1152                                 bool isTopLevel = true;
1153                                 /*
1154                                 foreach (DataRelation rel in table.ParentRelations) {
1155                                         if (rel.Nested) {
1156                                                 isTopLevel = false;
1157                                                 break;
1158                                         }
1159                                 }
1160                                 */
1161                                 if (isTopLevel) {
1162                                         WriteTable ( writer, table, mode, version);
1163                                 }
1164                         }
1165                 }
1166
1167                 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1168                 {
1169                         DataRow[] rows = new DataRow [table.Rows.Count];
1170                         table.Rows.CopyTo (rows, 0);
1171                         WriteTable (writer, rows, mode, version, true);
1172                 }
1173
1174                 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version, bool skipIfNested)
1175                 {
1176                         //The columns can be attributes, hidden, elements, or simple content
1177                         //There can be 0-1 simple content cols or 0-* elements
1178                         System.Collections.ArrayList atts;
1179                         System.Collections.ArrayList elements;
1180                         DataColumn simple = null;
1181
1182                         if (rows.Length == 0) return;
1183                         DataTable table = rows[0].Table;
1184                         SplitColumns (table, out atts, out elements, out simple);
1185                         //sort out the namespacing
1186                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1187                         int relationCount = table.ParentRelations.Count;
1188                         DataRelation oneRel = relationCount == 1 ? table.ParentRelations [0] : null;
1189
1190                         foreach (DataRow row in rows) {
1191                                 if (skipIfNested) {
1192                                         // Skip rows that is a child of any tables.
1193                                         switch (relationCount) {
1194                                         case 0:
1195                                                 break;
1196                                         case 1:
1197                                                 if (!oneRel.Nested)
1198                                                         break;
1199                                                 if (row.GetParentRow (oneRel) != null)
1200                                                         continue;
1201                                                 break;
1202                                         case 2:
1203                                                 bool skip = false;
1204                                                 for (int i = 0; i < table.ParentRelations.Count; i++) {
1205                                                         DataRelation prel = table.ParentRelations [i];
1206                                                         if (!prel.Nested)
1207                                                                 continue;
1208                                                         if (row.GetParentRow (prel) != null) {
1209                                                                 skip = true;
1210                                                                 continue;
1211                                                         }
1212                                                 }
1213                                                 if (skip)
1214                                                         continue;
1215                                                 break;
1216                                         }
1217                                 }
1218
1219                                 if (!row.HasVersion(version) || 
1220                                    (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged 
1221                                       && version == DataRowVersion.Original))
1222                                         continue;
1223                                 
1224                                 // First check are all the rows null. If they are we just write empty element
1225                                 bool AllNulls = true;
1226                                 foreach (DataColumn dc in table.Columns) {
1227                                 
1228                                         if (row [dc.ColumnName, version] != DBNull.Value) {
1229                                                 AllNulls = false;
1230                                                 break;
1231                                         } 
1232                                 }
1233
1234                                 // If all of the columns were null, we have to write empty element
1235                                 if (AllNulls) {
1236                                         writer.WriteElementString (XmlConvert.EncodeLocalName (table.TableName), "");
1237                                         continue;
1238                                 }
1239                                 
1240                                 WriteTableElement (writer, mode, table, row, version);
1241                                 
1242                                 foreach (DataColumn col in atts) {                                      
1243                                         WriteColumnAsAttribute (writer, mode, col, row, version);
1244                                 }
1245                                 
1246                                 if (simple != null) {
1247                                         writer.WriteString (WriteObjectXml (row[simple, version]));
1248                                 }
1249                                 else {                                  
1250                                         foreach (DataColumn col in elements) {
1251                                                 WriteColumnAsElement (writer, mode, col, row, version);
1252                                         }
1253                                 }
1254                                 
1255                                 foreach (DataRelation relation in table.ChildRelations) {
1256                                         if (relation.Nested) {
1257                                                 WriteTable (writer, row.GetChildRows (relation), mode, version, false);
1258                                         }
1259                                 }
1260                                 
1261                                 writer.WriteEndElement ();
1262                         }
1263
1264                 }
1265
1266                 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1267                 {
1268                         string colnspc = null;
1269                         object rowObject = row [col, version];
1270                                                                         
1271                         if (rowObject == null || rowObject == DBNull.Value)
1272                                 return;
1273
1274                         if (col.Namespace != String.Empty)
1275                                 colnspc = col.Namespace;
1276         
1277                         //TODO check if I can get away with write element string
1278                         WriteStartElement (writer, mode, colnspc, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName));
1279                         writer.WriteString (WriteObjectXml (rowObject));
1280                         writer.WriteEndElement ();
1281                 }
1282
1283                 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1284                 {
1285                         WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
1286                 }
1287
1288                 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1289                 {
1290                         //sort out the namespacing
1291                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1292
1293                         WriteStartElement (writer, mode, nspc, table.Prefix, XmlConvert.EncodeLocalName (table.TableName));
1294
1295                         if (mode == XmlWriteMode.DiffGram) {
1296                                 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1297                                 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", XmlConvert.ToString (row.XmlRowID));
1298                                 string modeName = null;
1299                                 if (row.RowState == DataRowState.Modified)
1300                                         modeName = "modified";
1301                                 else if (row.RowState == DataRowState.Added)
1302                                         modeName = "inserted";
1303
1304                                 if (version != DataRowVersion.Original && modeName != null)
1305                                         WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1306                         }
1307                 }
1308                     
1309                 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1310                 {
1311                         writer.WriteStartElement (prefix, name, nspc);
1312                 }
1313                 
1314                 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1315                 {
1316                         switch ( mode) {
1317                                 case XmlWriteMode.WriteSchema:
1318                                         writer.WriteAttributeString (prefix, name, nspc);
1319                                         break;
1320                                 case XmlWriteMode.DiffGram:
1321                                         writer.WriteAttributeString (prefix, name, nspc,stringValue);
1322                                         break;
1323                                 default:
1324                                         writer.WriteAttributeString (name, stringValue);
1325                                         break;                                  
1326                         };
1327                 }
1328                 
1329                 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1330                 {
1331                         ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1332
1333                         if (mode == XmlWriteMode.DiffGram) {
1334                                 SetTableRowsID (table);
1335                                 WriteDiffGramElement (writer);
1336                         }
1337                         
1338                         WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1339                         
1340                         WriteTable (writer, table, mode, DataRowVersion.Default);
1341                         
1342                         if (mode == XmlWriteMode.DiffGram) {
1343                                 writer.WriteEndElement (); //DataSet name
1344                                 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1345
1346                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
1347                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1348                                         WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1349                                         writer.WriteEndElement ();
1350                                 }
1351                         }
1352                         writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1353                 }
1354                 
1355
1356                 private void CheckNamespace (string prefix, string ns, XmlNamespaceManager nsmgr, XmlSchema schema)
1357                 {
1358                         if (ns == String.Empty)
1359                                 return;
1360                         if (ns != nsmgr.DefaultNamespace) {
1361                                 if (nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix)) != ns) {
1362                                         for (int i = 1; i < int.MaxValue; i++) {
1363                                                 string p = nsmgr.NameTable.Add ("app" + i);
1364                                                 if (!nsmgr.HasNamespace (p)) {
1365                                                         nsmgr.AddNamespace (p, ns);
1366                                                         HandleExternalNamespace (p, ns, schema);
1367                                                         break;
1368                                                 }
1369                                         }
1370                                 }
1371                         }
1372                 }
1373                 
1374                 XmlSchema BuildSchema ()
1375                 {
1376                         return BuildSchema (Tables, Relations);
1377                 }
1378                 
1379                 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1380                 {
1381                         string constraintPrefix = "";
1382                         XmlSchema schema = new XmlSchema ();
1383                         XmlNamespaceManager nsmgr = new XmlNamespaceManager (new NameTable ());
1384                         
1385                         if (Namespace != "") {
1386                                 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1387                                 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1388                                 schema.TargetNamespace = Namespace;
1389                                 constraintPrefix = XmlConstants.TnsPrefix + ":";
1390                         }
1391
1392                         // set the schema id
1393                         string xmlNSURI = "http://www.w3.org/2000/xmlns/";
1394                         schema.Id = DataSetName;
1395                         XmlDocument doc = new XmlDocument ();
1396                         XmlAttribute attr = null;
1397                         ArrayList atts = new ArrayList ();
1398
1399                         nsmgr.AddNamespace ("xs", XmlSchema.Namespace);
1400                         nsmgr.AddNamespace (XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1401                         if (Namespace != "") {
1402                                 nsmgr.AddNamespace (XmlConstants.TnsPrefix, Namespace);
1403                                 nsmgr.AddNamespace (String.Empty, Namespace);
1404                         }
1405                         if (CheckExtendedPropertyExists ())
1406                                 nsmgr.AddNamespace (XmlConstants.MspropPrefix, XmlConstants.MspropNamespace);
1407
1408                         if (atts.Count > 0)
1409                                 schema.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1410
1411                         XmlSchemaElement elem = new XmlSchemaElement ();
1412                         elem.Name = XmlConvert.EncodeName (DataSetName);
1413
1414                         // Add namespaces used in DataSet components (tables, columns, ...)
1415                         foreach (DataTable dt in Tables) {
1416                                 foreach (DataColumn col in dt.Columns)
1417                                         CheckNamespace (col.Prefix, col.Namespace, nsmgr, schema);
1418                                 CheckNamespace (dt.Prefix, dt.Namespace, nsmgr, schema);
1419                         }
1420
1421                         // Attributes for DataSet element
1422                         atts.Clear ();
1423                         attr = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1424                         attr.Value = "true";
1425                         atts.Add (attr);
1426
1427                         attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1428                         attr.Value = locale.Name;
1429                         atts.Add (attr);
1430
1431                         elem.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1432
1433                         AddExtendedPropertyAttributes (elem, ExtendedProperties, doc);
1434
1435                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1436                         elem.SchemaType = complex;
1437
1438                         XmlSchemaChoice choice = new XmlSchemaChoice ();
1439                         complex.Particle = choice;
1440                         choice.MaxOccursString = XmlConstants.Unbounded;
1441                         
1442                         //Write out schema for each table in order
1443                         foreach (DataTable table in tables) {           
1444                                 bool isTopLevel = true;
1445                                 foreach (DataRelation rel in table.ParentRelations) {
1446                                         if (rel.Nested) {
1447                                                 isTopLevel = false;
1448                                                 break;
1449                                         }
1450                                 }
1451                                 
1452                                 if (isTopLevel) {
1453                                         if (table.Namespace != SafeNS (schema.TargetNamespace)) {
1454                                                 XmlSchemaElement extElem = new XmlSchemaElement ();
1455                                                 extElem.RefName = new XmlQualifiedName (table.TableName, table.Namespace);
1456                                                 choice.Items.Add (extElem);
1457                                         }
1458                                         else
1459                                                 choice.Items.Add (GetTableSchema (doc, table, schema, nsmgr));
1460                                 }
1461                         }
1462
1463                         schema.Items.Add (elem);
1464                         
1465                         AddConstraintsToSchema (elem, constraintPrefix, tables, relations, doc);
1466                         foreach (string prefix in nsmgr) {
1467                                 string ns = nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix));
1468                                 if (prefix != "xmlns" && prefix != "xml" && ns != null && ns != String.Empty)
1469                                         schema.Namespaces.Add (prefix, ns);
1470                         }
1471                         return schema;
1472                 }
1473                 
1474                 private bool CheckExtendedPropertyExists ()
1475                 {
1476                         if (ExtendedProperties.Count > 0)
1477                                 return true;
1478                         foreach (DataTable dt in Tables) {
1479                                 if (dt.ExtendedProperties.Count > 0)
1480                                         return true;
1481                                 foreach (DataColumn col in dt.Columns)
1482                                         if (col.ExtendedProperties.Count > 0)
1483                                                 return true;
1484                                 foreach (Constraint c in dt.Constraints)
1485                                         if (c.ExtendedProperties.Count > 0)
1486                                                 return true;
1487                         }
1488                         foreach (DataRelation rel in Relations)
1489                                 if (rel.ExtendedProperties.Count > 0)
1490                                         return true;
1491                         return false;
1492                 }
1493
1494                 // Add all constraints in all tables to the schema.
1495                 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations, XmlDocument doc)
1496                 {
1497                         // first add all unique constraints.
1498                         Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables, doc);
1499                         // Add all foriegn key constraints.
1500                         AddForeignKeys (uniqueNames, elem, constraintPrefix, relations, doc);
1501                 }
1502                 
1503                 // Add unique constaraints to the schema.
1504                 // return hashtable with the names of all XmlSchemaUnique elements we created.
1505                 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, XmlDocument doc)
1506                 {
1507                         Hashtable uniqueNames = new Hashtable();
1508                         foreach (DataTable table in tables) {
1509                                 
1510                                 foreach (Constraint constraint in table.Constraints) {
1511                                         
1512                                         if (constraint is UniqueConstraint) {
1513                                                 ArrayList attrs = new ArrayList ();
1514                                                 XmlAttribute attrib;
1515                                                 UniqueConstraint uqConst = (UniqueConstraint) constraint;
1516                                                 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1517                                                 
1518                                                 // if column of the constraint is hidden do not write the constraint.
1519                                                 bool isHidden = false;
1520                                                 foreach (DataColumn column in uqConst.Columns) {
1521                                                         if (column.ColumnMapping == MappingType.Hidden) {
1522                                                                 isHidden = true;
1523                                                                 break;
1524                                                         }
1525                                                 }
1526
1527                                                 if (isHidden)
1528                                                         continue;
1529
1530                                                 // if constaraint name do not exist in the hashtable we can use it.
1531                                                 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1532                                                         uniq.Name = uqConst.ConstraintName;
1533                                                 }
1534                                                 // generate new constraint name for the XmlSchemaUnique element.
1535                                                 else {
1536                                                         uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1537                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1538                                                         attrib.Value = uqConst.ConstraintName;
1539                                                         attrs.Add (attrib);
1540                                                 }
1541                                                 if (uqConst.IsPrimaryKey) {
1542                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1543                                                         attrib.Value = "true";
1544                                                         attrs.Add (attrib);
1545                                                 }
1546                 
1547                                                 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1548
1549                                                 uniq.Selector = new XmlSchemaXPath();
1550                                                 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1551                                                 XmlSchemaXPath field;
1552                                                 foreach (DataColumn column in uqConst.Columns) {
1553                                                         field = new XmlSchemaXPath();
1554                                                         string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1555                                                         field.XPath = typePrefix + constraintPrefix + column.ColumnName;
1556                                                         uniq.Fields.Add(field);
1557                                                 }
1558                                 
1559                                                 AddExtendedPropertyAttributes (uniq, constraint.ExtendedProperties, doc);
1560
1561                                                 elem.Constraints.Add (uniq);
1562                                                 uniqueNames.Add (uniq.Name, null);
1563                                         }
1564                                 }
1565                         }
1566                         return uniqueNames;
1567                 }
1568                 
1569                 // Add the foriegn keys to the schema.
1570                 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations, XmlDocument doc)
1571                 {
1572                         if (relations == null) return;
1573                         
1574                         foreach (DataRelation rel in relations) {
1575                                 
1576                                 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1577                                         continue;
1578
1579                                 bool isHidden = false;
1580                                 foreach (DataColumn col in rel.ParentColumns) {
1581                                         if (col.ColumnMapping == MappingType.Hidden) {
1582                                                 isHidden = true;
1583                                                 break;
1584                                         }
1585                                 }
1586                                 foreach (DataColumn col in rel.ChildColumns) {
1587                                         if (col.ColumnMapping == MappingType.Hidden) {
1588                                                 isHidden = true;
1589                                                 break;
1590                                         }
1591                                 }
1592                                 if (isHidden)
1593                                         continue;
1594                                 
1595                                 ArrayList attrs = new ArrayList ();
1596                                 XmlAttribute attrib;
1597                                 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1598                                 keyRef.Name = rel.RelationName;
1599                                 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1600                                 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1601                                 
1602                                 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1603                                 // first try to find the concatenated name. If we didn't find it - use constraint name.
1604                                 if (uniqueNames.ContainsKey (concatName)) {
1605                                         keyRef.Refer = new XmlQualifiedName(concatName);
1606                                 }
1607                                 else {
1608                                         keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1609                                 }
1610
1611                                 if (rel.Nested) {
1612                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1613                                         attrib.Value = "true";
1614                                         attrs.Add (attrib);
1615                                 }
1616
1617                                 keyRef.Selector = new XmlSchemaXPath();
1618                                 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1619                                 XmlSchemaXPath field;
1620                                 foreach (DataColumn column in rel.ChildColumns) {
1621                                         field = new XmlSchemaXPath();
1622                                         string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1623                                         field.XPath = typePrefix + constraintPrefix + column.ColumnName;
1624                                         keyRef.Fields.Add(field);
1625                                 }
1626
1627                                 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1628                                 AddExtendedPropertyAttributes (keyRef, rel.ExtendedProperties, doc);
1629
1630                                 elem.Constraints.Add (keyRef);
1631                         }
1632                 }
1633
1634                 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table, XmlSchema schemaToAdd, XmlNamespaceManager nsmgr)
1635                 {
1636                         ArrayList elements;
1637                         ArrayList atts;
1638                         DataColumn simple;
1639                         
1640                         SplitColumns (table, out atts, out elements, out simple);
1641
1642                         XmlSchemaElement elem = new XmlSchemaElement ();
1643                         elem.Name = table.TableName;
1644
1645                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1646                         elem.SchemaType = complex;
1647
1648                         XmlSchemaObjectCollection schemaAttributes = null;
1649
1650                         if (simple != null) {
1651                                 // add simpleContent
1652                                 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1653                                 complex.ContentModel = simpleContent;
1654
1655                                 // add column name attribute
1656                                 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1657                                 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1658                                 xlmAttrs[0].Value = simple.ColumnName;
1659                                 
1660                                 // add ordinal attribute
1661                                 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1662                                 xlmAttrs[1].Value = XmlConvert.ToString (simple.Ordinal);
1663                                 simpleContent.UnhandledAttributes = xlmAttrs;
1664                                 
1665                                 
1666                                 // add extension
1667                                 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1668                                 simpleContent.Content = extension;
1669                                 extension.BaseTypeName = MapType (simple.DataType);
1670                                 schemaAttributes = extension.Attributes;
1671                         } else {
1672                                 schemaAttributes = complex.Attributes;
1673                                 //A sequence of element types or a simple content node
1674                                 //<xs:sequence>
1675                                 XmlSchemaSequence seq = new XmlSchemaSequence ();
1676
1677                                 foreach (DataColumn col in elements) {
1678                                         
1679                                         // Add element for the column.
1680                                         XmlSchemaElement colElem = new XmlSchemaElement ();
1681                                         ArrayList xattrs = new ArrayList();
1682                                         XmlAttribute xattr;
1683                                         colElem.Name = col.ColumnName;
1684                                 
1685                                         if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1686                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1687                                                 xattr.Value = col.Caption;
1688                                                 xattrs.Add (xattr);
1689                                         }
1690
1691                                         if (col.AutoIncrement == true) {
1692                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1693                                                 xattr.Value = "true";
1694                                                 xattrs.Add (xattr);
1695                                         }
1696
1697                                         if (col.AutoIncrementSeed != 0) {
1698                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1699                                                 xattr.Value = XmlConvert.ToString (col.AutoIncrementSeed);
1700                                                 xattrs.Add (xattr);
1701                                         }
1702
1703                                         if (col.DefaultValue.ToString () != String.Empty)
1704                                                 colElem.DefaultValue = WriteObjectXml (col.DefaultValue);
1705                                         
1706                                         if (col.MaxLength < 0)
1707                                                 colElem.SchemaTypeName = MapType (col.DataType);
1708                                         
1709                                         if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string) 
1710                                                 && col.DataType != typeof (char)) {
1711                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1712                                                 xattr.Value = col.DataType.AssemblyQualifiedName;
1713                                                 xattrs.Add (xattr);
1714                                         }
1715
1716                                         if (col.AllowDBNull) {
1717                                                 colElem.MinOccurs = 0;
1718                                         }
1719
1720                                         //writer.WriteAttributeString (XmlConstants.MsdataPrefix, 
1721                                         //                            XmlConstants.Ordinal, 
1722                                         //                            XmlConstants.MsdataNamespace, 
1723                                         //                            col.Ordinal.ToString ());
1724
1725                                         // Write SimpleType if column have MaxLength
1726                                         if (col.MaxLength > -1) {
1727                                                 colElem.SchemaType = GetTableSimpleType (doc, col);
1728                                         }
1729                                         
1730                                         colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1731                                         AddExtendedPropertyAttributes (colElem, col.ExtendedProperties, doc);
1732                                         seq.Items.Add (colElem);
1733                                 }
1734
1735                                 foreach (DataRelation rel in table.ChildRelations) {
1736                                         if (rel.Nested) {
1737                                                 if (rel.ChildTable.Namespace != SafeNS (schemaToAdd.TargetNamespace)) {
1738                                                         XmlSchemaElement el = new XmlSchemaElement ();
1739                                                         el.RefName = new XmlQualifiedName (rel.ChildTable.TableName, rel.ChildTable.Namespace);
1740                                                 } else {
1741                                                         XmlSchemaElement el = GetTableSchema (doc, rel.ChildTable, schemaToAdd, nsmgr);
1742                                                         el.MinOccurs = 0;
1743                                                         el.MaxOccursString = "unbounded";
1744                                                         XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
1745                                                         ct.Name = el.Name;
1746                                                         el.SchemaType = null;
1747                                                         el.SchemaTypeName = new XmlQualifiedName (ct.Name, schemaToAdd.TargetNamespace);
1748                                                         schemaToAdd.Items.Add (ct);
1749                                                         seq.Items.Add (el);
1750                                                 }
1751                                         }
1752                                 }
1753
1754                                 if (seq.Items.Count > 0)
1755                                         complex.Particle = seq;
1756                         }
1757
1758                         //Then a list of attributes
1759                         foreach (DataColumn col in atts) {
1760                                 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1761                                 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1762                                 att.Name = col.ColumnName;
1763                                 if (col.Namespace != String.Empty) {
1764                                         att.Form = XmlSchemaForm.Qualified;
1765                                         string prefix = col.Prefix == String.Empty ? "app" + schemaToAdd.Namespaces.Count : col.Prefix;
1766                                         att.Name = prefix + ":" + col.ColumnName;
1767                                         // FIXME: Handle prefix mapping correctly.
1768                                         schemaToAdd.Namespaces.Add (prefix, col.Namespace);
1769                                 }
1770                                 att.SchemaTypeName = MapType (col.DataType);
1771                                 // FIXME: what happens if extended properties are set on attribute columns??
1772                                 schemaAttributes.Add (att);
1773                         }
1774
1775                         AddExtendedPropertyAttributes (elem, table.ExtendedProperties, doc);
1776
1777                         return elem;
1778                 }
1779
1780                 private void AddExtendedPropertyAttributes (XmlSchemaAnnotated xsobj, PropertyCollection props, XmlDocument doc)
1781                 {
1782                         ArrayList attList = new ArrayList ();
1783                         XmlAttribute xmlAttr;
1784
1785                         if (xsobj.UnhandledAttributes != null)
1786                                 attList.AddRange (xsobj.UnhandledAttributes);
1787
1788                         // add extended properties to xs:element
1789                         foreach (DictionaryEntry de in props) {
1790                                 xmlAttr = doc.CreateAttribute (XmlConstants.MspropPrefix, XmlConvert.EncodeName (de.Key.ToString ()), XmlConstants.MspropNamespace);
1791                                 xmlAttr.Value = de.Value != null ? WriteObjectXml (de.Value) : String.Empty;
1792                                 attList.Add (xmlAttr);
1793                         }
1794                         if (attList.Count > 0)
1795                                 xsobj.UnhandledAttributes = attList.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1796                 }
1797
1798                 private string SafeNS (string ns)
1799                 {
1800                         return ns != null ? ns : String.Empty;
1801                 }
1802
1803                 private void HandleExternalNamespace (string prefix, string ns, XmlSchema schema)
1804                 {
1805                         foreach (XmlSchemaExternal ext in schema.Includes) {
1806                                 XmlSchemaImport imp = ext as XmlSchemaImport;
1807                                 if (imp != null && imp.Namespace == ns)
1808                                         return; // nothing to do
1809                         }
1810                         XmlSchemaImport i = new XmlSchemaImport ();
1811                         i.Namespace = ns;
1812                         i.SchemaLocation = "_" + prefix + ".xsd";
1813                         schema.Includes.Add (i);
1814                 }
1815
1816                 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1817                 {
1818                         // SimpleType
1819                         XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1820
1821                         // Restriction
1822                         XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1823                         restriction.BaseTypeName = MapType (col.DataType);
1824                         
1825                         // MaxValue
1826                         XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1827                         max.Value = XmlConvert.ToString (col.MaxLength);
1828                         restriction.Facets.Add (max);
1829                         
1830                         simple.Content = restriction;
1831                         return simple;
1832                 }
1833
1834                 private void DoWriteXmlSchema (XmlWriter writer)
1835                 {
1836                         BuildSchema ().Write (writer);
1837                 }
1838                 
1839                 ///<summary>
1840                 /// Helper function to split columns into attributes elements and simple
1841                 /// content
1842                 /// </summary>
1843                 private void SplitColumns (DataTable table, 
1844                         out ArrayList atts, 
1845                         out ArrayList elements, 
1846                         out DataColumn simple)
1847                 {
1848                         //The columns can be attributes, hidden, elements, or simple content
1849                         //There can be 0-1 simple content cols or 0-* elements
1850                         atts = new System.Collections.ArrayList ();
1851                         elements = new System.Collections.ArrayList ();
1852                         simple = null;
1853                         
1854                         //Sort out the columns
1855                         foreach (DataColumn col in table.Columns) {
1856                                 switch (col.ColumnMapping) {
1857                                         case MappingType.Attribute:
1858                                                 atts.Add (col);
1859                                                 break;
1860                                         case MappingType.Element:
1861                                                 elements.Add (col);
1862                                                 break;
1863                                         case MappingType.SimpleContent:
1864                                                 if (simple != null) {
1865                                                         throw new System.InvalidOperationException ("There may only be one simple content element");
1866                                                 }
1867                                                 simple = col;
1868                                                 break;
1869                                         default:
1870                                                 //ignore Hidden elements
1871                                                 break;
1872                                 }
1873                         }
1874                 }
1875
1876                 private void WriteDiffGramElement(XmlWriter writer)
1877                 {
1878                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1879                         WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1880                 }
1881
1882                 private void SetRowsID()
1883                 {
1884                         foreach (DataTable Table in Tables)
1885                                 SetTableRowsID (Table);
1886                 }
1887                 
1888                 private void SetTableRowsID (DataTable Table)
1889                 {
1890                         int dataRowID = 0;
1891                         foreach (DataRow Row in Table.Rows) {
1892                                 Row.XmlRowID = dataRowID;
1893                                 dataRowID++;
1894                         }
1895                 }
1896
1897                 
1898                 private XmlQualifiedName MapType (Type type)
1899                 {
1900                         switch (Type.GetTypeCode (type)) {
1901                                 case TypeCode.String: return XmlConstants.QnString;
1902                                 case TypeCode.Int16: return XmlConstants.QnShort;
1903                                 case TypeCode.Int32: return XmlConstants.QnInt;
1904                                 case TypeCode.Int64: return XmlConstants.QnLong;
1905                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1906                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1907                                 //case TypeCode.Char: return XmlConstants.QnChar;
1908                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1909                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1910                                 case TypeCode.Double: return XmlConstants.QnDouble;
1911                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
1912                                 case TypeCode.Single: return XmlConstants.QnFloat;
1913                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1914                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1915                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1916                         }
1917                         
1918                         if (typeof (TimeSpan) == type)
1919                                 return XmlConstants.QnDuration;
1920                         else if (typeof (System.Uri) == type)
1921                                 return XmlConstants.QnUri;
1922                         else if (typeof (byte[]) == type)
1923                                 return XmlConstants.QnBase64Binary;
1924                         else if (typeof (XmlQualifiedName) == type)
1925                                 return XmlConstants.QnXmlQualifiedName;
1926                         else
1927                                 return XmlConstants.QnString;
1928                 }
1929
1930                 #endregion //Private Xml Serialisation
1931         }
1932 }