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