2004-05-12 Atsushi Enomoto <atsushi@ximian.com>
authorAtsushi Eno <atsushieno@gmail.com>
Wed, 12 May 2004 20:09:51 +0000 (20:09 -0000)
committerAtsushi Eno <atsushieno@gmail.com>
Wed, 12 May 2004 20:09:51 +0000 (20:09 -0000)
* XmlDataDocument.cs : Radical design change.
  - GetElementFromRow() must return "detached" element, even if it is
    not added to table's Rows.
  - Introduced XmlDataElement class to map element (this class itself)
    and DataRow _always_ . It is impossible to map *all* DataRows
    to/from elements only in XmlDataDocument API, since DataRow can
    be created separate from it (i.e. by table.NewRow()).
  - Removed unreliable GetElementsByTagName() dependency from most
    of the code, since users might create elements that has the same
    name of any tables (that is possible especially unmapped elements).
  - Adding/Removing delegates on every event action is unnecessary.
    Use just flags.
  - For Load(), just using base.Load() will do most of the job.
  - Attributes and SimpleContents are not handled properly.

  "Removed" and "Rollback" events are still TODO.

svn path=/trunk/mcs/; revision=27194

mcs/class/System.Data/System.Xml/ChangeLog
mcs/class/System.Data/System.Xml/XmlDataDocument.cs

index f60aa8153c28583b008f8a802ea2d0fd15e455fc..2c163afb74b1fd900b88736d3948dda4ab2a978d 100644 (file)
@@ -1,3 +1,22 @@
+2004-05-12  Atsushi Enomoto <atsushi@ximian.com>
+
+       * XmlDataDocument.cs : Radical design change.
+         - GetElementFromRow() must return "detached" element, even if it is
+           not added to table's Rows.
+         - Introduced XmlDataElement class to map element (this class itself)
+           and DataRow _always_ . It is impossible to map *all* DataRows 
+           to/from elements only in XmlDataDocument API, since DataRow can 
+           be created separate from it (i.e. by table.NewRow()).
+         - Removed unreliable GetElementsByTagName() dependency from most
+           of the code, since users might create elements that has the same
+           name of any tables (that is possible especially unmapped elements).
+         - Adding/Removing delegates on every event action is unnecessary.
+           Use just flags.
+         - For Load(), just using base.Load() will do most of the job.
+         - Attributes and SimpleContents are not handled properly.
+
+         "Removed" and "Rollback" events are still TODO.
+
 2004-04-13  Atsushi Enomoto <atsushi@ximian.com>
 
        * XmlDataDocument.cs:
index 486e565f73a1263c0ee6940e5f84f31473dd89b6..14dce89ce7918cb2c741b468757f32eac1456a90 100644 (file)
 // Author:
 //     Daniel Morgan <danmorg@sc.rr.com>
 //     Ville Palo <vi64pa@koti.soon.fi>
+//     Atsushi Enomoto <atsushi@ximian.com>
 //
 // (c)copyright 2002 Daniel Morgan
 // (c)copyright 2003 Ville Palo
+// (c)2004 Novell Inc.
 //
 // XmlDataDocument is included within the Mono Class Library.
 //
@@ -27,18 +29,49 @@ using System.Collections;
 using System.Globalization;
 using System.ComponentModel;
 
-namespace System.Xml {
+namespace System.Xml 
+{
 
-       public class XmlDataDocument : XmlDocument {
+       public class XmlDataDocument : XmlDocument 
+       {
+               internal class XmlDataElement : XmlElement
+               {
+                       DataRow row;
+
+                       internal XmlDataElement (DataRow row, string prefix, string localName, string ns, XmlDataDocument doc)
+                               : base (prefix, localName, ns, doc)
+                       {
+                               this.row = row;
+                               // Embed row ID only when the element is mapped to
+                               // certain DataRow.
+                               if (row != null) {
+                                       row.XmlRowID = doc.dataRowID;
+                                       doc.dataRowIDList.Add (row.XmlRowID);
+                                       // It should not be done here. The node is detached
+                                       // dt.Rows.Add (tempRow);
+                                       doc.dataRowID++;
+                                       doc.mappedRows.Add (row, this);
+                               }
+                       }
+
+                       internal DataRow DataRow {
+                               get { return row; }
+                       }
+               }
 
                #region Fields
 
                private DataSet dataSet;
-               private bool isReadOnly = false;
 
                private int dataRowID = 1;
                private ArrayList dataRowIDList = new ArrayList ();
 
+               private Hashtable mappedRows = new Hashtable ();
+
+               // this keeps whether table change events should be handles
+               private bool raiseDataSetEvents = true;
+               private bool raiseDocumentEvents = true;
+
                // this is needed for inserting new row to datatable via xml
                private Hashtable TempTable = new Hashtable ();
 
@@ -74,36 +107,30 @@ namespace System.Xml {
                        this.dataSet = dataset;
                        this.dataSet._xmlDataDocument = this;
 
-                       XmlReader xmlReader = new XmlTextReader (new StringReader (dataSet.GetXml ()));
+                       XmlElement docElem = CreateElement (dataSet.Prefix, dataSet.DataSetName, dataSet.Namespace);
+                       foreach (DataTable dt in dataSet.Tables) {
+                               if (dt.ParentRelations.Count > 0)
+                                       continue; // don't add them here
+                               FillNodeRows (docElem, dt, dt.Rows);
+                       }
 
-                       // Load DataSet's xml-data
-                       base.Load (xmlReader);
-                       xmlReader.Close ();
                        // FIXME: This is required to avoid Load() error when for
                        // example empty DataSet will be filled on Load(), but
                        // not sure if it works correct.
-                       if (DocumentElement.ChildNodes.Count == 0)
-                               RemoveChild (DocumentElement);
+                       if (docElem.ChildNodes.Count > 0)
+                               AppendChild (docElem);
 
-                       foreach (DataTable Table in DataSet.Tables) {
-                               
-                               foreach (DataRow Row in Table.Rows) {
-                                       Row.XmlRowID = dataRowID;
-                                       dataRowIDList.Add (dataRowID);
-                                       dataRowID++;
-                               }
+                       foreach (DataTable dt in dataSet.Tables) {
+                               dt.ColumnChanged += columnChanged;
+                               dt.RowDeleted += rowDeleted;
+                               dt.RowChanged += rowChanged;
                        }
 
                        AddXmlDocumentListeners ();
-
-                       foreach (DataTable Table in dataSet.Tables) {
-                               Table.ColumnChanged += columnChanged;
-                               Table.RowDeleted += rowDeleted;
-                               Table.RowChanged += rowChanged;
-                       }
                }
 
                // bool clone. If we are cloning XmlDataDocument then clone should be true.
+               // FIXME: shouldn't DataSet be mapped to at most one document??
                private XmlDataDocument (DataSet dataset, bool clone)
                {
                        InitDelegateFields ();
@@ -143,6 +170,37 @@ namespace System.Xml {
 
                #region Public Methods
 
+               private void FillNodeRows (XmlElement parent, DataTable dt, ICollection rows)
+               {
+                       foreach (DataRow dr in dt.Rows) {
+                               XmlDataElement el = new XmlDataElement (dr, dt.Prefix, dt.TableName, dt.Namespace, this);
+                               for (int i = 0; i < dt.Columns.Count; i++) {
+                                       DataColumn col = dt.Columns [i];
+                                       string value = dr.IsNull (col) ? String.Empty : dr [col].ToString ();
+                                       switch (col.ColumnMapping) {
+                                       case MappingType.Element:
+                                               XmlElement cel = CreateElement (col.Prefix, col.ColumnName, col.Namespace);
+                                               cel.InnerText = value;
+                                               el.AppendChild (cel);
+                                               break;
+                                       case MappingType.Attribute:
+                                               XmlAttribute a = CreateAttribute (col.Prefix, col.ColumnName, col.Namespace);
+                                               a.Value = value;
+                                               el.SetAttributeNode (a);
+                                               break;
+                                       case MappingType.SimpleContent:
+                                               XmlText t = CreateTextNode (value);
+                                               el.AppendChild (t);
+                                               break;
+                                       // FIXME: What to do for Hidden?
+                                       }
+                               }
+                               foreach (DataRelation rel in dt.ChildRelations)
+                                       FillNodeRows (el, rel.ChildTable, dr.GetChildRows (rel));
+                               parent.AppendChild (el);
+                       }
+               }
+
                [MonoTODO]
                public override XmlNode CloneNode(bool deep) 
                {
@@ -170,87 +228,43 @@ namespace System.Xml {
                public override XmlElement CreateElement(
                         string prefix, string localName, string namespaceURI) 
                {
-                       return base.CreateElement (prefix, localName, namespaceURI);
+                       DataTable dt = DataSet.Tables [localName];
+                       DataRow row = dt != null ? dt.NewRow () : null;
+                       if (row != null)
+                               return GetElementFromRow (row);
+                       else
+                               return base.CreateElement (prefix, localName, namespaceURI);
                }
 
                #endregion // overloaded CreateElement Methods
                        
-               // will not be supported
+               // It is not supported in XmlDataDocument
                public override XmlEntityReference CreateEntityReference(string name) 
                {
-                       throw new NotSupportedException();
+                       throw new NotSupportedException ();
                }
                
-               // will not be supported
-               public override XmlElement GetElementById(string elemId) 
+               // It is not supported in XmlDataDocument
+               public override XmlElement GetElementById (string elemId) 
                {
-                       throw new NotSupportedException();
+                       throw new NotSupportedException ();
                }
 
                // get the XmlElement associated with the DataRow
                [MonoTODO ("Exceptions")]
-               public XmlElement GetElementFromRow(DataRow r) 
+               public XmlElement GetElementFromRow (DataRow r) 
                {
-                       if (r.XmlRowID == 0) // datarow was not in xmldatadocument
-                               throw new Exception ();
-
-                       int elementRow = dataRowIDList.IndexOf (r.XmlRowID);
-                       
-                       return (XmlElement)GetElementsByTagName (r.Table.TableName) [elementRow];
+                       return mappedRows [r] as XmlDataElement;
                }
 
                // get the DataRow associated with the XmlElement
                [MonoTODO ("Exceptions")]
                public DataRow GetRowFromElement(XmlElement e)
                {
-                       XmlElement node = e;
-                       if (node == null)
-                               return null;
-
-                       XPathNavigator nodeNavigator = node.CreateNavigator ();
-                       int c  = GetElementsByTagName (node.Name).Count;
-                       
-                       if (c == 0)
-                               return null;
-
-                       XmlNodeList nodeList = GetElementsByTagName (node.Name);
-
-                       int i = 0;
-                       bool isSame = false;
-
-                       while (i < c && !isSame) {
-
-                               XPathNavigator docNavigator = nodeList [i].CreateNavigator ();
-                               isSame = docNavigator.IsSamePosition (nodeNavigator);
-                               docNavigator = nodeList [i].CreateNavigator ();
-                               if (!isSame)
-                                       i++;
-                       }
-
-                       if (!isSame)
-                               return null;
-
-                       if (i >= dataRowIDList.Count)
-                               return null;
-
-                       // now we know rownum                   
-                       int xmlrowid = (int)dataRowIDList [i];
-                       if (xmlrowid <= 0)
+                       XmlDataElement el = e as XmlDataElement;
+                       if (el == null)
                                return null;
-
-                       DataTable dt = DataSet.Tables [node.Name];
-                       DataRow row = null;
-
-                       if (dt == null)
-                               return null;
-
-                       foreach (DataRow r in dt.Rows) {
-                               if (xmlrowid == r.XmlRowID) {
-                                       row = r;
-                               }
-                       }
-
-                       return row;                     
+                       return el.DataRow;
                }
 
                #region overload Load methods
@@ -274,75 +288,11 @@ namespace System.Xml {
 
                        bool OldEC = DataSet.EnforceConstraints;
                        DataSet.EnforceConstraints = false;
-
                        dataSet.Tables.CollectionChanged -= tablesChanged;
 
-                       // For reading xml to XmlDocument
-//                     XmlTextReader textReader = new XmlTextReader (
-//                             reader.BaseURI);
-
-                       // dont listen these events
-                       RemoveXmlDocumentListeners ();
-                       DataTable dt = null;
-
                        base.Load (reader);
-                       reader = new XmlNodeReader (this);
-
-                       if (reader.NodeType != XmlNodeType.Element)
-                               reader.MoveToContent ();
-
-                       // read to next element
-                       while (reader.Read () && reader.NodeType != XmlNodeType.Element);
-
-                       do {
-                               // Find right table from tablecollection
-                               if (DataSet.Tables.Contains (reader.LocalName)) {
-
-                                       dt = DataSet.Tables [reader.LocalName];
-
-                                       // Make sure event handlers are not added twice
-                                       dt.ColumnChanged -= columnChanged;
-                                       dt.ColumnChanged += columnChanged;
-
-                                       dt.RowDeleted -= rowDeleted;
-                                       dt.RowDeleted += rowDeleted;
-                                       
-                                       dt.RowChanged -= rowChanged;
-                                       dt.RowChanged += rowChanged;
-                               }
-                               else
-                                       continue;
-
-                               // Read rows to table
-                               DataRow tempRow = dt.NewRow ();
-                               while ((reader.NodeType != XmlNodeType.EndElement ||
-                                       reader.Name != dt.TableName) && reader.Read()) {
-                                       
-                                       switch (reader.NodeType) {
-                                               
-                                       case XmlNodeType.Element:
-                                               // Add column to DataRow
-                                               LoadRow (reader, ref tempRow);
-                                               break;
-                                       default:
-                                               break;
-                                       }                       
-                               }
-
-                               // Every row must have unique id.
-                               tempRow.XmlRowID = dataRowID;
-                               dataRowIDList.Add (dataRowID);
-                               dt.Rows.Add (tempRow);
-                               dataRowID++;                                    
-                               
-                               
-                       } while (reader.Read ());
-
-//                     base.Load (textReader);
-//                     textReader.Close ();
 
                        DataSet.EnforceConstraints = OldEC;
-                       AddXmlDocumentListeners ();
                        dataSet.Tables.CollectionChanged += tablesChanged;
                }
                
@@ -362,6 +312,8 @@ namespace System.Xml {
 
                private void OnNodeChanging (object sender, XmlNodeChangedEventArgs args)
                {
+                       if (!this.raiseDocumentEvents)
+                               return;
                        if (DataSet.EnforceConstraints) 
                                throw new InvalidOperationException (Locale.GetText ("Please set DataSet.EnforceConstraints == false before trying to edit XmlDataDocument using XML operations."));
                }
@@ -370,49 +322,77 @@ namespace System.Xml {
                [MonoTODO]
                private void OnNodeChanged (object sender, XmlNodeChangedEventArgs args)
                {
-
-                       if (args.Node == null)
+                       if (!raiseDocumentEvents)
                                return;
+                       bool escapedRaiseDataSetEvents = raiseDataSetEvents;
+                       raiseDataSetEvents = false;
+                       try {
 
-                       DataRow row = GetRowFromElement ((XmlElement)args.Node.ParentNode.ParentNode);
+                               if (args.Node == null)
+                                       return;
 
-                       if (row == null)
-                               return;
+                               DataRow row = GetRowFromElement ((XmlElement)args.Node.ParentNode.ParentNode);
 
-                       if (!row.Table.Columns.Contains (args.Node.ParentNode.Name))
-                               return;
+                               if (row == null)
+                                       return;
+
+                               if (!row.Table.Columns.Contains (args.Node.ParentNode.Name))
+                                       return;
 
-                       row.Table.ColumnChanged -= columnChanged;
+                               row.Table.ColumnChanged -= columnChanged;
 
-                       if (row [args.Node.ParentNode.Name].ToString () != args.Node.InnerText)         
-                               row [args.Node.ParentNode.Name] = args.Node.InnerText;          
+                               if (row [args.Node.ParentNode.Name].ToString () != args.Node.InnerText)         
+                                       row [args.Node.ParentNode.Name] = args.Node.InnerText;          
 
-                       row.Table.ColumnChanged += columnChanged;
+                               row.Table.ColumnChanged += columnChanged;
+                       } finally {
+                               raiseDataSetEvents = escapedRaiseDataSetEvents;
+                       }
                }
 
+               private void OnNodeRemoving (object sender, XmlNodeChangedEventArgs args) 
+               {
+                       if (!this.raiseDocumentEvents)
+                               return;
+                       if (DataSet.EnforceConstraints) 
+                               throw new InvalidOperationException (Locale.GetText ("Please set DataSet.EnforceConstraints == false before trying to edit XmlDataDocument using XML operations."));
+                       
+               }
+               
                // Invoked when XmlNode is removed
                [MonoTODO]
                private void OnNodeRemoved (object sender, XmlNodeChangedEventArgs args)
                {
-                       if (args.OldParent == null)
+                       if (!raiseDocumentEvents)
                                return;
+                       bool escapedRaiseDataSetEvents = raiseDataSetEvents;
+                       raiseDataSetEvents = false;
 
-                       if (!(args.OldParent is XmlElement))
-                               return;
-                       
-                       DataRow row = GetRowFromElement ((XmlElement)args.OldParent);
-                       
-                       if (row == null)
-                               return ;
+                       try {
 
-                       // Dont trig event again
-                       row.Table.ColumnChanged -= columnChanged;
-                       row [args.Node.Name] = null;
-                       row.Table.ColumnChanged += columnChanged;
+                               if (args.OldParent == null)
+                                       return;
+
+                               if (!(args.OldParent is XmlElement))
+                                       return;
+                               
+                               DataRow row = GetRowFromElement ((XmlElement)args.OldParent);
+                               
+                               if (row == null)
+                                       return ;
+
+                               row [args.Node.Name] = null;
+
+                               // FIXME: Should we detach rows and descendants as well?
+                       } finally {
+                               raiseDataSetEvents = escapedRaiseDataSetEvents;
+                       }
                }
 
                private void OnNodeInserting (object sender, XmlNodeChangedEventArgs args) 
                {
+                       if (!this.raiseDocumentEvents)
+                               return;
                        if (DataSet.EnforceConstraints) 
                                throw new InvalidOperationException (Locale.GetText ("Please set DataSet.EnforceConstraints == false before trying to edit XmlDataDocument using XML operations."));
                        
@@ -420,47 +400,97 @@ namespace System.Xml {
                
                private void OnNodeInserted (object sender, XmlNodeChangedEventArgs args)
                {
+                       if (!raiseDocumentEvents)
+                               return;
+                       bool escapedRaiseDataSetEvents = raiseDataSetEvents;
+                       raiseDataSetEvents = false;
+
+                       // If the parent node is mapped to a DataTable, then
+                       // add a DataRow and map the parent element to it.
+                       //
+                       // AND If the child node is mapped to a DataTable, then
+                       // 1. if it is mapped to a DataTable and relation, add
+                       // a new DataRow and map the child element to it.
+                       // 2. if it is mapped to a DataColumn, set the column
+                       // value of the parent DataRow as the child
+
+                       try {
+                               if (! (args.NewParent is XmlElement)) {
+                                       // i.e. adding document element
+                                       foreach (XmlNode table in args.Node.ChildNodes)
+                                               CheckDescendantRelationship (table);
+                                       return;
+                               }
 
-                       // this is table element 
-                       if (DataSet.Tables.Contains (args.NewParent.Name)) {
-
-                               Hashtable ht = null;
-                               if (TempTable.ContainsKey (args.NewParent.Name)) {
+                               DataRow row = GetRowFromElement (args.NewParent as XmlElement);
+                               if (row == null) {
+                                       // That happens only when adding table to existing DocumentElement (aka DataSet element)
+                                       if (args.NewParent == DocumentElement)
+                                               CheckDescendantRelationship (args.Node);
+                                       return;
+                               }
 
-                                       // if TempTable contains table name, get it and remove it from hashtable
-                                       // so we can later add it :)
-                                       ht = TempTable [args.NewParent.Name] as Hashtable;
-                                       TempTable.Remove (args.NewParent.Name);
+                               XmlAttribute attr = args.Node as XmlAttribute;
+                               if (attr != null) { // fill attribute value
+                                       DataColumn col = row.Table.Columns [attr.LocalName];
+                                       if (col != null)
+                                               row [col] = args.Node.Value;
+                               } else {
+                                       DataRow childRow = GetRowFromElement (args.Node as XmlElement);
+                                       if (childRow != null) {
+                                               // child might be a table row.
+                                               // I might be impossible to set parent
+                                               // since either of them might be detached
+                                               if (childRow.RowState != DataRowState.Detached && row.RowState != DataRowState.Detached) {
+                                                       FillRelationship (row, childRow, args.NewParent, args.Node);
+                                               }
+                                       } else {
+                                               // child might be a column
+                                               DataColumn col = row.Table.Columns [args.Node.LocalName];
+                                               if (col != null)
+                                                       row [col] = args.Node.InnerText;
+                                       }
                                }
-                               else 
-                                       ht = new Hashtable ();
+                       } finally {
+                               raiseDataSetEvents = escapedRaiseDataSetEvents;
+                       }
+               }
 
-                               ht.Add (args.Node.Name, args.Node.InnerText);                           
-                               TempTable.Add (args.NewParent.Name, ht);
-                       } 
-                       else if (DataSet.Tables.Contains (args.Node.Name)) {
-                               
-                               // if nodes name is same as some table in the list is is time to 
-                               // add row to datatable
+               private void CheckDescendantRelationship (XmlNode n)
+               {
+                       XmlElement el = n as XmlElement;
+                       DataRow row = GetRowFromElement (el);
+                       if (row == null)
+                               return;
+                       row.Table.Rows.Add (row); // attach
+                       CheckDescendantRelationship (n, row);
+               }
 
-                               DataTable dt = DataSet.Tables [args.Node.Name];
-                               dt.RowChanged -= rowChanged;
+               private void CheckDescendantRelationship (XmlNode p, DataRow row)
+               {
+                       foreach (XmlNode n in p.ChildNodes) {
+                               XmlElement el = n as XmlElement;
+                               if (el == null)
+                                       continue;
+                               DataRow childRow = GetRowFromElement (el);
+                               if (childRow == null)
+                                       continue;
+                               childRow.Table.Rows.Add (childRow);
+                               FillRelationship (row, childRow, p, el);
+                       }
+               }
 
-                               DataRow row = dt.NewRow ();
-                               Hashtable ht = TempTable [args.Node.Name] as Hashtable;
-                               
-                               IDictionaryEnumerator enumerator = ht.GetEnumerator ();
-                               while (enumerator.MoveNext ()) {
-                                       if (dt.Columns.Contains (enumerator.Key.ToString ()))
-                                               row [enumerator.Key.ToString ()] = enumerator.Value.ToString ();
+               private void FillRelationship (DataRow row, DataRow childRow, XmlNode parentNode, XmlNode childNode)
+               {
+                       for (int i = 0; i < childRow.Table.ParentRelations.Count; i++) {
+                               DataRelation rel = childRow.Table.ParentRelations [i];
+                               if (rel.ParentTable == row.Table) {
+                                       childRow.SetParentRow (row);
+                                       break;
                                }
-                               
-                               DataSet.Tables [args.Node.Name].Rows.Add (row);
-                               dt.RowChanged += rowChanged;
-                       } 
-
+                       }
+                       CheckDescendantRelationship (childNode, childRow);
                }
-
                #endregion // DataSet event handlers
 
                #region DataSet event handlers
@@ -468,11 +498,27 @@ namespace System.Xml {
                // If DataTable is added or removed from DataSet
                private void OnDataTableChanged (object sender, CollectionChangeEventArgs eventArgs)
                {
-                       DataTable Table = (DataTable)eventArgs.Element;
-                       if (eventArgs.Action == CollectionChangeAction.Add) {
-                               Table.ColumnChanged += columnChanged;
-                               Table.RowDeleted += rowDeleted;
-                               Table.RowChanged += rowChanged;
+                       if (!raiseDataSetEvents)
+                               return;
+                       bool escapedRaiseDocumentEvents = raiseDocumentEvents;
+                       raiseDocumentEvents = false;
+
+                       try {
+                               DataTable Table = (DataTable)eventArgs.Element;
+                               switch (eventArgs.Action) {
+                               case CollectionChangeAction.Add:
+                                       Table.ColumnChanged += columnChanged;
+                                       Table.RowDeleted += rowDeleted;
+                                       Table.RowChanged += rowChanged;
+                                       break;
+                               case CollectionChangeAction.Remove:
+                                       Table.ColumnChanged -= columnChanged;
+                                       Table.RowDeleted -= rowDeleted;
+                                       Table.RowChanged -= rowChanged;
+                                       break;
+                               }
+                       } finally {
+                               raiseDocumentEvents = escapedRaiseDocumentEvents;
                        }
                }
 
@@ -481,125 +527,219 @@ namespace System.Xml {
                private void OnDataTableColumnChanged(object sender, 
                                                             DataColumnChangeEventArgs eventArgs)
                {
-                       RemoveXmlDocumentListeners ();
-
-                       // row is not yet in datatable
-                       if (eventArgs.Row.XmlRowID == 0)
+                       if (!raiseDataSetEvents)
                                return;
-
-                       // TODO: Here should be some kind of error checking.
-                       GetElementsByTagName (eventArgs.Column.ColumnName) [dataRowIDList.IndexOf (
-                               eventArgs.Row.XmlRowID)].InnerText = eventArgs.ProposedValue.ToString ();
-                       
-                       AddXmlDocumentListeners ();
+                       bool escapedRaiseDocumentEvents = raiseDocumentEvents;
+                       raiseDocumentEvents = false;
+
+                       try {
+                               DataRow row = eventArgs.Row;
+                               XmlElement el = GetElementFromRow (row);
+                               if (el == null)
+                                       return;
+                               DataColumn col = eventArgs.Column;
+                               string value = row.IsNull (col) ? String.Empty : row [col].ToString ();
+                               switch (col.ColumnMapping) {
+                               case MappingType.Attribute:
+                                       el.SetAttribute (col.ColumnName, col.Namespace, value);
+                                       break;
+                               case MappingType.SimpleContent:
+                                       el.InnerText = value;
+                                       break;
+                               case MappingType.Element:
+                                       bool exists = false;
+                                       for (int i = 0; i < el.ChildNodes.Count; i++) {
+                                               XmlElement c = el.ChildNodes [i] as XmlElement;
+                                               if (c != null && c.LocalName == col.ColumnName && c.NamespaceURI == col.Namespace) {
+                                                       exists = true;
+                                                       c.InnerText = value;
+                                                       break;
+                                               }
+                                       }
+                                       if (!exists) {
+                                               XmlElement cel = CreateElement (col.Prefix, col.ColumnName, col.Namespace);
+                                               cel.InnerText = value;
+                                               el.AppendChild (cel);
+                                       }
+                                       break;
+                               // FIXME: how to handle hidden?
+                               }
+                       } finally {
+                               raiseDocumentEvents = escapedRaiseDocumentEvents;
+                       }
                }
        
                [MonoTODO]
                private void OnDataTableRowDeleted(object sender,
                                                          DataRowChangeEventArgs eventArgs)
                {
+                       if (!raiseDataSetEvents)
+                               return;
+                       bool escapedRaiseDocumentEvents = raiseDocumentEvents;
+                       raiseDocumentEvents = false;
 
-                       DataRow deletedRow = null;
-                       deletedRow = eventArgs.Row;
+                       try {
+                               // This code is obsolete XmlDataDocument one
 
-                       if (eventArgs.Row.XmlRowID == 0)
-                               return;
-                       
-                       int rowIndex = dataRowIDList.IndexOf (eventArgs.Row.XmlRowID);
-                       if (rowIndex == -1 || eventArgs.Row.XmlRowID == 0 || 
-                           rowIndex > GetElementsByTagName (deletedRow.Table.TableName).Count - 1)
-                               return;
-                       
-                       // Remove element from xmldocument and row indexlist
-                       // FIXME: this is one way to do this, but i hope someday i find out much better way.
-                       XmlNode p = GetElementsByTagName (deletedRow.Table.TableName) [rowIndex].ParentNode;
-                       if (p != null) {
-                               p.RemoveChild (GetElementsByTagName (deletedRow.Table.TableName) [rowIndex]);
-                               dataRowIDList.RemoveAt (rowIndex);
+                               DataRow deletedRow = null;
+                               deletedRow = eventArgs.Row;
+
+                               if (eventArgs.Row.XmlRowID == 0)
+                                       return;
+                               
+                               int rowIndex = dataRowIDList.IndexOf (eventArgs.Row.XmlRowID);
+                               if (rowIndex == -1 || eventArgs.Row.XmlRowID == 0 || 
+                               rowIndex > GetElementsByTagName (deletedRow.Table.TableName).Count - 1)
+                                       return;
+                               
+                               // Remove element from xmldocument and row indexlist
+                               // FIXME: this is one way to do this, but i hope someday i find out much better way.
+                               XmlNode p = GetElementsByTagName (deletedRow.Table.TableName) [rowIndex].ParentNode;
+                               if (p != null) {
+                                       p.RemoveChild (GetElementsByTagName (deletedRow.Table.TableName) [rowIndex]);
+                                       dataRowIDList.RemoveAt (rowIndex);
+                               }
+                       } finally {
+                               raiseDocumentEvents = escapedRaiseDocumentEvents;
                        }
                }
                
                [MonoTODO]
                private void OnDataTableRowChanged(object sender, DataRowChangeEventArgs eventArgs)
                {
-                       switch (eventArgs.Action) {
+                       if (!raiseDataSetEvents)
+                               return;
+                       bool escapedRaiseDocumentEvents = raiseDocumentEvents;
+                       raiseDocumentEvents = false;
+                       try {
+
+                               switch (eventArgs.Action) {
 
-                               case DataRowAction.Delete:
+                               case DataRowAction.Delete:
                                        OnDataTableRowDeleted (sender, eventArgs);
                                        break;
 
-                               case DataRowAction.Add:
+                               case DataRowAction.Add:
                                        OnDataTableRowAdded (eventArgs);
                                        break;
 
-                               case DataRowAction.Rollback:
+                               case DataRowAction.Rollback:
                                        OnDataTableRowRollback (eventArgs);
                                        break;
-                               default:
+                               default:
                                        break;
-                       } 
+                               } 
+                       } finally {
+                               raiseDocumentEvents = escapedRaiseDocumentEvents;
+                       }
                }
 
                // Added
                [MonoTODO]
                private void OnDataTableRowAdded (DataRowChangeEventArgs args)
                {
-                       RemoveXmlDocumentListeners ();
-
-                       // If XmlRowID is != 0 then it is already added
-                       if (args.Row.XmlRowID != 0)
+                       if (!raiseDataSetEvents)
                                return;
-                       
-                       // Create row element. Row's name same as TableName                                     
-                       DataRow row = args.Row;
-                       row.XmlRowID = dataRowID;
-                       dataRowIDList.Add (dataRowID);
-                       dataRowID++;
-
-                       if (DocumentElement == null)
-                               this.AppendChild (CreateElement (DataSet.DataSetName));
-
-                       XmlElement element = CreateElement (args.Row.Table.TableName);
-                       DocumentElement.AppendChild (element);
-
-                       XmlElement rowElement = null;
-
-                       for (int i = 0; i < row.Table.Columns.Count; i++) {
+                       bool escapedRaiseDocumentEvents = raiseDocumentEvents;
+                       raiseDocumentEvents = false;
+
+                       try {
+
+                               // Create row element. Row's name same as TableName                                     
+                               DataRow row = args.Row;
+
+                               // create document element if it does not exist
+                               if (DocumentElement == null)
+                                       this.AppendChild (CreateElement (DataSet.DataSetName));
+
+                               DataTable table= args.Row.Table;
+                               XmlElement element = GetElementFromRow (row);
+                               if (element == null)
+                                       element = CreateElement (table.Prefix, table.TableName, table.Namespace);
+                               if (element.ParentNode == null) {
+                                       // parent is not always DocumentElement.
+                                       XmlElement parent = null;
+
+                                       if (table.ParentRelations.Count > 0) {
+                                               for (int i = 0; i < table.ParentRelations.Count; i++) {
+                                                       DataRelation rel = table.ParentRelations [i];
+                                                       DataRow parentRow = row.GetParentRow (rel);
+                                                       if (parentRow == null)
+                                                               continue;
+                                                       parent = GetElementFromRow (parentRow);
+                                               }
+                                       }
+
+                                       // The row might be orphan. In such case, the 
+                                       // element is appended to DocumentElement.
+                                       if (parent == null)
+                                               parent = DocumentElement;
+                                       parent.AppendChild (element);
+                               }
+                       } finally {                     
+                               raiseDocumentEvents = escapedRaiseDocumentEvents;
+                       }
+               }
 
-                               rowElement = CreateElement (row.Table.Columns [i].ColumnName);
-                               object v = row [i];
-                               rowElement.InnerText = v != null ? v.ToString () : String.Empty;
-                               element.AppendChild (rowElement);
+               private void FillNodeChildrenFromRow (DataRow row, XmlElement element)
+               {
+                       DataTable table = row.Table;
+                       // fill columns for the row
+                       for (int i = 0; i < table.Columns.Count; i++) {
+                               DataColumn col = table.Columns [i];
+                               string value = row.IsNull (col) ? String.Empty : row [col].ToString ();
+                               switch (col.ColumnMapping) {
+                               case MappingType.Element:
+                                       XmlElement el = CreateElement (col.Prefix, col.ColumnName, col.Namespace);
+                                       el.InnerText = value;
+                                       element.AppendChild (el);
+                                       break;
+                               case MappingType.Attribute:
+                                       XmlAttribute attr = CreateAttribute (col.Prefix, col.ColumnName, col.Namespace);
+                                       attr.Value = value;
+                                       element.SetAttributeNode (attr);
+                                       break;
+                               case MappingType.SimpleContent:
+                                       XmlText text = CreateTextNode (value);
+                                       element.AppendChild (text);
+                                       break;
+                               // FIXME: how to handle hidden?
+                               }
                        }
-                       
-                       AddXmlDocumentListeners ();
                }
 
                // Rollback
                [MonoTODO]
                private void OnDataTableRowRollback (DataRowChangeEventArgs args)
                {
-                       RemoveXmlDocumentListeners ();
+                       if (!raiseDataSetEvents)
+                               return;
+                       bool escapedRaiseDocumentEvents = raiseDocumentEvents;
+                       raiseDocumentEvents = false;
 
-                       DataRow row = args.Row;                 
-                       int rowid = dataRowIDList.IndexOf (row.XmlRowID);
+                       try {
+                               // This code is obsolete XmlDataDocument one.
 
-                       // find right element in xmldocument
-                       if (rowid == 0 || rowid >= GetElementsByTagName (row.Table.TableName).Count)
-                               return;
+                               DataRow row = args.Row;                 
+                               int rowid = dataRowIDList.IndexOf (row.XmlRowID);
 
-                       XmlNode node = GetElementsByTagName (row.Table.TableName) [rowid];
-                       
-                       int rowValue = 0;
-                       for (int i = 0; i < node.ChildNodes.Count; i++) {
+                               // find right element in xmldocument
+                               if (rowid == 0 || rowid >= GetElementsByTagName (row.Table.TableName).Count)
+                                       return;
+
+                               XmlNode node = GetElementsByTagName (row.Table.TableName) [rowid];
                                
-                               XmlNode child = node.ChildNodes [i];
-                               if (child.NodeType != XmlNodeType.Whitespace) {
-                                       child.InnerText = (string)row [rowValue++];
+                               int rowValue = 0;
+                               for (int i = 0; i < node.ChildNodes.Count; i++) {
+                                       
+                                       XmlNode child = node.ChildNodes [i];
+                                       if (child.NodeType != XmlNodeType.Whitespace) {
+                                               child.InnerText = (string)row [rowValue++];
+                                       }
                                }
+                       } finally {
+                               raiseDocumentEvents = escapedRaiseDocumentEvents;
                        }
-
-                       AddXmlDocumentListeners ();
                }
 
                #endregion // DataSet event handlers
@@ -612,35 +752,14 @@ namespace System.Xml {
                        rowChanged = new DataRowChangeEventHandler (OnDataTableRowChanged);
                        tablesChanged = new CollectionChangeEventHandler (OnDataTableChanged);
                }
-
-               [MonoTODO]
-               private void LoadRow (XmlReader reader, ref DataRow row)
-               {                       
-                       // dt.Rows.Add (LoadRow (reader, dt.NewRow ()));
-                       // This method returns DataRow filled by values
-                       // from xmldocument
-                       string rowname = reader.Name;
-                       string column = "";
-                       
-                       if (reader.NodeType == XmlNodeType.Element)
-                               column = reader.Name;
-                       
-                       reader.Read ();
-                       
-                       if (reader.NodeType == XmlNodeType.Text) {
-                               
-                               string val = reader.Value;
-                               if (row.Table.Columns.Contains (column))
-                                       row [column] = val;
-                       }
-               }
                
                private void RemoveXmlDocumentListeners ()
                {
                        this.NodeInserting -= new XmlNodeChangedEventHandler (OnNodeInserting);
                        this.NodeInserted -= new XmlNodeChangedEventHandler (OnNodeInserted);
-                       this.NodeChanged -= new XmlNodeChangedEventHandler (OnNodeChanged);
                        this.NodeChanging -= new XmlNodeChangedEventHandler (OnNodeChanging);
+                       this.NodeChanged -= new XmlNodeChangedEventHandler (OnNodeChanged);
+                       this.NodeRemoving -= new XmlNodeChangedEventHandler (OnNodeRemoving);
                        this.NodeRemoved -= new XmlNodeChangedEventHandler (OnNodeRemoved);
                }
 
@@ -648,8 +767,9 @@ namespace System.Xml {
                {
                        this.NodeInserting += new XmlNodeChangedEventHandler (OnNodeInserting);
                        this.NodeInserted += new XmlNodeChangedEventHandler (OnNodeInserted);
-                       this.NodeChanged += new XmlNodeChangedEventHandler (OnNodeChanged);
                        this.NodeChanging += new XmlNodeChangedEventHandler (OnNodeChanging);
+                       this.NodeChanged += new XmlNodeChangedEventHandler (OnNodeChanged);
+                       this.NodeRemoving += new XmlNodeChangedEventHandler (OnNodeRemoving);
                        this.NodeRemoved += new XmlNodeChangedEventHandler (OnNodeRemoved);
                }
                #endregion // Private methods