Index.cs : Added.
[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                         WriteXml (writer);
607                         
608                         writer.Close ();
609                 }
610
611                 public void WriteXml (TextWriter writer)
612                 {
613                         XmlTextWriter xwriter = new XmlTextWriter (writer);
614                         xwriter.Formatting = Formatting.Indented;
615                         WriteXml (xwriter);
616                 }
617
618                 public void WriteXml (XmlWriter writer)
619                 {
620                         WriteXml (writer, XmlWriteMode.IgnoreSchema, true);
621                 }
622
623                 public void WriteXml (string filename, XmlWriteMode mode)
624                 {
625                         XmlTextWriter writer = new XmlTextWriter (filename, null);
626                         writer.Formatting = Formatting.Indented;
627                         WriteXml (writer, mode, true);
628                 }
629
630                 public void WriteXml (Stream stream, XmlWriteMode mode)
631                 {
632                         XmlTextWriter writer = new XmlTextWriter (stream, null);
633                         writer.Formatting = Formatting.Indented;
634                         WriteXml (writer, mode, true);
635                 }
636
637                 public void WriteXml (TextWriter writer, XmlWriteMode mode)
638                 {
639                         XmlTextWriter xwriter = new XmlTextWriter (writer);
640                         xwriter.Formatting = Formatting.Indented;
641                         WriteXml (xwriter, mode, true);
642                 }
643
644                 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
645                 {
646                         WriteXml (writer, mode, true);
647                 }
648                 
649                 internal void WriteXml (Stream stream, XmlWriteMode mode, bool writePI)
650                 {
651                         XmlTextWriter writer = new XmlTextWriter (stream, null);
652                         writer.Formatting = Formatting.Indented;
653                         WriteXml (writer, mode, writePI);
654                 }
655
656                 internal void WriteXml (string fileName, XmlWriteMode mode, bool writePI)
657                 {
658                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
659                         writer.Formatting = Formatting.Indented;
660                         WriteXml (writer, mode, writePI);
661                         
662                         writer.Close ();
663                 }
664
665                 internal void WriteXml (TextWriter writer, XmlWriteMode mode, bool writePI)
666                 {
667                         XmlTextWriter xwriter = new XmlTextWriter (writer);
668                         xwriter.Formatting = Formatting.Indented;
669                         WriteXml (xwriter, mode, writePI);
670                 }
671
672                 internal void WriteXml (XmlWriter writer, XmlWriteMode mode, bool writePI)
673                 {
674                         if (writePI && (writer.WriteState == WriteState.Start))
675                                 writer.WriteStartDocument (true);
676
677                         if (mode == XmlWriteMode.DiffGram) {
678                                 SetRowsID();
679                                 WriteDiffGramElement(writer);
680                         }
681                         
682                         WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
683                         
684                         /*********************************************************
685                          * This is a patch for interoperability with ms.net.     *
686                          * Because in web services the .net client expects this  *
687                          * atrribute even if namespace is an empty string        *
688                          ********************************************************/
689                         if (Namespace == null || Namespace.Length == 0)
690                                 WriteAttributeString (writer, mode, null, null, "xmlns", Namespace);
691                         
692                         
693                         if (mode == XmlWriteMode.WriteSchema) {
694                                 DoWriteXmlSchema (writer);
695                         }
696                         
697                         WriteTables (writer, mode, Tables, DataRowVersion.Default);
698                         if (mode == XmlWriteMode.DiffGram) {
699                                 writer.WriteEndElement (); //DataSet name
700                                 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
701
702                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
703                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
704                                         WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
705                                         writer.WriteEndElement ();
706                                 }
707                         }
708                         writer.WriteEndElement (); // DataSet name or diffgr:diffgram
709                 }
710
711                 public void WriteXmlSchema (Stream stream)
712                 {
713                         XmlTextWriter writer = new XmlTextWriter (stream, null );
714                         writer.Formatting = Formatting.Indented;
715                         WriteXmlSchema (writer);        
716                 }
717
718                 public void WriteXmlSchema (string fileName)
719                 {
720                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
721                         writer.Formatting = Formatting.Indented;
722                         WriteXmlSchema (writer);
723                 }
724
725                 public void WriteXmlSchema (TextWriter writer)
726                 {
727                         XmlTextWriter xwriter = new XmlTextWriter (writer);
728                         xwriter.Formatting = Formatting.Indented;
729                         WriteXmlSchema (xwriter);
730                 }
731
732                 public void WriteXmlSchema (XmlWriter writer)
733                 {
734                         //Create a skeleton doc and then write the schema 
735                         //proper which is common to the WriteXml method in schema mode
736                         writer.WriteStartDocument ();
737                         DoWriteXmlSchema (writer);
738                         
739                         writer.WriteEndDocument ();
740                 }
741
742                 public void ReadXmlSchema (Stream stream)
743                 {
744                         XmlReader reader = new XmlTextReader (stream, null);
745                         ReadXmlSchema (reader);
746                 }
747
748                 public void ReadXmlSchema (string str)
749                 {
750                         XmlReader reader = new XmlTextReader (str);
751                         ReadXmlSchema (reader);
752                 }
753
754                 public void ReadXmlSchema (TextReader treader)
755                 {
756                         XmlReader reader = new XmlTextReader (treader);
757                         ReadXmlSchema (reader);                 
758                 }
759
760                 public void ReadXmlSchema (XmlReader reader)
761                 {
762                         XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
763                         SchemaMapper.Read (reader);
764                 }
765
766                 public XmlReadMode ReadXml (Stream stream)
767                 {
768                         return ReadXml (new XmlTextReader (stream));
769                 }
770
771                 public XmlReadMode ReadXml (string str)
772                 {
773                         return ReadXml (new XmlTextReader (str));
774                 }
775
776                 public XmlReadMode ReadXml (TextReader reader)
777                 {
778                         return ReadXml (new XmlTextReader (reader));
779                 }
780
781                 public XmlReadMode ReadXml (XmlReader r)
782                 {
783                         XmlDataLoader Loader = new XmlDataLoader (this);
784                         // FIXME: somekinda exception?
785                         if (!r.Read ())
786                                 return XmlReadMode.Auto; // FIXME
787                         
788                         // Check if the curent element is the process instruction (PI).
789                         // if it is move to next element.
790                         if (r.LocalName == "xml")
791                                 r.MoveToContent();
792
793                         // The document can be diffgram if :
794                         // 1. The first element is diffgram.
795                         if (r.LocalName == "diffgram") {
796                                 return ReadXml (r, XmlReadMode.DiffGram);
797                         }
798
799                         bool schemaRead = false;
800                         if (r.LocalName == "schema") {
801                                 ReadXmlSchema (r);
802                                 r.MoveToContent();
803                                 schemaRead = true;
804                         }
805                         
806                         // If we read the schema the next element can be a diffgram
807                         // that is what happen in web services soap message.
808                         if (schemaRead && r.LocalName == "diffgram") {
809                                 return ReadXml (r, XmlReadMode.DiffGram);
810                         }
811                         
812                         // Get the DataSet name.
813                         // if this xml is not diffgram then the first element will be the 
814                         // DataSet name.
815                         string dataSetName = XmlConvert.DecodeName (r.LocalName);
816                         DataSetName = dataSetName;
817                         
818                         r.ReadStartElement ();
819                         r.MoveToContent();
820                         
821                         
822                         // After reading the dataset name there can be three scenarios:
823                         // 1. The next part will be the schema of the dataset.
824                         // 2. The next part will be the data of the dataset using diffgram.
825                         // 3. The next part will be the data of tha dataset without diffgram.
826                         // Check if the current element is the schema
827                         if (r.LocalName == "schema") {
828                                 ReadXmlSchema (r);
829                                 r.MoveToContent();
830                                 schemaRead = true;
831                         }
832                         
833                         // check if the data was written in a diffgram mode.
834                         if (r.LocalName == "diffgram") {
835                                 return ReadXml (r, XmlReadMode.DiffGram);
836                         }
837                         
838                         // If the schema has been read we should read the rest data of the dataset
839                         // with ignoreschema mode.
840                         if (schemaRead) {
841                                 ReadXml (r, XmlReadMode.IgnoreSchema, false);
842                                 return XmlReadMode.ReadSchema;
843                         }
844                         
845                         // Read the data of the dataset with inferschema.
846                         return ReadXml (r, XmlReadMode.InferSchema, false);
847
848                 }
849
850                 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
851                 {
852                         return ReadXml (new XmlTextReader (stream), mode);
853                 }
854
855                 public XmlReadMode ReadXml (string str, XmlReadMode mode)
856                 {
857                         return ReadXml (new XmlTextReader (str), mode);
858                 }
859
860                 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
861                 {
862                         return ReadXml (new XmlTextReader (reader), mode);
863                 }
864
865                 [MonoTODO]
866                 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
867                 {
868                         // we have to initiate the reader.
869                         if (reader.ReadState == ReadState.Initial)
870                                 reader.Read();
871                         
872                         // Check if the curent element is the process instruction (PI).
873                         // if it is move to next element.
874                         if (reader.LocalName == "xml")
875                                 reader.MoveToContent();
876
877                         XmlReadMode Result = XmlReadMode.Auto;
878
879                         if (mode == XmlReadMode.DiffGram) {
880                                 if (reader.LocalName != "diffgram"){
881                                         reader.MoveToContent ();
882                                         reader.ReadStartElement ();     // <DataSet>
883
884                                         reader.MoveToContent ();
885                                         if (reader.LocalName == "schema")
886                                                 ReadXmlSchema (reader);
887
888                                         reader.MoveToContent ();
889                                 }
890                                 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
891                                 DiffLoader.Load (reader);
892                                 Result =  XmlReadMode.DiffGram;
893                         }
894                         else 
895                                 Result = ReadXml(reader, mode, true);
896
897                         return Result;
898                 }
899
900                 private XmlReadMode ReadXml (XmlReader r, XmlReadMode mode, bool readDataSet) {
901                         
902                         if (readDataSet) {
903                                 string dataSetName = XmlConvert.DecodeName (r.LocalName);
904                                 DataSetName = dataSetName;
905                                 // get the Namespace of the DataSet.
906                                 string tmp = r.GetAttribute("xmlns");
907                                 if (tmp != null)
908                                         Namespace = tmp;
909                                 
910                                 r.ReadStartElement ();
911                                 r.MoveToContent();
912                         }
913
914                         XmlDataLoader Loader = new XmlDataLoader (this);
915                         return Loader.LoadData (r, mode);
916                 }
917
918                 #endregion // Public Methods
919
920                 #region Public Events
921
922                 [DataCategory ("Action")]
923                 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
924                 public event MergeFailedEventHandler MergeFailed;
925
926                 #endregion // Public Events
927
928                 #region Destructors
929
930                 ~DataSet ()
931                 {
932                 }
933
934                 #endregion Destructors
935
936                 #region IListSource methods
937                 IList IListSource.GetList ()
938                 {
939                         return DefaultViewManager;
940                 }
941                 
942                 bool IListSource.ContainsListCollection {
943                         get {
944                                 return true;
945                         }
946                 }
947                 #endregion IListSource methods
948                 
949                 #region ISupportInitialize methods
950                 public void BeginInit ()
951                 {
952                 }
953                 
954                 public void EndInit ()
955                 {
956                 }
957                 #endregion
958
959                 #region ISerializable
960                 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
961                 {
962                         throw new NotImplementedException ();
963                 }
964                 #endregion
965                 
966                 #region Protected Methods
967                 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
968                 {
969                         string s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
970                         if (s != null) ReadXmlSerializable (new XmlTextReader (new StringReader (s)));
971                 }
972                 
973                 
974                 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
975                 {
976                         return null;
977                 }
978                 
979                 protected virtual void ReadXmlSerializable (XmlReader reader)
980                 {
981                         ReadXml (reader, XmlReadMode.DiffGram); // FIXME
982                 }
983
984                 void IXmlSerializable.ReadXml (XmlReader reader)
985                 {
986
987                         ReadXmlSerializable(reader);
988                         
989                         // the XmlSerializationReader does this lines!!!
990                         //reader.MoveToContent ();
991                         //reader.ReadEndElement ();     // </DataSet>
992                 }
993                 
994                 void IXmlSerializable.WriteXml (XmlWriter writer)
995                 {
996                         DoWriteXmlSchema (writer);
997                         WriteXml (writer, XmlWriteMode.DiffGram, true);
998                 }
999
1000                 protected virtual bool ShouldSerializeRelations ()
1001                 {
1002                         return true;
1003                 }
1004                 
1005                 protected virtual bool ShouldSerializeTables ()
1006                 {
1007                         return true;
1008                 }
1009
1010                 [MonoTODO]
1011                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
1012                 {
1013                 }
1014
1015                 [MonoTODO]
1016                 protected virtual void OnRemoveRelation (DataRelation relation)
1017                 {
1018                 }
1019
1020                 [MonoTODO]
1021                 protected virtual void OnRemoveTable (DataTable table)
1022                 {
1023                 }
1024
1025                 protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1026                 {
1027                         if (MergeFailed != null)
1028                                 MergeFailed (this, e);
1029                 }
1030
1031                 [MonoTODO]
1032                 protected internal void RaisePropertyChanging (string name)
1033                 {
1034                 }
1035                 #endregion
1036
1037                 #region Private Xml Serialisation
1038
1039                 private string WriteObjectXml (object o)
1040                 {
1041                         switch (Type.GetTypeCode (o.GetType ())) {
1042                                 case TypeCode.Boolean:
1043                                         return XmlConvert.ToString ((Boolean) o);
1044                                 case TypeCode.Byte:
1045                                         return XmlConvert.ToString ((Byte) o);
1046                                 case TypeCode.Char:
1047                                         return XmlConvert.ToString ((Char) o);
1048                                 case TypeCode.DateTime:
1049                                         return XmlConvert.ToString ((DateTime) o);
1050                                 case TypeCode.Decimal:
1051                                         return XmlConvert.ToString ((Decimal) o);
1052                                 case TypeCode.Double:
1053                                         return XmlConvert.ToString ((Double) o);
1054                                 case TypeCode.Int16:
1055                                         return XmlConvert.ToString ((Int16) o);
1056                                 case TypeCode.Int32:
1057                                         return XmlConvert.ToString ((Int32) o);
1058                                 case TypeCode.Int64:
1059                                         return XmlConvert.ToString ((Int64) o);
1060                                 case TypeCode.SByte:
1061                                         return XmlConvert.ToString ((SByte) o);
1062                                 case TypeCode.Single:
1063                                         return XmlConvert.ToString ((Single) o);
1064                                 case TypeCode.UInt16:
1065                                         return XmlConvert.ToString ((UInt16) o);
1066                                 case TypeCode.UInt32:
1067                                         return XmlConvert.ToString ((UInt32) o);
1068                                 case TypeCode.UInt64:
1069                                         return XmlConvert.ToString ((UInt64) o);
1070                         }
1071                         if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1072                         if (o is Guid) return XmlConvert.ToString ((Guid) o);
1073                         return o.ToString ();
1074                 }
1075                 
1076                 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1077                 {
1078                         //Write out each table in order, providing it is not
1079                         //part of another table structure via a nested parent relationship
1080                         foreach (DataTable table in tableCollection) {
1081                                 bool isTopLevel = true;
1082                                 foreach (DataRelation rel in table.ParentRelations) {
1083                                         if (rel.Nested) {
1084                                                 isTopLevel = false;
1085                                                 break;
1086                                         }
1087                                 }
1088                                 
1089                                 if (isTopLevel) {
1090                                         WriteTable ( writer, table, mode, version);
1091                                 }
1092                         }
1093                 }
1094
1095                 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1096                 {
1097                         DataRow[] rows = new DataRow [table.Rows.Count];
1098                         table.Rows.CopyTo (rows, 0);
1099                         WriteTable (writer, rows, mode, version);
1100                 }
1101
1102                 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version)
1103                 {
1104                         //The columns can be attributes, hidden, elements, or simple content
1105                         //There can be 0-1 simple content cols or 0-* elements
1106                         System.Collections.ArrayList atts;
1107                         System.Collections.ArrayList elements;
1108                         DataColumn simple = null;
1109
1110                         if (rows.Length == 0) return;
1111                         DataTable table = rows[0].Table;
1112                         SplitColumns (table, out atts, out elements, out simple);
1113                         //sort out the namespacing
1114                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1115
1116                         foreach (DataRow row in rows) {
1117                                 if (!row.HasVersion(version) || 
1118                                    (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged 
1119                                       && version == DataRowVersion.Original))
1120                                         continue;
1121                                 
1122                                 // First check are all the rows null. If they are we just write empty element
1123                                 bool AllNulls = true;
1124                                 foreach (DataColumn dc in table.Columns) {
1125                                 
1126                                         if (row [dc.ColumnName, version] != DBNull.Value) {
1127                                                 AllNulls = false;
1128                                                 break;
1129                                         } 
1130                                 }
1131
1132                                 // If all of the columns were null, we have to write empty element
1133                                 if (AllNulls) {
1134                                         writer.WriteElementString (table.TableName, "");
1135                                         continue;
1136                                 }
1137                                 
1138                                 WriteTableElement (writer, mode, table, row, version);
1139                                 
1140                                 foreach (DataColumn col in atts) {                                      
1141                                         WriteColumnAsAttribute (writer, mode, col, row, version);
1142                                 }
1143                                 
1144                                 if (simple != null) {
1145                                         writer.WriteString (WriteObjectXml (row[simple, version]));
1146                                 }
1147                                 else {                                  
1148                                         foreach (DataColumn col in elements) {
1149                                                 WriteColumnAsElement (writer, mode, nspc, col, row, version);
1150                                         }
1151                                 }
1152                                 
1153                                 foreach (DataRelation relation in table.ChildRelations) {
1154                                         if (relation.Nested) {
1155                                                 WriteTable (writer, row.GetChildRows (relation), mode, version);
1156                                         }
1157                                 }
1158                                 
1159                                 writer.WriteEndElement ();
1160                         }
1161
1162                 }
1163
1164                 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, string nspc, DataColumn col, DataRow row, DataRowVersion version)
1165                 {
1166                         string colnspc = nspc;
1167                         object rowObject = row [col, version];
1168                                                                         
1169                         if (rowObject == null || rowObject == DBNull.Value)
1170                                 return;
1171
1172                         if (col.Namespace != null) {
1173                                 colnspc = col.Namespace;
1174                         }
1175         
1176                         //TODO check if I can get away with write element string
1177                         WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
1178                         writer.WriteString (WriteObjectXml (rowObject));
1179                         writer.WriteEndElement ();
1180                 }
1181
1182                 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1183                 {
1184                         WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col, version].ToString ());
1185                 }
1186
1187                 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1188                 {
1189                         //sort out the namespacing
1190                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1191
1192                         WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
1193
1194                         if (mode == XmlWriteMode.DiffGram) {
1195                                 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1196                                 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", row.XmlRowID.ToString());
1197                                 string modeName = null;
1198                                 if (row.RowState == DataRowState.Modified)
1199                                         modeName = "modified";
1200                                 else if (row.RowState == DataRowState.Added)
1201                                         modeName = "inserted";
1202
1203                                 if (version != DataRowVersion.Original && modeName != null)
1204                                         WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1205                         }
1206                 }
1207                     
1208                 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1209                 {
1210                         if (nspc == null || nspc == "") {
1211                                 writer.WriteStartElement (name);
1212                         }
1213                         else if (prefix != null) {
1214                                 writer.WriteStartElement (prefix, name, nspc);
1215                         }
1216                         else {
1217                                 writer.WriteStartElement (writer.LookupPrefix (nspc), name, nspc);
1218                         }
1219                 }
1220                 
1221                 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1222                 {
1223                         switch ( mode) {
1224                                 case XmlWriteMode.WriteSchema:
1225                                         writer.WriteAttributeString (prefix, name, nspc);
1226                                         break;
1227                                 case XmlWriteMode.DiffGram:
1228                                         writer.WriteAttributeString (prefix, name, nspc,stringValue);
1229                                         break;
1230                                 default:
1231                                         writer.WriteAttributeString (name, stringValue);
1232                                         break;                                  
1233                         };
1234                 }
1235                 
1236                 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1237                 {
1238                         ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1239
1240                         if (mode == XmlWriteMode.DiffGram) {
1241                                 SetTableRowsID (table);
1242                                 WriteDiffGramElement (writer);
1243                         }
1244                         
1245                         WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1246                         
1247                         WriteTable (writer, table, mode, DataRowVersion.Default);
1248                         
1249                         if (mode == XmlWriteMode.DiffGram) {
1250                                 writer.WriteEndElement (); //DataSet name
1251                                 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1252
1253                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
1254                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1255                                         WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1256                                         writer.WriteEndElement ();
1257                                 }
1258                         }
1259                         writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1260                 }
1261
1262                 XmlSchema IXmlSerializable.GetSchema ()
1263                 {
1264                         return BuildSchema ();
1265                 }
1266                 
1267                 XmlSchema BuildSchema ()
1268                 {
1269                         return BuildSchema (Tables, Relations);
1270                 }
1271                 
1272                 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1273                 {
1274                         string constraintPrefix = "";
1275                         XmlSchema schema = new XmlSchema ();
1276
1277                         schema.Namespaces.Add("xs", XmlSchema.Namespace);
1278                         schema.Namespaces.Add(XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1279                         
1280                         if (Namespace != "" && Namespace != null) {
1281                                 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1282                                 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1283                                 schema.TargetNamespace = Namespace;
1284                                 schema.Namespaces.Add(XmlConstants.TnsPrefix, Namespace);
1285                                 constraintPrefix = XmlConstants.TnsPrefix + ":";
1286                         }
1287                                 
1288                         // set the schema id
1289                         schema.Id = DataSetName;
1290                         XmlDocument doc = new XmlDocument ();
1291                         XmlAttribute xmlnsAttr = doc.CreateAttribute("xmlns");
1292                         xmlnsAttr.Value = Namespace;
1293
1294                         schema.UnhandledAttributes = new XmlAttribute[] {xmlnsAttr};
1295                                                 
1296                         XmlSchemaElement elem = new XmlSchemaElement ();
1297                         elem.Name = XmlConvert.EncodeName (DataSetName);
1298
1299                         XmlAttribute[] atts = new XmlAttribute [2];
1300                         atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1301                         atts[0].Value = "true";
1302
1303                         atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1304                         atts[1].Value = locale.Name;
1305                         elem.UnhandledAttributes = atts;
1306
1307                         schema.Items.Add (elem);
1308
1309                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1310                         elem.SchemaType = complex;
1311
1312                         XmlSchemaChoice choice = new XmlSchemaChoice ();
1313                         complex.Particle = choice;
1314                         choice.MaxOccursString = XmlConstants.Unbounded;
1315                         
1316                         //Write out schema for each table in order
1317                         foreach (DataTable table in tables) {           
1318                                 bool isTopLevel = true;
1319                                 foreach (DataRelation rel in table.ParentRelations) {
1320                                         if (rel.Nested) {
1321                                                 isTopLevel = false;
1322                                                 break;
1323                                         }
1324                                 }
1325                                 
1326                                 if (isTopLevel){
1327                                         choice.Items.Add (GetTableSchema (doc, table));
1328                                 }
1329                         }
1330                         
1331                         AddConstraintsToSchema (elem, constraintPrefix, tables, relations);
1332                         return schema;
1333                 }
1334                 
1335                 // Add all constraints in all tables to the schema.
1336                 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations) 
1337                 {
1338                         // first add all unique constraints.
1339                         Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables);
1340                         // Add all foriegn key constraints.
1341                         AddForeignKeys (uniqueNames, elem, constraintPrefix, relations);
1342                 }
1343                 
1344                 // Add unique constaraints to the schema.
1345                 // return hashtable with the names of all XmlSchemaUnique elements we created.
1346                 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables)
1347                 {
1348                         XmlDocument doc = new XmlDocument();
1349                         Hashtable uniqueNames = new Hashtable();
1350                         foreach (DataTable table in tables) {
1351                                 
1352                                 foreach (Constraint constaint in table.Constraints) {
1353                                         
1354                                         if (constaint is UniqueConstraint) {
1355                                                 ArrayList attrs = new ArrayList ();
1356                                                 XmlAttribute attrib;
1357                                                 UniqueConstraint uqConst = (UniqueConstraint)constaint;
1358                                                 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1359                                                 
1360                                                 // if column of the constraint is hidden do not write the constraint.
1361                                                 bool isHidden = false;
1362                                                 foreach (DataColumn column in uqConst.Columns) {
1363                                                         if (column.ColumnMapping == MappingType.Hidden) {
1364                                                                 isHidden = true;
1365                                                                 break;
1366                                                         }
1367                                                 }
1368
1369                                                 if (isHidden)
1370                                                         continue;
1371
1372                                                 // if constaraint name do not exist in the hashtable we can use it.
1373                                                 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1374                                                         uniq.Name = uqConst.ConstraintName;
1375                                                 }
1376                                                 // generate new constraint name for the XmlSchemaUnique element.
1377                                                 else {
1378                                                         uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1379                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1380                                                         attrib.Value = uqConst.ConstraintName;
1381                                                         attrs.Add (attrib);
1382                                                 }
1383                                                 if (uqConst.IsPrimaryKey) {
1384                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1385                                                         attrib.Value = "true";
1386                                                         attrs.Add (attrib);
1387                                                 }
1388                 
1389                                                 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1390
1391                                                 uniq.Selector = new XmlSchemaXPath();
1392                                                 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1393                                                 XmlSchemaXPath field;
1394                                                 foreach (DataColumn column in uqConst.Columns) {
1395                                                         field = new XmlSchemaXPath();
1396                                                         field.XPath = constraintPrefix+column.ColumnName;
1397                                                         uniq.Fields.Add(field);
1398                                                 }
1399                                 
1400                                                 elem.Constraints.Add (uniq);
1401                                                 uniqueNames.Add (uniq.Name, null);
1402                                         }
1403                                 }
1404                         }
1405                         return uniqueNames;
1406                 }
1407                 
1408                 // Add the foriegn keys to the schema.
1409                 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations)
1410                 {
1411                         if (relations == null) return;
1412                         
1413                         XmlDocument doc = new XmlDocument();
1414                         foreach (DataRelation rel in relations) {
1415                                 
1416                                 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1417                                         continue;
1418                                 
1419                                 ArrayList attrs = new ArrayList ();
1420                                 XmlAttribute attrib;
1421                                 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1422                                 keyRef.Name = rel.RelationName;
1423                                 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1424                                 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1425                                 
1426                                 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1427                                 // first try to find the concatenated name. If we didn't find it - use constraint name.
1428                                 if (uniqueNames.ContainsKey (concatName)) {
1429                                         keyRef.Refer = new XmlQualifiedName(concatName);
1430                                 }
1431                                 else {
1432                                         keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1433                                 }
1434
1435                                 if (rel.Nested) {
1436                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1437                                         attrib.Value = "true";
1438                                         attrs.Add (attrib);
1439                                 }
1440
1441                                 keyRef.Selector = new XmlSchemaXPath();
1442                                 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1443                                 XmlSchemaXPath field;
1444                                 foreach (DataColumn column in rel.ChildColumns) {
1445                                         field = new XmlSchemaXPath();
1446                                         field.XPath = constraintPrefix+column.ColumnName;
1447                                         keyRef.Fields.Add(field);
1448                                 }
1449                                 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1450                                 elem.Constraints.Add (keyRef);
1451                         }
1452                 }
1453
1454                 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
1455                 {
1456                         ArrayList elements;
1457                         ArrayList atts;
1458                         DataColumn simple;
1459                         
1460                         SplitColumns (table, out atts, out elements, out simple);
1461
1462                         XmlSchemaElement elem = new XmlSchemaElement ();
1463                         elem.Name = table.TableName;
1464
1465                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1466                         elem.SchemaType = complex;
1467
1468                         //TODO - what about the simple content?
1469                         if (simple != null) {
1470                                 // add simpleContent
1471                                 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1472                                 complex.ContentModel = simpleContent;
1473
1474                                 // add column name attribute
1475                                 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1476                                 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1477                                 xlmAttrs[0].Value = simple.ColumnName;
1478                                 
1479                                 // add ordinal attribute
1480                                 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1481                                 xlmAttrs[1].Value = simple.Ordinal.ToString();
1482                                 simpleContent.UnhandledAttributes = xlmAttrs;
1483                                 
1484                                 
1485                                 // add extension
1486                                 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1487                                 simpleContent.Content = extension;
1488                                 extension.BaseTypeName = MapType (simple.DataType);
1489                         
1490                         }
1491                         else {
1492                                 //A sequence of element types or a simple content node
1493                                 //<xs:sequence>
1494                                 XmlSchemaSequence seq = new XmlSchemaSequence ();
1495                                 complex.Particle = seq;
1496
1497                                 foreach (DataColumn col in elements) {
1498                                         
1499                                         // Add element for the column.
1500                                         XmlSchemaElement colElem = new XmlSchemaElement ();
1501                                         ArrayList xattrs = new ArrayList();
1502                                         XmlAttribute xattr;
1503                                         colElem.Name = col.ColumnName;
1504                                 
1505                                         if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1506                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1507                                                 xattr.Value = col.Caption;
1508                                                 xattrs.Add (xattr);
1509                                         }
1510
1511                                         if (col.AutoIncrement == true) {
1512                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1513                                                 xattr.Value = "true";
1514                                                 xattrs.Add (xattr);
1515                                         }
1516
1517                                         if (col.AutoIncrementSeed != 0) {
1518                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1519                                                 xattr.Value = col.AutoIncrementSeed.ToString();
1520                                                 xattrs.Add (xattr);
1521                                         }
1522
1523                                         if (col.DefaultValue.ToString () != String.Empty)
1524                                                 colElem.DefaultValue = col.DefaultValue.ToString ();
1525                                         
1526                                         if (col.MaxLength < 0)
1527                                                 colElem.SchemaTypeName = MapType (col.DataType);
1528                                         
1529                                         if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string) 
1530                                                 && col.DataType != typeof (char)) {
1531                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1532                                                 xattr.Value = col.DataType.ToString();
1533                                                 xattrs.Add (xattr);
1534                                         }
1535
1536                                         if (col.AllowDBNull) {
1537                                                 colElem.MinOccurs = 0;
1538                                         }
1539
1540                                         //writer.WriteAttributeString (XmlConstants.MsdataPrefix, 
1541                                         //                            XmlConstants.Ordinal, 
1542                                         //                            XmlConstants.MsdataNamespace, 
1543                                         //                            col.Ordinal.ToString ());
1544
1545                                         // Write SimpleType if column have MaxLength
1546                                         if (col.MaxLength > -1) {
1547                                                 colElem.SchemaType = GetTableSimpleType (doc, col);
1548                                         }
1549                                         
1550                                         colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1551                                         seq.Items.Add (colElem);
1552                                 }
1553
1554                                 foreach (DataRelation rel in table.ChildRelations) {
1555                                         if (rel.Nested) {
1556                                                 seq.Items.Add(GetTableSchema (doc, rel.ChildTable));
1557                                         }
1558                                 }
1559                         }
1560
1561                         //Then a list of attributes
1562                         foreach (DataColumn col in atts) {
1563                                 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1564                                 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1565                                 att.Name = col.ColumnName;
1566                                 att.Form = XmlSchemaForm.Unqualified;
1567                                 att.SchemaTypeName = MapType (col.DataType);
1568                                 complex.Attributes.Add (att);
1569                         }
1570
1571                         return elem;
1572                 }
1573
1574                 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1575                 {
1576                         // SimpleType
1577                         XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1578
1579                         // Restriction
1580                         XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1581                         restriction.BaseTypeName = MapType (col.DataType);
1582                         
1583                         // MaxValue
1584                         XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1585                         max.Value = XmlConvert.ToString (col.MaxLength);
1586                         restriction.Facets.Add (max);
1587                         
1588                         simple.Content = restriction;
1589                         return simple;
1590                 }
1591
1592                 private void DoWriteXmlSchema (XmlWriter writer)
1593                 {
1594                         BuildSchema ().Write (writer);
1595                 }
1596                 
1597                 ///<summary>
1598                 /// Helper function to split columns into attributes elements and simple
1599                 /// content
1600                 /// </summary>
1601                 private void SplitColumns (DataTable table, 
1602                         out ArrayList atts, 
1603                         out ArrayList elements, 
1604                         out DataColumn simple)
1605                 {
1606                         //The columns can be attributes, hidden, elements, or simple content
1607                         //There can be 0-1 simple content cols or 0-* elements
1608                         atts = new System.Collections.ArrayList ();
1609                         elements = new System.Collections.ArrayList ();
1610                         simple = null;
1611                         
1612                         //Sort out the columns
1613                         foreach (DataColumn col in table.Columns) {
1614                                 switch (col.ColumnMapping) {
1615                                         case MappingType.Attribute:
1616                                                 atts.Add (col);
1617                                                 break;
1618                                         case MappingType.Element:
1619                                                 elements.Add (col);
1620                                                 break;
1621                                         case MappingType.SimpleContent:
1622                                                 if (simple != null) {
1623                                                         throw new System.InvalidOperationException ("There may only be one simple content element");
1624                                                 }
1625                                                 simple = col;
1626                                                 break;
1627                                         default:
1628                                                 //ignore Hidden elements
1629                                                 break;
1630                                 }
1631                         }
1632                 }
1633
1634                 private void WriteDiffGramElement(XmlWriter writer)
1635                 {
1636                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1637                         WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1638                 }
1639
1640                 private void SetRowsID()
1641                 {
1642                         foreach (DataTable Table in Tables)
1643                                 SetTableRowsID (Table);
1644                 }
1645                 
1646                 private void SetTableRowsID (DataTable Table)
1647                 {
1648                         int dataRowID = 0;
1649                         foreach (DataRow Row in Table.Rows) {
1650                                 Row.XmlRowID = dataRowID;
1651                                 dataRowID++;
1652                         }
1653                 }
1654
1655                 
1656                 private XmlQualifiedName MapType (Type type)
1657                 {
1658                         switch (Type.GetTypeCode (type)) {
1659                                 case TypeCode.String: return XmlConstants.QnString;
1660                                 case TypeCode.Int16: return XmlConstants.QnShort;
1661                                 case TypeCode.Int32: return XmlConstants.QnInt;
1662                                 case TypeCode.Int64: return XmlConstants.QnLong;
1663                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1664                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1665                                 //case TypeCode.Char: return XmlConstants.QnChar;
1666                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1667                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1668                                 case TypeCode.Double: return XmlConstants.QnDouble;
1669                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
1670                                 case TypeCode.Single: return XmlConstants.QnFloat;
1671                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1672                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1673                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1674                         }
1675                         
1676                         if (typeof (TimeSpan) == type)
1677                                 return XmlConstants.QnDuration;
1678                         else if (typeof (System.Uri) == type)
1679                                 return XmlConstants.QnUri;
1680                         else if (typeof (byte[]) == type)
1681                                 return XmlConstants.QnBase64Binary;
1682                         else if (typeof (XmlQualifiedName) == type)
1683                                 return XmlConstants.QnXmlQualifiedName;
1684                         else
1685                                 return XmlConstants.QnString;
1686                 }
1687
1688                 #endregion //Private Xml Serialisation
1689         }
1690 }