* DataRow.cs: Added SetOriginalValue(), which is used to set the original
[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                         /*\
790                          *  If document is diffgram we will use diffgram
791                         \*/
792                         if (r.LocalName == "diffgram")
793                                 return ReadXml (r, XmlReadMode.DiffGram);
794                         
795                         // Get the DataSet name.
796                         string dataSetName = XmlConvert.DecodeName (r.LocalName);
797                         DataSetName = dataSetName;
798                         
799                         r.ReadStartElement ();
800                         r.MoveToContent();
801                         
802                         bool schemaRead = false;
803                         // Check if the current element is the schema
804                         if (r.LocalName == "schema") {
805                                 ReadXmlSchema (r);
806                                 r.MoveToContent();
807                                 schemaRead = true;
808                         }
809                         
810                         if (r.LocalName == "diffgram") {
811                                 return ReadXml (r, XmlReadMode.DiffGram);
812                         }
813                         
814                         // If the schema has been read we should read the rest of the document
815                         if (schemaRead) {
816                                 ReadXml (r, XmlReadMode.IgnoreSchema, false);
817                                 return XmlReadMode.ReadSchema;
818                         }
819                         
820                         // Read with inferschema.
821                         return ReadXml (r, XmlReadMode.InferSchema, false);
822
823                 }
824
825                 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
826                 {
827                         return ReadXml (new XmlTextReader (stream), mode);
828                 }
829
830                 public XmlReadMode ReadXml (string str, XmlReadMode mode)
831                 {
832                         return ReadXml (new XmlTextReader (str), mode);
833                 }
834
835                 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
836                 {
837                         return ReadXml (new XmlTextReader (reader), mode);
838                 }
839
840                 [MonoTODO]
841                 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
842                 {
843                         // we have to initiate the reader.
844                         if (reader.ReadState == ReadState.Initial)
845                                 reader.Read();
846                         
847                         // Check if the curent element is the process instruction (PI).
848                         // if it is move to next element.
849                         if (reader.LocalName == "xml")
850                                 reader.MoveToContent();
851
852                         XmlReadMode Result = XmlReadMode.Auto;
853
854                         if (mode == XmlReadMode.DiffGram) {
855                                 if (reader.LocalName != "diffgram"){
856                                         reader.MoveToContent ();
857                                         reader.ReadStartElement ();     // <DataSet>
858
859                                         reader.MoveToContent ();
860                                         if (reader.LocalName == "schema")
861                                                 ReadXmlSchema (reader);
862
863                                         reader.MoveToContent ();
864                                 }
865                                 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
866                                 DiffLoader.Load (reader);
867                                 Result =  XmlReadMode.DiffGram;
868                         }
869                         else 
870                                 Result = ReadXml(reader, mode, true);
871
872                         return Result;
873                 }
874
875                 private XmlReadMode ReadXml (XmlReader r, XmlReadMode mode, bool readDataSet) {
876                         
877                         if (readDataSet) {
878                                 string dataSetName = XmlConvert.DecodeName (r.LocalName);
879                                 DataSetName = dataSetName;
880                                 // get the Namespace of the DataSet.
881                                 string tmp = r.GetAttribute("xmlns");
882                                 if (tmp != null)
883                                         Namespace = tmp;
884                                 
885                                 r.ReadStartElement ();
886                                 r.MoveToContent();
887                         }
888
889                         XmlDataLoader Loader = new XmlDataLoader (this);
890                         return Loader.LoadData (r, mode);
891                 }
892
893                 #endregion // Public Methods
894
895                 #region Public Events
896
897                 [DataCategory ("Action")]
898                 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
899                 public event MergeFailedEventHandler MergeFailed;
900
901                 #endregion // Public Events
902
903                 #region Destructors
904
905                 ~DataSet ()
906                 {
907                 }
908
909                 #endregion Destructors
910
911                 #region IListSource methods
912                 IList IListSource.GetList ()
913                 {
914                         return DefaultViewManager;
915                 }
916                 
917                 bool IListSource.ContainsListCollection {
918                         get {
919                                 return true;
920                         }
921                 }
922                 #endregion IListSource methods
923                 
924                 #region ISupportInitialize methods
925                 public void BeginInit ()
926                 {
927                 }
928                 
929                 public void EndInit ()
930                 {
931                 }
932                 #endregion
933
934                 #region ISerializable
935                 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
936                 {
937                         throw new NotImplementedException ();
938                 }
939                 #endregion
940                 
941                 #region Protected Methods
942                 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
943                 {
944                         string s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
945                         if (s != null) ReadXmlSerializable (new XmlTextReader (new StringReader (s)));
946                 }
947                 
948                 
949                 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
950                 {
951                         return BuildSchema ();
952                 }
953                 
954                 protected virtual void ReadXmlSerializable (XmlReader reader)
955                 {
956                         ReadXml (reader, XmlReadMode.DiffGram); // FIXME
957                 }
958
959                 void IXmlSerializable.ReadXml (XmlReader reader)
960                 {
961
962                         ReadXmlSerializable(reader);
963                         
964                         // the XmlSerializationReader does this lines!!!
965                         //reader.MoveToContent ();
966                         //reader.ReadEndElement ();     // </DataSet>
967                 }
968                 
969                 void IXmlSerializable.WriteXml (XmlWriter writer)
970                 {
971                         DoWriteXmlSchema (writer);
972                         WriteXml (writer, XmlWriteMode.DiffGram, true);
973                 }
974
975                 protected virtual bool ShouldSerializeRelations ()
976                 {
977                         return true;
978                 }
979                 
980                 protected virtual bool ShouldSerializeTables ()
981                 {
982                         return true;
983                 }
984
985                 [MonoTODO]
986                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
987                 {
988                 }
989
990                 [MonoTODO]
991                 protected virtual void OnRemoveRelation (DataRelation relation)
992                 {
993                 }
994
995                 [MonoTODO]
996                 protected virtual void OnRemoveTable (DataTable table)
997                 {
998                 }
999
1000                 protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1001                 {
1002                         if (MergeFailed != null)
1003                                 MergeFailed (this, e);
1004                 }
1005
1006                 [MonoTODO]
1007                 protected internal void RaisePropertyChanging (string name)
1008                 {
1009                 }
1010                 #endregion
1011
1012                 #region Private Xml Serialisation
1013
1014                 private string WriteObjectXml (object o)
1015                 {
1016                         switch (Type.GetTypeCode (o.GetType ())) {
1017                                 case TypeCode.Boolean:
1018                                         return XmlConvert.ToString ((Boolean) o);
1019                                 case TypeCode.Byte:
1020                                         return XmlConvert.ToString ((Byte) o);
1021                                 case TypeCode.Char:
1022                                         return XmlConvert.ToString ((Char) o);
1023                                 case TypeCode.DateTime:
1024                                         return XmlConvert.ToString ((DateTime) o);
1025                                 case TypeCode.Decimal:
1026                                         return XmlConvert.ToString ((Decimal) o);
1027                                 case TypeCode.Double:
1028                                         return XmlConvert.ToString ((Double) o);
1029                                 case TypeCode.Int16:
1030                                         return XmlConvert.ToString ((Int16) o);
1031                                 case TypeCode.Int32:
1032                                         return XmlConvert.ToString ((Int32) o);
1033                                 case TypeCode.Int64:
1034                                         return XmlConvert.ToString ((Int64) o);
1035                                 case TypeCode.SByte:
1036                                         return XmlConvert.ToString ((SByte) o);
1037                                 case TypeCode.Single:
1038                                         return XmlConvert.ToString ((Single) o);
1039                                 case TypeCode.UInt16:
1040                                         return XmlConvert.ToString ((UInt16) o);
1041                                 case TypeCode.UInt32:
1042                                         return XmlConvert.ToString ((UInt32) o);
1043                                 case TypeCode.UInt64:
1044                                         return XmlConvert.ToString ((UInt64) o);
1045                         }
1046                         if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1047                         if (o is Guid) return XmlConvert.ToString ((Guid) o);
1048                         return o.ToString ();
1049                 }
1050                 
1051                 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1052                 {
1053                         //Write out each table in order, providing it is not
1054                         //part of another table structure via a nested parent relationship
1055                         foreach (DataTable table in tableCollection) {
1056                                 bool isTopLevel = true;
1057                                 foreach (DataRelation rel in table.ParentRelations) {
1058                                         if (rel.Nested) {
1059                                                 isTopLevel = false;
1060                                                 break;
1061                                         }
1062                                 }
1063                                 
1064                                 if (isTopLevel) {
1065                                         WriteTable ( writer, table, mode, version);
1066                                 }
1067                         }
1068                 }
1069
1070                 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1071                 {
1072                         DataRow[] rows = new DataRow [table.Rows.Count];
1073                         table.Rows.CopyTo (rows, 0);
1074                         WriteTable (writer, rows, mode, version);
1075                 }
1076
1077                 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version)
1078                 {
1079                         //The columns can be attributes, hidden, elements, or simple content
1080                         //There can be 0-1 simple content cols or 0-* elements
1081                         System.Collections.ArrayList atts;
1082                         System.Collections.ArrayList elements;
1083                         DataColumn simple = null;
1084
1085                         if (rows.Length == 0) return;
1086                         DataTable table = rows[0].Table;
1087                         SplitColumns (table, out atts, out elements, out simple);
1088                         //sort out the namespacing
1089                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1090
1091                         foreach (DataRow row in rows) {
1092                                 if (!row.HasVersion(version) || 
1093                                    (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged 
1094                                       && version == DataRowVersion.Original))
1095                                         continue;
1096                                 
1097                                 // First check are all the rows null. If they are we just write empty element
1098                                 bool AllNulls = true;
1099                                 foreach (DataColumn dc in table.Columns) {
1100                                 
1101                                         if (row [dc.ColumnName, version] != DBNull.Value) {
1102                                                 AllNulls = false;
1103                                                 break;
1104                                         } 
1105                                 }
1106
1107                                 // If all of the columns were null, we have to write empty element
1108                                 if (AllNulls) {
1109                                         writer.WriteElementString (table.TableName, "");
1110                                         continue;
1111                                 }
1112                                 
1113                                 WriteTableElement (writer, mode, table, row, version);
1114                                 
1115                                 foreach (DataColumn col in atts) {                                      
1116                                         WriteColumnAsAttribute (writer, mode, col, row, version);
1117                                 }
1118                                 
1119                                 if (simple != null) {
1120                                         writer.WriteString (WriteObjectXml (row[simple, version]));
1121                                 }
1122                                 else {                                  
1123                                         foreach (DataColumn col in elements) {
1124                                                 WriteColumnAsElement (writer, mode, nspc, col, row, version);
1125                                         }
1126                                 }
1127                                 
1128                                 foreach (DataRelation relation in table.ChildRelations) {
1129                                         if (relation.Nested) {
1130                                                 WriteTable (writer, row.GetChildRows (relation), mode, version);
1131                                         }
1132                                 }
1133                                 
1134                                 writer.WriteEndElement ();
1135                         }
1136
1137                 }
1138
1139                 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, string nspc, DataColumn col, DataRow row, DataRowVersion version)
1140                 {
1141                         string colnspc = nspc;
1142                         object rowObject = row [col, version];
1143                                                                         
1144                         if (rowObject == null || rowObject == DBNull.Value)
1145                                 return;
1146
1147                         if (col.Namespace != null) {
1148                                 colnspc = col.Namespace;
1149                         }
1150         
1151                         //TODO check if I can get away with write element string
1152                         WriteStartElement (writer, mode, colnspc, col.Prefix, col.ColumnName);
1153                         writer.WriteString (WriteObjectXml (rowObject));
1154                         writer.WriteEndElement ();
1155                 }
1156
1157                 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1158                 {
1159                         WriteAttributeString (writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col, version].ToString ());
1160                 }
1161
1162                 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1163                 {
1164                         //sort out the namespacing
1165                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1166
1167                         WriteStartElement (writer, mode, nspc, table.Prefix, table.TableName);
1168
1169                         if (mode == XmlWriteMode.DiffGram) {
1170                                 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1171                                 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", row.XmlRowID.ToString());
1172                                 string modeName = null;
1173                                 if (row.RowState == DataRowState.Modified)
1174                                         modeName = "modified";
1175                                 else if (row.RowState == DataRowState.Added)
1176                                         modeName = "inserted";
1177
1178                                 if (version != DataRowVersion.Original && modeName != null)
1179                                         WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1180                         }
1181                 }
1182                     
1183                 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1184                 {
1185                         if (nspc == null || nspc == "") {
1186                                 writer.WriteStartElement (name);
1187                         }
1188                         else if (prefix != null) {
1189                                 writer.WriteStartElement (prefix, name, nspc);
1190                         }
1191                         else {
1192                                 writer.WriteStartElement (writer.LookupPrefix (nspc), name, nspc);
1193                         }
1194                 }
1195                 
1196                 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1197                 {
1198                         switch ( mode) {
1199                                 case XmlWriteMode.WriteSchema:
1200                                         writer.WriteAttributeString (prefix, name, nspc);
1201                                         break;
1202                                 case XmlWriteMode.DiffGram:
1203                                         writer.WriteAttributeString (prefix, name, nspc,stringValue);
1204                                         break;
1205                                 default:
1206                                         writer.WriteAttributeString (name, stringValue);
1207                                         break;                                  
1208                         };
1209                 }
1210                 
1211                 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1212                 {
1213                         ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1214
1215                         if (mode == XmlWriteMode.DiffGram) {
1216                                 SetTableRowsID (table);
1217                                 WriteDiffGramElement (writer);
1218                         }
1219                         
1220                         WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1221                         
1222                         WriteTable (writer, table, mode, DataRowVersion.Default);
1223                         
1224                         if (mode == XmlWriteMode.DiffGram) {
1225                                 writer.WriteEndElement (); //DataSet name
1226                                 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1227
1228                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
1229                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1230                                         WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1231                                         writer.WriteEndElement ();
1232                                 }
1233                         }
1234                         writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1235                 }
1236
1237                 XmlSchema IXmlSerializable.GetSchema ()
1238                 {
1239                         return BuildSchema ();
1240                 }
1241                 
1242                 XmlSchema BuildSchema ()
1243                 {
1244                         return BuildSchema (Tables, Relations);
1245                 }
1246                 
1247                 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1248                 {
1249                         string constraintPrefix = "";
1250                         XmlSchema schema = new XmlSchema ();
1251
1252                         schema.Namespaces.Add("xs", XmlSchema.Namespace);
1253                         schema.Namespaces.Add(XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1254                         
1255                         if (Namespace != "" && Namespace != null) {
1256                                 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1257                                 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1258                                 schema.TargetNamespace = Namespace;
1259                                 schema.Namespaces.Add(XmlConstants.TnsPrefix, Namespace);
1260                                 constraintPrefix = XmlConstants.TnsPrefix + ":";
1261                         }
1262                                 
1263                         // set the schema id
1264                         schema.Id = DataSetName;
1265                         XmlDocument doc = new XmlDocument ();
1266                         XmlAttribute xmlnsAttr = doc.CreateAttribute("xmlns");
1267                         xmlnsAttr.Value = Namespace;
1268
1269                         schema.UnhandledAttributes = new XmlAttribute[] {xmlnsAttr};
1270                                                 
1271                         XmlSchemaElement elem = new XmlSchemaElement ();
1272                         elem.Name = XmlConvert.EncodeName (DataSetName);
1273
1274                         XmlAttribute[] atts = new XmlAttribute [2];
1275                         atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1276                         atts[0].Value = "true";
1277
1278                         atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1279                         atts[1].Value = locale.Name;
1280                         elem.UnhandledAttributes = atts;
1281
1282                         schema.Items.Add (elem);
1283
1284                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1285                         elem.SchemaType = complex;
1286
1287                         XmlSchemaChoice choice = new XmlSchemaChoice ();
1288                         complex.Particle = choice;
1289                         choice.MaxOccursString = XmlConstants.Unbounded;
1290                         
1291                         //Write out schema for each table in order
1292                         foreach (DataTable table in tables) {           
1293                                 bool isTopLevel = true;
1294                                 foreach (DataRelation rel in table.ParentRelations) {
1295                                         if (rel.Nested) {
1296                                                 isTopLevel = false;
1297                                                 break;
1298                                         }
1299                                 }
1300                                 
1301                                 if (isTopLevel){
1302                                         choice.Items.Add (GetTableSchema (doc, table));
1303                                 }
1304                         }
1305                         
1306                         AddConstraintsToSchema (elem, constraintPrefix, tables, relations);
1307                         return schema;
1308                 }
1309                 
1310                 // Add all constraints in all tables to the schema.
1311                 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations) 
1312                 {
1313                         // first add all unique constraints.
1314                         Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables);
1315                         // Add all foriegn key constraints.
1316                         AddForeignKeys (uniqueNames, elem, constraintPrefix, relations);
1317                 }
1318                 
1319                 // Add unique constaraints to the schema.
1320                 // return hashtable with the names of all XmlSchemaUnique elements we created.
1321                 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables)
1322                 {
1323                         XmlDocument doc = new XmlDocument();
1324                         Hashtable uniqueNames = new Hashtable();
1325                         foreach (DataTable table in tables) {
1326                                 
1327                                 foreach (Constraint constaint in table.Constraints) {
1328                                         
1329                                         if (constaint is UniqueConstraint) {
1330                                                 ArrayList attrs = new ArrayList ();
1331                                                 XmlAttribute attrib;
1332                                                 UniqueConstraint uqConst = (UniqueConstraint)constaint;
1333                                                 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1334                                                 
1335                                                 // if constaraint name do not exist in the hashtable we can use it.
1336                                                 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1337                                                         uniq.Name = uqConst.ConstraintName;
1338                                                 }
1339                                                 // generate new constraint name for the XmlSchemaUnique element.
1340                                                 else {
1341                                                         uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1342                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1343                                                         attrib.Value = uqConst.ConstraintName;
1344                                                         attrs.Add (attrib);
1345                                                 }
1346                                                 if (uqConst.IsPrimaryKey) {
1347                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1348                                                         attrib.Value = "true";
1349                                                         attrs.Add (attrib);
1350                                                 }
1351                 
1352                                                 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1353
1354                                                 uniq.Selector = new XmlSchemaXPath();
1355                                                 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1356                                                 XmlSchemaXPath field;
1357                                                 foreach (DataColumn column in uqConst.Columns) {
1358                                                         field = new XmlSchemaXPath();
1359                                                         field.XPath = constraintPrefix+column.ColumnName;
1360                                                         uniq.Fields.Add(field);
1361                                                 }
1362                                 
1363                                                 elem.Constraints.Add (uniq);
1364                                                 uniqueNames.Add (uniq.Name, null);
1365                                         }
1366                                 }
1367                         }
1368                         return uniqueNames;
1369                 }
1370                 
1371                 // Add the foriegn keys to the schema.
1372                 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations)
1373                 {
1374                         if (relations == null) return;
1375                         
1376                         XmlDocument doc = new XmlDocument();
1377                         foreach (DataRelation rel in relations) {
1378                                 
1379                                 ArrayList attrs = new ArrayList ();
1380                                 XmlAttribute attrib;
1381                                 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1382                                 keyRef.Name = rel.RelationName;
1383                                 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1384                                 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1385                                 
1386                                 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1387                                 // first try to find the concatenated name. If we didn't find it - use constraint name.
1388                                 if (uniqueNames.ContainsKey (concatName)) {
1389                                         keyRef.Refer = new XmlQualifiedName(concatName);
1390                                 }
1391                                 else {
1392                                         keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1393                                 }
1394
1395                                 if (rel.Nested) {
1396                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1397                                         attrib.Value = "true";
1398                                         attrs.Add (attrib);
1399                                 }
1400
1401                                 keyRef.Selector = new XmlSchemaXPath();
1402                                 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1403                                 XmlSchemaXPath field;
1404                                 foreach (DataColumn column in rel.ChildColumns) {
1405                                         field = new XmlSchemaXPath();
1406                                         field.XPath = constraintPrefix+column.ColumnName;
1407                                         keyRef.Fields.Add(field);
1408                                 }
1409                                 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1410                                 elem.Constraints.Add (keyRef);
1411                         }
1412                 }
1413
1414                 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
1415                 {
1416                         ArrayList elements;
1417                         ArrayList atts;
1418                         DataColumn simple;
1419                         
1420                         SplitColumns (table, out atts, out elements, out simple);
1421
1422                         XmlSchemaElement elem = new XmlSchemaElement ();
1423                         elem.Name = table.TableName;
1424
1425                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1426                         elem.SchemaType = complex;
1427
1428                         //TODO - what about the simple content?
1429                         if (elements.Count == 0) {                              
1430                         }
1431                         else {
1432                                 //A sequence of element types or a simple content node
1433                                 //<xs:sequence>
1434                                 XmlSchemaSequence seq = new XmlSchemaSequence ();
1435                                 complex.Particle = seq;
1436
1437                                 foreach (DataColumn col in elements) {
1438                                         //<xs:element name=ColumnName type=MappedType Ordinal=index>
1439                                         XmlSchemaElement colElem = new XmlSchemaElement ();
1440                                         ArrayList xattrs = new ArrayList();
1441                                         XmlAttribute xattr;
1442                                         colElem.Name = col.ColumnName;
1443                                 
1444                                         if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1445                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1446                                                 xattr.Value = col.Caption;
1447                                                 xattrs.Add (xattr);
1448                                         }
1449
1450                                         if (col.AutoIncrement == true) {
1451                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1452                                                 xattr.Value = "true";
1453                                                 xattrs.Add (xattr);
1454                                         }
1455
1456                                         if (col.AutoIncrementSeed != 0) {
1457                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1458                                                 xattr.Value = col.AutoIncrementSeed.ToString();
1459                                                 xattrs.Add (xattr);
1460                                         }
1461
1462                                         if (col.DefaultValue.ToString () != String.Empty)
1463                                                 colElem.DefaultValue = col.DefaultValue.ToString ();
1464                                         
1465                                         if (col.MaxLength < 0)
1466                                                 colElem.SchemaTypeName = MapType (col.DataType);
1467                                         
1468                                         if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string) 
1469                                                 && col.DataType != typeof (char)) {
1470                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1471                                                 xattr.Value = col.DataType.ToString();
1472                                                 xattrs.Add (xattr);
1473                                         }
1474
1475                                         if (col.AllowDBNull) {
1476                                                 colElem.MinOccurs = 0;
1477                                         }
1478
1479                                         //writer.WriteAttributeString (XmlConstants.MsdataPrefix, 
1480                                         //                            XmlConstants.Ordinal, 
1481                                         //                            XmlConstants.MsdataNamespace, 
1482                                         //                            col.Ordinal.ToString ());
1483
1484                                         // Write SimpleType if column have MaxLength
1485                                         if (col.MaxLength > -1) {
1486                                                 colElem.SchemaType = GetTableSimpleType (doc, col);
1487                                         }
1488                                         
1489                                         colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1490                                         seq.Items.Add (colElem);
1491                                 }
1492
1493                                 foreach (DataRelation rel in table.ChildRelations) {
1494                                         if (rel.Nested) {
1495                                                 seq.Items.Add(GetTableSchema (doc, rel.ChildTable));
1496                                         }
1497                                 }
1498                         }
1499
1500                         //Then a list of attributes
1501                         foreach (DataColumn col in atts) {
1502                                 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1503                                 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1504                                 att.Name = col.ColumnName;
1505                                 att.Form = XmlSchemaForm.Unqualified;
1506                                 att.SchemaTypeName = MapType (col.DataType);
1507                                 complex.Attributes.Add (att);
1508                         }
1509
1510                         return elem;
1511                 }
1512
1513                 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1514                 {
1515                         // SimpleType
1516                         XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1517
1518                         // Restriction
1519                         XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1520                         restriction.BaseTypeName = MapType (col.DataType);
1521                         
1522                         // MaxValue
1523                         XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1524                         max.Value = XmlConvert.ToString (col.MaxLength);
1525                         restriction.Facets.Add (max);
1526                         
1527                         simple.Content = restriction;
1528                         return simple;
1529                 }
1530
1531                 private void DoWriteXmlSchema (XmlWriter writer)
1532                 {
1533                         GetSchemaSerializable ().Write (writer);
1534                 }
1535                 
1536                 ///<summary>
1537                 /// Helper function to split columns into attributes elements and simple
1538                 /// content
1539                 /// </summary>
1540                 private void SplitColumns (DataTable table, 
1541                         out ArrayList atts, 
1542                         out ArrayList elements, 
1543                         out DataColumn simple)
1544                 {
1545                         //The columns can be attributes, hidden, elements, or simple content
1546                         //There can be 0-1 simple content cols or 0-* elements
1547                         atts = new System.Collections.ArrayList ();
1548                         elements = new System.Collections.ArrayList ();
1549                         simple = null;
1550                         
1551                         //Sort out the columns
1552                         foreach (DataColumn col in table.Columns) {
1553                                 switch (col.ColumnMapping) {
1554                                         case MappingType.Attribute:
1555                                                 atts.Add (col);
1556                                                 break;
1557                                         case MappingType.Element:
1558                                                 elements.Add (col);
1559                                                 break;
1560                                         case MappingType.SimpleContent:
1561                                                 if (simple != null) {
1562                                                         throw new System.InvalidOperationException ("There may only be one simple content element");
1563                                                 }
1564                                                 simple = col;
1565                                                 break;
1566                                         default:
1567                                                 //ignore Hidden elements
1568                                                 break;
1569                                 }
1570                         }
1571                 }
1572
1573                 private void WriteDiffGramElement(XmlWriter writer)
1574                 {
1575                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1576                         WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1577                 }
1578
1579                 private void SetRowsID()
1580                 {
1581                         foreach (DataTable Table in Tables) {
1582                                 int dataRowID = 0;
1583                                 foreach (DataRow Row in Table.Rows) {
1584                                         Row.XmlRowID = dataRowID;
1585                                         dataRowID++;
1586                                 }
1587                         }
1588                 }
1589
1590                 
1591                 private XmlQualifiedName MapType (Type type)
1592                 {
1593                         switch (Type.GetTypeCode (type)) {
1594                                 case TypeCode.String: return XmlConstants.QnString;
1595                                 case TypeCode.Int16: return XmlConstants.QnShort;
1596                                 case TypeCode.Int32: return XmlConstants.QnInt;
1597                                 case TypeCode.Int64: return XmlConstants.QnLong;
1598                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1599                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1600                                 //case TypeCode.Char: return XmlConstants.QnChar;
1601                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1602                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1603                                 case TypeCode.Double: return XmlConstants.QnDouble;
1604                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
1605                                 case TypeCode.Single: return XmlConstants.QnFloat;
1606                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1607                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1608                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1609                         }
1610                         
1611                         if (typeof (TimeSpan) == type)
1612                                 return XmlConstants.QnDuration;
1613                         else if (typeof (System.Uri) == type)
1614                                 return XmlConstants.QnUri;
1615                         else if (typeof (byte[]) == type)
1616                                 return XmlConstants.QnBase64Binary;
1617                         else if (typeof (XmlQualifiedName) == type)
1618                                 return XmlConstants.QnXmlQualifiedName;
1619                         else
1620                                 return XmlConstants.QnString;
1621                 }
1622
1623                 #endregion //Private Xml Serialisation
1624         }
1625 }