* Mono.Posix.dll.sources: Rename Mono.Posix to Mono.Unix.
[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 //   Atsushi Enomoto <atsushi@ximian.com>
12 //
13 // (C) Ximian, Inc. 2002
14 // Copyright (C) Tim Coleman, 2002, 2003
15 //
16
17 //
18 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
19 //
20 // Permission is hereby granted, free of charge, to any person obtaining
21 // a copy of this software and associated documentation files (the
22 // "Software"), to deal in the Software without restriction, including
23 // without limitation the rights to use, copy, modify, merge, publish,
24 // distribute, sublicense, and/or sell copies of the Software, and to
25 // permit persons to whom the Software is furnished to do so, subject to
26 // the following conditions:
27 // 
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
30 // 
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 //
39
40 using System;
41 using System.Collections;
42 using System.ComponentModel;
43 using System.Globalization;
44 using System.Threading;
45 using System.IO;
46 using System.Runtime.Serialization;
47 using System.Xml;
48 using System.Xml.Schema;
49 using System.Xml.Serialization;
50 using System.Data.Common;
51
52 namespace System.Data {
53
54         [ToolboxItem (false)]
55         [DefaultProperty ("DataSetName")]
56         [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.DataSetDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
57
58         [Serializable]
59         public class DataSet : MarshalByValueComponent, IListSource, 
60                 ISupportInitialize, ISerializable, IXmlSerializable 
61         {
62                 private string dataSetName;
63                 private string _namespace = "";
64                 private string prefix;
65                 private bool caseSensitive;
66                 private bool enforceConstraints = true;
67                 private DataTableCollection tableCollection;
68                 private DataRelationCollection relationCollection;
69                 private PropertyCollection properties;
70                 private DataViewManager defaultView;
71                 private CultureInfo locale = System.Threading.Thread.CurrentThread.CurrentCulture;
72                 internal XmlDataDocument _xmlDataDocument = null;
73                 
74                 #region Constructors
75
76                 public DataSet () : this ("NewDataSet") 
77                 {               
78                 }
79                 
80                 public DataSet (string name)
81                 {
82                         dataSetName = name;
83                         tableCollection = new DataTableCollection (this);
84                         relationCollection = new DataRelationCollection.DataSetRelationCollection (this);
85                         properties = new PropertyCollection ();
86                         this.prefix = String.Empty;
87                         
88                         this.Locale = CultureInfo.CurrentCulture;
89                 }
90
91                 protected DataSet (SerializationInfo info, StreamingContext context) : this ()
92                 {
93                         GetSerializationData (info, context);
94                 }
95
96                 #endregion // Constructors
97
98                 #region Public Properties
99
100                 [DataCategory ("Data")]
101                 [DataSysDescription ("Indicates whether comparing strings within the DataSet is case sensitive.")]
102                 [DefaultValue (false)]
103                 public bool CaseSensitive {
104                         get {
105                                 return caseSensitive;
106                         } 
107                         set {
108                                 caseSensitive = value; 
109                                 if (!caseSensitive) {
110                                         foreach (DataTable table in Tables) {
111                                                 foreach (Constraint c in table.Constraints)
112                                                         c.AssertConstraint ();
113                                         }
114                                 }
115                         }
116                 }
117
118                 [DataCategory ("Data")]
119                 [DataSysDescription ("The name of this DataSet.")]
120                 [DefaultValue ("")]
121                 public string DataSetName {
122                         get { return dataSetName; } 
123                         set { dataSetName = value; }
124                 }
125
126                 [DataSysDescription ("Indicates a custom \"view\" of the data contained by the DataSet. This view allows filtering, searching, and navigating through the custom data view.")]
127                 [Browsable (false)]
128                 public DataViewManager DefaultViewManager {
129                         get {
130                                 if (defaultView == null)
131                                         defaultView = new DataViewManager (this);
132                                 return defaultView;
133                         } 
134                 }
135
136                 [DataSysDescription ("Indicates whether constraint rules are to be followed.")]
137                 [DefaultValue (true)]
138                 public bool EnforceConstraints {
139                         get { return enforceConstraints; } 
140                         set { 
141                                 if (value != enforceConstraints) {
142                                         enforceConstraints = value; 
143                                         if (value) {
144                                                 foreach (DataTable table in Tables) {
145                                                         // first assert all unique constraints
146                                                         foreach (UniqueConstraint uc in table.Constraints.UniqueConstraints)
147                                                                 uc.AssertConstraint ();
148                                                         // then assert all foreign keys
149                                                         foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
150                                                                 fk.AssertConstraint ();
151                                                 }
152                                         }
153                                 }
154                         }
155                 }
156
157                 [Browsable (false)]
158                 [DataCategory ("Data")]
159                 [DataSysDescription ("The collection that holds custom user information.")]
160                 public PropertyCollection ExtendedProperties {
161                         get { return properties; }
162                 }
163
164                 [Browsable (false)]
165                 [DataSysDescription ("Indicates that the DataSet has errors.")]
166                 public bool HasErrors {
167                         [MonoTODO]
168                         get {
169                                 for (int i = 0; i < Tables.Count; i++) {
170                                         if (Tables[i].HasErrors)
171                                                 return true;
172                                 }
173                                 return false;
174                         }
175                 }
176
177                 [DataCategory ("Data")]
178                 [DataSysDescription ("Indicates a locale under which to compare strings within the DataSet.")]
179                 public CultureInfo Locale {
180                         get {
181                                 return locale;
182                         }
183                         set {
184                                 if (locale == null || !locale.Equals (value)) {
185                                         // TODO: check if the new locale is valid
186                                         // TODO: update locale of all tables
187                                         locale = value;
188                                 }
189                         }
190                 }
191
192                 public void Merge (DataRow[] rows)
193                 {
194                         Merge (rows, false, MissingSchemaAction.Add);
195                 }
196                 
197                 public void Merge (DataSet dataSet)
198                 {
199                         Merge (dataSet, false, MissingSchemaAction.Add);
200                 }
201                 
202                 public void Merge (DataTable table)
203                 {
204                         Merge (table, false, MissingSchemaAction.Add);
205                 }
206                 
207                 public void Merge (DataSet dataSet, bool preserveChanges)
208                 {
209                         Merge (dataSet, preserveChanges, MissingSchemaAction.Add);
210                 }
211                 
212                 [MonoTODO]
213                 public void Merge (DataRow[] rows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
214                 {
215                         if (rows == null)
216                                 throw new ArgumentNullException ("rows");
217                         if (!IsLegalSchemaAction (missingSchemaAction))
218                                 throw new ArgumentOutOfRangeException ("missingSchemaAction");
219                         
220                         MergeManager.Merge (this, rows, preserveChanges, missingSchemaAction);
221                 }
222                 
223                 [MonoTODO]
224                 public void Merge (DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction)
225                 {
226                         if (dataSet == null)
227                                 throw new ArgumentNullException ("dataSet");
228                         if (!IsLegalSchemaAction (missingSchemaAction))
229                                 throw new ArgumentOutOfRangeException ("missingSchemaAction");
230                         
231                         MergeManager.Merge (this, dataSet, preserveChanges, missingSchemaAction);
232                 }
233                 
234                 [MonoTODO]
235                 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
236                 {
237                         if (table == null)
238                                 throw new ArgumentNullException ("table");
239                         if (!IsLegalSchemaAction (missingSchemaAction))
240                                 throw new ArgumentOutOfRangeException ("missingSchemaAction");
241                         
242                         MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);
243                 }
244
245                 private static bool IsLegalSchemaAction (MissingSchemaAction missingSchemaAction)
246                 {
247                         if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey
248                                 || missingSchemaAction == MissingSchemaAction.Error || missingSchemaAction == MissingSchemaAction.Ignore)
249                                 return true;
250                         return false;
251                 }
252                 
253                 [DataCategory ("Data")]
254                 [DataSysDescription ("Indicates the XML uri namespace for the root element pointed at by this DataSet.")]
255                 [DefaultValue ("")]
256                 public string Namespace {
257                         get { return _namespace; } 
258                         set {
259                                 //TODO - trigger an event if this happens?
260                                 if (value == null)
261                                         value = String.Empty;
262                                  if (value != this._namespace)
263                                         RaisePropertyChanging ("Namespace");
264                                 _namespace = value;
265                         }
266                 }
267
268                 [DataCategory ("Data")]
269                 [DataSysDescription ("Indicates the prefix of the namespace used for this DataSet.")]
270                 [DefaultValue ("")]
271                 public string Prefix {
272                         get { return prefix; } 
273                         set {
274                                 if (value == null)
275                                         value = String.Empty;
276                               // Prefix cannot contain any special characters other than '_' and ':'
277                                for (int i = 0; i < value.Length; i++) {
278                                        if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
279                                                throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
280                                }
281
282
283                                 if (value == null)
284                                         value = string.Empty;
285                                 
286                                 if (value != this.prefix) 
287                                         RaisePropertyChanging ("Prefix");
288                                 prefix = value;
289                         }
290                 }
291
292                 [DataCategory ("Data")]
293                 [DataSysDescription ("The collection that holds the relations for this DatSet.")]
294                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
295                 public DataRelationCollection Relations {
296                         get {
297                                 return relationCollection;              
298                         }
299                 }
300
301                 [Browsable (false)]
302                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
303                 public override ISite Site {
304                         [MonoTODO]
305                         get {
306                                 throw new NotImplementedException ();
307                         } 
308                         
309                         [MonoTODO]
310                         set {
311                                 throw new NotImplementedException ();
312                         }
313                 }
314
315                 [DataCategory ("Data")]
316                 [DataSysDescription ("The collection that holds the tables for this DataSet.")]
317                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
318                 public DataTableCollection Tables {
319                         get { return tableCollection; }
320                 }
321
322                 #endregion // Public Properties
323
324                 #region Public Methods
325
326                 [MonoTODO]
327                 public void AcceptChanges ()
328                 {
329                         foreach (DataTable tempTable in tableCollection)
330                                 tempTable.AcceptChanges ();
331                 }
332
333                 /// <summary>
334                 /// Clears all the tables
335                 /// </summary>
336                 public void Clear ()
337                 {
338                         if (_xmlDataDocument != null)
339                                 throw new NotSupportedException ("Clear function on dataset and datatable is not supported when XmlDataDocument is bound to the DataSet.");
340                         bool enforceConstraints = this.EnforceConstraints;
341                         this.EnforceConstraints = false;
342                         for (int t = 0; t < tableCollection.Count; t++) {
343                                 tableCollection[t].Clear ();
344                         }
345                         this.EnforceConstraints = enforceConstraints;
346                 }
347
348                 public virtual DataSet Clone ()
349                 {
350                         // need to return the same type as this...
351                         DataSet Copy = (DataSet) Activator.CreateInstance(GetType(), true);
352         
353                         CopyProperties (Copy);
354
355                         foreach (DataTable Table in Tables) {
356                         // tables are often added in no-args constructor, don't add them
357                         // twice.
358                                 if (!Copy.Tables.Contains(Table.TableName)) {
359                                         Copy.Tables.Add (Table.Clone ());
360                                 }
361                         }
362
363                         //Copy Relationships between tables after existance of tables
364                         //and setting properties correctly
365                         CopyRelations (Copy);
366                         
367                         return Copy;
368                 }
369
370                 // Copies both the structure and data for this DataSet.
371                 public DataSet Copy ()
372                 {
373                         DataSet Copy = new DataSet ();
374                         CopyProperties (Copy);
375
376                         // Copy DatSet's tables
377                         foreach (DataTable Table in Tables) 
378                                 Copy.Tables.Add (Table.Copy ());
379
380                         //Copy Relationships between tables after existance of tables
381                         //and setting properties correctly
382                         CopyRelations (Copy);
383
384                         return Copy;
385                 }
386
387                 private void CopyProperties (DataSet Copy)
388                 {
389                         Copy.CaseSensitive = CaseSensitive;
390                         //Copy.Container = Container
391                         Copy.DataSetName = DataSetName;
392                         //Copy.DefaultViewManager
393                         //Copy.DesignMode
394                         Copy.EnforceConstraints = EnforceConstraints;
395                         if(ExtendedProperties.Count > 0) {
396                                 //  Cannot copy extended properties directly as the property does not have a set accessor
397                 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
398                 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
399                 for (int i=0; i < ExtendedProperties.Count; i++)
400                                         Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
401                         }
402             Copy.Locale = Locale;
403                         Copy.Namespace = Namespace;
404                         Copy.Prefix = Prefix;                   
405                         //Copy.Site = Site; // FIXME : Not sure of this.
406
407                 }
408                 
409                 
410                 private void CopyRelations (DataSet Copy)
411                 {
412
413                         //Creation of the relation contains some of the properties, and the constructor
414                         //demands these values. instead changing the DataRelation constructor and behaviour the
415                         //parameters are pre-configured and sent to the most general constructor
416
417                         foreach (DataRelation MyRelation in this.Relations) {
418                                 string pTable = MyRelation.ParentTable.TableName;
419                                 string cTable = MyRelation.ChildTable.TableName;
420                                 DataColumn[] P_DC = new DataColumn[MyRelation.ParentColumns.Length]; 
421                                 DataColumn[] C_DC = new DataColumn[MyRelation.ChildColumns.Length];
422                                 int i = 0;
423                                 
424                                 foreach (DataColumn DC in MyRelation.ParentColumns) {
425                                         P_DC[i]=Copy.Tables[pTable].Columns[DC.ColumnName];
426                                         i++;
427                                 }
428
429                                 i = 0;
430
431                                 foreach (DataColumn DC in MyRelation.ChildColumns) {
432                                         C_DC[i]=Copy.Tables[cTable].Columns[DC.ColumnName];
433                                         i++;
434                                 }
435                                 
436                                 DataRelation cRel = new DataRelation (MyRelation.RelationName, P_DC, C_DC);
437                                 //cRel.ChildColumns = MyRelation.ChildColumns;
438                                 //cRel.ChildTable = MyRelation.ChildTable;
439                                 //cRel.ExtendedProperties = cRel.ExtendedProperties; 
440                                 //cRel.Nested = MyRelation.Nested;
441                                 //cRel.ParentColumns = MyRelation.ParentColumns;
442                                 //cRel.ParentTable = MyRelation.ParentTable;
443                                                                 
444                                 Copy.Relations.Add (cRel);
445                         }
446                 }
447
448                 
449
450
451                 public DataSet GetChanges ()
452                 {
453                         return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
454                 }
455
456                 
457                 public DataSet GetChanges (DataRowState rowStates)
458                 {
459                         if (!HasChanges (rowStates))
460                                 return null;
461                         
462                         DataSet copySet = Clone ();
463                         Hashtable addedRows = new Hashtable ();
464
465                         IEnumerator tableEnumerator = Tables.GetEnumerator ();
466                         DataTable origTable;
467                         DataTable copyTable;
468                         while (tableEnumerator.MoveNext ()) {
469                                 origTable = (DataTable)tableEnumerator.Current;
470                                 copyTable = copySet.Tables[origTable.TableName];
471                                 
472                                 // Look for relations that have this table as child
473                                 IEnumerator relations = origTable.ParentRelations.GetEnumerator ();
474
475                                 IEnumerator rowEnumerator = origTable.Rows.GetEnumerator ();
476                                 while (rowEnumerator.MoveNext ()) {
477                                         DataRow row = (DataRow)rowEnumerator.Current;
478                                         
479                                         if (row.IsRowChanged (rowStates))
480                                                 AddChangedRow (addedRows, copySet, copyTable, relations, row);
481                                 }
482                         }
483                         return copySet;
484                 }
485                 
486                 void AddChangedRow (Hashtable addedRows, DataSet copySet, DataTable copyTable, IEnumerator relations, DataRow row)
487                 {
488                         if (addedRows.ContainsKey (row)) return;
489                         
490                         relations.Reset ();
491                         while (relations.MoveNext ()) {
492                                 DataRow parentRow = row.GetParentRow ((DataRelation) relations.Current);
493                                 if (parentRow == null || addedRows.ContainsKey (parentRow)) continue;
494                                 DataTable parentCopyTable = copySet.Tables [parentRow.Table.TableName];
495                                 AddChangedRow (addedRows, copySet, parentCopyTable, parentRow.Table.ParentRelations.GetEnumerator (), parentRow);
496                         }
497                 
498                         DataRow newRow = copyTable.NewRow ();
499                         copyTable.Rows.Add (newRow);
500                         row.CopyValuesToRow (newRow);
501                         newRow.XmlRowID = row.XmlRowID;
502                         addedRows.Add (row,row);
503                 }
504
505 #if NET_2_0
506                 [MonoTODO]
507                 public DataTableReader GetDataReader (DataTable[] dataTables)
508                 {
509                         throw new NotImplementedException ();
510                 }
511
512                 [MonoTODO]
513                 public DataTableReader GetDataReader ()
514                 {
515                         throw new NotImplementedException ();
516                 }
517 #endif
518                 
519                 public string GetXml ()
520                 {
521                         StringWriter Writer = new StringWriter ();
522                         WriteXml (Writer, XmlWriteMode.IgnoreSchema);
523                         return Writer.ToString ();
524                 }
525
526                 public string GetXmlSchema ()
527                 {
528                         StringWriter Writer = new StringWriter ();
529                         WriteXmlSchema (Writer);
530                         return Writer.ToString ();
531                 }
532
533                 [MonoTODO]
534                 public bool HasChanges ()
535                 {
536                         return HasChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
537                 }
538
539                 [MonoTODO]
540                 public bool HasChanges (DataRowState rowState)
541                 {
542                         if (((int)rowState & 0xffffffe0) != 0)
543                                 throw new ArgumentOutOfRangeException ("rowState");
544
545                         DataTableCollection tableCollection = Tables;
546                         DataTable table;
547                         DataRowCollection rowCollection;
548                         DataRow row;
549
550                         for (int i = 0; i < tableCollection.Count; i++) {
551                                 table = tableCollection[i];
552                                 rowCollection = table.Rows;
553                                 for (int j = 0; j < rowCollection.Count; j++) {
554                                         row = rowCollection[j];
555                                         if ((row.RowState & rowState) != 0)
556                                                 return true;
557                                 }
558                         }
559
560                         return false;           
561                 }
562
563                 public void InferXmlSchema (XmlReader reader, string[] nsArray)
564                 {
565                         if (reader == null)
566                                 return;
567                         XmlDocument doc = new XmlDocument ();
568                         doc.Load (reader);
569                         InferXmlSchema (doc, nsArray);
570                 }
571
572                 private void InferXmlSchema (XmlDocument doc, string [] nsArray)
573                 {
574                         XmlDataInferenceLoader.Infer (this, doc, XmlReadMode.InferSchema, nsArray);
575                 }
576
577                 public void InferXmlSchema (Stream stream, string[] nsArray)
578                 {
579                         InferXmlSchema (new XmlTextReader (stream), nsArray);
580                 }
581
582                 public void InferXmlSchema (TextReader reader, string[] nsArray)
583                 {
584                         InferXmlSchema (new XmlTextReader (reader), nsArray);
585                 }
586
587                 public void InferXmlSchema (string fileName, string[] nsArray)
588                 {
589                         XmlTextReader reader = new XmlTextReader (fileName);
590                         try {
591                                 InferXmlSchema (reader, nsArray);
592                         } finally {
593                                 reader.Close ();
594                         }
595                 }
596
597 #if NET_2_0
598                 [MonoTODO]
599                 public void Load (IDataReader reader, LoadOption loadOption, DataTable[] tables)
600                 {
601                         throw new NotImplementedException ();
602                 }
603
604                 [MonoTODO]
605                 public void Load (IDataReader reader, LoadOption loadOption, string[] tables)
606                 {
607                         throw new NotImplementedException ();
608                 }
609 #endif
610
611                 public virtual void RejectChanges ()
612                 {
613                         int i;
614                         bool oldEnforceConstraints = this.EnforceConstraints;
615                         this.EnforceConstraints = false;
616                         
617                         for (i = 0; i < this.Tables.Count;i++) 
618                                 this.Tables[i].RejectChanges ();
619
620                         this.EnforceConstraints = oldEnforceConstraints;
621                 }
622
623                 public virtual void Reset ()
624                 {
625                         IEnumerator constraintEnumerator;
626
627                         // first we remove all ForeignKeyConstraints (if we will not do that
628                         // we will get an exception when clearing the tables).
629                         for (int i = 0; i < Tables.Count; i++) {
630                                 ConstraintCollection cc = Tables[i].Constraints;
631                                 for (int j = 0; j < cc.Count; j++) {
632                                         if (cc[j] is ForeignKeyConstraint)
633                                                 cc.Remove (cc[j]);
634                                 }
635                         }
636
637                         Clear ();
638                         Relations.Clear ();
639                         Tables.Clear ();
640                 }
641
642                 public void WriteXml (Stream stream)
643                 {
644                         XmlTextWriter writer = new XmlTextWriter (stream, null);
645                         writer.Formatting = Formatting.Indented;
646                         WriteXml (writer);
647                 }
648
649                 ///<summary>
650                 /// Writes the current data for the DataSet to the specified file.
651                 /// </summary>
652                 /// <param name="filename">Fully qualified filename to write to</param>
653                 public void WriteXml (string fileName)
654                 {
655                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
656                         writer.Formatting = Formatting.Indented;
657                         writer.WriteStartDocument (true);
658                         try {
659                                 WriteXml (writer);
660                         }
661                         finally {
662                                 writer.WriteEndDocument ();
663                                 writer.Close ();
664                         }
665                 }
666
667                 public void WriteXml (TextWriter writer)
668                 {
669                         XmlTextWriter xwriter = new XmlTextWriter (writer);
670                         xwriter.Formatting = Formatting.Indented;
671                         WriteXml (xwriter);
672                 }
673
674                 public void WriteXml (XmlWriter writer)
675                 {
676                         WriteXml (writer, XmlWriteMode.IgnoreSchema);
677                 }
678
679                 public void WriteXml (string filename, XmlWriteMode mode)
680                 {
681                         XmlTextWriter writer = new XmlTextWriter (filename, null);
682                         writer.Formatting = Formatting.Indented;
683                         writer.WriteStartDocument (true);
684                         
685                         try {
686                                 WriteXml (writer, mode);
687                         }
688                         finally {
689                                 writer.WriteEndDocument ();
690                                 writer.Close ();
691                         }
692                 }
693
694                 public void WriteXml (Stream stream, XmlWriteMode mode)
695                 {
696                         XmlTextWriter writer = new XmlTextWriter (stream, null);
697                         writer.Formatting = Formatting.Indented;
698                         WriteXml (writer, mode);
699                 }
700
701                 public void WriteXml (TextWriter writer, XmlWriteMode mode)
702                 {
703                         XmlTextWriter xwriter = new XmlTextWriter (writer);
704                         xwriter.Formatting = Formatting.Indented;
705                         WriteXml (xwriter, mode);
706                 }
707
708                 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
709                 {
710                         if (mode == XmlWriteMode.DiffGram) {
711                                 SetRowsID();
712                                 WriteDiffGramElement(writer);
713                         }
714                         
715                         // It should not write when there is no content to be written
716                         bool shouldOutputContent = (mode != XmlWriteMode.DiffGram);
717                         for (int n = 0; n < tableCollection.Count && !shouldOutputContent; n++)
718                                 shouldOutputContent = tableCollection [n].Rows.Count > 0;
719                                 
720                         if (shouldOutputContent) {
721                                 WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
722                                 
723                                 if (mode == XmlWriteMode.WriteSchema)
724                                         DoWriteXmlSchema (writer);
725                                 
726                                 WriteTables (writer, mode, Tables, DataRowVersion.Default);
727                                 writer.WriteEndElement ();
728                         }
729                         
730                         if (mode == XmlWriteMode.DiffGram) {
731                                 if (HasChanges(DataRowState.Modified | DataRowState.Deleted)) {
732
733                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);
734                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
735                                         WriteTables (writer, mode, beforeDS.Tables, DataRowVersion.Original);
736                                         writer.WriteEndElement ();
737                                 }
738                         }
739                         
740                         if (mode == XmlWriteMode.DiffGram)
741                                 writer.WriteEndElement (); // diffgr:diffgram
742
743                         writer.Flush ();
744                 }
745
746                 public void WriteXmlSchema (Stream stream)
747                 {
748                         XmlTextWriter writer = new XmlTextWriter (stream, null );
749                         writer.Formatting = Formatting.Indented;
750                         WriteXmlSchema (writer);        
751                 }
752
753                 public void WriteXmlSchema (string fileName)
754                 {
755                         XmlTextWriter writer = new XmlTextWriter (fileName, null);
756                         try {
757                                 writer.Formatting = Formatting.Indented;
758                                 writer.WriteStartDocument (true);
759                                 WriteXmlSchema (writer);
760                         } finally {
761                                 writer.WriteEndDocument ();
762                                 writer.Close ();
763                         }
764                 }
765
766                 public void WriteXmlSchema (TextWriter writer)
767                 {
768                         XmlTextWriter xwriter = new XmlTextWriter (writer);
769                         try {
770                                 xwriter.Formatting = Formatting.Indented;
771 //                              xwriter.WriteStartDocument ();
772                                 WriteXmlSchema (xwriter);
773                         } finally {
774 //                              xwriter.WriteEndDocument ();
775                                 xwriter.Close ();
776                         }
777                 }
778
779                 public void WriteXmlSchema (XmlWriter writer)
780                 {
781                         //Create a skeleton doc and then write the schema 
782                         //proper which is common to the WriteXml method in schema mode
783                         DoWriteXmlSchema (writer);
784                 }
785
786                 public void ReadXmlSchema (Stream stream)
787                 {
788                         XmlReader reader = new XmlTextReader (stream, null);
789                         ReadXmlSchema (reader);
790                 }
791
792                 public void ReadXmlSchema (string str)
793                 {
794                         XmlReader reader = new XmlTextReader (str);
795                         try {
796                                 ReadXmlSchema (reader);
797                         }
798                         finally {
799                                 reader.Close ();
800                         }
801                 }
802
803                 public void ReadXmlSchema (TextReader treader)
804                 {
805                         XmlReader reader = new XmlTextReader (treader);
806                         ReadXmlSchema (reader);                 
807                 }
808
809                 public void ReadXmlSchema (XmlReader reader)
810                 {
811 #if true
812                         new XmlSchemaDataImporter (this, reader).Process ();
813 #else
814                         XmlSchemaMapper SchemaMapper = new XmlSchemaMapper (this);
815                         SchemaMapper.Read (reader);
816 #endif
817                 }
818
819                 public XmlReadMode ReadXml (Stream stream)
820                 {
821                         return ReadXml (new XmlTextReader (stream));
822                 }
823
824                 public XmlReadMode ReadXml (string str)
825                 {
826                         XmlTextReader reader = new XmlTextReader (str);
827                         try {
828                                 return ReadXml (reader);
829                         }
830                         finally {
831                                 reader.Close ();
832                         }
833                 }
834
835                 public XmlReadMode ReadXml (TextReader reader)
836                 {
837                         return ReadXml (new XmlTextReader (reader));
838                 }
839
840                 public XmlReadMode ReadXml (XmlReader r)
841                 {
842                         return ReadXml (r, XmlReadMode.Auto);
843                 }
844
845                 public XmlReadMode ReadXml (Stream stream, XmlReadMode mode)
846                 {
847                         return ReadXml (new XmlTextReader (stream), mode);
848                 }
849
850                 public XmlReadMode ReadXml (string str, XmlReadMode mode)
851                 {
852                         XmlTextReader reader = new XmlTextReader (str);
853                         try {
854                                 return ReadXml (reader, mode);
855                         }
856                         finally {
857                                 reader.Close ();
858                         }
859                 }
860
861                 public XmlReadMode ReadXml (TextReader reader, XmlReadMode mode)
862                 {
863                         return ReadXml (new XmlTextReader (reader), mode);
864                 }
865
866                 public XmlReadMode ReadXml (XmlReader reader, XmlReadMode mode)
867                 {
868                         switch (reader.ReadState) {
869                         case ReadState.EndOfFile:
870                         case ReadState.Error:
871                         case ReadState.Closed:
872                                 return mode;
873                         }
874                         // Skip XML declaration and prolog
875                         reader.MoveToContent();
876                         if (reader.EOF)
877                                 return mode;
878
879                         XmlReadMode Result = mode;
880
881                         // If diffgram, then read the first element as diffgram 
882                         if (reader.LocalName == "diffgram" && reader.NamespaceURI == XmlConstants.DiffgrNamespace) {
883                                 switch (mode) {
884                                 case XmlReadMode.Auto:
885                                 case XmlReadMode.DiffGram:
886                                         XmlDiffLoader DiffLoader = new XmlDiffLoader (this);
887                                         DiffLoader.Load (reader);
888                                         // (and leave rest of the reader as is)
889                                         return  XmlReadMode.DiffGram;
890                                 case XmlReadMode.Fragment:
891                                         reader.Skip ();
892                                         // (and continue to read)
893                                         break;
894                                 default:
895                                         reader.Skip ();
896                                         // (and leave rest of the reader as is)
897                                         return mode;
898                                 }
899                         }
900                         // If schema, then read the first element as schema 
901                         if (reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace) {
902                                 switch (mode) {
903                                 case XmlReadMode.IgnoreSchema:
904                                 case XmlReadMode.InferSchema:
905                                         reader.Skip ();
906                                         // (and break up read)
907                                         return mode;
908                                 case XmlReadMode.Fragment:
909                                         ReadXmlSchema (reader);
910                                         // (and continue to read)
911                                         break;
912                                 case XmlReadMode.Auto:
913                                         if (Tables.Count == 0) {
914                                                 ReadXmlSchema (reader);
915                                                 return XmlReadMode.ReadSchema;
916                                         } else {
917                                         // otherwise just ignore and return IgnoreSchema
918                                                 reader.Skip ();
919                                                 return XmlReadMode.IgnoreSchema;
920                                         }
921                                 default:
922                                         ReadXmlSchema (reader);
923                                         // (and leave rest of the reader as is)
924                                         return mode; // When DiffGram, return DiffGram
925                                 }
926                         }
927                         // Otherwise, read as dataset... but only when required.
928                         XmlReadMode explicitReturnMode = XmlReadMode.Auto;
929                         XmlDocument doc;
930                         switch (mode) {
931                         case XmlReadMode.Auto:
932                                 if (Tables.Count > 0)
933                                         goto case XmlReadMode.IgnoreSchema;
934                                 else
935                                         goto case XmlReadMode.InferSchema;
936                         case XmlReadMode.InferSchema:
937                                 doc = new XmlDocument ();
938                                 do {
939                                         doc.AppendChild (doc.ReadNode (reader));
940                                         reader.MoveToContent ();
941                                         if (doc.DocumentElement != null)
942                                                 break;
943                                 } while (!reader.EOF);
944                                 InferXmlSchema (doc, null);
945                                 reader = new XmlNodeReader (doc);
946                                 explicitReturnMode = XmlReadMode.InferSchema;
947                                 break;
948                         case XmlReadMode.ReadSchema:
949                                 doc = new XmlDocument ();
950                                 do {
951                                         doc.AppendChild (doc.ReadNode (reader));
952                                         reader.MoveToContent ();
953                                         if (doc.DocumentElement != null)
954                                                 break;
955                                 } while (!reader.EOF);
956                                 if (doc.DocumentElement != null) {
957                                         XmlElement schema = doc.DocumentElement ["schema", XmlSchema.Namespace] as XmlElement;
958                                         if (schema != null) {
959                                                 ReadXmlSchema (new XmlNodeReader (schema));
960                                                 explicitReturnMode = XmlReadMode.ReadSchema;
961                                         }
962                                 }
963                                 reader = new XmlNodeReader (doc);
964                                 break;
965                         case XmlReadMode.IgnoreSchema:
966                         case XmlReadMode.Fragment:
967                                 break;
968                         default:
969                                 reader.Skip ();
970                                 return mode;
971                         }
972
973                         XmlDataReader.ReadXml (this, reader, mode);
974                         if (explicitReturnMode != XmlReadMode.Auto)
975                                 return explicitReturnMode;
976                         return mode == XmlReadMode.Auto ? XmlReadMode.IgnoreSchema : mode;
977                 }
978                 #endregion // Public Methods
979
980                 #region Public Events
981
982                 [DataCategory ("Action")]
983                 [DataSysDescription ("Occurs when it is not possible to merge schemas for two tables with the same name.")]
984                 public event MergeFailedEventHandler MergeFailed;
985
986                 #endregion // Public Events
987
988                 #region IListSource methods
989                 IList IListSource.GetList ()
990                 {
991                         return DefaultViewManager;
992                 }
993                 
994                 bool IListSource.ContainsListCollection {
995                         get {
996                                 return true;
997                         }
998                 }
999                 #endregion IListSource methods
1000                 
1001                 #region ISupportInitialize methods
1002                 public void BeginInit ()
1003                 {
1004                 }
1005                 
1006                 public void EndInit ()
1007                 {
1008                 }
1009                 #endregion
1010
1011                 #region ISerializable
1012                 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext sc)
1013                 {
1014                         StringWriter sw = new StringWriter ();
1015                         XmlTextWriter writer = new XmlTextWriter (sw);
1016                         DoWriteXmlSchema (writer);
1017                         writer.Flush ();
1018                         si.AddValue ("XmlSchema", sw.ToString ());
1019                         
1020                         sw = new StringWriter ();
1021                         writer = new XmlTextWriter (sw);
1022                         WriteXml (writer, XmlWriteMode.DiffGram);
1023                         writer.Flush ();
1024                         si.AddValue ("XmlDiffGram", sw.ToString ());
1025                 }
1026                 #endregion
1027                 
1028                 #region Protected Methods
1029                 protected void GetSerializationData (SerializationInfo info, StreamingContext context)
1030                 {
1031                         string s = info.GetValue ("XmlSchema", typeof (String)) as String;
1032                         XmlTextReader reader = new XmlTextReader (new StringReader (s));
1033                         ReadXmlSchema (reader);
1034                         reader.Close ();
1035                         
1036                         s = info.GetValue ("XmlDiffGram", typeof (String)) as String;
1037                         reader = new XmlTextReader (new StringReader (s));
1038                         ReadXml (reader, XmlReadMode.DiffGram);
1039                         reader.Close ();
1040                 }
1041                 
1042                 
1043                 protected virtual System.Xml.Schema.XmlSchema GetSchemaSerializable ()
1044                 {
1045                         return null;
1046                 }
1047                 
1048                 protected virtual void ReadXmlSerializable (XmlReader reader)
1049                 {
1050                         reader.MoveToContent ();
1051                         reader.ReadStartElement ();
1052                         reader.MoveToContent ();
1053                         ReadXmlSchema (reader);
1054                         reader.MoveToContent ();
1055                         ReadXml (reader, XmlReadMode.DiffGram);
1056                         reader.MoveToContent ();
1057                         reader.ReadEndElement ();
1058                 }
1059
1060                 void IXmlSerializable.ReadXml (XmlReader reader)
1061                 {
1062                         ReadXmlSerializable(reader);
1063                 }
1064                 
1065                 void IXmlSerializable.WriteXml (XmlWriter writer)
1066                 {
1067                         DoWriteXmlSchema (writer);
1068                         WriteXml (writer, XmlWriteMode.DiffGram);
1069                 }
1070
1071                 XmlSchema IXmlSerializable.GetSchema ()
1072                 {
1073                         return BuildSchema ();
1074                 }
1075
1076                 protected virtual bool ShouldSerializeRelations ()
1077                 {
1078                         return true;
1079                 }
1080                 
1081                 protected virtual bool ShouldSerializeTables ()
1082                 {
1083                         return true;
1084                 }
1085
1086                 [MonoTODO]
1087                 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
1088                 {
1089                 }
1090
1091                 [MonoTODO]
1092                 protected virtual void OnRemoveRelation (DataRelation relation)
1093                 {
1094                 }
1095
1096                 [MonoTODO]
1097                 protected virtual void OnRemoveTable (DataTable table)
1098                 {
1099                 }
1100
1101                 internal virtual void OnMergeFailed (MergeFailedEventArgs e)
1102                 {
1103                         if (MergeFailed != null)
1104                                 MergeFailed (this, e);
1105                 }
1106
1107                 [MonoTODO]
1108                 protected internal void RaisePropertyChanging (string name)
1109                 {
1110                 }
1111                 #endregion
1112
1113                 #region Private Xml Serialisation
1114
1115                 private string WriteObjectXml (object o)
1116                 {
1117                         switch (Type.GetTypeCode (o.GetType ())) {
1118                                 case TypeCode.Boolean:
1119                                         return XmlConvert.ToString ((Boolean) o);
1120                                 case TypeCode.Byte:
1121                                         return XmlConvert.ToString ((Byte) o);
1122                                 case TypeCode.Char:
1123                                         return XmlConvert.ToString ((Char) o);
1124                                 case TypeCode.DateTime:
1125                                         return XmlConvert.ToString ((DateTime) o);
1126                                 case TypeCode.Decimal:
1127                                         return XmlConvert.ToString ((Decimal) o);
1128                                 case TypeCode.Double:
1129                                         return XmlConvert.ToString ((Double) o);
1130                                 case TypeCode.Int16:
1131                                         return XmlConvert.ToString ((Int16) o);
1132                                 case TypeCode.Int32:
1133                                         return XmlConvert.ToString ((Int32) o);
1134                                 case TypeCode.Int64:
1135                                         return XmlConvert.ToString ((Int64) o);
1136                                 case TypeCode.SByte:
1137                                         return XmlConvert.ToString ((SByte) o);
1138                                 case TypeCode.Single:
1139                                         return XmlConvert.ToString ((Single) o);
1140                                 case TypeCode.UInt16:
1141                                         return XmlConvert.ToString ((UInt16) o);
1142                                 case TypeCode.UInt32:
1143                                         return XmlConvert.ToString ((UInt32) o);
1144                                 case TypeCode.UInt64:
1145                                         return XmlConvert.ToString ((UInt64) o);
1146                         }
1147                         if (o is TimeSpan) return XmlConvert.ToString ((TimeSpan) o);
1148                         if (o is Guid) return XmlConvert.ToString ((Guid) o);
1149                         if (o is byte[]) return Convert.ToBase64String ((byte[])o);
1150                         return o.ToString ();
1151                 }
1152                 
1153                 private void WriteTables (XmlWriter writer, XmlWriteMode mode, DataTableCollection tableCollection, DataRowVersion version)
1154                 {
1155                         //Write out each table in order, providing it is not
1156                         //part of another table structure via a nested parent relationship
1157                         foreach (DataTable table in tableCollection) {
1158                                 bool isTopLevel = true;
1159                                 /*
1160                                 foreach (DataRelation rel in table.ParentRelations) {
1161                                         if (rel.Nested) {
1162                                                 isTopLevel = false;
1163                                                 break;
1164                                         }
1165                                 }
1166                                 */
1167                                 if (isTopLevel) {
1168                                         WriteTable ( writer, table, mode, version);
1169                                 }
1170                         }
1171                 }
1172
1173                 private void WriteTable (XmlWriter writer, DataTable table, XmlWriteMode mode, DataRowVersion version)
1174                 {
1175                         DataRow[] rows = new DataRow [table.Rows.Count];
1176                         table.Rows.CopyTo (rows, 0);
1177                         WriteTable (writer, rows, mode, version, true);
1178                 }
1179
1180                 private void WriteTable (XmlWriter writer, DataRow[] rows, XmlWriteMode mode, DataRowVersion version, bool skipIfNested)
1181                 {
1182                         //The columns can be attributes, hidden, elements, or simple content
1183                         //There can be 0-1 simple content cols or 0-* elements
1184                         System.Collections.ArrayList atts;
1185                         System.Collections.ArrayList elements;
1186                         DataColumn simple = null;
1187
1188                         if (rows.Length == 0) return;
1189                         DataTable table = rows[0].Table;
1190                         SplitColumns (table, out atts, out elements, out simple);
1191                         //sort out the namespacing
1192                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1193                         int relationCount = table.ParentRelations.Count;
1194                         DataRelation oneRel = relationCount == 1 ? table.ParentRelations [0] : null;
1195
1196                         foreach (DataRow row in rows) {
1197                                 if (skipIfNested) {
1198                                         // Skip rows that is a child of any tables.
1199                                         switch (relationCount) {
1200                                         case 0:
1201                                                 break;
1202                                         case 1:
1203                                                 if (!oneRel.Nested)
1204                                                         break;
1205                                                 if (row.GetParentRow (oneRel) != null)
1206                                                         continue;
1207                                                 break;
1208                                         case 2:
1209                                                 bool skip = false;
1210                                                 for (int i = 0; i < table.ParentRelations.Count; i++) {
1211                                                         DataRelation prel = table.ParentRelations [i];
1212                                                         if (!prel.Nested)
1213                                                                 continue;
1214                                                         if (row.GetParentRow (prel) != null) {
1215                                                                 skip = true;
1216                                                                 continue;
1217                                                         }
1218                                                 }
1219                                                 if (skip)
1220                                                         continue;
1221                                                 break;
1222                                         }
1223                                 }
1224
1225                                 if (!row.HasVersion(version) || 
1226                                    (mode == XmlWriteMode.DiffGram && row.RowState == DataRowState.Unchanged 
1227                                       && version == DataRowVersion.Original))
1228                                         continue;
1229                                 
1230                                 // First check are all the rows null. If they are we just write empty element
1231                                 bool AllNulls = true;
1232                                 foreach (DataColumn dc in table.Columns) {
1233                                 
1234                                         if (row [dc.ColumnName, version] != DBNull.Value) {
1235                                                 AllNulls = false;
1236                                                 break;
1237                                         } 
1238                                 }
1239
1240                                 // If all of the columns were null, we have to write empty element
1241                                 if (AllNulls) {
1242                                         writer.WriteElementString (XmlConvert.EncodeLocalName (table.TableName), "");
1243                                         continue;
1244                                 }
1245                                 
1246                                 WriteTableElement (writer, mode, table, row, version);
1247                                 
1248                                 foreach (DataColumn col in atts) {                                      
1249                                         WriteColumnAsAttribute (writer, mode, col, row, version);
1250                                 }
1251                                 
1252                                 if (simple != null) {
1253                                         writer.WriteString (WriteObjectXml (row[simple, version]));
1254                                 }
1255                                 else {                                  
1256                                         foreach (DataColumn col in elements) {
1257                                                 WriteColumnAsElement (writer, mode, col, row, version);
1258                                         }
1259                                 }
1260                                 
1261                                 foreach (DataRelation relation in table.ChildRelations) {
1262                                         if (relation.Nested) {
1263                                                 WriteTable (writer, row.GetChildRows (relation), mode, version, false);
1264                                         }
1265                                 }
1266                                 
1267                                 writer.WriteEndElement ();
1268                         }
1269
1270                 }
1271
1272                 private void WriteColumnAsElement (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1273                 {
1274                         string colnspc = null;
1275                         object rowObject = row [col, version];
1276                                                                         
1277                         if (rowObject == null || rowObject == DBNull.Value)
1278                                 return;
1279
1280                         if (col.Namespace != String.Empty)
1281                                 colnspc = col.Namespace;
1282         
1283                         //TODO check if I can get away with write element string
1284                         WriteStartElement (writer, mode, colnspc, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName));
1285                         writer.WriteString (WriteObjectXml (rowObject));
1286                         writer.WriteEndElement ();
1287                 }
1288
1289                 private void WriteColumnAsAttribute (XmlWriter writer, XmlWriteMode mode, DataColumn col, DataRow row, DataRowVersion version)
1290                 {
1291                         if (!row.IsNull (col))
1292                                 WriteAttributeString (writer, mode, col.Namespace, col.Prefix, XmlConvert.EncodeLocalName (col.ColumnName), WriteObjectXml (row[col, version]));
1293                 }
1294
1295                 private void WriteTableElement (XmlWriter writer, XmlWriteMode mode, DataTable table, DataRow row, DataRowVersion version)
1296                 {
1297                         //sort out the namespacing
1298                         string nspc = table.Namespace.Length > 0 ? table.Namespace : Namespace;
1299
1300                         WriteStartElement (writer, mode, nspc, table.Prefix, XmlConvert.EncodeLocalName (table.TableName));
1301
1302                         if (mode == XmlWriteMode.DiffGram) {
1303                                 WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "id", table.TableName + (row.XmlRowID + 1));
1304                                 WriteAttributeString (writer, mode, XmlConstants.MsdataNamespace, XmlConstants.MsdataPrefix, "rowOrder", XmlConvert.ToString (row.XmlRowID));
1305                                 string modeName = null;
1306                                 if (row.RowState == DataRowState.Modified)
1307                                         modeName = "modified";
1308                                 else if (row.RowState == DataRowState.Added)
1309                                         modeName = "inserted";
1310
1311                                 if (version != DataRowVersion.Original && modeName != null)
1312                                         WriteAttributeString (writer, mode, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "hasChanges", modeName);
1313                         }
1314                 }
1315                     
1316                 private void WriteStartElement (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name)
1317                 {
1318                         writer.WriteStartElement (prefix, name, nspc);
1319                 }
1320                 
1321                 private void WriteAttributeString (XmlWriter writer, XmlWriteMode mode, string nspc, string prefix, string name, string stringValue)
1322                 {
1323                         switch ( mode) {
1324                                 case XmlWriteMode.WriteSchema:
1325                                         writer.WriteAttributeString (prefix, name, nspc);
1326                                         break;
1327                                 case XmlWriteMode.DiffGram:
1328                                         writer.WriteAttributeString (prefix, name, nspc,stringValue);
1329                                         break;
1330                                 default:
1331                                         writer.WriteAttributeString (name, stringValue);
1332                                         break;                                  
1333                         };
1334                 }
1335                 
1336                 internal void WriteIndividualTableContent (XmlWriter writer, DataTable table, XmlWriteMode mode)
1337                 {
1338                         ((XmlTextWriter)writer).Formatting = Formatting.Indented;
1339
1340                         if (mode == XmlWriteMode.DiffGram) {
1341                                 SetTableRowsID (table);
1342                                 WriteDiffGramElement (writer);
1343                         }
1344                         
1345                         WriteStartElement (writer, mode, Namespace, Prefix, XmlConvert.EncodeName (DataSetName));
1346                         
1347                         WriteTable (writer, table, mode, DataRowVersion.Default);
1348                         
1349                         if (mode == XmlWriteMode.DiffGram) {
1350                                 writer.WriteEndElement (); //DataSet name
1351                                 if (HasChanges (DataRowState.Modified | DataRowState.Deleted)) {
1352
1353                                         DataSet beforeDS = GetChanges (DataRowState.Modified | DataRowState.Deleted);   
1354                                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "before");
1355                                         WriteTable (writer, beforeDS.Tables [table.TableName], mode, DataRowVersion.Original);
1356                                         writer.WriteEndElement ();
1357                                 }
1358                         }
1359                         writer.WriteEndElement (); // DataSet name or diffgr:diffgram
1360                 }
1361                 
1362
1363                 private void CheckNamespace (string prefix, string ns, XmlNamespaceManager nsmgr, XmlSchema schema)
1364                 {
1365                         if (ns == String.Empty)
1366                                 return;
1367                         if (ns != nsmgr.DefaultNamespace) {
1368                                 if (nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix)) != ns) {
1369                                         for (int i = 1; i < int.MaxValue; i++) {
1370                                                 string p = nsmgr.NameTable.Add ("app" + i);
1371                                                 if (!nsmgr.HasNamespace (p)) {
1372                                                         nsmgr.AddNamespace (p, ns);
1373                                                         HandleExternalNamespace (p, ns, schema);
1374                                                         break;
1375                                                 }
1376                                         }
1377                                 }
1378                         }
1379                 }
1380                 
1381                 XmlSchema BuildSchema ()
1382                 {
1383                         return BuildSchema (Tables, Relations);
1384                 }
1385                 
1386                 internal XmlSchema BuildSchema (DataTableCollection tables, DataRelationCollection relations)
1387                 {
1388                         string constraintPrefix = "";
1389                         XmlSchema schema = new XmlSchema ();
1390                         XmlNamespaceManager nsmgr = new XmlNamespaceManager (new NameTable ());
1391                         
1392                         if (Namespace != "") {
1393                                 schema.AttributeFormDefault = XmlSchemaForm.Qualified;
1394                                 schema.ElementFormDefault = XmlSchemaForm.Qualified;
1395                                 schema.TargetNamespace = Namespace;
1396                                 constraintPrefix = XmlConstants.TnsPrefix + ":";
1397                         }
1398
1399                         // set the schema id
1400                         string xmlNSURI = "http://www.w3.org/2000/xmlns/";
1401                         schema.Id = DataSetName;
1402                         XmlDocument doc = new XmlDocument ();
1403                         XmlAttribute attr = null;
1404                         ArrayList atts = new ArrayList ();
1405
1406                         attr = doc.CreateAttribute ("", "xmlns", xmlNSURI);
1407                         atts.Add (attr);
1408
1409                         nsmgr.AddNamespace ("xs", XmlSchema.Namespace);
1410                         nsmgr.AddNamespace (XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1411                         if (Namespace != "") {
1412                                 nsmgr.AddNamespace (XmlConstants.TnsPrefix, Namespace);
1413                                 nsmgr.AddNamespace (String.Empty, Namespace);
1414                         }
1415                         if (CheckExtendedPropertyExists ())
1416                                 nsmgr.AddNamespace (XmlConstants.MspropPrefix, XmlConstants.MspropNamespace);
1417
1418                         if (atts.Count > 0)
1419                                 schema.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1420
1421                         XmlSchemaElement elem = new XmlSchemaElement ();
1422                         elem.Name = XmlConvert.EncodeName (DataSetName);
1423
1424                         // Add namespaces used in DataSet components (tables, columns, ...)
1425                         foreach (DataTable dt in Tables) {
1426                                 foreach (DataColumn col in dt.Columns)
1427                                         CheckNamespace (col.Prefix, col.Namespace, nsmgr, schema);
1428                                 CheckNamespace (dt.Prefix, dt.Namespace, nsmgr, schema);
1429                         }
1430
1431                         // Attributes for DataSet element
1432                         atts.Clear ();
1433                         attr = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsDataSet, XmlConstants.MsdataNamespace);
1434                         attr.Value = "true";
1435                         atts.Add (attr);
1436
1437                         attr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Locale, XmlConstants.MsdataNamespace);
1438                         attr.Value = locale.Name;
1439                         atts.Add (attr);
1440
1441                         elem.UnhandledAttributes = atts.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1442
1443                         AddExtendedPropertyAttributes (elem, ExtendedProperties, doc);
1444
1445                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1446                         elem.SchemaType = complex;
1447
1448                         XmlSchemaChoice choice = new XmlSchemaChoice ();
1449                         complex.Particle = choice;
1450                         choice.MaxOccursString = XmlConstants.Unbounded;
1451                         
1452                         //Write out schema for each table in order
1453                         foreach (DataTable table in tables) {           
1454                                 bool isTopLevel = true;
1455                                 foreach (DataRelation rel in table.ParentRelations) {
1456                                         if (rel.Nested) {
1457                                                 isTopLevel = false;
1458                                                 break;
1459                                         }
1460                                 }
1461                                 
1462                                 if (isTopLevel) {
1463                                         if (table.Namespace != SafeNS (schema.TargetNamespace)) {
1464                                                 XmlSchemaElement extElem = new XmlSchemaElement ();
1465                                                 extElem.RefName = new XmlQualifiedName (table.TableName, table.Namespace);
1466                                                 choice.Items.Add (extElem);
1467                                         }
1468                                         else
1469                                                 choice.Items.Add (GetTableSchema (doc, table, schema, nsmgr));
1470                                 }
1471                         }
1472
1473                         schema.Items.Add (elem);
1474                         
1475                         AddConstraintsToSchema (elem, constraintPrefix, tables, relations, doc);
1476                         foreach (string prefix in nsmgr) {
1477                                 string ns = nsmgr.LookupNamespace (nsmgr.NameTable.Get (prefix));
1478                                 if (prefix != "xmlns" && prefix != "xml" && ns != null && ns != String.Empty)
1479                                         schema.Namespaces.Add (prefix, ns);
1480                         }
1481                         return schema;
1482                 }
1483
1484                 private bool CheckExtendedPropertyExists ()
1485                 {
1486                         if (ExtendedProperties.Count > 0)
1487                                 return true;
1488                         foreach (DataTable dt in Tables) {
1489                                 if (dt.ExtendedProperties.Count > 0)
1490                                         return true;
1491                                 foreach (DataColumn col in dt.Columns)
1492                                         if (col.ExtendedProperties.Count > 0)
1493                                                 return true;
1494                                 foreach (Constraint c in dt.Constraints)
1495                                         if (c.ExtendedProperties.Count > 0)
1496                                                 return true;
1497                         }
1498                         foreach (DataRelation rel in Relations)
1499                                 if (rel.ExtendedProperties.Count > 0)
1500                                         return true;
1501                         return false;
1502                 }
1503
1504                 // Add all constraints in all tables to the schema.
1505                 private void AddConstraintsToSchema (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, DataRelationCollection relations, XmlDocument doc) 
1506                 {
1507                         // first add all unique constraints.
1508                         Hashtable uniqueNames = AddUniqueConstraints (elem, constraintPrefix, tables, doc);
1509                         // Add all foriegn key constraints.
1510                         AddForeignKeys (uniqueNames, elem, constraintPrefix, relations, doc);
1511                 }
1512                 
1513                 // Add unique constaraints to the schema.
1514                 // return hashtable with the names of all XmlSchemaUnique elements we created.
1515                 private Hashtable AddUniqueConstraints (XmlSchemaElement elem, string constraintPrefix, DataTableCollection tables, XmlDocument doc)
1516                 {
1517                         Hashtable uniqueNames = new Hashtable();
1518                         foreach (DataTable table in tables) {
1519                                 
1520                                 foreach (Constraint constraint in table.Constraints) {
1521                                         
1522                                         if (constraint is UniqueConstraint) {
1523                                                 ArrayList attrs = new ArrayList ();
1524                                                 XmlAttribute attrib;
1525                                                 UniqueConstraint uqConst = (UniqueConstraint) constraint;
1526                                                 XmlSchemaUnique uniq = new XmlSchemaUnique ();
1527                                                 
1528                                                 // if column of the constraint is hidden do not write the constraint.
1529                                                 bool isHidden = false;
1530                                                 foreach (DataColumn column in uqConst.Columns) {
1531                                                         if (column.ColumnMapping == MappingType.Hidden) {
1532                                                                 isHidden = true;
1533                                                                 break;
1534                                                         }
1535                                                 }
1536
1537                                                 if (isHidden)
1538                                                         continue;
1539
1540                                                 // if constaraint name do not exist in the hashtable we can use it.
1541                                                 if (!uniqueNames.ContainsKey (uqConst.ConstraintName)) {
1542                                                         uniq.Name = uqConst.ConstraintName;
1543                                                 }
1544                                                 // generate new constraint name for the XmlSchemaUnique element.
1545                                                 else {
1546                                                         uniq.Name = uqConst.Table.TableName + "_" + uqConst.ConstraintName;
1547                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ConstraintName, XmlConstants.MsdataNamespace);
1548                                                         attrib.Value = uqConst.ConstraintName;
1549                                                         attrs.Add (attrib);
1550                                                 }
1551                                                 if (uqConst.IsPrimaryKey) {
1552                                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.PrimaryKey, XmlConstants.MsdataNamespace);
1553                                                         attrib.Value = "true";
1554                                                         attrs.Add (attrib);
1555                                                 }
1556                 
1557                                                 uniq.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1558
1559                                                 uniq.Selector = new XmlSchemaXPath();
1560                                                 uniq.Selector.XPath = ".//"+constraintPrefix + uqConst.Table.TableName;
1561                                                 XmlSchemaXPath field;
1562                                                 foreach (DataColumn column in uqConst.Columns) {
1563                                                         field = new XmlSchemaXPath();
1564                                                         string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1565                                                         field.XPath = typePrefix + constraintPrefix+column.ColumnName;
1566                                                         uniq.Fields.Add(field);
1567                                                 }
1568                                 
1569                                                 AddExtendedPropertyAttributes (uniq, constraint.ExtendedProperties, doc);
1570
1571                                                 elem.Constraints.Add (uniq);
1572                                                 uniqueNames.Add (uniq.Name, null);
1573                                         }
1574                                 }
1575                         }
1576                         return uniqueNames;
1577                 }
1578                 
1579                 // Add the foriegn keys to the schema.
1580                 private void AddForeignKeys (Hashtable uniqueNames, XmlSchemaElement elem, string constraintPrefix, DataRelationCollection relations, XmlDocument doc)
1581                 {
1582                         if (relations == null) return;
1583                         
1584                         foreach (DataRelation rel in relations) {
1585                                 
1586                                 if (rel.ParentKeyConstraint == null || rel.ChildKeyConstraint == null)
1587                                         continue;
1588
1589                                 bool isHidden = false;
1590                                 foreach (DataColumn col in rel.ParentColumns) {
1591                                         if (col.ColumnMapping == MappingType.Hidden) {
1592                                                 isHidden = true;
1593                                                 break;
1594                                         }
1595                                 }
1596                                 foreach (DataColumn col in rel.ChildColumns) {
1597                                         if (col.ColumnMapping == MappingType.Hidden) {
1598                                                 isHidden = true;
1599                                                 break;
1600                                         }
1601                                 }
1602                                 if (isHidden)
1603                                         continue;
1604                                 
1605                                 ArrayList attrs = new ArrayList ();
1606                                 XmlAttribute attrib;
1607                                 XmlSchemaKeyref keyRef = new XmlSchemaKeyref();
1608                                 keyRef.Name = rel.RelationName;
1609                                 ForeignKeyConstraint fkConst = rel.ChildKeyConstraint;
1610                                 UniqueConstraint uqConst = rel.ParentKeyConstraint;
1611                                 
1612                                 string concatName = rel.ParentTable.TableName + "_" + uqConst.ConstraintName;
1613                                 // first try to find the concatenated name. If we didn't find it - use constraint name.
1614                                 if (uniqueNames.ContainsKey (concatName)) {
1615                                         keyRef.Refer = new XmlQualifiedName(concatName);
1616                                 }
1617                                 else {
1618                                         keyRef.Refer = new XmlQualifiedName(uqConst.ConstraintName);
1619                                 }
1620
1621                                 if (rel.Nested) {
1622                                         attrib = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.IsNested, XmlConstants.MsdataNamespace);
1623                                         attrib.Value = "true";
1624                                         attrs.Add (attrib);
1625                                 }
1626
1627                                 keyRef.Selector = new XmlSchemaXPath();
1628                                 keyRef.Selector.XPath = ".//" + constraintPrefix + rel.ChildTable.TableName;
1629                                 XmlSchemaXPath field;
1630                                 foreach (DataColumn column in rel.ChildColumns) {
1631                                         field = new XmlSchemaXPath();
1632                                         string typePrefix = column.ColumnMapping == MappingType.Attribute ? "@" : "";
1633                                         field.XPath = typePrefix + constraintPrefix + column.ColumnName;
1634                                         keyRef.Fields.Add(field);
1635                                 }
1636
1637                                 keyRef.UnhandledAttributes = (XmlAttribute[])attrs.ToArray (typeof (XmlAttribute));
1638                                 AddExtendedPropertyAttributes (keyRef, rel.ExtendedProperties, doc);
1639
1640                                 elem.Constraints.Add (keyRef);
1641                         }
1642                 }
1643
1644                 private XmlSchemaElement GetTableSchema (XmlDocument doc, DataTable table, XmlSchema schemaToAdd, XmlNamespaceManager nsmgr)
1645                 {
1646                         ArrayList elements;
1647                         ArrayList atts;
1648                         DataColumn simple;
1649
1650                         ArrayList xattrs = new ArrayList();
1651                         XmlAttribute xattr;
1652
1653                         SplitColumns (table, out atts, out elements, out simple);
1654
1655                         XmlSchemaElement elem = new XmlSchemaElement ();
1656                         elem.Name = table.TableName;
1657
1658                         XmlSchemaComplexType complex = new XmlSchemaComplexType ();
1659                         elem.SchemaType = complex;
1660
1661                         XmlSchemaObjectCollection schemaAttributes = null;
1662
1663                         if (simple != null) {
1664                                 // add simpleContent
1665                                 XmlSchemaSimpleContent simpleContent = new XmlSchemaSimpleContent();
1666                                 complex.ContentModel = simpleContent;
1667
1668                                 // add column name attribute
1669                                 XmlAttribute[] xlmAttrs = new XmlAttribute [2];
1670                                 xlmAttrs[0] = doc.CreateAttribute (XmlConstants.MsdataPrefix,  XmlConstants.ColumnName, XmlConstants.MsdataNamespace);
1671                                 xlmAttrs[0].Value = simple.ColumnName;
1672                                 
1673                                 // add ordinal attribute
1674                                 xlmAttrs[1] = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Ordinal, XmlConstants.MsdataNamespace);
1675                                 xlmAttrs[1].Value = XmlConvert.ToString (simple.Ordinal);
1676                                 simpleContent.UnhandledAttributes = xlmAttrs;
1677                                 
1678                                 
1679                                 // add extension
1680                                 XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
1681                                 simpleContent.Content = extension;
1682                                 extension.BaseTypeName = MapType (simple.DataType);
1683                                 schemaAttributes = extension.Attributes;
1684                         } else {
1685                                 schemaAttributes = complex.Attributes;
1686                                 //A sequence of element types or a simple content node
1687                                 //<xs:sequence>
1688                                 XmlSchemaSequence seq = new XmlSchemaSequence ();
1689
1690                                 foreach (DataColumn col in elements) {
1691                                         
1692                                         // Add element for the column.
1693                                         XmlSchemaElement colElem = new XmlSchemaElement ();
1694                                         colElem.Name = col.ColumnName;
1695                                 
1696                                         if (col.ColumnName != col.Caption && col.Caption != String.Empty) {
1697                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.Caption, XmlConstants.MsdataNamespace);
1698                                                 xattr.Value = col.Caption;
1699                                                 xattrs.Add (xattr);
1700                                         }
1701
1702                                         if (col.AutoIncrement == true) {
1703                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrement, XmlConstants.MsdataNamespace);
1704                                                 xattr.Value = "true";
1705                                                 xattrs.Add (xattr);
1706                                         }
1707
1708                                         if (col.AutoIncrementSeed != 0) {
1709                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.AutoIncrementSeed, XmlConstants.MsdataNamespace);
1710                                                 xattr.Value = XmlConvert.ToString (col.AutoIncrementSeed);
1711                                                 xattrs.Add (xattr);
1712                                         }
1713
1714                                         if (col.DefaultValue.ToString () != String.Empty)
1715                                                 colElem.DefaultValue = WriteObjectXml (col.DefaultValue);
1716
1717                                         if (col.ReadOnly) {
1718                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ReadOnly, XmlConstants.MsdataNamespace);
1719                                                 xattr.Value = "true";
1720                                                 xattrs.Add (xattr);
1721                                         }
1722
1723                                         if (col.MaxLength < 0)
1724                                                 colElem.SchemaTypeName = MapType (col.DataType);
1725                                         
1726                                         if (colElem.SchemaTypeName == XmlConstants.QnString && col.DataType != typeof (string) 
1727                                                 && col.DataType != typeof (char)) {
1728                                                 xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.DataType, XmlConstants.MsdataNamespace);
1729                                                 xattr.Value = col.DataType.AssemblyQualifiedName;
1730                                                 xattrs.Add (xattr);
1731                                         }
1732
1733                                         if (col.AllowDBNull) {
1734                                                 colElem.MinOccurs = 0;
1735                                         }
1736
1737                                         //writer.WriteAttributeString (XmlConstants.MsdataPrefix, 
1738                                         //                            XmlConstants.Ordinal, 
1739                                         //                            XmlConstants.MsdataNamespace, 
1740                                         //                            col.Ordinal.ToString ());
1741
1742                                         // Write SimpleType if column have MaxLength
1743                                         if (col.MaxLength > -1) {
1744                                                 colElem.SchemaType = GetTableSimpleType (doc, col);
1745                                         }
1746                                         
1747                                         colElem.UnhandledAttributes = (XmlAttribute[])xattrs.ToArray(typeof (XmlAttribute));
1748                                         xattrs.Clear ();
1749                                         AddExtendedPropertyAttributes (colElem, col.ExtendedProperties, doc);
1750                                         seq.Items.Add (colElem);
1751                                 }
1752
1753                                 foreach (DataRelation rel in table.ChildRelations) {
1754                                         if (rel.Nested) {
1755                                                 if (rel.ChildTable.Namespace != SafeNS (schemaToAdd.TargetNamespace)) {
1756                                                         XmlSchemaElement el = new XmlSchemaElement ();
1757                                                         el.RefName = new XmlQualifiedName (rel.ChildTable.TableName, rel.ChildTable.Namespace);
1758                                                 } else {
1759                                                         XmlSchemaElement el = GetTableSchema (doc, rel.ChildTable, schemaToAdd, nsmgr);
1760                                                         el.MinOccurs = 0;
1761                                                         el.MaxOccursString = "unbounded";
1762                                                         XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
1763                                                         ct.Name = el.Name;
1764                                                         el.SchemaType = null;
1765                                                         el.SchemaTypeName = new XmlQualifiedName (ct.Name, schemaToAdd.TargetNamespace);
1766                                                         schemaToAdd.Items.Add (ct);
1767                                                         seq.Items.Add (el);
1768                                                 }
1769                                         }
1770                                 }
1771
1772                                 if (seq.Items.Count > 0)
1773                                         complex.Particle = seq;
1774                         }
1775
1776                         //Then a list of attributes
1777                         foreach (DataColumn col in atts) {
1778                                 //<xs:attribute name=col.ColumnName form="unqualified" type=MappedType/>
1779                                 XmlSchemaAttribute att = new XmlSchemaAttribute ();
1780                                 att.Name = col.ColumnName;
1781                                 if (col.Namespace != String.Empty) {
1782                                         att.Form = XmlSchemaForm.Qualified;
1783                                         string prefix = col.Prefix == String.Empty ? "app" + schemaToAdd.Namespaces.Count : col.Prefix;
1784                                         att.Name = prefix + ":" + col.ColumnName;
1785                                         // FIXME: Handle prefix mapping correctly.
1786                                         schemaToAdd.Namespaces.Add (prefix, col.Namespace);
1787                                 }
1788                                 if (!col.AllowDBNull)
1789                                         att.Use = XmlSchemaUse.Required;
1790                                 if (col.DefaultValue.ToString () != String.Empty)
1791                                         att.DefaultValue = WriteObjectXml (col.DefaultValue);
1792
1793                                 if (col.ReadOnly) {
1794                                         xattr = doc.CreateAttribute (XmlConstants.MsdataPrefix, XmlConstants.ReadOnly, XmlConstants.MsdataNamespace);
1795                                         xattr.Value = "true";
1796                                         xattrs.Add (xattr);
1797                                 }
1798
1799                                 att.UnhandledAttributes = xattrs.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1800                                 xattrs.Clear ();
1801
1802                                 if (col.MaxLength > -1)
1803                                         att.SchemaType = GetTableSimpleType (doc, col);
1804                                 else
1805                                         att.SchemaTypeName = MapType (col.DataType);
1806                                 // FIXME: what happens if extended properties are set on attribute columns??
1807                                 schemaAttributes.Add (att);
1808                         }
1809
1810                         AddExtendedPropertyAttributes (elem, table.ExtendedProperties, doc);
1811
1812                         return elem;
1813                 }
1814
1815                 private void AddExtendedPropertyAttributes (XmlSchemaAnnotated xsobj, PropertyCollection props, XmlDocument doc)
1816                 {
1817                         ArrayList attList = new ArrayList ();
1818                         XmlAttribute xmlAttr;
1819
1820                         if (xsobj.UnhandledAttributes != null)
1821                                 attList.AddRange (xsobj.UnhandledAttributes);
1822
1823                         // add extended properties to xs:element
1824                         foreach (DictionaryEntry de in props) {
1825                                 xmlAttr = doc.CreateAttribute (XmlConstants.MspropPrefix, XmlConvert.EncodeName (de.Key.ToString ()), XmlConstants.MspropNamespace);
1826                                 xmlAttr.Value = de.Value != null ? WriteObjectXml (de.Value) : String.Empty;
1827                                 attList.Add (xmlAttr);
1828                         }
1829                         if (attList.Count > 0)
1830                                 xsobj.UnhandledAttributes = attList.ToArray (typeof (XmlAttribute)) as XmlAttribute [];
1831                 }
1832
1833                 private string SafeNS (string ns)
1834                 {
1835                         return ns != null ? ns : String.Empty;
1836                 }
1837
1838                 private void HandleExternalNamespace (string prefix, string ns, XmlSchema schema)
1839                 {
1840                         foreach (XmlSchemaExternal ext in schema.Includes) {
1841                                 XmlSchemaImport imp = ext as XmlSchemaImport;
1842                                 if (imp != null && imp.Namespace == ns)
1843                                         return; // nothing to do
1844                         }
1845                         XmlSchemaImport i = new XmlSchemaImport ();
1846                         i.Namespace = ns;
1847                         i.SchemaLocation = "_" + prefix + ".xsd";
1848                         schema.Includes.Add (i);
1849                 }
1850
1851                 private XmlSchemaSimpleType GetTableSimpleType (XmlDocument doc, DataColumn col)
1852                 {
1853                         // SimpleType
1854                         XmlSchemaSimpleType simple = new XmlSchemaSimpleType ();
1855
1856                         // Restriction
1857                         XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction ();
1858                         restriction.BaseTypeName = MapType (col.DataType);
1859                         
1860                         // MaxValue
1861                         XmlSchemaMaxLengthFacet max = new XmlSchemaMaxLengthFacet ();
1862                         max.Value = XmlConvert.ToString (col.MaxLength);
1863                         restriction.Facets.Add (max);
1864                         
1865                         simple.Content = restriction;
1866                         return simple;
1867                 }
1868
1869                 private void DoWriteXmlSchema (XmlWriter writer)
1870                 {
1871                         BuildSchema ().Write (writer);
1872                 }
1873                 
1874                 ///<summary>
1875                 /// Helper function to split columns into attributes elements and simple
1876                 /// content
1877                 /// </summary>
1878                 private void SplitColumns (DataTable table, 
1879                         out ArrayList atts, 
1880                         out ArrayList elements, 
1881                         out DataColumn simple)
1882                 {
1883                         //The columns can be attributes, hidden, elements, or simple content
1884                         //There can be 0-1 simple content cols or 0-* elements
1885                         atts = new System.Collections.ArrayList ();
1886                         elements = new System.Collections.ArrayList ();
1887                         simple = null;
1888                         
1889                         //Sort out the columns
1890                         foreach (DataColumn col in table.Columns) {
1891                                 switch (col.ColumnMapping) {
1892                                         case MappingType.Attribute:
1893                                                 atts.Add (col);
1894                                                 break;
1895                                         case MappingType.Element:
1896                                                 elements.Add (col);
1897                                                 break;
1898                                         case MappingType.SimpleContent:
1899                                                 if (simple != null) {
1900                                                         throw new System.InvalidOperationException ("There may only be one simple content element");
1901                                                 }
1902                                                 simple = col;
1903                                                 break;
1904                                         default:
1905                                                 //ignore Hidden elements
1906                                                 break;
1907                                 }
1908                         }
1909                 }
1910
1911                 private void WriteDiffGramElement(XmlWriter writer)
1912                 {
1913                         WriteStartElement (writer, XmlWriteMode.DiffGram, XmlConstants.DiffgrNamespace, XmlConstants.DiffgrPrefix, "diffgram");
1914                         WriteAttributeString(writer, XmlWriteMode.DiffGram, null, "xmlns", XmlConstants.MsdataPrefix, XmlConstants.MsdataNamespace);
1915                 }
1916
1917                 private void SetRowsID()
1918                 {
1919                         foreach (DataTable Table in Tables)
1920                                 SetTableRowsID (Table);
1921                 }
1922                 
1923                 private void SetTableRowsID (DataTable Table)
1924                 {
1925                         int dataRowID = 0;
1926                         foreach (DataRow Row in Table.Rows) {
1927                                 Row.XmlRowID = dataRowID;
1928                                 dataRowID++;
1929                         }
1930                 }
1931
1932                 
1933                 private XmlQualifiedName MapType (Type type)
1934                 {
1935                         switch (Type.GetTypeCode (type)) {
1936                                 case TypeCode.String: return XmlConstants.QnString;
1937                                 case TypeCode.Int16: return XmlConstants.QnShort;
1938                                 case TypeCode.Int32: return XmlConstants.QnInt;
1939                                 case TypeCode.Int64: return XmlConstants.QnLong;
1940                                 case TypeCode.Boolean: return XmlConstants.QnBoolean;
1941                                 case TypeCode.Byte: return XmlConstants.QnUnsignedByte;
1942                                 //case TypeCode.Char: return XmlConstants.QnChar;
1943                                 case TypeCode.DateTime: return XmlConstants.QnDateTime;
1944                                 case TypeCode.Decimal: return XmlConstants.QnDecimal;
1945                                 case TypeCode.Double: return XmlConstants.QnDouble;
1946                                 case TypeCode.SByte: return XmlConstants.QnSbyte;
1947                                 case TypeCode.Single: return XmlConstants.QnFloat;
1948                                 case TypeCode.UInt16: return XmlConstants.QnUsignedShort;
1949                                 case TypeCode.UInt32: return XmlConstants.QnUnsignedInt;
1950                                 case TypeCode.UInt64: return XmlConstants.QnUnsignedLong;
1951                         }
1952                         
1953                         if (typeof (TimeSpan) == type)
1954                                 return XmlConstants.QnDuration;
1955                         else if (typeof (System.Uri) == type)
1956                                 return XmlConstants.QnUri;
1957                         else if (typeof (byte[]) == type)
1958                                 return XmlConstants.QnBase64Binary;
1959                         else if (typeof (XmlQualifiedName) == type)
1960                                 return XmlConstants.QnXmlQualifiedName;
1961                         else
1962                                 return XmlConstants.QnString;
1963                 }
1964
1965                 #endregion //Private Xml Serialisation
1966         }
1967 }