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