* DataRow.cs : Throw exceptions if Row is Detached.
[mono.git] / mcs / class / System.Data / System.Data / DataSet.cs
1 // 
2 // System.Data/DataSet.cs
3 //
4 // Author:
5 //   Christopher Podurgiel <cpodurgiel@msn.com>
6 //   Daniel Morgan <danmorg@sc.rr.com>
7 //   Rodrigo Moya <rodrigo@ximian.com>
8 //   Stuart Caborn <stuart.caborn@virgin.net>
9 //   Tim Coleman (tim@timcoleman.com)
10 //   Ville Palo <vi64pa@koti.soon.fi>
11 //
12 // (C) Ximian, Inc. 2002
13 // Copyright (C) Tim Coleman, 2002
14 //
15
16 using System;
17 using System.Collections;
18 using System.ComponentModel;
19 using System.Globalization;
20 using System.Threading;
21 using System.IO;
22 using System.Runtime.Serialization;
23 using System.Xml;
24 using System.Xml.Schema;
25 using System.Xml.Serialization;
26 using System.Data.Common;
27
28 namespace System.Data {
29         /// <summary>
30         /// an in-memory cache of data 
31         /// </summary>
32         //[Designer]
33         [ToolboxItem (false)]
34         [DefaultProperty ("DataSetName")]
35         [Serializable]
36         public class DataSet : MarshalByValueComponent, IListSource,
37                 ISupportInitialize, ISerializable, IXmlSerializable {
38                 private string dataSetName;
39                 private string _namespace = "";
40                 private string prefix;
41                 private bool caseSensitive;
42                 private bool enforceConstraints = true;
43                 private DataTableCollection tableCollection;
44                 private DataRelationCollection relationCollection;
45                 private PropertyCollection properties;
46                 private DataViewManager defaultView;
47                 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
48                 
49                 #region Constructors
50
51                 public DataSet() : this ("NewDataSet") {                
52                 }
53
54                 public DataSet(string name) {
55                         dataSetName = name;
56                         tableCollection = new DataTableCollection (this);
57                         relationCollection = new DataRelationCollection.DataSetRelationCollection (this);
58                         properties = new PropertyCollection();
59                         this.prefix = String.Empty;
60                         
61                         this.Locale = CultureInfo.CurrentCulture;
62                 }
63
64                 [MonoTODO]
65                 protected DataSet(SerializationInfo info, StreamingContext context) : this () {
66                         throw new NotImplementedException ();
67                 }
68
69                 #endregion // Constructors
70
71                 #region Public Properties
72
73                 [DataCategory ("Data")]
74                 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
75                 [DefaultValue (false)]
76                 public bool CaseSensitive {
77                         get { return caseSensitive; } 
78                         set {
79                                 foreach (DataTable T in Tables) {
80                                         if (T.VirginCaseSensitive)
81                                                 T.CaseSensitive = value;
82                                 }
83
84                                 caseSensitive = value; 
85                                 if (!caseSensitive)
86                                 {
87                                         foreach (DataTable table in Tables)
88                                         {
89                                                 foreach (Constraint c in table.Constraints)
90                                                         c.AssertConstraint();
91                                         }
92                                 }
93                         }
94                 }
95
96                 [DataCategory ("Data")]
97                 [DataSysDescription ("The name of this DataSet.")]
98                 [DefaultValue ("")]
99                 public string DataSetName {
100                         get { return dataSetName; } 
101                         set { dataSetName = value; }
102                 }
103
104                 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
105                 [Browsable (false)]
106                 public DataViewManager DefaultViewManager {
107                         get {
108                                 if (defaultView == null)
109                                         defaultView = new DataViewManager (this);
110                                 return defaultView;
111                         } 
112                 }
113
114                 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
115                 [DefaultValue (true)]
116                 public bool EnforceConstraints {
117                         get { return enforceConstraints; } 
118                         set { 
119                                 if (value != enforceConstraints)
120                                 {
121                                         enforceConstraints = value; 
122                                         if (value)
123                                         {
124                                                 foreach (DataTable table in Tables)
125                                                 {
126                                                         foreach (Constraint c in table.Constraints)
127                                                                 c.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                                 {
148                                         if (Tables[i].HasErrors)
149                                                 return true;
150                                 }
151                                 return false;
152                         }
153                 }
154
155                 [DataCategory ("Data")]
156                 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
157                 public CultureInfo Locale {
158                         get {
159                                 return locale;
160                         }
161                         set {
162                                 if (locale == null || !locale.Equals(value)) {
163                                         // TODO: check if the new locale is valid
164                                         // TODO: update locale of all tables
165                                         locale = value;
166                                 }
167                         }
168                 }
169
170                 public void Merge (DataRow[] rows)
171                 {
172                         Merge (rows, false, MissingSchemaAction.Add);
173                 }
174                 
175                 public void Merge (DataSet dataSet)
176                 {
177                         Merge (dataSet, false, MissingSchemaAction.Add);
178                 }
179                 
180                 public void Merge (DataTable table)
181                 {
182                         Merge (table, false, MissingSchemaAction.Add);
183                 }
184                 
185                 public void Merge (DataSet dataSet, bool preserveChanges)
186                 {
187                         Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
188                 }
189                 
190                 [MonoTODO]
191                 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
192                 {
193                         if(rows == null)
194                                 throw new ArgumentNullException("rows");
195                         if(!IsLegalSchemaAction(missingSchemaAction))
196                                 throw new ArgumentOutOfRangeException("missingSchemaAction");
197                         
198                         MergeManager.Merge(this, rows, preserveChanges, missingSchemaAction);
199                 }
200                 
201                 [MonoTODO]
202                 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
203                 {
204                         if(dataSet == null)
205                                 throw new ArgumentNullException("dataSet");
206                         if(!IsLegalSchemaAction(missingSchemaAction))
207                                 throw new ArgumentOutOfRangeException("missingSchemaAction");
208                         
209                         MergeManager.Merge(this, dataSet, preserveChanges, missingSchemaAction);
210                 }
211                 
212                 [MonoTODO]
213                 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
214                 {
215                         if(table == null)
216                                 throw new ArgumentNullException("table");
217                         if(!IsLegalSchemaAction(missingSchemaAction))
218                                 throw new ArgumentOutOfRangeException("missingSchemaAction");
219                         
220                         MergeManager.Merge(this, table, preserveChanges, missingSchemaAction);
221                 }
222
223                 private static bool IsLegalSchemaAction(MissingSchemaAction missingSchemaAction)
224                 {
225                         if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
226                                 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
227                                 return true;
228                         return false;
229                 }
230                 
231                 [DataCategory ("Data")]
232                 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
233                 [DefaultValue ("")]
234                 public string Namespace {
235                         [MonoTODO]
236                         get { return _namespace; } 
237                         [MonoTODO]
238                         set {
239                                 //TODO - trigger an event if this happens?
240                                 _namespace = value;
241                         }
242                 }
243
244                 [DataCategory ("Data")]
245                 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
246                 [DefaultValue ("")]
247                 public string Prefix {
248                         [MonoTODO]
249                         get { return prefix; } 
250                         [MonoTODO]
251                         set {
252                                 //TODO - trigger an event if this happens?
253
254                                 if (value == null)
255                                 {
256                                         value = string.Empty;
257                                 }
258                                 
259                                 if (value != this.prefix)
260                                 {
261                                         RaisePropertyChanging("Prefix");
262                                 }
263                                 prefix = value;
264                         }
265                 }
266
267                 [DataCategory ("Data")]
268                 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
269                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
270                 public DataRelationCollection Relations {
271                         get {
272                                 return relationCollection;              
273                         }
274                 }
275
276                 [Browsable (false)]
277                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
278                 public override ISite Site {
279                         [MonoTODO]
280                         get {
281                                 throw new NotImplementedException ();
282                         } 
283                         
284                         [MonoTODO]
285                         set {
286                                 throw new NotImplementedException ();
287                         }
288                 }
289
290                 [DataCategory ("Data")]
291                 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
292                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
293                 public DataTableCollection Tables {
294                         get { return tableCollection; }
295                 }
296
297                 #endregion // Public Properties
298
299                 #region Public Methods
300
301                 [MonoTODO]
302                 public void AcceptChanges()
303                 {
304                         foreach (DataTable tempTable in tableCollection)
305                                 tempTable.AcceptChanges ();
306                 }
307
308                 public void Clear()
309                 {
310                         // TODO: if currently bound to a XmlDataDocument
311                         //       throw a NotSupportedException
312                         for (int t = 0; t < tableCollection.Count; t++) {
313                                 tableCollection[t].Clear ();
314                         }
315                 }
316
317                 public virtual DataSet Clone()
318                 {
319                         DataSet Copy = new DataSet ();
320                         CopyProperties (Copy);
321
322                         foreach (DataTable Table in Tables) {
323                                 Copy.Tables.Add (Table.Clone ());
324                         }
325
326                         //Copy Relationships between tables after existance of tables
327                         //and setting properties correctly
328                         CopyRelations(Copy);
329                         
330                         return Copy;
331                 }
332
333                 // Copies both the structure and data for this DataSet.
334                 public DataSet Copy()
335                 {
336                         DataSet Copy = new DataSet ();
337                         CopyProperties (Copy);
338
339                         // Copy DatSet's tables
340                         foreach (DataTable Table in Tables) {                           
341                                 Copy.Tables.Add (Table.Copy ());
342                         }
343
344                         //Copy Relationships between tables after existance of tables
345                         //and setting properties correctly
346                         CopyRelations(Copy);
347
348                         return Copy;
349                 }
350
351                 [MonoTODO]
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                         //Copy.ExtendedProperties 
361                         //Copy.HasErrors
362                         //Copy.Locale = Locale;
363                         Copy.Namespace = Namespace;
364                         Copy.Prefix = Prefix;                   
365                         //Copy.Site = Site;
366
367                 }
368                 
369                 
370                 private void CopyRelations (DataSet Copy)
371                 {
372
373                         //Creation of the relation contains some of the properties, and the constructor
374                         //demands these values. instead changing the DataRelation constructor and behaviour the
375                         //parameters are pre-configured and sent to the most general constructor
376
377                         foreach (DataRelation MyRelation in this.Relations)
378                         {
379                                 string pTable = MyRelation.ParentTable.TableName;
380                                 string cTable = MyRelation.ChildTable.TableName;
381                                 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length]; 
382                                 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
383                                 int i = 0;
384                                 foreach(DataColumn DC in MyRelation.ParentColumns)
385                                 {
386                                         P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
387                                         i++;
388                                 }
389
390                                 i = 0;
391
392                                 foreach(DataColumn DC in MyRelation.ChildColumns)
393                                 {
394                                         C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
395                                         i++;
396                                 }
397                                 
398
399                                 DataRelation cRel = new DataRelation(MyRelation.RelationName,P_DC,C_DC);
400                                 //cRel.ChildColumns = MyRelation.ChildColumns;
401                                 //cRel.ChildTable = MyRelation.ChildTable;
402                                 //cRel.ExtendedProperties = cRel.ExtendedProperties; 
403                                 //cRel.Nested = MyRelation.Nested;
404                                 //cRel.ParentColumns = MyRelation.ParentColumns;
405                                 //cRel.ParentTable = MyRelation.ParentTable;
406                                                                 
407                                 Copy.Relations.Add(cRel);
408                         }
409                 }
410
411                 
412
413
414                 public DataSet GetChanges()
415                 {
416                         return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
417                 }
418
419                 
420                 public DataSet GetChanges(DataRowState rowStates)
421                 {
422                         if(!HasChanges(rowStates))
423                                 return null;
424                         
425                         DataSet copySet = Clone();
426                         IEnumerator tableEnumerator = Tables.GetEnumerator();
427                         DataTable origTable;
428                         DataTable copyTable;
429                         while (tableEnumerator.MoveNext())
430                         {
431                                 origTable = (DataTable)tableEnumerator.Current;
432                                 copyTable = copySet.Tables[origTable.TableName];
433
434                                 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator();
435                                 while (rowEnumerator.MoveNext())
436                                 {
437                                         DataRow row = (DataRow)rowEnumerator.Current;
438                                         if (row.IsRowChanged(rowStates))
439                                         {
440                                                 DataRow newRow = copyTable.NewRow();
441                                                 copyTable.Rows.Add(newRow);
442                                                 row.CopyValuesToRow(newRow);
443                                         }
444                                 }
445                         }
446                         return copySet;
447                 }
448
449                 
450                 public string GetXml()
451                 {
452                         StringWriter Writer = new StringWriter ();
453
454                         // Sending false for not printing the Processing instruction
455                         WriteXml (Writer, XmlWriteMode.IgnoreSchema,false);
456                         return Writer.ToString ();
457                 }
458
459                 public string GetXmlSchema()
460                 {
461                         StringWriter Writer = new StringWriter ();
462                         WriteXmlSchema (Writer);
463                         return Writer.ToString ();
464                 }
465
466                 [MonoTODO]
467                 public bool HasChanges()
468                 {
469                         return HasChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
470                 }
471
472                 [MonoTODO]
473                 public bool HasChanges(DataRowState rowState)
474                 {
475                         if(((int)rowState & 0xffffffe0) != 0)
476                                 throw new ArgumentOutOfRangeException("rowState");
477
478                         DataTableCollection tableCollection = Tables;
479                         DataTable table;
480                         DataRowCollection rowCollection;
481                         DataRow row;
482                         for (int i = 0; i < tableCollection.Count; i++)
483                         {
484                                 table = tableCollection[i];
485                                 rowCollection = table.Rows;
486                                 for (int j = 0; j < rowCollection.Count; j++)
487                                 {
488                                         row = rowCollection[j];
489                                         if((row.RowState & rowState) != 0)
490                                                 return true;
491                                 }
492                         }
493
494                         return false;           
495                 }
496
497                 [MonoTODO]
498                 public void InferXmlSchema(XmlReader reader, string[] nsArray)
499                 {
500                 }
501
502                 public void InferXmlSchema(Stream stream, string[] nsArray)
503                 {
504                         InferXmlSchema (new XmlTextReader(stream), nsArray);
505                 }
506
507                 public void InferXmlSchema(TextReader reader, string[] nsArray)
508                 {
509                         InferXmlSchema (new XmlTextReader(reader), nsArray);
510                 }
511
512                 public void InferXmlSchema(string fileName, string[] nsArray)
513                 {
514                         XmlTextReader reader = new XmlTextReader(fileName);
515                         try {
516                                 InferXmlSchema (reader, nsArray);
517                         } finally {
518                                 reader.Close ();
519                         }
520                 }
521
522                 public virtual void RejectChanges()
523                 {
524                         int i;
525                         bool oldEnforceConstraints = this.EnforceConstraints;
526                         this.EnforceConstraints = false;
527                         for (i=0;(i<this.Tables.Count);i++)
528                         {
529                                 this.Tables[i].RejectChanges();
530                         }
531                         this.EnforceConstraints = oldEnforceConstraints;
532                 }
533
534                 public virtual void Reset()
535                 {
536                         IEnumerator constraintEnumerator;
537                         // first we remove all ForeignKeyConstraints (if we will not do that
538                         // we will get an exception when clearing the tables).
539                         for (int i = 0; i < Tables.Count; i++)
540                         {
541                                 ConstraintCollection cc = Tables[i].Constraints;
542                                 for (int j = 0; j < cc.Count; j++)
543                                 {
544                                         if (cc[j] is ForeignKeyConstraint)
545                                                 cc.Remove(cc[j]);
546                                 }
547                         }
548
549                         Clear();
550                         Relations.Clear();
551                         Tables.Clear();
552                 }
553
554                 public void WriteXml(Stream stream)
555                 {
556                         XmlWriter writer = new XmlTextWriter(stream, null );
557                         
558                         WriteXml( writer );
559                 }
560
561                 ///<summary>
562                 /// Writes the current data for the DataSet to the specified file.
563                 /// </summary>
564                 /// <param name="filename">Fully qualified filename to write to</param>
565                 public void WriteXml(string fileName)
566                 {
567                         XmlWriter writer = new XmlTextWriter(fileName, null );
568                         
569                         WriteXml( writer );
570                         
571                         writer.Close();
572                 }
573
574                 public void WriteXml(TextWriter writer)
575                 {
576                         XmlWriter xwriter = new XmlTextWriter(writer );
577                         
578                         WriteXml( xwriter );
579                 }
580
581                 public void WriteXml(XmlWriter writer)
582                 {
583                         WriteXml( writer, XmlWriteMode.IgnoreSchema,false);
584                 }
585
586                 public void WriteXml(Stream stream, XmlWriteMode mode,bool writePI)
587                 {
588                         XmlWriter writer = new XmlTextWriter(stream, null );
589                         
590                         WriteXml( writer, mode, writePI );
591                 }
592
593                 public void WriteXml(string fileName, XmlWriteMode mode, bool writePI)
594                 {
595                         XmlWriter writer = new XmlTextWriter(fileName, null );
596                         
597                         WriteXml( writer, mode, writePI );
598                         
599                         writer.Close();
600                 }
601
602                 public void WriteXml(TextWriter writer, XmlWriteMode mode, bool writePI)
603                 {
604                         XmlWriter xwriter = new XmlTextWriter(writer);
605                         
606                         WriteXml( xwriter, mode, writePI);
607                 }
608
609                 public void WriteXml(XmlWriter writer, XmlWriteMode mode, bool writePI)
610                 {
611                         if (writePI && (writer.WriteState == WriteState.Start))
612                                 writer.WriteStartDocument (true);
613
614                         ((XmlTextWriter)writer).Formatting = Formatting.Indented;
615                         WriteStartElement( writer, mode, Namespace, Prefix, DataSetName );
616                         
617                         if( mode == XmlWriteMode.WriteSchema )
618                         {
619                                 DoWriteXmlSchema( writer );
620                         }
621                         
622                         //Write out each table in order, providing it is not
623                         //part of another table structure via a nested parent relationship
624                         foreach( DataTable table in Tables )
625                         {
626                                 bool isTopLevel = true;
627                                 foreach( DataRelation rel in table.ParentRelations )
628                                 {
629                                         if( rel.Nested )
630                                         {
631                                                 isTopLevel = false;
632                                                 break;
633                                         }
634                                 }
635                                 
636                                 if( isTopLevel )
637                                 {
638                                         WriteTable(  writer, table, mode );
639                                 }
640                         }
641                         
642                         writer.WriteEndElement();                       
643                 }
644
645                 public void WriteXmlSchema(Stream stream)
646                 {
647                         XmlWriter writer = new XmlTextWriter(stream, null  );
648                         
649                         WriteXmlSchema( writer );       
650                 }
651
652                 public void WriteXmlSchema(string fileName)
653                 {
654                         XmlWriter writer = new XmlTextWriter( fileName, null );
655                 
656                         WriteXmlSchema( writer );
657                 }
658
659                 public void WriteXmlSchema(TextWriter writer)
660                 {
661                         XmlWriter xwriter = new XmlTextWriter( writer );
662                         
663                         WriteXmlSchema( xwriter );
664                 }
665
666                 public void WriteXmlSchema(XmlWriter writer)
667                 {
668                         ((XmlTextWriter)writer).Formatting = Formatting.Indented;
669                         //Create a skeleton doc and then write the schema 
670                         //proper which is common to the WriteXml method in schema mode
671                         writer.WriteStartDocument();
672                         
673                         DoWriteXmlSchema( writer );
674                         
675                         writer.WriteEndDocument();
676                 }
677
678                 public void ReadXmlSchema(Stream stream)
679                 {
680                         XmlReader reader = new XmlTextReader( stream, null );
681                         ReadXmlSchema( reader);
682                 }
683
684                 public void ReadXmlSchema(string str)
685                 {
686                         XmlReader reader = new XmlTextReader( str );
687                         ReadXmlSchema( reader );
688                 }
689
690                 public void ReadXmlSchema(TextReader treader)
691                 {
692                         XmlReader reader = new XmlTextReader( treader );
693                         ReadXmlSchema( reader );                        
694                 }
695
696                 public void ReadXmlSchema(XmlReader reader)
697                 {
698                         XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
699                         SchemaMapper.Read (reader);
700                 }
701
702                 public XmlReadMode ReadXml (Stream stream)
703                 {
704                         return ReadXml (new XmlTextReader (stream));
705                 }
706
707                 public XmlReadMode ReadXml (string str)
708                 {
709                         return ReadXml (new XmlTextReader (str));
710                 }
711
712                 public XmlReadMode ReadXml (TextReader reader)
713                 {
714                         return ReadXml (new XmlTextReader (reader));
715                 }
716
717                 public XmlReadMode ReadXml (XmlReader r)
718                 {
719                         XmlDataLoader Loader = new XmlDataLoader (this);
720                         // FIXME: somekinda exception?
721                         if (!r.Read ())
722                                 return XmlReadMode.Auto; // FIXME
723
724                         /*\
725                          *  If document is diffgram we will use diffgram
726                         \*/
727                         if (r.LocalName == "diffgram")
728                                 return ReadXml (r, XmlReadMode.DiffGram);
729
730                         /*\
731                          *  If we already have a schema, or the document 
732                          *  contains an in-line schema, sets XmlReadMode to ReadSchema.
733                         \*/
734
735                         // FIXME: is this always true: "if we have tables we have to have schema also"
736                         if (Tables.Count > 0)                           
737                                 return ReadXml (r, XmlReadMode.ReadSchema);
738
739                         /*\
740                          *  If we dont have a schema yet and document 
741                          *  contains no inline-schema  mode is XmlReadMode.InferSchema
742                         \*/
743
744                         return ReadXml (r, XmlReadMode.InferSchema);
745
746                 }
747
748                 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
749                 {
750                         return ReadXml (new XmlTextReader (stream), mode);
751                 }
752
753                 public XmlReadMode ReadXml (string str, XmlReadMode mode)
754                 {
755                         return ReadXml (new XmlTextReader (str), mode);
756                 }
757
758                 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
759                 {
760                         return ReadXml (new XmlTextReader (reader), mode);
761                 }
762
763                 [MonoTODO]
764                 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
765                 {
766                         XmlReadMode Result = XmlReadMode.Auto;
767
768                         if (mode == XmlReadMode.DiffGram) {
769                                 XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
770                                 DiffLoader.Load (reader);
771                                 Result =  XmlReadMode.DiffGram;
772                         }
773                         else {
774                                 XmlDataLoader Loader = new XmlDataLoader (this);
775                                 Result = Loader.LoadData (reader, mode);
776                         }
777
778                         return Result;
779                 }
780
781                 #endregion // Public Methods
782
783                 #region Public Events
784
785                 [DataCategory ("Action")]
786                 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
787                 public event MergeFailedEventHandler MergeFailed;
788
789                 #endregion // Public Events
790
791                 #region Destructors
792
793                 ~DataSet()
794                 {
795                 }
796
797                 #endregion Destructors
798
799                 #region IListSource methods
800                 IList IListSource.GetList ()
801                 {
802                         return DefaultViewManager;
803                 }
804                 
805                 bool IListSource.ContainsListCollection {
806                         get {
807                                 return true;
808                         }
809                 }
810                 #endregion IListSource methods
811                 
812                 #region ISupportInitialize methods
813                 public void BeginInit ()
814                 {
815                 }
816                 
817                 public void EndInit ()
818                 {
819                 }
820                 #endregion
821
822                 #region ISerializable
823                 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
824                 {
825                         throw new NotImplementedException ();
826                 }
827                 #endregion
828                 
829                 #region Protected Methods
830                 protected void GetSerializationData(SerializationInfo info, StreamingContext context)
831                 {
832                         string s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
833                         if (s != null) ReadXmlSerializable (new XmlTextReader(new StringReader(s)));
834                 }
835                 
836                 
837                 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable()
838                 {
839                         return BuildSchema ();
840                 }
841                 
842                 protected virtual void ReadXmlSerializable(XmlReader reader)
843                 {
844                         ReadXml(reader, XmlReadMode.DiffGram); // FIXME
845                 }
846
847                 void IXmlSerializable.ReadXml(XmlReader reader)
848                 {
849                         reader.MoveToContent ();
850                         reader.ReadStartElement ();     // <DataSet>
851
852                         reader.MoveToContent ();
853                         ReadXmlSchema (reader);
854
855                         reader.MoveToContent ();
856                         ReadXml(reader, XmlReadMode.IgnoreSchema);
857
858                         reader.MoveToContent ();
859                         reader.ReadEndElement ();       // </DataSet>
860                 }
861                 
862                 void IXmlSerializable.WriteXml(XmlWriter writer)
863                 {
864                         DoWriteXmlSchema (writer);
865                         WriteXml(writer, XmlWriteMode.IgnoreSchema, true);
866                 }
867
868                 protected virtual bool ShouldSerializeRelations ()
869                 {
870                         return true;
871                 }
872                 
873                 protected virtual bool ShouldSerializeTables ()
874                 {
875                         return true;
876                 }
877
878                 [MonoTODO]
879                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
880                 {
881                 }
882
883                 [MonoTODO]
884                 protected virtual void OnRemoveRelation (DataRelation relation)
885                 {
886                 }
887
888                 [MonoTODO]
889                 protected virtual void OnRemoveTable (DataTable table)
890                 {
891                 }
892
893                 protected internal virtual void OnMergeFailed (MergeFailedEventArgs e)
894                 {
895                         if (MergeFailed != null)
896                                 MergeFailed(this, e);
897                 }
898
899                 [MonoTODO]
900                 protected internal void RaisePropertyChanging (string name)
901                 {
902                 }
903                 #endregion
904
905                 #region Private Xml Serialisation
906
907                 private string WriteObjectXml( object o ) {
908                         switch (Type.GetTypeCode (o.GetType ())) {
909                         case TypeCode.Boolean:
910                                 return XmlConvert.ToString ((Boolean) o);
911                         case TypeCode.Byte:
912                                 return XmlConvert.ToString ((Byte) o);
913                         case TypeCode.Char:
914                                 return XmlConvert.ToString ((Char) o);
915                         case TypeCode.DateTime:
916                                 return XmlConvert.ToString ((DateTime) o);
917                         case TypeCode.Decimal:
918                                 return XmlConvert.ToString ((Decimal) o);
919                         case TypeCode.Double:
920                                 return XmlConvert.ToString ((Double) o);
921                         case TypeCode.Int16:
922                                 return XmlConvert.ToString ((Int16) o);
923                         case TypeCode.Int32:
924                                 return XmlConvert.ToString ((Int32) o);
925                         case TypeCode.Int64:
926                                 return XmlConvert.ToString ((Int64) o);
927                         case TypeCode.SByte:
928                                 return XmlConvert.ToString ((SByte) o);
929                         case TypeCode.Single:
930                                 return XmlConvert.ToString ((Single) o);
931                         case TypeCode.UInt16:
932                                 return XmlConvert.ToString ((UInt16) o);
933                         case TypeCode.UInt32:
934                                 return XmlConvert.ToString ((UInt32) o);
935                         case TypeCode.UInt64:
936                                 return XmlConvert.ToString ((UInt64) o);
937                         }
938                         if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
939                         if (o is Guid) return XmlConvert.ToString ((Guid) o);
940                         return o.ToString();
941                 }
942         
943                 private void WriteTable( XmlWriter writer, DataTable table, XmlWriteMode mode )
944                 {
945                         DataRow[] rows = new DataRow [table.Rows.Count];
946                         table.Rows.CopyTo (rows, 0);
947                         WriteTable (writer, rows, mode);
948                 }
949
950                 private void WriteTable( XmlWriter writer, DataRow[] rows, XmlWriteMode mode )
951                 {
952                         //The columns can be attributes, hidden, elements, or simple content
953                         //There can be 0-1 simple content cols or 0-* elements
954                         System.Collections.ArrayList atts;
955                         System.Collections.ArrayList elements;
956                         DataColumn simple = null;
957
958                         if (rows.Length == 0) return;
959                         DataTable table = rows[0].Table;
960                         SplitColumns( table, out atts, out elements, out simple );
961
962                         foreach( DataRow row in rows )
963                         {
964                                 //sort out the namespacing
965                                 string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
966
967                                 // First check are all the rows null. If they are we just write empty element
968                                 bool AllNulls = true;
969                                 foreach (DataColumn dc in table.Columns) {
970                                 
971                                         if (row [dc.ColumnName] != DBNull.Value) {
972                                                 AllNulls = false;
973                                                 break;
974                                         } 
975                                 }
976
977                                 // If all of the columns were null, we have to write empty element
978                                 if (AllNulls) {
979                                         writer.WriteElementString (table.TableName, "");
980                                         continue;
981                                 }
982
983                                 WriteStartElement( writer, mode, nspc, table.Prefix, table.TableName );
984                                 
985                                 foreach( DataColumn col in atts )
986                                 {                                       
987                                         WriteAttributeString( writer, mode, col.Namespace, col.Prefix, col.ColumnName, row[col].ToString() );
988                                 }
989                                 
990                                 if( simple != null )
991                                 {
992                                         writer.WriteString( WriteObjectXml(row[simple]) );
993                                 }
994                                 else
995                                 {                                       
996                                         foreach( DataColumn col in elements )
997                                         {
998                                                 string colnspc = nspc;
999                                                 object rowObject = row [col];
1000                                                                                                 
1001                                                 if (rowObject == null || rowObject == DBNull.Value)
1002                                                         continue;
1003
1004                                                 if( col.Namespace != null )
1005                                                 {
1006                                                         colnspc = col.Namespace;
1007                                                 }
1008                                 
1009                                                 //TODO check if I can get away with write element string
1010                                                 WriteStartElement( writer, mode, colnspc, col.Prefix, col.ColumnName );
1011                                                 writer.WriteString( WriteObjectXml(rowObject) );
1012                                                 writer.WriteEndElement();
1013                                         }
1014                                 }
1015                                 
1016                                 foreach (DataRelation relation in table.ChildRelations) {
1017                                         if (relation.Nested) {
1018                                                 WriteTable (writer, row.GetChildRows(relation), mode);
1019                                         }
1020                                 }
1021                                 
1022                                 writer.WriteEndElement();
1023                         }
1024
1025                 }
1026                     
1027                 private void WriteStartElement( XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name )
1028                 {                       
1029                         switch(  mode )
1030                                 {
1031                                         case XmlWriteMode.WriteSchema:
1032                                                 if( nspc == null || nspc == "" )
1033                                                 {
1034                                                         writer.WriteStartElement( name );
1035                                                 }
1036                                                 else if( prefix != null )
1037                                                 {                                                       
1038                                                         writer.WriteStartElement(prefix, name, nspc );
1039                                                 }                                               
1040                                                 else
1041                                                 {                                       
1042                                                         writer.WriteStartElement( writer.LookupPrefix( nspc ), name, nspc );
1043                                                 }
1044                                                 break;
1045                                         case XmlWriteMode.DiffGram:
1046                                                 throw new NotImplementedException();
1047                                         default:                                               
1048                                                 writer.WriteStartElement(name );
1049                                                 break;                                  
1050                                 };
1051                 }
1052                 
1053                 private void WriteAttributeString( XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue )
1054                 {
1055                         switch(  mode )
1056                                 {
1057                                         case XmlWriteMode.WriteSchema:
1058                                                 writer.WriteAttributeString(prefix, name, nspc );
1059                                                 break;
1060                                         case XmlWriteMode.DiffGram:
1061                                                 throw new NotImplementedException();                            
1062                                         default:
1063                                                 writer.WriteAttributeString(name, stringValue );
1064                                                 break;                                  
1065                                 };
1066                 }
1067
1068                 XmlSchema IXmlSerializable.GetSchema()
1069                 {
1070                         return BuildSchema ();
1071                 }
1072                 
1073                 XmlSchema BuildSchema()
1074                 {
1075                         XmlSchema schema = new XmlSchema ();
1076                         schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1077
1078                         XmlSchemaElement elem = new XmlSchemaElement ();
1079                         elem.Name = DataSetName;
1080
1081                         XmlDocument doc = new XmlDocument ();
1082
1083                         XmlAttribute[] atts = new XmlAttribute [2];
1084                         atts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1085                         atts[0].Value = "true";
1086
1087                         atts[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1088                         atts[1].Value = locale.Name;
1089                         elem.UnhandledAttributes = atts;
1090
1091                         schema.Items.Add (elem);
1092
1093                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1094                         elem.SchemaType = complex;
1095
1096                         XmlSchemaChoice choice = new XmlSchemaChoice ();
1097                         complex.Particle = choice;
1098                         choice.MaxOccursString = XmlConstants.Unbounded;
1099                         
1100                         //Write out schema for each table in order, providing it is not
1101                         //part of another table structure via a nested parent relationship
1102                         foreach( DataTable table in Tables )
1103                         {               
1104                                 bool isTopLevel = true;
1105                                 foreach( DataRelation rel in table.ParentRelations )
1106                                 {
1107                                         if( rel.Nested )
1108                                         {
1109                                                 isTopLevel = false;
1110                                                 break;
1111                                         }
1112                                 }
1113                                 
1114                                 if( isTopLevel )
1115                                 {
1116                                         choice.Items.Add (GetTableSchema (doc, table));
1117                                 }
1118                         }
1119                         
1120                         //TODO - now add in the relationships as key and unique constraints etc
1121
1122                         return schema;
1123                 }
1124
1125                 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table)
1126                 {
1127                         ArrayList elements;
1128                         ArrayList atts;
1129                         DataColumn simple;
1130                         
1131                         SplitColumns (table, out atts, out elements, out simple);
1132
1133                         XmlSchemaElement elem = new XmlSchemaElement ();
1134                         elem.Name = table.TableName;
1135
1136                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1137                         elem.SchemaType = complex;
1138
1139                         //TODO - what about the simple content?
1140                         if( elements.Count == 0 )                               
1141                         {                               
1142                         }
1143                         else
1144                         {
1145                                 //A sequence of element types or a simple content node
1146                                 //<xs:sequence>
1147                                 XmlSchemaSequence seq = new XmlSchemaSequence ();
1148                                 complex.Particle = seq;
1149
1150                                 foreach( DataColumn col in elements )
1151                                 {
1152                                         //<xs:element name=ColumnName type=MappedType Ordinal=index>
1153                                         XmlSchemaElement colElem = new XmlSchemaElement ();
1154                                         colElem.Name = col.ColumnName;
1155                                 
1156                                         if (col.ColumnName != col.Caption && col.Caption != string.Empty)
1157                                         {
1158                                                 XmlAttribute[] xatts = new XmlAttribute[1];
1159                                                 xatts[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1160                                                 xatts[0].Value = col.Caption;
1161                                                 colElem.UnhandledAttributes = xatts;
1162                                         }
1163
1164                                         if (col.DefaultValue.ToString () != string.Empty)
1165                                                 colElem.DefaultValue = col.DefaultValue.ToString ();
1166
1167                                         colElem.SchemaTypeName = MapType (col.DataType);
1168
1169                                         if( col.AllowDBNull )
1170                                         {
1171                                                 colElem.MinOccurs = 0;
1172                                         }
1173
1174                                         //writer.WriteAttributeString( XmlConstants.MsdataPrefix,
1175                                         //                            XmlConstants.Ordinal,
1176                                         //                            XmlConstants.MsdataNamespace,
1177                                         //                            col.Ordinal.ToString() );
1178
1179                                         // Write SimpleType if column have MaxLength
1180                                         if (col.MaxLength > -1) 
1181                                         {
1182                                                 colElem.SchemaType = GetTableSimpleType (doc, col);
1183                                         }
1184
1185                                         seq.Items.Add (colElem);
1186                                 }
1187                         }
1188
1189                         //Then a list of attributes
1190                         foreach( DataColumn col in atts )
1191                         {
1192                                 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1193                                 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1194                                 att.Name = col.ColumnName;
1195                                 att.Form = XmlSchemaForm.Unqualified;
1196                                 att.SchemaTypeName = MapType (col.DataType);
1197                                 complex.Attributes.Add (att);
1198                         }
1199                         return elem;
1200                 }
1201
1202                 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1203                 {
1204                         // SimpleType
1205                         XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1206
1207                         // Restriction
1208                         XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1209                         restriction.BaseTypeName = MapType (col.DataType);
1210                         
1211                         // MaxValue
1212                         XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1213                         max.Value = XmlConvert.ToString (col.MaxLength);
1214                         restriction.Facets.Add (max);
1215
1216                         return simple;
1217                 }
1218
1219                 private void DoWriteXmlSchema( XmlWriter writer )
1220                 {
1221                         GetSchemaSerializable ().Write (writer);
1222                 }
1223                 
1224                 ///<summary>
1225                 /// Helper function to split columns into attributes elements and simple
1226                 /// content
1227                 /// </summary>
1228                 private void SplitColumns(      DataTable table,
1229                                                                         out ArrayList atts,
1230                                                                         out ArrayList elements,
1231                                                                         out DataColumn simple)
1232                 {
1233                         //The columns can be attributes, hidden, elements, or simple content
1234                         //There can be 0-1 simple content cols or 0-* elements
1235                         atts = new System.Collections.ArrayList();
1236                         elements = new System.Collections.ArrayList();
1237                         simple = null;
1238                         
1239                         //Sort out the columns
1240                         foreach( DataColumn col in table.Columns )
1241                         {
1242                                 switch( col.ColumnMapping )
1243                                 {
1244                                         case MappingType.Attribute:
1245                                                 atts.Add( col );
1246                                                 break;
1247                                         case MappingType.Element:
1248                                                 elements.Add( col );
1249                                                 break;
1250                                         case MappingType.SimpleContent:
1251                                                 if( simple != null )
1252                                                 {
1253                                                         throw new System.InvalidOperationException( "There may only be one simple content element" );
1254                                                 }
1255                                                 simple = col;
1256                                                 break;
1257                                         default:
1258                                                 //ignore Hidden elements
1259                                                 break;
1260                                 }
1261                         }
1262                 }
1263                 
1264                 private XmlQualifiedName MapType (Type type)
1265                 {
1266                         switch (Type.GetTypeCode (type))
1267                         {
1268                                 case TypeCode.String: return XmlConstants.QnString;
1269                                 case TypeCode.Int16: return XmlConstants.QnShort;
1270                                 case TypeCode.Int32: return XmlConstants.QnInt;
1271                                 case TypeCode.Int64: return XmlConstants.QnLong;
1272                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1273                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1274                                 case TypeCode.Char: return XmlConstants.QnChar;
1275                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1276                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1277                                 case TypeCode.Double: return XmlConstants.QnDouble;
1278                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
1279                                 case TypeCode.Single: return XmlConstants.QnFloat;
1280                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1281                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1282                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1283                         }
1284                         
1285                         if (typeof (TimeSpan) == type) return XmlConstants.QnDuration;
1286                         else if (typeof (System.Uri) == type) return XmlConstants.QnUri;
1287                         else if (typeof (byte[]) == type) return XmlConstants.QnBase64Binary;
1288                         else if (typeof (XmlQualifiedName) == type) return XmlConstants.QnXmlQualifiedName;
1289                         else return XmlConstants.QnString;
1290                 }
1291
1292                 #endregion //Private Xml Serialisation
1293         }
1294 }