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