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