2004-05-13 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.Data / System.Xml / XmlDataDocument.cs
1 //
2 // mcs/class/System.Data/System.Xml/XmlDataDocument.cs
3 //
4 // Purpose: Provides a W3C XML DOM Document to interact with
5 //          relational data in a DataSet
6 //
7 // class: XmlDataDocument
8 // assembly: System.Data.dll
9 // namespace: System.Xml
10 //
11 // Author:
12 //     Daniel Morgan <danmorg@sc.rr.com>
13 //     Ville Palo <vi64pa@koti.soon.fi>
14 //     Atsushi Enomoto <atsushi@ximian.com>
15 //
16 // (c)copyright 2002 Daniel Morgan
17 // (c)copyright 2003 Ville Palo
18 // (c)2004 Novell Inc.
19 //
20 // XmlDataDocument is included within the Mono Class Library.
21 //
22
23 using System;
24 using System.Data;
25 using System.IO;
26 using System.Text;
27 using System.Xml.XPath;
28 using System.Collections;
29 using System.Globalization;
30 using System.ComponentModel;
31
32 namespace System.Xml 
33 {
34
35         public class XmlDataDocument : XmlDocument 
36         {
37                 // Should we consider overriding CloneNode() ? By default
38                 // base CloneNode() will be invoked and thus no DataRow conflict
39                 // would happen, that sounds the best (that means, no mapped
40                 // DataRow will be provided).
41                 internal class XmlDataElement : XmlElement
42                 {
43                         DataRow row;
44
45                         internal XmlDataElement (DataRow row, string prefix, string localName, string ns, XmlDataDocument doc)
46                                 : base (prefix, localName, ns, doc)
47                         {
48                                 this.row = row;
49                                 // Embed row ID only when the element is mapped to
50                                 // certain DataRow.
51                                 if (row != null) {
52                                         row.DataElement = this;
53                                         row.XmlRowID = doc.dataRowID;
54                                         doc.dataRowIDList.Add (row.XmlRowID);
55                                         // It should not be done here. The node is detached
56                                         // dt.Rows.Add (tempRow);
57                                         doc.dataRowID++;
58                                 }
59                         }
60
61                         internal DataRow DataRow {
62                                 get { return row; }
63                         }
64                 }
65
66                 #region Fields
67
68                 private DataSet dataSet;
69
70                 private int dataRowID = 1;
71                 private ArrayList dataRowIDList = new ArrayList ();
72
73                 // this keeps whether table change events should be handles
74                 private bool raiseDataSetEvents = true;
75                 private bool raiseDocumentEvents = true;
76
77                 // this is needed for inserting new row to datatable via xml
78                 private Hashtable TempTable = new Hashtable ();
79
80                 DataColumnChangeEventHandler columnChanged;
81                 DataRowChangeEventHandler rowDeleted;
82                 DataRowChangeEventHandler rowChanged;
83                 CollectionChangeEventHandler tablesChanged;
84                 #endregion // Fields
85
86                 #region Constructors
87
88                 public XmlDataDocument ()
89                 {
90                         InitDelegateFields ();
91
92                         dataSet = new DataSet();
93                         dataSet._xmlDataDocument = this;
94                         dataSet.Tables.CollectionChanged += tablesChanged;
95
96                         AddXmlDocumentListeners ();
97                         DataSet.EnforceConstraints = false;
98                 }
99
100                 public XmlDataDocument (DataSet dataset) 
101                 {
102                         if (dataset == null)
103                                 throw new ArgumentException ("Parameter dataset cannot be null.");\r
104                         if (dataset._xmlDataDocument != null)\r
105                                 throw new ArgumentException ("DataSet cannot be associated with two or more XmlDataDocument.");\r
106
107                         InitDelegateFields ();
108
109                         this.dataSet = dataset;
110                         this.dataSet._xmlDataDocument = this;
111
112                         XmlElement docElem = CreateElement (dataSet.Prefix, dataSet.DataSetName, dataSet.Namespace);
113                         foreach (DataTable dt in dataSet.Tables) {
114                                 if (dt.ParentRelations.Count > 0)
115                                         continue; // don't add them here
116                                 FillNodeRows (docElem, dt, dt.Rows);
117                         }
118
119                         // This seems required to avoid Load() error when for
120                         // example empty DataSet will be filled on Load().
121                         if (docElem.ChildNodes.Count > 0)
122                                 AppendChild (docElem);
123
124                         foreach (DataTable dt in dataSet.Tables) {
125                                 dt.ColumnChanged += columnChanged;
126                                 dt.RowDeleted += rowDeleted;
127                                 dt.RowChanged += rowChanged;
128                         }
129
130                         AddXmlDocumentListeners ();
131                 }
132
133                 // bool clone. If we are cloning XmlDataDocument then clone should be true.
134                 // FIXME: shouldn't DataSet be mapped to at most one document??
135                 private XmlDataDocument (DataSet dataset, bool clone)
136                 {
137                         InitDelegateFields ();
138
139                         this.dataSet = dataset;
140                         this.dataSet._xmlDataDocument = this;
141
142                         foreach (DataTable Table in DataSet.Tables) {
143                                 
144                                 foreach (DataRow Row in Table.Rows) {
145                                         Row.XmlRowID = dataRowID;
146                                         dataRowIDList.Add (dataRowID);
147                                         dataRowID++;
148                                 }
149                         }
150
151                         AddXmlDocumentListeners ();
152
153                         foreach (DataTable Table in dataSet.Tables) {
154                                 Table.ColumnChanged += columnChanged;
155                                 Table.RowDeleted += rowDeleted;
156                                 Table.RowChanged += rowChanged;
157                         }
158                 }
159
160                 #endregion // Constructors
161
162                 #region Public Properties
163
164                 public DataSet DataSet {
165                         get {
166                                 return dataSet;
167                         }
168                 }
169
170                 #endregion // Public Properties
171
172                 #region Public Methods
173
174                 private void FillNodeRows (XmlElement parent, DataTable dt, ICollection rows)
175                 {
176                         foreach (DataRow dr in dt.Rows) {
177                                 XmlDataElement el = new XmlDataElement (dr, dt.Prefix, dt.TableName, dt.Namespace, this);
178                                 for (int i = 0; i < dt.Columns.Count; i++) {
179                                         DataColumn col = dt.Columns [i];
180                                         string value = dr.IsNull (col) ? String.Empty : dr [col].ToString ();
181                                         switch (col.ColumnMapping) {
182                                         case MappingType.Element:
183                                                 XmlElement cel = CreateElement (col.Prefix, col.ColumnName, col.Namespace);
184                                                 cel.InnerText = value;
185                                                 el.AppendChild (cel);
186                                                 break;
187                                         case MappingType.Attribute:
188                                                 XmlAttribute a = CreateAttribute (col.Prefix, col.ColumnName, col.Namespace);
189                                                 a.Value = value;
190                                                 el.SetAttributeNode (a);
191                                                 break;
192                                         case MappingType.SimpleContent:
193                                                 XmlText t = CreateTextNode (value);
194                                                 el.AppendChild (t);
195                                                 break;
196                                         }
197                                 }
198                                 foreach (DataRelation rel in dt.ChildRelations)
199                                         FillNodeRows (el, rel.ChildTable, dr.GetChildRows (rel));
200                                 parent.AppendChild (el);
201                         }
202                 }
203
204                 [MonoTODO]
205                 public override XmlNode CloneNode(bool deep) 
206                 {
207                         XmlDataDocument Document;
208                         if (deep)
209                                 Document = new XmlDataDocument (DataSet.Copy (), true);
210                         else
211                                 Document = new XmlDataDocument (DataSet.Clone (), true);
212
213                         Document.RemoveXmlDocumentListeners ();
214
215                         Document.PreserveWhitespace = PreserveWhitespace;
216                         if (deep) {
217                                 foreach(XmlNode n in ChildNodes)
218                                         Document.AppendChild (Document.ImportNode (n, deep));
219                         }
220
221                         Document.AddXmlDocumentListeners ();
222
223                         return Document;                        
224                 }
225
226                 #region overloaded CreateElement methods
227
228                 public override XmlElement CreateElement(
229                         string prefix, string localName, string namespaceURI) 
230                 {
231                         DataTable dt = DataSet.Tables [localName];
232                         DataRow row = dt != null ? dt.NewRow () : null;
233                         if (row != null)
234                                 return GetElementFromRow (row);
235                         else
236                                 return base.CreateElement (prefix, localName, namespaceURI);
237                 }
238
239                 #endregion // overloaded CreateElement Methods
240                         
241                 // It is not supported in XmlDataDocument
242                 public override XmlEntityReference CreateEntityReference(string name) 
243                 {
244                         throw new NotSupportedException ();
245                 }
246                 
247                 // It is not supported in XmlDataDocument
248                 public override XmlElement GetElementById (string elemId) 
249                 {
250                         throw new NotSupportedException ();
251                 }
252
253                 // get the XmlElement associated with the DataRow
254                 public XmlElement GetElementFromRow (DataRow r) 
255                 {
256                         return r.DataElement;
257                 }
258
259                 // get the DataRow associated with the XmlElement
260                 public DataRow GetRowFromElement (XmlElement e)
261                 {
262                         XmlDataElement el = e as XmlDataElement;
263                         if (el == null)
264                                 return null;
265                         return el.DataRow;
266                 }
267
268                 #region overload Load methods
269
270                 public override void Load(Stream inStream) {
271                         Load (new XmlTextReader (inStream));
272                 }
273
274                 public override void Load(string filename) {
275                         Load (new XmlTextReader (filename));
276                 }
277
278                 public override void Load(TextReader txtReader) {
279                         Load (new XmlTextReader (txtReader));
280                 }
281
282                 public override void Load (XmlReader reader) 
283                 {
284                         if (DocumentElement != null)
285                                 throw new InvalidOperationException ("XmlDataDocument does not support multi-time loading. New XmlDadaDocument is always required.");
286
287                         bool OldEC = DataSet.EnforceConstraints;
288                         DataSet.EnforceConstraints = false;
289                         dataSet.Tables.CollectionChanged -= tablesChanged;
290
291                         base.Load (reader);
292
293                         DataSet.EnforceConstraints = OldEC;
294                         dataSet.Tables.CollectionChanged += tablesChanged;
295                 }
296                 
297                 #endregion // overloaded Load methods
298                 #endregion // Public Methods
299
300                 #region Protected Methods
301
302                 [MonoTODO ("Create optimized XPathNavigator")]
303                 protected override XPathNavigator CreateNavigator(XmlNode node) {
304                         return base.CreateNavigator (node);
305                 }
306
307                 #endregion // Protected Methods
308                 
309                 #region XmlDocument event handlers
310
311                 private void OnNodeChanging (object sender, XmlNodeChangedEventArgs args)
312                 {
313                         if (!this.raiseDocumentEvents)
314                                 return;
315                         if (DataSet.EnforceConstraints) 
316                                 throw new InvalidOperationException (Locale.GetText ("Please set DataSet.EnforceConstraints == false before trying to edit XmlDataDocument using XML operations."));
317                 }
318
319                 // Invoked when XmlNode is changed colum is changed
320                 [MonoTODO]
321                 private void OnNodeChanged (object sender, XmlNodeChangedEventArgs args)
322                 {
323                         if (!raiseDocumentEvents)
324                                 return;
325                         bool escapedRaiseDataSetEvents = raiseDataSetEvents;
326                         raiseDataSetEvents = false;
327                         try {
328
329                                 if (args.Node == null)
330                                         return;
331
332                                 DataRow row = GetRowFromElement ((XmlElement)args.Node.ParentNode.ParentNode);
333
334                                 if (row == null)
335                                         return;
336
337                                 if (!row.Table.Columns.Contains (args.Node.ParentNode.Name))
338                                         return;
339
340                                 row.Table.ColumnChanged -= columnChanged;
341
342                                 if (row [args.Node.ParentNode.Name].ToString () != args.Node.InnerText)         
343                                         row [args.Node.ParentNode.Name] = args.Node.InnerText;          
344
345                                 row.Table.ColumnChanged += columnChanged;
346                         } finally {
347                                 raiseDataSetEvents = escapedRaiseDataSetEvents;
348                         }
349                 }
350
351                 private void OnNodeRemoving (object sender, XmlNodeChangedEventArgs args) 
352                 {
353                         if (!this.raiseDocumentEvents)
354                                 return;
355                         if (DataSet.EnforceConstraints) 
356                                 throw new InvalidOperationException (Locale.GetText ("Please set DataSet.EnforceConstraints == false before trying to edit XmlDataDocument using XML operations."));
357                         
358                 }
359                 
360                 // Invoked when XmlNode is removed
361                 [MonoTODO]
362                 private void OnNodeRemoved (object sender, XmlNodeChangedEventArgs args)
363                 {
364                         if (!raiseDocumentEvents)
365                                 return;
366                         bool escapedRaiseDataSetEvents = raiseDataSetEvents;
367                         raiseDataSetEvents = false;
368
369                         try {
370                                 // FIXME: This code is obsolete one.
371
372                                 if (args.OldParent == null)
373                                         return;
374
375                                 if (!(args.OldParent is XmlElement))
376                                         return;
377                                 
378                                 DataRow row = GetRowFromElement ((XmlElement)args.OldParent);
379                                 
380                                 if (row == null)
381                                         return ;
382
383                                 row [args.Node.Name] = null;
384
385                                 // FIXME: Should we detach rows and descendants as well?
386                         } finally {
387                                 raiseDataSetEvents = escapedRaiseDataSetEvents;
388                         }
389                 }
390
391                 private void OnNodeInserting (object sender, XmlNodeChangedEventArgs args) 
392                 {
393                         if (!this.raiseDocumentEvents)
394                                 return;
395                         if (DataSet.EnforceConstraints) 
396                                 throw new InvalidOperationException (Locale.GetText ("Please set DataSet.EnforceConstraints == false before trying to edit XmlDataDocument using XML operations."));
397                         
398                 }
399                 
400                 private void OnNodeInserted (object sender, XmlNodeChangedEventArgs args)
401                 {
402                         if (!raiseDocumentEvents)
403                                 return;
404                         bool escapedRaiseDataSetEvents = raiseDataSetEvents;
405                         raiseDataSetEvents = false;
406
407                         // If the parent node is mapped to a DataTable, then
408                         // add a DataRow and map the parent element to it.
409                         //
410                         // AND If the child node is mapped to a DataTable, then
411                         // 1. if it is mapped to a DataTable and relation, add
412                         // a new DataRow and map the child element to it.
413                         // 2. if it is mapped to a DataColumn, set the column
414                         // value of the parent DataRow as the child
415
416                         try {
417                                 if (! (args.NewParent is XmlElement)) {
418                                         // i.e. adding document element
419                                         foreach (XmlNode table in args.Node.ChildNodes)
420                                                 CheckDescendantRelationship (table);
421                                         return;
422                                 }
423
424                                 DataRow row = GetRowFromElement (args.NewParent as XmlElement);
425                                 if (row == null) {
426                                         // That happens only when adding table to existing DocumentElement (aka DataSet element)
427                                         if (args.NewParent == DocumentElement)
428                                                 CheckDescendantRelationship (args.Node);
429                                         return;
430                                 }
431
432                                 XmlAttribute attr = args.Node as XmlAttribute;
433                                 if (attr != null) { // fill attribute value
434                                         DataColumn col = row.Table.Columns [attr.LocalName];
435                                         if (col != null)
436                                                 row [col] = args.Node.Value;
437                                 } else {
438                                         DataRow childRow = GetRowFromElement (args.Node as XmlElement);
439                                         if (childRow != null) {
440                                                 // child might be a table row.
441                                                 // I might be impossible to set parent
442                                                 // since either of them might be detached
443                                                 if (childRow.RowState != DataRowState.Detached && row.RowState != DataRowState.Detached) {
444                                                         FillRelationship (row, childRow, args.NewParent, args.Node);
445                                                 }
446                                         } else {
447                                                 // child might be a column
448                                                 DataColumn col = row.Table.Columns [args.Node.LocalName];
449                                                 if (col != null)
450                                                         row [col] = args.Node.InnerText;
451                                         }
452                                 }
453                         } finally {
454                                 raiseDataSetEvents = escapedRaiseDataSetEvents;
455                         }
456                 }
457
458                 private void CheckDescendantRelationship (XmlNode n)
459                 {
460                         XmlElement el = n as XmlElement;
461                         DataRow row = GetRowFromElement (el);
462                         if (row == null)
463                                 return;
464                         row.Table.Rows.Add (row); // attach
465                         CheckDescendantRelationship (n, row);
466                 }
467
468                 private void CheckDescendantRelationship (XmlNode p, DataRow row)
469                 {
470                         foreach (XmlNode n in p.ChildNodes) {
471                                 XmlElement el = n as XmlElement;
472                                 if (el == null)
473                                         continue;
474                                 DataRow childRow = GetRowFromElement (el);
475                                 if (childRow == null)
476                                         continue;
477                                 childRow.Table.Rows.Add (childRow);
478                                 FillRelationship (row, childRow, p, el);
479                         }
480                 }
481
482                 private void FillRelationship (DataRow row, DataRow childRow, XmlNode parentNode, XmlNode childNode)
483                 {
484                         for (int i = 0; i < childRow.Table.ParentRelations.Count; i++) {
485                                 DataRelation rel = childRow.Table.ParentRelations [i];
486                                 if (rel.ParentTable == row.Table) {
487                                         childRow.SetParentRow (row);
488                                         break;
489                                 }
490                         }
491                         CheckDescendantRelationship (childNode, childRow);
492                 }
493                 #endregion // DataSet event handlers
494
495                 #region DataSet event handlers
496
497                 // If DataTable is added or removed from DataSet
498                 private void OnDataTableChanged (object sender, CollectionChangeEventArgs eventArgs)
499                 {
500                         if (!raiseDataSetEvents)
501                                 return;
502                         bool escapedRaiseDocumentEvents = raiseDocumentEvents;
503                         raiseDocumentEvents = false;
504
505                         try {
506                                 DataTable Table = (DataTable)eventArgs.Element;
507                                 switch (eventArgs.Action) {
508                                 case CollectionChangeAction.Add:
509                                         Table.ColumnChanged += columnChanged;
510                                         Table.RowDeleted += rowDeleted;
511                                         Table.RowChanged += rowChanged;
512                                         break;
513                                 case CollectionChangeAction.Remove:
514                                         Table.ColumnChanged -= columnChanged;
515                                         Table.RowDeleted -= rowDeleted;
516                                         Table.RowChanged -= rowChanged;
517                                         break;
518                                 }
519                         } finally {
520                                 raiseDocumentEvents = escapedRaiseDocumentEvents;
521                         }
522                 }
523
524                 // If column has changed 
525                 [MonoTODO]                      
526                 private void OnDataTableColumnChanged(object sender, 
527                                                              DataColumnChangeEventArgs eventArgs)
528                 {
529                         if (!raiseDataSetEvents)
530                                 return;
531                         bool escapedRaiseDocumentEvents = raiseDocumentEvents;
532                         raiseDocumentEvents = false;
533
534                         try {
535                                 DataRow row = eventArgs.Row;
536                                 XmlElement el = GetElementFromRow (row);
537                                 if (el == null)
538                                         return;
539                                 DataColumn col = eventArgs.Column;
540                                 string value = row.IsNull (col) ? String.Empty : row [col].ToString ();
541                                 switch (col.ColumnMapping) {
542                                 case MappingType.Attribute:
543                                         el.SetAttribute (col.ColumnName, col.Namespace, value);
544                                         break;
545                                 case MappingType.SimpleContent:
546                                         el.InnerText = value;
547                                         break;
548                                 case MappingType.Element:
549                                         bool exists = false;
550                                         for (int i = 0; i < el.ChildNodes.Count; i++) {
551                                                 XmlElement c = el.ChildNodes [i] as XmlElement;
552                                                 if (c != null && c.LocalName == col.ColumnName && c.NamespaceURI == col.Namespace) {
553                                                         exists = true;
554                                                         c.InnerText = value;
555                                                         break;
556                                                 }
557                                         }
558                                         if (!exists) {
559                                                 XmlElement cel = CreateElement (col.Prefix, col.ColumnName, col.Namespace);
560                                                 cel.InnerText = value;
561                                                 el.AppendChild (cel);
562                                         }
563                                         break;
564                                 // FIXME: how to handle hidden?
565                                 }
566                         } finally {
567                                 raiseDocumentEvents = escapedRaiseDocumentEvents;
568                         }
569                 }
570         
571                 [MonoTODO]
572                 private void OnDataTableRowDeleted(object sender,
573                                                           DataRowChangeEventArgs eventArgs)
574                 {
575                         if (!raiseDataSetEvents)
576                                 return;
577                         bool escapedRaiseDocumentEvents = raiseDocumentEvents;
578                         raiseDocumentEvents = false;
579
580                         try {
581                                 // This code is obsolete XmlDataDocument one
582
583                                 DataRow deletedRow = null;
584                                 deletedRow = eventArgs.Row;
585
586                                 if (eventArgs.Row.XmlRowID == 0)
587                                         return;
588                                 
589                                 int rowIndex = dataRowIDList.IndexOf (eventArgs.Row.XmlRowID);
590                                 if (rowIndex == -1 || eventArgs.Row.XmlRowID == 0 || 
591                                 rowIndex > GetElementsByTagName (deletedRow.Table.TableName).Count - 1)
592                                         return;
593                                 
594                                 // Remove element from xmldocument and row indexlist
595                                 // FIXME: this is one way to do this, but i hope someday i find out much better way.
596                                 XmlNode p = GetElementsByTagName (deletedRow.Table.TableName) [rowIndex].ParentNode;
597                                 if (p != null) {
598                                         p.RemoveChild (GetElementsByTagName (deletedRow.Table.TableName) [rowIndex]);
599                                         dataRowIDList.RemoveAt (rowIndex);
600                                 }
601                         } finally {
602                                 raiseDocumentEvents = escapedRaiseDocumentEvents;
603                         }
604                 }
605                 
606                 [MonoTODO]
607                 private void OnDataTableRowChanged(object sender, DataRowChangeEventArgs eventArgs)
608                 {
609                         if (!raiseDataSetEvents)
610                                 return;
611                         bool escapedRaiseDocumentEvents = raiseDocumentEvents;
612                         raiseDocumentEvents = false;
613                         try {
614
615                                 switch (eventArgs.Action) {
616
617                                 case DataRowAction.Delete:
618                                         OnDataTableRowDeleted (sender, eventArgs);
619                                         break;
620
621                                 case DataRowAction.Add:
622                                         OnDataTableRowAdded (eventArgs);
623                                         break;
624
625                                 case DataRowAction.Rollback:
626                                         OnDataTableRowRollback (eventArgs);
627                                         break;
628                                 default:
629                                         break;
630                                 } 
631                         } finally {
632                                 raiseDocumentEvents = escapedRaiseDocumentEvents;
633                         }
634                 }
635
636                 // Added
637                 [MonoTODO]
638                 private void OnDataTableRowAdded (DataRowChangeEventArgs args)
639                 {
640                         if (!raiseDataSetEvents)
641                                 return;
642                         bool escapedRaiseDocumentEvents = raiseDocumentEvents;
643                         raiseDocumentEvents = false;
644
645                         try {
646
647                                 // Create row element. Row's name same as TableName                                     
648                                 DataRow row = args.Row;
649
650                                 // create document element if it does not exist
651                                 if (DocumentElement == null)
652                                         this.AppendChild (CreateElement (DataSet.DataSetName));
653
654                                 DataTable table= args.Row.Table;
655                                 XmlElement element = GetElementFromRow (row);
656                                 if (element == null)
657                                         element = CreateElement (table.Prefix, table.TableName, table.Namespace);
658                                 if (element.ParentNode == null) {
659                                         // parent is not always DocumentElement.
660                                         XmlElement parent = null;
661
662                                         if (table.ParentRelations.Count > 0) {
663                                                 for (int i = 0; i < table.ParentRelations.Count; i++) {
664                                                         DataRelation rel = table.ParentRelations [i];
665                                                         DataRow parentRow = row.GetParentRow (rel);
666                                                         if (parentRow == null)
667                                                                 continue;
668                                                         parent = GetElementFromRow (parentRow);
669                                                 }
670                                         }
671
672                                         // The row might be orphan. In such case, the 
673                                         // element is appended to DocumentElement.
674                                         if (parent == null)
675                                                 parent = DocumentElement;
676                                         parent.AppendChild (element);
677                                 }
678                         } finally {                     
679                                 raiseDocumentEvents = escapedRaiseDocumentEvents;
680                         }
681                 }
682
683                 private void FillNodeChildrenFromRow (DataRow row, XmlElement element)
684                 {
685                         DataTable table = row.Table;
686                         // fill columns for the row
687                         for (int i = 0; i < table.Columns.Count; i++) {
688                                 DataColumn col = table.Columns [i];
689                                 string value = row.IsNull (col) ? String.Empty : row [col].ToString ();
690                                 switch (col.ColumnMapping) {
691                                 case MappingType.Element:
692                                         XmlElement el = CreateElement (col.Prefix, col.ColumnName, col.Namespace);
693                                         el.InnerText = value;
694                                         element.AppendChild (el);
695                                         break;
696                                 case MappingType.Attribute:
697                                         XmlAttribute attr = CreateAttribute (col.Prefix, col.ColumnName, col.Namespace);
698                                         attr.Value = value;
699                                         element.SetAttributeNode (attr);
700                                         break;
701                                 case MappingType.SimpleContent:
702                                         XmlText text = CreateTextNode (value);
703                                         element.AppendChild (text);
704                                         break;
705                                 // FIXME: how to handle hidden?
706                                 }
707                         }
708                 }
709
710                 // Rollback
711                 [MonoTODO]
712                 private void OnDataTableRowRollback (DataRowChangeEventArgs args)
713                 {
714                         if (!raiseDataSetEvents)
715                                 return;
716                         bool escapedRaiseDocumentEvents = raiseDocumentEvents;
717                         raiseDocumentEvents = false;
718
719                         try {
720                                 // This code is obsolete XmlDataDocument one.
721
722                                 DataRow row = args.Row;                 
723                                 int rowid = dataRowIDList.IndexOf (row.XmlRowID);
724
725                                 // find right element in xmldocument
726                                 if (rowid == 0 || rowid >= GetElementsByTagName (row.Table.TableName).Count)
727                                         return;
728
729                                 XmlNode node = GetElementsByTagName (row.Table.TableName) [rowid];
730                                 
731                                 int rowValue = 0;
732                                 for (int i = 0; i < node.ChildNodes.Count; i++) {
733                                         
734                                         XmlNode child = node.ChildNodes [i];
735                                         if (child.NodeType != XmlNodeType.Whitespace) {
736                                                 child.InnerText = (string)row [rowValue++];
737                                         }
738                                 }
739                         } finally {
740                                 raiseDocumentEvents = escapedRaiseDocumentEvents;
741                         }
742                 }
743
744                 #endregion // DataSet event handlers
745
746                 #region Private methods
747                 private void InitDelegateFields ()
748                 {
749                         columnChanged = new DataColumnChangeEventHandler (OnDataTableColumnChanged);
750                         rowDeleted = new DataRowChangeEventHandler (OnDataTableRowDeleted);
751                         rowChanged = new DataRowChangeEventHandler (OnDataTableRowChanged);
752                         tablesChanged = new CollectionChangeEventHandler (OnDataTableChanged);
753                 }
754                 
755                 private void RemoveXmlDocumentListeners ()
756                 {
757                         this.NodeInserting -= new XmlNodeChangedEventHandler (OnNodeInserting);
758                         this.NodeInserted -= new XmlNodeChangedEventHandler (OnNodeInserted);
759                         this.NodeChanging -= new XmlNodeChangedEventHandler (OnNodeChanging);
760                         this.NodeChanged -= new XmlNodeChangedEventHandler (OnNodeChanged);
761                         this.NodeRemoving -= new XmlNodeChangedEventHandler (OnNodeRemoving);
762                         this.NodeRemoved -= new XmlNodeChangedEventHandler (OnNodeRemoved);
763                 }
764
765                 private void AddXmlDocumentListeners ()
766                 {
767                         this.NodeInserting += new XmlNodeChangedEventHandler (OnNodeInserting);
768                         this.NodeInserted += new XmlNodeChangedEventHandler (OnNodeInserted);
769                         this.NodeChanging += new XmlNodeChangedEventHandler (OnNodeChanging);
770                         this.NodeChanged += new XmlNodeChangedEventHandler (OnNodeChanged);
771                         this.NodeRemoving += new XmlNodeChangedEventHandler (OnNodeRemoving);
772                         this.NodeRemoved += new XmlNodeChangedEventHandler (OnNodeRemoved);
773                 }
774                 #endregion // Private methods
775         }
776 }
777