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