2004-05-06 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 when XmlDataDocument is bound to the DataSet.");
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);
676                         for (int n=0; n<tableCollection.Count && !shouldOutputContent; n++)
677                                 shouldOutputContent = tableCollection [n].Rows.Count > 0;
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).Process ();
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                 private void CheckNamespace (string prefix, string ns, XmlNamespaceManager nsmgr, XmlSchema schema)
1255                 {
1256                         if (ns == String.Empty)
1257                                 return;
1258                         if (ns != nsmgr.DefaultNamespace) {
1259                                 if (nsmgr.LookupNamespace (prefix) != ns) {
1260                                         for (int i = 1; i < int.MaxValue; i++) {
1261                                                 string p = nsmgr.NameTable.Add ("app" + i);
1262                                                 if (!nsmgr.HasNamespace (p)) {
1263                                                         nsmgr.AddNamespace (p, ns);
1264                                                         HandleExternalNamespace (p, ns, schema);
1265                                                         break;
1266                                                 }
1267                                         }
1268                                 }
1269                         }
1270                 }
1271
1272                 XmlSchema IXmlSerializable.GetSchema ()
1273                 {
1274                         return BuildSchema ();
1275                 }
1276                 
1277                 XmlSchema BuildSchema ()
1278                 {
1279                         return BuildSchema (Tables, Relations);
1280                 }
1281                 
1282                 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1283                 {
1284                         string constraintPrefix = "";
1285                         XmlSchema schema = new XmlSchema ();
1286                         XmlNamespaceManager nsmgr = new XmlNamespaceManager (new NameTable ());
1287                         
1288                         if (Namespace != "") {
1289                                 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1290                                 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1291                                 schema.TargetNamespace = Namespace;
1292                                 constraintPrefix = XmlConstants.TnsPrefix + ":";
1293                         }
1294
1295                         // set the schema id
1296                         string xmlNSURI = "http://www.w3.org/2000/xmlns/";
1297                         schema.Id = DataSetName;
1298                         XmlDocument doc = new XmlDocument ();
1299                         XmlAttribute attr = null;
1300                         ArrayList atts = new ArrayList ();
1301
1302                         nsmgr.AddNamespace ("xs", XmlSchema.Namespace);
1303                         nsmgr.AddNamespace (XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1304                         if (Namespace != "") {
1305                                 nsmgr.AddNamespace (XmlConstants.TnsPrefix, Namespace);
1306                                 nsmgr.AddNamespace (String.Empty, Namespace);
1307                         }
1308
1309                         if (atts.Count > 0)
1310                                 schema.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1311
1312                         XmlSchemaElement elem = new XmlSchemaElement ();
1313                         elem.Name = XmlConvert.EncodeName (DataSetName);
1314
1315                         // Add namespaces used in DataSet components (tables, columns, ...)
1316                         foreach (DataTable dt in Tables) {
1317                                 foreach (DataColumn col in dt.Columns)
1318                                         CheckNamespace (col.Prefix, col.Namespace, nsmgr, schema);
1319                                 CheckNamespace (dt.Prefix, dt.Namespace, nsmgr, schema);
1320                         }
1321
1322                         // Attributes for DataSet element
1323                         atts.Clear ();
1324                         attr = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1325                         attr.Value = "true";
1326                         atts.Add (attr);
1327
1328                         attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1329                         attr.Value = locale.Name;
1330                         atts.Add (attr);
1331
1332                         elem.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1333
1334                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1335                         elem.SchemaType = complex;
1336
1337                         XmlSchemaChoice choice = new XmlSchemaChoice ();
1338                         complex.Particle = choice;
1339                         choice.MaxOccursString = XmlConstants.Unbounded;
1340                         
1341                         //Write out schema for each table in order
1342                         foreach (DataTable table in tables) {           
1343                                 bool isTopLevel = true;
1344                                 foreach (DataRelation rel in table.ParentRelations) {
1345                                         if (rel.Nested) {
1346                                                 isTopLevel = false;
1347                                                 break;
1348                                         }
1349                                 }
1350                                 
1351                                 if (isTopLevel) {
1352                                         if (table.Namespace != SafeNS (schema.TargetNamespace)) {
1353                                                 XmlSchemaElement extElem = new XmlSchemaElement ();
1354                                                 extElem.RefName = new XmlQualifiedName (table.TableName, table.Namespace);
1355                                                 choice.Items.Add (extElem);
1356                                         }
1357                                         else
1358                                                 choice.Items.Add (GetTableSchema (doc, table, schema, nsmgr));
1359                                 }
1360                         }
1361
1362                         schema.Items.Add (elem);
1363                         
1364                         AddConstraintsToSchema (elem, constraintPrefix, tables, relations);
1365                         foreach (string prefix in nsmgr) {
1366                                 string ns = nsmgr.LookupNamespace (prefix);
1367                                 if (prefix != "xmlns" && prefix != "xml" && ns != null && ns != String.Empty)
1368                                         schema.Namespaces.Add (prefix, ns);
1369                         }
1370                         return schema;
1371                 }
1372                 
1373                 
1374                 // Add all constraints in all tables to the schema.
1375                 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations) 
1376                 {
1377                         // first add all unique constraints.
1378                         Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables);
1379                         // Add all foriegn key constraints.
1380                         AddForeignKeys (uniqueNames, elem, constraintPrefix, relations);
1381                 }
1382                 
1383                 // Add unique constaraints to the schema.
1384                 // return hashtable with the names of all XmlSchemaUnique elements we created.
1385                 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables)
1386                 {
1387                         XmlDocument doc = new XmlDocument();
1388                         Hashtable uniqueNames = new Hashtable();
1389                         foreach (DataTable table in tables) {
1390                                 
1391                                 foreach (Constraint constaint in table.Constraints) {
1392                                         
1393                                         if (constaint is UniqueConstraint) {
1394                                                 ArrayList attrs = new ArrayList ();
1395                                                 XmlAttribute attrib;
1396                                                 UniqueConstraint uqConst = (UniqueConstraint)constaint;
1397                                                 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1398                                                 
1399                                                 // if column of the constraint is hidden do not write the constraint.
1400                                                 bool isHidden = false;
1401                                                 foreach (DataColumn column in uqConst.Columns) {
1402                                                         if (column.ColumnMapping == MappingType.Hidden) {
1403                                                                 isHidden = true;
1404                                                                 break;
1405                                                         }
1406                                                 }
1407
1408                                                 if (isHidden)
1409                                                         continue;
1410
1411                                                 // if constaraint name do not exist in the hashtable we can use it.
1412                                                 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1413                                                         uniq.Name = uqConst.ConstraintName;
1414                                                 }
1415                                                 // generate new constraint name for the XmlSchemaUnique element.
1416                                                 else {
1417                                                         uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1418                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1419                                                         attrib.Value = uqConst.ConstraintName;
1420                                                         attrs.Add (attrib);
1421                                                 }
1422                                                 if (uqConst.IsPrimaryKey) {
1423                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1424                                                         attrib.Value = "true";
1425                                                         attrs.Add (attrib);
1426                                                 }
1427                 
1428                                                 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1429
1430                                                 uniq.Selector = new XmlSchemaXPath();
1431                                                 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1432                                                 XmlSchemaXPath field;
1433                                                 foreach (DataColumn column in uqConst.Columns) {
1434                                                         field = new XmlSchemaXPath();
1435                                                         field.XPath = constraintPrefix+column.ColumnName;
1436                                                         uniq.Fields.Add(field);
1437                                                 }
1438                                 
1439                                                 elem.Constraints.Add (uniq);
1440                                                 uniqueNames.Add (uniq.Name, null);
1441                                         }
1442                                 }
1443                         }
1444                         return uniqueNames;
1445                 }
1446                 
1447                 // Add the foriegn keys to the schema.
1448                 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations)
1449                 {
1450                         if (relations == null) return;
1451                         
1452                         XmlDocument doc = new XmlDocument();
1453                         foreach (DataRelation rel in relations) {
1454                                 
1455                                 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1456                                         continue;
1457                                 
1458                                 ArrayList attrs = new ArrayList ();
1459                                 XmlAttribute attrib;
1460                                 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1461                                 keyRef.Name = rel.RelationName;
1462                                 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1463                                 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1464                                 
1465                                 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1466                                 // first try to find the concatenated name. If we didn't find it - use constraint name.
1467                                 if (uniqueNames.ContainsKey (concatName)) {
1468                                         keyRef.Refer = new XmlQualifiedName(concatName);
1469                                 }
1470                                 else {
1471                                         keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1472                                 }
1473
1474                                 if (rel.Nested) {
1475                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1476                                         attrib.Value = "true";
1477                                         attrs.Add (attrib);
1478                                 }
1479
1480                                 keyRef.Selector = new XmlSchemaXPath();
1481                                 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1482                                 XmlSchemaXPath field;
1483                                 foreach (DataColumn column in rel.ChildColumns) {
1484                                         field = new XmlSchemaXPath();
1485                                         field.XPath = constraintPrefix+column.ColumnName;
1486                                         keyRef.Fields.Add(field);
1487                                 }
1488                                 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1489                                 elem.Constraints.Add (keyRef);
1490                         }
1491                 }
1492
1493                 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table, XmlSchema schemaToAdd, XmlNamespaceManager nsmgr)
1494                 {
1495                         ArrayList elements;
1496                         ArrayList atts;
1497                         DataColumn simple;
1498                         
1499                         SplitColumns (table, out atts, out elements, out simple);
1500
1501                         XmlSchemaElement elem = new XmlSchemaElement ();
1502                         elem.Name = table.TableName;
1503
1504                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1505                         elem.SchemaType = complex;
1506
1507                         //TODO - what about the simple content?
1508                         if (simple != null) {
1509                                 // add simpleContent
1510                                 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1511                                 complex.ContentModel = simpleContent;
1512
1513                                 // add column name attribute
1514                                 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1515                                 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1516                                 xlmAttrs[0].Value = simple.ColumnName;
1517                                 
1518                                 // add ordinal attribute
1519                                 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1520                                 xlmAttrs[1].Value = simple.Ordinal.ToString();
1521                                 simpleContent.UnhandledAttributes = xlmAttrs;
1522                                 
1523                                 
1524                                 // add extension
1525                                 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1526                                 simpleContent.Content = extension;
1527                                 extension.BaseTypeName = MapType (simple.DataType);
1528                         
1529                         }
1530                         else {
1531                                 //A sequence of element types or a simple content node
1532                                 //<xs:sequence>
1533                                 XmlSchemaSequence seq = new XmlSchemaSequence ();
1534
1535                                 foreach (DataColumn col in elements) {
1536                                         
1537                                         // Add element for the column.
1538                                         XmlSchemaElement colElem = new XmlSchemaElement ();
1539                                         ArrayList xattrs = new ArrayList();
1540                                         XmlAttribute xattr;
1541                                         colElem.Name = col.ColumnName;
1542                                 
1543                                         if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1544                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1545                                                 xattr.Value = col.Caption;
1546                                                 xattrs.Add (xattr);
1547                                         }
1548
1549                                         if (col.AutoIncrement == true) {
1550                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1551                                                 xattr.Value = "true";
1552                                                 xattrs.Add (xattr);
1553                                         }
1554
1555                                         if (col.AutoIncrementSeed != 0) {
1556                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1557                                                 xattr.Value = col.AutoIncrementSeed.ToString();
1558                                                 xattrs.Add (xattr);
1559                                         }
1560
1561                                         if (col.DefaultValue.ToString () != String.Empty)
1562                                                 colElem.DefaultValue = col.DefaultValue.ToString ();
1563                                         
1564                                         if (col.MaxLength < 0)
1565                                                 colElem.SchemaTypeName = MapType (col.DataType);
1566                                         
1567                                         if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string) 
1568                                                 && col.DataType != typeof (char)) {
1569                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1570                                                 xattr.Value = col.DataType.AssemblyQualifiedName;
1571                                                 xattrs.Add (xattr);
1572                                         }
1573
1574                                         if (col.AllowDBNull) {
1575                                                 colElem.MinOccurs = 0;
1576                                         }
1577
1578                                         //writer.WriteAttributeString (XmlConstants.MsdataPrefix, 
1579                                         //                            XmlConstants.Ordinal, 
1580                                         //                            XmlConstants.MsdataNamespace, 
1581                                         //                            col.Ordinal.ToString ());
1582
1583                                         // Write SimpleType if column have MaxLength
1584                                         if (col.MaxLength > -1) {
1585                                                 colElem.SchemaType = GetTableSimpleType (doc, col);
1586                                         }
1587                                         
1588                                         colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1589                                         seq.Items.Add (colElem);
1590                                 }
1591                                 if (seq.Items.Count > 0)
1592                                         complex.Particle = seq;
1593
1594                                 foreach (DataRelation rel in table.ChildRelations) {
1595                                         if (rel.Nested) {
1596                                                 if (rel.ChildTable.Namespace != SafeNS (schemaToAdd.TargetNamespace)) {
1597                                                         XmlSchemaElement el = new XmlSchemaElement ();
1598                                                         el.RefName = new XmlQualifiedName (rel.ChildTable.TableName, rel.ChildTable.Namespace);
1599                                                 } else {
1600                                                         XmlSchemaElement el = GetTableSchema (doc, rel.ChildTable, schemaToAdd, nsmgr);
1601                                                         XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
1602                                                         ct.Name = el.Name;
1603                                                         el.SchemaType = null;
1604                                                         el.SchemaTypeName = new XmlQualifiedName (ct.Name, schemaToAdd.TargetNamespace);
1605                                                         schemaToAdd.Items.Add (ct);
1606                                                         seq.Items.Add (el);
1607                                                 }
1608                                         }
1609                                 }
1610                         }
1611
1612                         //Then a list of attributes
1613                         foreach (DataColumn col in atts) {
1614                                 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1615                                 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1616                                 att.Name = col.ColumnName;
1617                                 if (col.Namespace != String.Empty) {
1618                                         att.Form = XmlSchemaForm.Qualified;
1619                                         string prefix = col.Prefix == String.Empty ? "app" + schemaToAdd.Namespaces.Count : col.Prefix;
1620                                         att.Name = prefix + ":" + col.ColumnName;
1621                                         // FIXME: Handle prefix mapping correctly.
1622                                         schemaToAdd.Namespaces.Add (prefix, col.Namespace);
1623                                 }
1624                                 att.SchemaTypeName = MapType (col.DataType);
1625                                 complex.Attributes.Add (att);
1626                         }
1627
1628                         return elem;
1629                 }
1630
1631                 private string SafeNS (string ns)
1632                 {
1633                         return ns != null ? ns : String.Empty;
1634                 }
1635
1636                 private void HandleExternalNamespace (string prefix, string ns, XmlSchema schema)
1637                 {
1638                         foreach (XmlSchemaExternal ext in schema.Includes) {
1639                                 XmlSchemaImport imp = ext as XmlSchemaImport;
1640                                 if (imp != null && imp.Namespace == ns)
1641                                         return; // nothing to do
1642                         }
1643                         XmlSchemaImport i = new XmlSchemaImport ();
1644                         i.Namespace = ns;
1645                         i.SchemaLocation = "_" + prefix + ".xsd";
1646                         schema.Includes.Add (i);
1647                 }
1648
1649                 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1650                 {
1651                         // SimpleType
1652                         XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1653
1654                         // Restriction
1655                         XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1656                         restriction.BaseTypeName = MapType (col.DataType);
1657                         
1658                         // MaxValue
1659                         XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1660                         max.Value = XmlConvert.ToString (col.MaxLength);
1661                         restriction.Facets.Add (max);
1662                         
1663                         simple.Content = restriction;
1664                         return simple;
1665                 }
1666
1667                 private void DoWriteXmlSchema (XmlWriter writer)
1668                 {
1669                         BuildSchema ().Write (writer);
1670                 }
1671                 
1672                 ///<summary>
1673                 /// Helper function to split columns into attributes elements and simple
1674                 /// content
1675                 /// </summary>
1676                 private void SplitColumns (DataTable table, 
1677                         out ArrayList atts, 
1678                         out ArrayList elements, 
1679                         out DataColumn simple)
1680                 {
1681                         //The columns can be attributes, hidden, elements, or simple content
1682                         //There can be 0-1 simple content cols or 0-* elements
1683                         atts = new System.Collections.ArrayList ();
1684                         elements = new System.Collections.ArrayList ();
1685                         simple = null;
1686                         
1687                         //Sort out the columns
1688                         foreach (DataColumn col in table.Columns) {
1689                                 switch (col.ColumnMapping) {
1690                                         case MappingType.Attribute:
1691                                                 atts.Add (col);
1692                                                 break;
1693                                         case MappingType.Element:
1694                                                 elements.Add (col);
1695                                                 break;
1696                                         case MappingType.SimpleContent:
1697                                                 if (simple != null) {
1698                                                         throw new System.InvalidOperationException ("There may only be one simple content element");
1699                                                 }
1700                                                 simple = col;
1701                                                 break;
1702                                         default:
1703                                                 //ignore Hidden elements
1704                                                 break;
1705                                 }
1706                         }
1707                 }
1708
1709                 private void WriteDiffGramElement(XmlWriter writer)
1710                 {
1711                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1712                         WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1713                 }
1714
1715                 private void SetRowsID()
1716                 {
1717                         foreach (DataTable Table in Tables)
1718                                 SetTableRowsID (Table);
1719                 }
1720                 
1721                 private void SetTableRowsID (DataTable Table)
1722                 {
1723                         int dataRowID = 0;
1724                         foreach (DataRow Row in Table.Rows) {
1725                                 Row.XmlRowID = dataRowID;
1726                                 dataRowID++;
1727                         }
1728                 }
1729
1730                 
1731                 private XmlQualifiedName MapType (Type type)
1732                 {
1733                         switch (Type.GetTypeCode (type)) {
1734                                 case TypeCode.String: return XmlConstants.QnString;
1735                                 case TypeCode.Int16: return XmlConstants.QnShort;
1736                                 case TypeCode.Int32: return XmlConstants.QnInt;
1737                                 case TypeCode.Int64: return XmlConstants.QnLong;
1738                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1739                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1740                                 //case TypeCode.Char: return XmlConstants.QnChar;
1741                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1742                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1743                                 case TypeCode.Double: return XmlConstants.QnDouble;
1744                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
1745                                 case TypeCode.Single: return XmlConstants.QnFloat;
1746                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1747                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1748                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1749                         }
1750                         
1751                         if (typeof (TimeSpan) == type)
1752                                 return XmlConstants.QnDuration;
1753                         else if (typeof (System.Uri) == type)
1754                                 return XmlConstants.QnUri;
1755                         else if (typeof (byte[]) == type)
1756                                 return XmlConstants.QnBase64Binary;
1757                         else if (typeof (XmlQualifiedName) == type)
1758                                 return XmlConstants.QnXmlQualifiedName;
1759                         else
1760                                 return XmlConstants.QnString;
1761                 }
1762
1763                 #endregion //Private Xml Serialisation
1764         }
1765 }