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