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