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