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