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