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