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