* DataTable.cs: Add rows to the row list if there is
[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 //
12 // (C) Ximian, Inc. 2002
13 // Copyright (C) Tim Coleman, 2002, 2003
14 //
15
16 using System;
17 using System.Collections;
18 using System.ComponentModel;
19 using System.Globalization;
20 using System.Threading;
21 using System.IO;
22 using System.Runtime.Serialization;
23 using System.Xml;
24 using System.Xml.Schema;
25 using System.Xml.Serialization;
26 using System.Data.Common;
27
28 namespace System.Data {
29
30         [ToolboxItem (false)]
31         [DefaultProperty ("DataSetName")]
32         [Serializable]
33         public class DataSet : MarshalByValueComponent, IListSource, 
34                 ISupportInitialize, ISerializable, IXmlSerializable 
35         {
36                 private string dataSetName;
37                 private string _namespace = "";
38                 private string prefix;
39                 private bool caseSensitive;
40                 private bool enforceConstraints = true;
41                 private DataTableCollection tableCollection;
42                 private DataRelationCollection relationCollection;
43                 private PropertyCollection properties;
44                 private DataViewManager defaultView;
45                 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
46                 internal XmlDataDocument _xmlDataDocument = null;
47                 
48                 #region Constructors
49
50                 public DataSet () : this ("NewDataSet") 
51                 {               
52                 }
53                 
54                 public DataSet (string name)
55                 {
56                         dataSetName = name;
57                         tableCollection = new DataTableCollection (this);
58                         relationCollection = new DataRelationCollection.DataSetRelationCollection (this);
59                         properties = new PropertyCollection ();
60                         this.prefix = String.Empty;
61                         
62                         this.Locale = CultureInfo.CurrentCulture;
63                 }
64
65                 [MonoTODO]
66                 protected DataSet (SerializationInfo info, StreamingContext context) : this ()
67                 {
68                         throw new NotImplementedException ();
69                 }
70
71                 #endregion // Constructors
72
73                 #region Public Properties
74
75                 [DataCategory ("Data")]
76                 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
77                 [DefaultValue (false)]
78                 public bool CaseSensitive {
79                         get {
80                                 return caseSensitive;
81                         } 
82                         set {
83                                 foreach (DataTable T in Tables) {
84                                         if (T.VirginCaseSensitive)
85                                                 T.CaseSensitive = value;
86                                 }
87
88                                 caseSensitive = value; 
89                                 if (!caseSensitive) {
90                                         foreach (DataTable table in Tables) {
91                                                 foreach (Constraint c in table.Constraints)
92                                                         c.AssertConstraint ();
93                                         }
94                                 }
95                         }
96                 }
97
98                 [DataCategory ("Data")]
99                 [DataSysDescription ("The name of this DataSet.")]
100                 [DefaultValue ("")]
101                 public string DataSetName {
102                         get { return dataSetName; } 
103                         set { dataSetName = value; }
104                 }
105
106                 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
107                 [Browsable (false)]
108                 public DataViewManager DefaultViewManager {
109                         get {
110                                 if (defaultView == null)
111                                         defaultView = new DataViewManager (this);
112                                 return defaultView;
113                         } 
114                 }
115
116                 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
117                 [DefaultValue (true)]
118                 public bool EnforceConstraints {
119                         get { return enforceConstraints; } 
120                         set { 
121                                 if (value != enforceConstraints) {
122                                         enforceConstraints = value; 
123                                         if (value) {
124                                                 foreach (DataTable table in Tables) {
125                                                         // first assert all unique constraints
126                                                         foreach (UniqueConstraint uc in table.Constraints.UniqueConstraints)
127                                                                 uc.AssertConstraint ();
128                                                         // then assert all foreign keys
129                                                         foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
130                                                                 fk.AssertConstraint ();
131                                                 }
132                                         }
133                                 }
134                         }
135                 }
136
137                 [Browsable (false)]
138                 [DataCategory ("Data")]
139                 [DataSysDescription ("The collection that holds custom user information.")]
140                 public PropertyCollection ExtendedProperties {
141                         get { return properties; }
142                 }
143
144                 [Browsable (false)]
145                 [DataSysDescription ("Indicates that the DataSet has errors.")]
146                 public bool HasErrors {
147                         [MonoTODO]
148                         get {
149                                 for (int i = 0; i < Tables.Count; i++) {
150                                         if (Tables[i].HasErrors)
151                                                 return true;
152                                 }
153                                 return false;
154                         }
155                 }
156
157                 [DataCategory ("Data")]
158                 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
159                 public CultureInfo Locale {
160                         get {
161                                 return locale;
162                         }
163                         set {
164                                 if (locale == null || !locale.Equals (value)) {
165                                         // TODO: check if the new locale is valid
166                                         // TODO: update locale of all tables
167                                         locale = value;
168                                 }
169                         }
170                 }
171
172                 public void Merge (DataRow[] rows)
173                 {
174                         Merge (rows, false, MissingSchemaAction.Add);
175                 }
176                 
177                 public void Merge (DataSet dataSet)
178                 {
179                         Merge (dataSet, false, MissingSchemaAction.Add);
180                 }
181                 
182                 public void Merge (DataTable table)
183                 {
184                         Merge (table, false, MissingSchemaAction.Add);
185                 }
186                 
187                 public void Merge (DataSet dataSet, bool preserveChanges)
188                 {
189                         Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
190                 }
191                 
192                 [MonoTODO]
193                 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
194                 {
195                         if (rows == null)
196                                 throw new ArgumentNullException ("rows");
197                         if (!IsLegalSchemaAction (missingSchemaAction))
198                                 throw new ArgumentOutOfRangeException ("missingSchemaAction");
199                         
200                         MergeManager.Merge (this, rows, preserveChanges, missingSchemaAction);
201                 }
202                 
203                 [MonoTODO]
204                 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
205                 {
206                         if (dataSet == null)
207                                 throw new ArgumentNullException ("dataSet");
208                         if (!IsLegalSchemaAction (missingSchemaAction))
209                                 throw new ArgumentOutOfRangeException ("missingSchemaAction");
210                         
211                         MergeManager.Merge (this, dataSet, preserveChanges, missingSchemaAction);
212                 }
213                 
214                 [MonoTODO]
215                 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
216                 {
217                         if (table == null)
218                                 throw new ArgumentNullException ("table");
219                         if (!IsLegalSchemaAction (missingSchemaAction))
220                                 throw new ArgumentOutOfRangeException ("missingSchemaAction");
221                         
222                         MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);
223                 }
224
225                 private static bool IsLegalSchemaAction (MissingSchemaAction missingSchemaAction)
226                 {
227                         if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
228                                 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
229                                 return true;
230                         return false;
231                 }
232                 
233                 [DataCategory ("Data")]
234                 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
235                 [DefaultValue ("")]
236                 public string Namespace {
237                         [MonoTODO]
238                         get { return _namespace; } 
239                         [MonoTODO]
240                         set {
241                                 //TODO - trigger an event if this happens?
242                                 _namespace = value;
243                         }
244                 }
245
246                 [DataCategory ("Data")]
247                 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
248                 [DefaultValue ("")]
249                 public string Prefix {
250                         [MonoTODO]
251                         get { return prefix; } 
252                         [MonoTODO]
253                         set {
254                                 //TODO - trigger an event if this happens?
255
256                                 if (value == null)
257                                         value = string.Empty;
258                                 
259                                 if (value != this.prefix) 
260                                         RaisePropertyChanging ("Prefix");
261                                 prefix = value;
262                         }
263                 }
264
265                 [DataCategory ("Data")]
266                 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
267                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
268                 public DataRelationCollection Relations {
269                         get {
270                                 return relationCollection;              
271                         }
272                 }
273
274                 [Browsable (false)]
275                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
276                 public override ISite Site {
277                         [MonoTODO]
278                         get {
279                                 throw new NotImplementedException ();
280                         } 
281                         
282                         [MonoTODO]
283                         set {
284                                 throw new NotImplementedException ();
285                         }
286                 }
287
288                 [DataCategory ("Data")]
289                 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
290                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
291                 public DataTableCollection Tables {
292                         get { return tableCollection; }
293                 }
294
295                 #endregion // Public Properties
296
297                 #region Public Methods
298
299                 [MonoTODO]
300                 public void AcceptChanges ()
301                 {
302                         foreach (DataTable tempTable in tableCollection)
303                                 tempTable.AcceptChanges ();
304                 }
305
306                 public void Clear ()
307                 {
308                         if (_xmlDataDocument != null)
309                                 throw new NotSupportedException ("Clear function on dataset and datatable is not supported on XmlDataDocument.");
310                         for (int t = 0; t < tableCollection.Count; t++) {
311                                 tableCollection[t].Clear ();
312                         }
313                 }
314
315                 public virtual DataSet Clone ()
316                 {
317                         DataSet Copy = new DataSet ();
318                         CopyProperties (Copy);
319
320                         foreach (DataTable Table in Tables) {
321                                 Copy.Tables.Add (Table.Clone ());
322                         }
323
324                         //Copy Relationships between tables after existance of tables
325                         //and setting properties correctly
326                         CopyRelations (Copy);
327                         
328                         return Copy;
329                 }
330
331                 // Copies both the structure and data for this DataSet.
332                 public DataSet Copy ()
333                 {
334                         DataSet Copy = new DataSet ();
335                         CopyProperties (Copy);
336
337                         // Copy DatSet's tables
338                         foreach (DataTable Table in Tables) 
339                                 Copy.Tables.Add (Table.Copy ());
340
341                         //Copy Relationships between tables after existance of tables
342                         //and setting properties correctly
343                         CopyRelations (Copy);
344
345                         return Copy;
346                 }
347
348                 [MonoTODO]
349                 private void CopyProperties (DataSet Copy)
350                 {
351                         Copy.CaseSensitive = CaseSensitive;
352                         //Copy.Container = Container
353                         Copy.DataSetName = DataSetName;
354                         //Copy.DefaultViewManager
355                         //Copy.DesignMode
356                         Copy.EnforceConstraints = EnforceConstraints;
357                         //Copy.ExtendedProperties 
358                         //Copy.HasErrors
359                         //Copy.Locale = Locale;
360                         Copy.Namespace = Namespace;
361                         Copy.Prefix = Prefix;                   
362                         //Copy.Site = Site;
363
364                 }
365                 
366                 
367                 private void CopyRelations (DataSet Copy)
368                 {
369
370                         //Creation of the relation contains some of the properties, and the constructor
371                         //demands these values. instead changing the DataRelation constructor and behaviour the
372                         //parameters are pre-configured and sent to the most general constructor
373
374                         foreach (DataRelation MyRelation in this.Relations) {
375                                 string pTable = MyRelation.ParentTable.TableName;
376                                 string cTable = MyRelation.ChildTable.TableName;
377                                 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length]; 
378                                 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
379                                 int i = 0;
380                                 
381                                 foreach (DataColumn DC in MyRelation.ParentColumns) {
382                                         P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
383                                         i++;
384                                 }
385
386                                 i = 0;
387
388                                 foreach (DataColumn DC in MyRelation.ChildColumns) {
389                                         C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
390                                         i++;
391                                 }
392                                 
393                                 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
394                                 //cRel.ChildColumns = MyRelation.ChildColumns;
395                                 //cRel.ChildTable = MyRelation.ChildTable;
396                                 //cRel.ExtendedProperties = cRel.ExtendedProperties; 
397                                 //cRel.Nested = MyRelation.Nested;
398                                 //cRel.ParentColumns = MyRelation.ParentColumns;
399                                 //cRel.ParentTable = MyRelation.ParentTable;
400                                                                 
401                                 Copy.Relations.Add (cRel);
402                         }
403                 }
404
405                 
406
407
408                 public DataSet GetChanges ()
409                 {
410                         return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
411                 }
412
413                 
414                 public DataSet GetChanges (DataRowState rowStates)
415                 {
416                         if (!HasChanges (rowStates))
417                                 return null;
418                         
419                         DataSet copySet = Clone ();
420                         Hashtable addedRows = new Hashtable ();
421
422                         IEnumerator tableEnumerator = Tables.GetEnumerator ();
423                         DataTable origTable;
424                         DataTable copyTable;
425                         while (tableEnumerator.MoveNext ()) {
426                                 origTable = (DataTable)tableEnumerator.Current;
427                                 copyTable = copySet.Tables[origTable.TableName];
428                                 
429                                 // Look for relations that have this table as child
430                                 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
431
432                                 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
433                                 while (rowEnumerator.MoveNext ()) {
434                                         DataRow row = (DataRow)rowEnumerator.Current;
435                                         
436                                         if (row.IsRowChanged (rowStates))
437                                                 AddChangedRow (addedRows, copySet, copyTable, relations, row);
438                                 }
439                         }
440                         return copySet;
441                 }
442                 
443                 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
444                 {
445                         if (addedRows.ContainsKey (row)) return;
446                         
447                         relations.Reset ();
448                         while (relations.MoveNext ()) {
449                                 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
450                                 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
451                                 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
452                                 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
453                         }
454                 
455                         DataRow newRow = copyTable.NewRow ();
456                         copyTable.Rows.Add (newRow);
457                         row.CopyValuesToRow (newRow);
458                         newRow.XmlRowID = row.XmlRowID;
459                         addedRows.Add (row,row);
460                 }
461
462 #if NET_1_2
463                 [MonoTODO]
464                 public DataTableReader GetDataReader (DataTable[] dataTables)
465                 {
466                         throw new NotImplementedException ();
467                 }
468
469                 [MonoTODO]
470                 public DataTableReader GetDataReader ()
471                 {
472                         throw new NotImplementedException ();
473                 }
474 #endif
475                 
476                 public string GetXml ()
477                 {
478                         StringWriter Writer = new StringWriter ();
479
480                         // Sending false for not printing the Processing instruction
481                         WriteXml (Writer, XmlWriteMode.IgnoreSchema, false);
482                         return Writer.ToString ();
483                 }
484
485                 public string GetXmlSchema ()
486                 {
487                         StringWriter Writer = new StringWriter ();
488                         WriteXmlSchema (Writer);
489                         return Writer.ToString ();
490                 }
491
492                 [MonoTODO]
493                 public bool HasChanges ()
494                 {
495                         return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
496                 }
497
498                 [MonoTODO]
499                 public bool HasChanges (DataRowState rowState)
500                 {
501                         if (((int)rowState & 0xffffffe0) != 0)
502                                 throw new ArgumentOutOfRangeException ("rowState");
503
504                         DataTableCollection tableCollection = Tables;
505                         DataTable table;
506                         DataRowCollection rowCollection;
507                         DataRow row;
508
509                         for (int i = 0; i < tableCollection.Count; i++) {
510                                 table = tableCollection[i];
511                                 rowCollection = table.Rows;
512                                 for (int j = 0; j < rowCollection.Count; j++) {
513                                         row = rowCollection[j];
514                                         if ((row.RowState & rowState) != 0)
515                                                 return true;
516                                 }
517                         }
518
519                         return false;           
520                 }
521
522                 [MonoTODO]
523                 public void InferXmlSchema (XmlReader reader, string[] nsArray)
524                 {
525                 }
526
527                 public void InferXmlSchema (Stream stream, string[] nsArray)
528                 {
529                         InferXmlSchema (new XmlTextReader (stream), nsArray);
530                 }
531
532                 public void InferXmlSchema (TextReader reader, string[] nsArray)
533                 {
534                         InferXmlSchema (new XmlTextReader (reader), nsArray);
535                 }
536
537                 public void InferXmlSchema (string fileName, string[] nsArray)
538                 {
539                         XmlTextReader reader = new XmlTextReader (fileName);
540                         try {
541                                 InferXmlSchema (reader, nsArray);
542                         } finally {
543                                 reader.Close ();
544                         }
545                 }
546
547 #if NET_1_2
548                 [MonoTODO]
549                 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
550                 {
551                         throw new NotImplementedException ();
552                 }
553
554                 [MonoTODO]
555                 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
556                 {
557                         throw new NotImplementedException ();
558                 }
559 #endif
560
561                 public virtual void RejectChanges ()
562                 {
563                         int i;
564                         bool oldEnforceConstraints = this.EnforceConstraints;
565                         this.EnforceConstraints = false;
566                         
567                         for (i = 0; i < this.Tables.Count;i++) 
568                                 this.Tables[i].RejectChanges ();
569
570                         this.EnforceConstraints = oldEnforceConstraints;
571                 }
572
573                 public virtual void Reset ()
574                 {
575                         IEnumerator constraintEnumerator;
576
577                         // first we remove all ForeignKeyConstraints (if we will not do that
578                         // we will get an exception when clearing the tables).
579                         for (int i = 0; i < Tables.Count; i++) {
580                                 ConstraintCollection cc = Tables[i].Constraints;
581                                 for (int j = 0; j < cc.Count; j++) {
582                                         if (cc[j] is ForeignKeyConstraint)
583                                                 cc.Remove (cc[j]);
584                                 }
585                         }
586
587                         Clear ();
588                         Relations.Clear ();
589                         Tables.Clear ();
590                 }
591
592                 public void WriteXml (Stream stream)
593                 {
594                         XmlTextWriter writer = new XmlTextWriter (stream, null);
595                         writer.Formatting = Formatting.Indented;
596                         WriteXml (writer);
597                 }
598
599                 ///<summary>
600                 /// Writes the current data for the DataSet to the specified file.
601                 /// </summary>
602                 /// <param name="filename">Fully qualified filename to write to</param>
603                 public void WriteXml (string fileName)
604                 {
605                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
606                         writer.Formatting = Formatting.Indented;
607                         
608                         try {
609                                 WriteXml (writer);
610                         }
611                         finally {
612                                 writer.Close ();
613                         }
614                 }
615
616                 public void WriteXml (TextWriter writer)
617                 {
618                         XmlTextWriter xwriter = new XmlTextWriter (writer);
619                         xwriter.Formatting = Formatting.Indented;
620                         WriteXml (xwriter);
621                 }
622
623                 public void WriteXml (XmlWriter writer)
624                 {
625                         WriteXml (writer, XmlWriteMode.IgnoreSchema, true);
626                 }
627
628                 public void WriteXml (string filename, XmlWriteMode mode)
629                 {
630                         XmlTextWriter writer = new XmlTextWriter (filename, null);
631                         writer.Formatting = Formatting.Indented;
632                         
633                         try {
634                                 WriteXml (writer, mode, true);
635                         }
636                         finally {
637                                 writer.Close ();
638                         }
639                 }
640
641                 public void WriteXml (Stream stream, XmlWriteMode mode)
642                 {
643                         XmlTextWriter writer = new XmlTextWriter (stream, null);
644                         writer.Formatting = Formatting.Indented;
645                         WriteXml (writer, mode, true);
646                 }
647
648                 public void WriteXml (TextWriter writer, XmlWriteMode mode)
649                 {
650                         XmlTextWriter xwriter = new XmlTextWriter (writer);
651                         xwriter.Formatting = Formatting.Indented;
652                         WriteXml (xwriter, mode, true);
653                 }
654
655                 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
656                 {
657                         WriteXml (writer, mode, true);
658                 }
659                 
660                 internal void WriteXml (Stream stream, XmlWriteMode mode, bool writePI)
661                 {
662                         XmlTextWriter writer = new XmlTextWriter (stream, null);
663                         writer.Formatting = Formatting.Indented;
664                         WriteXml (writer, mode, writePI);
665                 }
666
667                 internal void WriteXml (string fileName, XmlWriteMode mode, bool writePI)
668                 {
669                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
670                         writer.Formatting = Formatting.Indented;
671                         
672                         try {
673                                 WriteXml (writer, mode, writePI);
674                         }
675                         finally {
676                                 writer.Close ();
677                         }
678                 }
679
680                 internal void WriteXml (TextWriter writer, XmlWriteMode mode, bool writePI)
681                 {
682                         XmlTextWriter xwriter = new XmlTextWriter (writer);
683                         xwriter.Formatting = Formatting.Indented;
684                         WriteXml (xwriter, mode, writePI);
685                 }
686
687                 internal void WriteXml (XmlWriter writer, XmlWriteMode mode, bool writePI)
688                 {
689                         if (writePI && (writer.WriteState == WriteState.Start))
690                                 writer.WriteStartDocument (true);
691
692                         if (mode == XmlWriteMode.DiffGram) {
693                                 SetRowsID();
694                                 WriteDiffGramElement(writer);
695                         }
696                         
697                         WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
698                         
699                         /*********************************************************
700                          * This is a patch for interoperability with ms.net.     *
701                          * Because in web services the .net client expects this  *
702                          * atrribute even if namespace is an empty string        *
703                          ********************************************************/
704                         if (Namespace == null || Namespace.Length == 0)
705                                 WriteAttributeString (writer, mode, null, null, "xmlns", Namespace);
706                         
707                         if (mode == XmlWriteMode.WriteSchema) {
708                                 DoWriteXmlSchema (writer);
709                         }
710                         
711                         WriteTables (writer, mode, Tables, DataRowVersion.Default);
712                         if (mode == XmlWriteMode.DiffGram) {
713                                 writer.WriteEndElement (); //DataSet name
714                                 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
715
716                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
717                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
718                                         WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
719                                         writer.WriteEndElement ();
720                                 }
721                         }
722                         writer.WriteEndElement (); // DataSet name or diffgr:diffgram
723                 }
724
725                 public void WriteXmlSchema (Stream stream)
726                 {
727                         XmlTextWriter writer = new XmlTextWriter (stream, null );
728                         writer.Formatting = Formatting.Indented;
729                         WriteXmlSchema (writer);        
730                 }
731
732                 public void WriteXmlSchema (string fileName)
733                 {
734                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
735                         writer.Formatting = Formatting.Indented;
736                         try {
737                                 WriteXmlSchema (writer);
738                         }
739                         finally {
740                                 writer.Close ();
741                         }
742                 }
743
744                 public void WriteXmlSchema (TextWriter writer)
745                 {
746                         XmlTextWriter xwriter = new XmlTextWriter (writer);
747                         xwriter.Formatting = Formatting.Indented;
748                         WriteXmlSchema (xwriter);
749                 }
750
751                 public void WriteXmlSchema (XmlWriter writer)
752                 {
753                         //Create a skeleton doc and then write the schema 
754                         //proper which is common to the WriteXml method in schema mode
755                         writer.WriteStartDocument ();
756                         DoWriteXmlSchema (writer);
757                         
758                         writer.WriteEndDocument ();
759                 }
760
761                 public void ReadXmlSchema (Stream stream)
762                 {
763                         XmlReader reader = new XmlTextReader (stream, null);
764                         ReadXmlSchema (reader);
765                 }
766
767                 public void ReadXmlSchema (string str)
768                 {
769                         XmlReader reader = new XmlTextReader (str);
770                         try {
771                                 ReadXmlSchema (reader);
772                         }
773                         finally {
774                                 reader.Close ();
775                         }
776                 }
777
778                 public void ReadXmlSchema (TextReader treader)
779                 {
780                         XmlReader reader = new XmlTextReader (treader);
781                         ReadXmlSchema (reader);                 
782                 }
783
784                 public void ReadXmlSchema (XmlReader reader)
785                 {
786                         XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
787                         SchemaMapper.Read (reader);
788                 }
789
790                 public XmlReadMode ReadXml (Stream stream)
791                 {
792                         return ReadXml (new XmlTextReader (stream));
793                 }
794
795                 public XmlReadMode ReadXml (string str)
796                 {
797                         XmlTextReader reader = new XmlTextReader (str);
798                         try {
799                                 return ReadXml (reader);
800                         }
801                         finally {
802                                 reader.Close ();
803                         }
804                 }
805
806                 public XmlReadMode ReadXml (TextReader reader)
807                 {
808                         return ReadXml (new XmlTextReader (reader));
809                 }
810
811                 public XmlReadMode ReadXml (XmlReader r)
812                 {
813                         XmlDataLoader Loader = new XmlDataLoader (this);
814                         // FIXME: somekinda exception?
815                         if (!r.Read ())
816                                 return XmlReadMode.Auto; // FIXME
817                         
818                         // Check if the curent element is the process instruction (PI).
819                         // if it is move to next element.
820                         if (r.LocalName == "xml")
821                                 r.MoveToContent();
822
823                         // The document can be diffgram if :
824                         // 1. The first element is diffgram.
825                         if (r.LocalName == "diffgram") {
826                                 return ReadXml (r, XmlReadMode.DiffGram);
827                         }
828
829                         bool schemaRead = false;
830                         if (r.LocalName == "schema") {
831                                 ReadXmlSchema (r);
832                                 r.MoveToContent();
833                                 schemaRead = true;
834                         }
835                         
836                         // If we read the schema the next element can be a diffgram
837                         // that is what happen in web services soap message.
838                         if (schemaRead && r.LocalName == "diffgram") {
839                                 return ReadXml (r, XmlReadMode.DiffGram);
840                         }
841                         
842                         // Get the DataSet name.
843                         // if this xml is not diffgram then the first element will be the 
844                         // DataSet name.
845                         string dataSetName = XmlConvert.DecodeName (r.LocalName);
846                         DataSetName = dataSetName;
847                         
848                         r.ReadStartElement ();
849                         r.MoveToContent();
850                         
851                         
852                         // After reading the dataset name there can be three scenarios:
853                         // 1. The next part will be the schema of the dataset.
854                         // 2. The next part will be the data of the dataset using diffgram.
855                         // 3. The next part will be the data of tha dataset without diffgram.
856                         // Check if the current element is the schema
857                         if (r.LocalName == "schema") {
858                                 ReadXmlSchema (r);
859                                 r.MoveToContent();
860                                 schemaRead = true;
861                         }
862                         
863                         // check if the data was written in a diffgram mode.
864                         if (r.LocalName == "diffgram") {
865                                 return ReadXml (r, XmlReadMode.DiffGram);
866                         }
867                         
868                         // If the schema has been read we should read the rest data of the dataset
869                         // with ignoreschema mode.
870                         if (schemaRead) {
871                                 ReadXml (r, XmlReadMode.IgnoreSchema, false);
872                                 return XmlReadMode.ReadSchema;
873                         }
874                         
875                         // Read the data of the dataset with inferschema.
876                         return ReadXml (r, XmlReadMode.InferSchema, false);
877
878                 }
879
880                 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
881                 {
882                         return ReadXml (new XmlTextReader (stream), mode);
883                 }
884
885                 public XmlReadMode ReadXml (string str, XmlReadMode mode)
886                 {
887                         XmlTextReader reader = new XmlTextReader (str);
888                         try {
889                                 return ReadXml (reader, mode);
890                         }
891                         finally {
892                                 reader.Close ();
893                         }
894                 }
895
896                 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
897                 {
898                         return ReadXml (new XmlTextReader (reader), mode);
899                 }
900
901                 [MonoTODO]
902                 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
903                 {
904                         // we have to initiate the reader.
905                         if (reader.ReadState == ReadState.Initial)
906                                 reader.Read();
907                         
908                         // Check if the curent element is the process instruction (PI).
909                         // if it is move to next element.
910                         if (reader.LocalName == "xml")
911                                 reader.MoveToContent();
912
913                         XmlReadMode Result = XmlReadMode.Auto;
914
915                         if (mode == XmlReadMode.DiffGram) {
916                                 if (reader.LocalName != "diffgram"){
917                                         reader.MoveToContent ();
918                                         reader.ReadStartElement ();     // <DataSet>
919
920                                         reader.MoveToContent ();
921                                         if (reader.LocalName == "schema")
922                                                 ReadXmlSchema (reader);
923
924                                         reader.MoveToContent ();
925                                 }
926                                 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
927                                 DiffLoader.Load (reader);
928                                 Result =  XmlReadMode.DiffGram;
929                         }
930                         else 
931                                 Result = ReadXml(reader, mode, true);
932
933                         return Result;
934                 }
935
936                 private XmlReadMode ReadXml (XmlReader r, XmlReadMode mode, bool readDataSet) {
937                         
938                         if (readDataSet) {
939                                 string dataSetName = XmlConvert.DecodeName (r.LocalName);
940                                 DataSetName = dataSetName;
941                                 // get the Namespace of the DataSet.
942                                 string tmp = r.GetAttribute("xmlns");
943                                 if (tmp != null)
944                                         Namespace = tmp;
945                                 
946                                 r.ReadStartElement ();
947                                 r.MoveToContent();
948                         }
949
950                         XmlDataLoader Loader = new XmlDataLoader (this);
951                         return Loader.LoadData (r, mode);
952                 }
953
954                 #endregion // Public Methods
955
956                 #region Public Events
957
958                 [DataCategory ("Action")]
959                 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
960                 public event MergeFailedEventHandler MergeFailed;
961
962                 #endregion // Public Events
963
964                 #region Destructors
965
966                 ~DataSet ()
967                 {
968                 }
969
970                 #endregion Destructors
971
972                 #region IListSource methods
973                 IList IListSource.GetList ()
974                 {
975                         return DefaultViewManager;
976                 }
977                 
978                 bool IListSource.ContainsListCollection {
979                         get {
980                                 return true;
981                         }
982                 }
983                 #endregion IListSource methods
984                 
985                 #region ISupportInitialize methods
986                 public void BeginInit ()
987                 {
988                 }
989                 
990                 public void EndInit ()
991                 {
992                 }
993                 #endregion
994
995                 #region ISerializable
996                 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
997                 {
998                         throw new NotImplementedException ();
999                 }
1000                 #endregion
1001                 
1002                 #region Protected Methods
1003                 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
1004                 {
1005                         string s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
1006                         if (s != null) ReadXmlSerializable (new XmlTextReader (new StringReader (s)));
1007                 }
1008                 
1009                 
1010                 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
1011                 {
1012                         return null;
1013                 }
1014                 
1015                 protected virtual void ReadXmlSerializable (XmlReader reader)
1016                 {
1017                         ReadXml (reader, XmlReadMode.DiffGram); // FIXME
1018                 }
1019
1020                 void IXmlSerializable.ReadXml (XmlReader reader)
1021                 {
1022
1023                         ReadXmlSerializable(reader);
1024                         
1025                         // the XmlSerializationReader does this lines!!!
1026                         //reader.MoveToContent ();
1027                         //reader.ReadEndElement ();     // </DataSet>
1028                 }
1029                 
1030                 void IXmlSerializable.WriteXml (XmlWriter writer)
1031                 {
1032                         DoWriteXmlSchema (writer);
1033                         WriteXml (writer, XmlWriteMode.DiffGram, true);
1034                 }
1035
1036                 protected virtual bool ShouldSerializeRelations ()
1037                 {
1038                         return true;
1039                 }
1040                 
1041                 protected virtual bool ShouldSerializeTables ()
1042                 {
1043                         return true;
1044                 }
1045
1046                 [MonoTODO]
1047                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
1048                 {
1049                 }
1050
1051                 [MonoTODO]
1052                 protected virtual void OnRemoveRelation (DataRelation relation)
1053                 {
1054                 }
1055
1056                 [MonoTODO]
1057                 protected virtual void OnRemoveTable (DataTable table)
1058                 {
1059                 }
1060
1061                 protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1062                 {
1063                         if (MergeFailed != null)
1064                                 MergeFailed (this, e);
1065                 }
1066
1067                 [MonoTODO]
1068                 protected internal void RaisePropertyChanging (string name)
1069                 {
1070                 }
1071                 #endregion
1072
1073                 #region Private Xml Serialisation
1074
1075                 private string WriteObjectXml (object o)
1076                 {
1077                         switch (Type.GetTypeCode (o.GetType ())) {
1078                                 case TypeCode.Boolean:
1079                                         return XmlConvert.ToString ((Boolean) o);
1080                                 case TypeCode.Byte:
1081                                         return XmlConvert.ToString ((Byte) o);
1082                                 case TypeCode.Char:
1083                                         return XmlConvert.ToString ((Char) o);
1084                                 case TypeCode.DateTime:
1085                                         return XmlConvert.ToString ((DateTime) o);
1086                                 case TypeCode.Decimal:
1087                                         return XmlConvert.ToString ((Decimal) o);
1088                                 case TypeCode.Double:
1089                                         return XmlConvert.ToString ((Double) o);
1090                                 case TypeCode.Int16:
1091                                         return XmlConvert.ToString ((Int16) o);
1092                                 case TypeCode.Int32:
1093                                         return XmlConvert.ToString ((Int32) o);
1094                                 case TypeCode.Int64:
1095                                         return XmlConvert.ToString ((Int64) o);
1096                                 case TypeCode.SByte:
1097                                         return XmlConvert.ToString ((SByte) o);
1098                                 case TypeCode.Single:
1099                                         return XmlConvert.ToString ((Single) o);
1100                                 case TypeCode.UInt16:
1101                                         return XmlConvert.ToString ((UInt16) o);
1102                                 case TypeCode.UInt32:
1103                                         return XmlConvert.ToString ((UInt32) o);
1104                                 case TypeCode.UInt64:
1105                                         return XmlConvert.ToString ((UInt64) o);
1106                         }
1107                         if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1108                         if (o is Guid) return XmlConvert.ToString ((Guid) o);
1109                         if (o is byte[]) return Convert.ToBase64String ((byte[])o);
1110                         return o.ToString ();
1111                 }
1112                 
1113                 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1114                 {
1115                         //Write out each table in order, providing it is not
1116                         //part of another table structure via a nested parent relationship
1117                         foreach (DataTable table in tableCollection) {
1118                                 bool isTopLevel = true;
1119                                 foreach (DataRelation rel in table.ParentRelations) {
1120                                         if (rel.Nested) {
1121                                                 isTopLevel = false;
1122                                                 break;
1123                                         }
1124                                 }
1125                                 
1126                                 if (isTopLevel) {
1127                                         WriteTable ( writer, table, mode, version);
1128                                 }
1129                         }
1130                 }
1131
1132                 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1133                 {
1134                         DataRow[] rows = new DataRow [table.Rows.Count];
1135                         table.Rows.CopyTo (rows, 0);
1136                         WriteTable (writer, rows, mode, version);
1137                 }
1138
1139                 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version)
1140                 {
1141                         //The columns can be attributes, hidden, elements, or simple content
1142                         //There can be 0-1 simple content cols or 0-* elements
1143                         System.Collections.ArrayList atts;
1144                         System.Collections.ArrayList elements;
1145                         DataColumn simple = null;
1146
1147                         if (rows.Length == 0) return;
1148                         DataTable table = rows[0].Table;
1149                         SplitColumns (table, out atts, out elements, out simple);
1150                         //sort out the namespacing
1151                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1152
1153                         foreach (DataRow row in rows) {
1154                                 if (!row.HasVersion(version) || 
1155                                    (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged 
1156                                       && version == DataRowVersion.Original))
1157                                         continue;
1158                                 
1159                                 // First check are all the rows null. If they are we just write empty element
1160                                 bool AllNulls = true;
1161                                 foreach (DataColumn dc in table.Columns) {
1162                                 
1163                                         if (row [dc.ColumnName, version] != DBNull.Value) {
1164                                                 AllNulls = false;
1165                                                 break;
1166                                         } 
1167                                 }
1168
1169                                 // If all of the columns were null, we have to write empty element
1170                                 if (AllNulls) {
1171                                         writer.WriteElementString (table.TableName, "");
1172                                         continue;
1173                                 }
1174                                 
1175                                 WriteTableElement (writer, mode, table, row, version);
1176                                 
1177                                 foreach (DataColumn col in atts) {                                      
1178                                         WriteColumnAsAttribute (writer, mode, col, row, version);
1179                                 }
1180                                 
1181                                 if (simple != null) {
1182                                         writer.WriteString (WriteObjectXml (row[simple, version]));
1183                                 }
1184                                 else {                                  
1185                                         foreach (DataColumn col in elements) {
1186                                                 WriteColumnAsElement (writer, mode, nspc, col, row, version);
1187                                         }
1188                                 }
1189                                 
1190                                 foreach (DataRelation relation in table.ChildRelations) {
1191                                         if (relation.Nested) {
1192                                                 WriteTable (writer, row.GetChildRows (relation), mode, version);
1193                                         }
1194                                 }
1195                                 
1196                                 writer.WriteEndElement ();
1197                         }
1198
1199                 }
1200
1201                 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, string nspc, DataColumn col, DataRow row, DataRowVersion version)
1202                 {
1203                         string colnspc = nspc;
1204                         object rowObject = row [col, version];
1205                                                                         
1206                         if (rowObject == null || rowObject == DBNull.Value)
1207                                 return;
1208
1209                         if (col.Namespace != null) {
1210                                 colnspc = col.Namespace;
1211                         }
1212         
1213                         //TODO check if I can get away with write element string
1214                         WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
1215                         writer.WriteString (WriteObjectXml (rowObject));
1216                         writer.WriteEndElement ();
1217                 }
1218
1219                 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1220                 {
1221                         WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col, version].ToString ());
1222                 }
1223
1224                 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1225                 {
1226                         //sort out the namespacing
1227                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1228
1229                         WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
1230
1231                         if (mode == XmlWriteMode.DiffGram) {
1232                                 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1233                                 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", row.XmlRowID.ToString());
1234                                 string modeName = null;
1235                                 if (row.RowState == DataRowState.Modified)
1236                                         modeName = "modified";
1237                                 else if (row.RowState == DataRowState.Added)
1238                                         modeName = "inserted";
1239
1240                                 if (version != DataRowVersion.Original && modeName != null)
1241                                         WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1242                         }
1243                 }
1244                     
1245                 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1246                 {
1247                         if (nspc == null || nspc == "") {
1248                                 writer.WriteStartElement (name);
1249                         }
1250                         else if (prefix != null) {
1251                                 writer.WriteStartElement (prefix, name, nspc);
1252                         }
1253                         else {
1254                                 writer.WriteStartElement (writer.LookupPrefix (nspc), name, nspc);
1255                         }
1256                 }
1257                 
1258                 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1259                 {
1260                         switch ( mode) {
1261                                 case XmlWriteMode.WriteSchema:
1262                                         writer.WriteAttributeString (prefix, name, nspc);
1263                                         break;
1264                                 case XmlWriteMode.DiffGram:
1265                                         writer.WriteAttributeString (prefix, name, nspc,stringValue);
1266                                         break;
1267                                 default:
1268                                         writer.WriteAttributeString (name, stringValue);
1269                                         break;                                  
1270                         };
1271                 }
1272                 
1273                 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1274                 {
1275                         ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1276
1277                         if (mode == XmlWriteMode.DiffGram) {
1278                                 SetTableRowsID (table);
1279                                 WriteDiffGramElement (writer);
1280                         }
1281                         
1282                         WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1283                         
1284                         WriteTable (writer, table, mode, DataRowVersion.Default);
1285                         
1286                         if (mode == XmlWriteMode.DiffGram) {
1287                                 writer.WriteEndElement (); //DataSet name
1288                                 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1289
1290                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
1291                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1292                                         WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1293                                         writer.WriteEndElement ();
1294                                 }
1295                         }
1296                         writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1297                 }
1298
1299                 XmlSchema IXmlSerializable.GetSchema ()
1300                 {
1301                         return BuildSchema ();
1302                 }
1303                 
1304                 XmlSchema BuildSchema ()
1305                 {
1306                         return BuildSchema (Tables, Relations);
1307                 }
1308                 
1309                 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1310                 {
1311                         string constraintPrefix = "";
1312                         XmlSchema schema = new XmlSchema ();
1313
1314                         schema.Namespaces.Add("xs", XmlSchema.Namespace);
1315                         schema.Namespaces.Add(XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1316                         
1317                         if (Namespace != "" && Namespace != null) {
1318                                 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1319                                 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1320                                 schema.TargetNamespace = Namespace;
1321                                 schema.Namespaces.Add(XmlConstants.TnsPrefix, Namespace);
1322                                 constraintPrefix = XmlConstants.TnsPrefix + ":";
1323                         }
1324                                 
1325                         // set the schema id
1326                         schema.Id = DataSetName;
1327                         XmlDocument doc = new XmlDocument ();
1328                         XmlAttribute xmlnsAttr = doc.CreateAttribute("xmlns");
1329                         xmlnsAttr.Value = Namespace;
1330
1331                         schema.UnhandledAttributes = new XmlAttribute[] {xmlnsAttr};
1332                                                 
1333                         XmlSchemaElement elem = new XmlSchemaElement ();
1334                         elem.Name = XmlConvert.EncodeName (DataSetName);
1335
1336                         XmlAttribute[] atts = new XmlAttribute [2];
1337                         atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1338                         atts[0].Value = "true";
1339
1340                         atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1341                         atts[1].Value = locale.Name;
1342                         elem.UnhandledAttributes = atts;
1343
1344                         schema.Items.Add (elem);
1345
1346                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1347                         elem.SchemaType = complex;
1348
1349                         XmlSchemaChoice choice = new XmlSchemaChoice ();
1350                         complex.Particle = choice;
1351                         choice.MaxOccursString = XmlConstants.Unbounded;
1352                         
1353                         //Write out schema for each table in order
1354                         foreach (DataTable table in tables) {           
1355                                 bool isTopLevel = true;
1356                                 foreach (DataRelation rel in table.ParentRelations) {
1357                                         if (rel.Nested) {
1358                                                 isTopLevel = false;
1359                                                 break;
1360                                         }
1361                                 }
1362                                 
1363                                 if (isTopLevel){
1364                                         choice.Items.Add (GetTableSchema (doc, table));
1365                                 }
1366                         }
1367                         
1368                         AddConstraintsToSchema (elem, constraintPrefix, tables, relations);
1369                         return schema;
1370                 }
1371                 
1372                 // Add all constraints in all tables to the schema.
1373                 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations) 
1374                 {
1375                         // first add all unique constraints.
1376                         Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables);
1377                         // Add all foriegn key constraints.
1378                         AddForeignKeys (uniqueNames, elem, constraintPrefix, relations);
1379                 }
1380                 
1381                 // Add unique constaraints to the schema.
1382                 // return hashtable with the names of all XmlSchemaUnique elements we created.
1383                 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables)
1384                 {
1385                         XmlDocument doc = new XmlDocument();
1386                         Hashtable uniqueNames = new Hashtable();
1387                         foreach (DataTable table in tables) {
1388                                 
1389                                 foreach (Constraint constaint in table.Constraints) {
1390                                         
1391                                         if (constaint is UniqueConstraint) {
1392                                                 ArrayList attrs = new ArrayList ();
1393                                                 XmlAttribute attrib;
1394                                                 UniqueConstraint uqConst = (UniqueConstraint)constaint;
1395                                                 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1396                                                 
1397                                                 // if column of the constraint is hidden do not write the constraint.
1398                                                 bool isHidden = false;
1399                                                 foreach (DataColumn column in uqConst.Columns) {
1400                                                         if (column.ColumnMapping == MappingType.Hidden) {
1401                                                                 isHidden = true;
1402                                                                 break;
1403                                                         }
1404                                                 }
1405
1406                                                 if (isHidden)
1407                                                         continue;
1408
1409                                                 // if constaraint name do not exist in the hashtable we can use it.
1410                                                 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1411                                                         uniq.Name = uqConst.ConstraintName;
1412                                                 }
1413                                                 // generate new constraint name for the XmlSchemaUnique element.
1414                                                 else {
1415                                                         uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1416                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1417                                                         attrib.Value = uqConst.ConstraintName;
1418                                                         attrs.Add (attrib);
1419                                                 }
1420                                                 if (uqConst.IsPrimaryKey) {
1421                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1422                                                         attrib.Value = "true";
1423                                                         attrs.Add (attrib);
1424                                                 }
1425                 
1426                                                 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1427
1428                                                 uniq.Selector = new XmlSchemaXPath();
1429                                                 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1430                                                 XmlSchemaXPath field;
1431                                                 foreach (DataColumn column in uqConst.Columns) {
1432                                                         field = new XmlSchemaXPath();
1433                                                         field.XPath = constraintPrefix+column.ColumnName;
1434                                                         uniq.Fields.Add(field);
1435                                                 }
1436                                 
1437                                                 elem.Constraints.Add (uniq);
1438                                                 uniqueNames.Add (uniq.Name, null);
1439                                         }
1440                                 }
1441                         }
1442                         return uniqueNames;
1443                 }
1444                 
1445                 // Add the foriegn keys to the schema.
1446                 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations)
1447                 {
1448                         if (relations == null) return;
1449                         
1450                         XmlDocument doc = new XmlDocument();
1451                         foreach (DataRelation rel in relations) {
1452                                 
1453                                 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1454                                         continue;
1455                                 
1456                                 ArrayList attrs = new ArrayList ();
1457                                 XmlAttribute attrib;
1458                                 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1459                                 keyRef.Name = rel.RelationName;
1460                                 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1461                                 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1462                                 
1463                                 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1464                                 // first try to find the concatenated name. If we didn't find it - use constraint name.
1465                                 if (uniqueNames.ContainsKey (concatName)) {
1466                                         keyRef.Refer = new XmlQualifiedName(concatName);
1467                                 }
1468                                 else {
1469                                         keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1470                                 }
1471
1472                                 if (rel.Nested) {
1473                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1474                                         attrib.Value = "true";
1475                                         attrs.Add (attrib);
1476                                 }
1477
1478                                 keyRef.Selector = new XmlSchemaXPath();
1479                                 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1480                                 XmlSchemaXPath field;
1481                                 foreach (DataColumn column in rel.ChildColumns) {
1482                                         field = new XmlSchemaXPath();
1483                                         field.XPath = constraintPrefix+column.ColumnName;
1484                                         keyRef.Fields.Add(field);
1485                                 }
1486                                 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1487                                 elem.Constraints.Add (keyRef);
1488                         }
1489                 }
1490
1491                 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
1492                 {
1493                         ArrayList elements;
1494                         ArrayList atts;
1495                         DataColumn simple;
1496                         
1497                         SplitColumns (table, out atts, out elements, out simple);
1498
1499                         XmlSchemaElement elem = new XmlSchemaElement ();
1500                         elem.Name = table.TableName;
1501
1502                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1503                         elem.SchemaType = complex;
1504
1505                         //TODO - what about the simple content?
1506                         if (simple != null) {
1507                                 // add simpleContent
1508                                 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1509                                 complex.ContentModel = simpleContent;
1510
1511                                 // add column name attribute
1512                                 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1513                                 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1514                                 xlmAttrs[0].Value = simple.ColumnName;
1515                                 
1516                                 // add ordinal attribute
1517                                 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1518                                 xlmAttrs[1].Value = simple.Ordinal.ToString();
1519                                 simpleContent.UnhandledAttributes = xlmAttrs;
1520                                 
1521                                 
1522                                 // add extension
1523                                 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1524                                 simpleContent.Content = extension;
1525                                 extension.BaseTypeName = MapType (simple.DataType);
1526                         
1527                         }
1528                         else {
1529                                 //A sequence of element types or a simple content node
1530                                 //<xs:sequence>
1531                                 XmlSchemaSequence seq = new XmlSchemaSequence ();
1532                                 complex.Particle = seq;
1533
1534                                 foreach (DataColumn col in elements) {
1535                                         
1536                                         // Add element for the column.
1537                                         XmlSchemaElement colElem = new XmlSchemaElement ();
1538                                         ArrayList xattrs = new ArrayList();
1539                                         XmlAttribute xattr;
1540                                         colElem.Name = col.ColumnName;
1541                                 
1542                                         if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1543                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1544                                                 xattr.Value = col.Caption;
1545                                                 xattrs.Add (xattr);
1546                                         }
1547
1548                                         if (col.AutoIncrement == true) {
1549                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1550                                                 xattr.Value = "true";
1551                                                 xattrs.Add (xattr);
1552                                         }
1553
1554                                         if (col.AutoIncrementSeed != 0) {
1555                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1556                                                 xattr.Value = col.AutoIncrementSeed.ToString();
1557                                                 xattrs.Add (xattr);
1558                                         }
1559
1560                                         if (col.DefaultValue.ToString () != String.Empty)
1561                                                 colElem.DefaultValue = col.DefaultValue.ToString ();
1562                                         
1563                                         if (col.MaxLength < 0)
1564                                                 colElem.SchemaTypeName = MapType (col.DataType);
1565                                         
1566                                         if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string) 
1567                                                 && col.DataType != typeof (char)) {
1568                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1569                                                 xattr.Value = col.DataType.ToString();
1570                                                 xattrs.Add (xattr);
1571                                         }
1572
1573                                         if (col.AllowDBNull) {
1574                                                 colElem.MinOccurs = 0;
1575                                         }
1576
1577                                         //writer.WriteAttributeString (XmlConstants.MsdataPrefix, 
1578                                         //                            XmlConstants.Ordinal, 
1579                                         //                            XmlConstants.MsdataNamespace, 
1580                                         //                            col.Ordinal.ToString ());
1581
1582                                         // Write SimpleType if column have MaxLength
1583                                         if (col.MaxLength > -1) {
1584                                                 colElem.SchemaType = GetTableSimpleType (doc, col);
1585                                         }
1586                                         
1587                                         colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1588                                         seq.Items.Add (colElem);
1589                                 }
1590
1591                                 foreach (DataRelation rel in table.ChildRelations) {
1592                                         if (rel.Nested) {
1593                                                 seq.Items.Add(GetTableSchema (doc, rel.ChildTable));
1594                                         }
1595                                 }
1596                         }
1597
1598                         //Then a list of attributes
1599                         foreach (DataColumn col in atts) {
1600                                 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1601                                 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1602                                 att.Name = col.ColumnName;
1603                                 att.Form = XmlSchemaForm.Unqualified;
1604                                 att.SchemaTypeName = MapType (col.DataType);
1605                                 complex.Attributes.Add (att);
1606                         }
1607
1608                         return elem;
1609                 }
1610
1611                 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1612                 {
1613                         // SimpleType
1614                         XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1615
1616                         // Restriction
1617                         XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1618                         restriction.BaseTypeName = MapType (col.DataType);
1619                         
1620                         // MaxValue
1621                         XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1622                         max.Value = XmlConvert.ToString (col.MaxLength);
1623                         restriction.Facets.Add (max);
1624                         
1625                         simple.Content = restriction;
1626                         return simple;
1627                 }
1628
1629                 private void DoWriteXmlSchema (XmlWriter writer)
1630                 {
1631                         BuildSchema ().Write (writer);
1632                 }
1633                 
1634                 ///<summary>
1635                 /// Helper function to split columns into attributes elements and simple
1636                 /// content
1637                 /// </summary>
1638                 private void SplitColumns (DataTable table, 
1639                         out ArrayList atts, 
1640                         out ArrayList elements, 
1641                         out DataColumn simple)
1642                 {
1643                         //The columns can be attributes, hidden, elements, or simple content
1644                         //There can be 0-1 simple content cols or 0-* elements
1645                         atts = new System.Collections.ArrayList ();
1646                         elements = new System.Collections.ArrayList ();
1647                         simple = null;
1648                         
1649                         //Sort out the columns
1650                         foreach (DataColumn col in table.Columns) {
1651                                 switch (col.ColumnMapping) {
1652                                         case MappingType.Attribute:
1653                                                 atts.Add (col);
1654                                                 break;
1655                                         case MappingType.Element:
1656                                                 elements.Add (col);
1657                                                 break;
1658                                         case MappingType.SimpleContent:
1659                                                 if (simple != null) {
1660                                                         throw new System.InvalidOperationException ("There may only be one simple content element");
1661                                                 }
1662                                                 simple = col;
1663                                                 break;
1664                                         default:
1665                                                 //ignore Hidden elements
1666                                                 break;
1667                                 }
1668                         }
1669                 }
1670
1671                 private void WriteDiffGramElement(XmlWriter writer)
1672                 {
1673                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1674                         WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1675                 }
1676
1677                 private void SetRowsID()
1678                 {
1679                         foreach (DataTable Table in Tables)
1680                                 SetTableRowsID (Table);
1681                 }
1682                 
1683                 private void SetTableRowsID (DataTable Table)
1684                 {
1685                         int dataRowID = 0;
1686                         foreach (DataRow Row in Table.Rows) {
1687                                 Row.XmlRowID = dataRowID;
1688                                 dataRowID++;
1689                         }
1690                 }
1691
1692                 
1693                 private XmlQualifiedName MapType (Type type)
1694                 {
1695                         switch (Type.GetTypeCode (type)) {
1696                                 case TypeCode.String: return XmlConstants.QnString;
1697                                 case TypeCode.Int16: return XmlConstants.QnShort;
1698                                 case TypeCode.Int32: return XmlConstants.QnInt;
1699                                 case TypeCode.Int64: return XmlConstants.QnLong;
1700                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1701                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1702                                 //case TypeCode.Char: return XmlConstants.QnChar;
1703                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1704                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1705                                 case TypeCode.Double: return XmlConstants.QnDouble;
1706                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
1707                                 case TypeCode.Single: return XmlConstants.QnFloat;
1708                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1709                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1710                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1711                         }
1712                         
1713                         if (typeof (TimeSpan) == type)
1714                                 return XmlConstants.QnDuration;
1715                         else if (typeof (System.Uri) == type)
1716                                 return XmlConstants.QnUri;
1717                         else if (typeof (byte[]) == type)
1718                                 return XmlConstants.QnBase64Binary;
1719                         else if (typeof (XmlQualifiedName) == type)
1720                                 return XmlConstants.QnXmlQualifiedName;
1721                         else
1722                                 return XmlConstants.QnString;
1723                 }
1724
1725                 #endregion //Private Xml Serialisation
1726         }
1727 }