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