// 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.
//
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
using System;
using System.Data;
using System.IO;
using System.Text;
using System.Xml.XPath;
using System.Collections;
+using System.Globalization;
+using System.ComponentModel;
+
+namespace System.Xml
+{
+
+ public class XmlDataDocument : XmlDocument
+ {
+ // Should we consider overriding CloneNode() ? By default
+ // base CloneNode() will be invoked and thus no DataRow conflict
+ // would happen, that sounds the best (that means, no mapped
+ // DataRow will be provided).
+ 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.DataElement = this;
+ 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++;
+ }
+ }
-namespace System.Xml {
-
- public class XmlDataDocument : XmlDocument {
+ internal DataRow DataRow {
+ get { return row; }
+ }
+ }
#region Fields
private DataSet dataSet;
- private bool isReadOnly = false;
private int dataRowID = 1;
private ArrayList dataRowIDList = new ArrayList ();
+ // 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 ();
+
+ DataColumnChangeEventHandler columnChanged;
+ DataRowChangeEventHandler rowDeleted;
+ DataRowChangeEventHandler rowChanged;
+ CollectionChangeEventHandler tablesChanged;
#endregion // Fields
#region Constructors
- public XmlDataDocument() {
+ public XmlDataDocument ()
+ {
+ InitDelegateFields ();
+
dataSet = new DataSet();
- this.NodeChanged += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
- //this.NodeChanging += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
- //this.NodeInserted += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
- //this.NodeRemoved += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
- }
+ dataSet._xmlDataDocument = this;
+ dataSet.Tables.CollectionChanged += tablesChanged;
- public XmlDataDocument(DataSet dataset) {
- this.dataSet = dataset;
- this.NodeChanged += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
+ AddXmlDocumentListeners ();
+ DataSet.EnforceConstraints = false;
}
- #endregion // Constructors
+ public XmlDataDocument (DataSet dataset)
+ {
+ if (dataset == null)
+ throw new ArgumentException ("Parameter dataset cannot be null.");\r
+ if (dataset._xmlDataDocument != null)\r
+ throw new ArgumentException ("DataSet cannot be associated with two or more XmlDataDocument.");\r
- #region Public Properties
+ InitDelegateFields ();
- public override string BaseURI {
- [MonoTODO]
- get {
- // TODO: why are we overriding?
- return base.BaseURI;
- }
- }
+ this.dataSet = dataset;
+ this.dataSet._xmlDataDocument = this;
- public DataSet DataSet {
- [MonoTODO]
- get {
- return dataSet;
+ 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);
}
- }
- // override inheritted method from XmlDocument
- public override string InnerXml {
- [MonoTODO]
- get {
- throw new NotImplementedException();
- }
-
- [MonoTODO]
- set {
- throw new NotImplementedException();
- }
- }
+ // This seems required to avoid Load() error when for
+ // example empty DataSet will be filled on Load().
+ if (docElem.ChildNodes.Count > 0)
+ AppendChild (docElem);
- public override bool IsReadOnly {
- [MonoTODO]
- get {
- return isReadOnly;
+ foreach (DataTable dt in dataSet.Tables) {
+ dt.ColumnChanged += columnChanged;
+ dt.RowDeleted += rowDeleted;
+ dt.RowChanged += rowChanged;
}
+ AddXmlDocumentListeners ();
}
- // Item indexer
- public override XmlElement this[string name] {
- [MonoTODO]
- get {
- throw new NotImplementedException();
- }
- }
+ // 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 ();
- // Item indexer
- public override XmlElement this[string localname, string ns] {
- [MonoTODO]
- get {
- throw new NotImplementedException();
- }
- }
+ this.dataSet = dataset;
+ this.dataSet._xmlDataDocument = this;
- public override string LocalName {
- [MonoTODO]
- get {
- throw new NotImplementedException();
+ foreach (DataTable Table in DataSet.Tables) {
+
+ foreach (DataRow Row in Table.Rows) {
+ Row.XmlRowID = dataRowID;
+ dataRowIDList.Add (dataRowID);
+ dataRowID++;
+ }
}
- }
- public override string Name {
- [MonoTODO]
- get {
- throw new NotImplementedException();
+ AddXmlDocumentListeners ();
+
+ foreach (DataTable Table in dataSet.Tables) {
+ Table.ColumnChanged += columnChanged;
+ Table.RowDeleted += rowDeleted;
+ Table.RowChanged += rowChanged;
}
}
- public override XmlDocument OwnerDocument {
- [MonoTODO]
+ #endregion // Constructors
+
+ #region Public Properties
+
+ public DataSet DataSet {
get {
- return null;
+ return dataSet;
}
}
#region Public Methods
- [MonoTODO]
- public override XmlNode CloneNode(bool deep)
+ private void FillNodeRows (XmlElement parent, DataTable dt, ICollection rows)
{
- throw new NotImplementedException();
+ 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;
+ }
+ }
+ foreach (DataRelation rel in dt.ChildRelations)
+ FillNodeRows (el, rel.ChildTable, dr.GetChildRows (rel));
+ parent.AppendChild (el);
+ }
+ }
+
+ public override XmlNode CloneNode (bool deep)
+ {
+ XmlDataDocument Document;
+ if (deep)
+ Document = new XmlDataDocument (DataSet.Copy (), true);
+ else
+ Document = new XmlDataDocument (DataSet.Clone (), true);
+
+ Document.RemoveXmlDocumentListeners ();
+
+ Document.PreserveWhitespace = PreserveWhitespace;
+ if (deep) {
+ foreach(XmlNode n in ChildNodes)
+ Document.AppendChild (Document.ImportNode (n, deep));
+ }
+
+ Document.AddXmlDocumentListeners ();
+
+ return Document;
}
#region overloaded CreateElement methods
- [MonoTODO]
- public override XmlElement CreateElement(string prefix,
- string localName, string namespaceURI)
+ public override XmlElement CreateElement(
+ string prefix, string localName, string namespaceURI)
{
- if ((localName == null) || (localName == String.Empty))
- throw new ArgumentException ("The local name for elements or attributes cannot be null" +
- "or an empty string.");
- string pref = prefix != null ? prefix : String.Empty;
- return base.CreateElement (pref, localName, namespaceURI != null ? namespaceURI : String.Empty);
-
+ 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
- public XmlElement GetElementFromRow(DataRow r)
+ public XmlElement GetElementFromRow (DataRow r)
{
- throw new NotImplementedException();
+ return r.DataElement;
}
// get the DataRow associated with the XmlElement
- [MonoTODO]
- public DataRow GetRowFromElement(XmlElement e)
+ public DataRow GetRowFromElement (XmlElement e)
{
- throw new NotImplementedException();
+ XmlDataElement el = e as XmlDataElement;
+ if (el == null)
+ return null;
+ return el.DataRow;
}
#region overload Load methods
Load (new XmlTextReader (txtReader));
}
- public override void Load(XmlReader reader) {
-
- DataTable dt = null;
+ public override void Load (XmlReader reader)
+ {
+ if (DocumentElement != null)
+ throw new InvalidOperationException ("XmlDataDocument does not support multi-time loading. New XmlDadaDocument is always required.");
- // For reading xml to XmlDocument
- XmlTextReader textReader = new XmlTextReader (
- reader.BaseURI);
+ bool OldEC = DataSet.EnforceConstraints;
+ DataSet.EnforceConstraints = false;
+ dataSet.Tables.CollectionChanged -= tablesChanged;
- if (reader.NodeType != XmlNodeType.Element)
- reader.MoveToContent ();
+ base.Load (reader);
- // TODO: Findout which exception should be throwen
- if (reader.NodeType != XmlNodeType.Element)
- throw new Exception ();
+ DataSet.EnforceConstraints = OldEC;
+ dataSet.Tables.CollectionChanged += tablesChanged;
+ }
+
+ #endregion // overloaded Load methods
+ #endregion // Public Methods
- if (dataSet.DataSetName != reader.Name)
- throw new Exception ();
+ #region Protected Methods
- // read to next element
- while (reader.Read () && reader.NodeType != XmlNodeType.Element);
+ [MonoTODO ("Create optimized XPathNavigator")]
+ protected override XPathNavigator CreateNavigator(XmlNode node) {
+ return base.CreateNavigator (node);
+ }
+
+ #endregion // Protected Methods
+
+ #region XmlDocument event handlers
+
+ 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."));
+ }
+
+ // Invoked when XmlNode is changed colum is changed
+ private void OnNodeChanged (object sender, XmlNodeChangedEventArgs args)
+ {
+ if (!raiseDocumentEvents)
+ return;
+ bool escapedRaiseDataSetEvents = raiseDataSetEvents;
+ raiseDataSetEvents = false;
+ try {
- do {
+ if (args.Node == null)
+ return;
+ DataRow row = GetRowFromElement ((XmlElement)args.Node.ParentNode.ParentNode);
- // Find right table from tablecollection
- for (int i = 0; i < DataSet.Tables.Count && dt == null; i++) {
+ if (row == null)
+ return;
- if (reader.Name == DataSet.Tables [i].TableName) {
+ if (!row.Table.Columns.Contains (args.Node.ParentNode.Name))
+ return;
- dt = DataSet.Tables [i];
- dt.ColumnChanged += new DataColumnChangeEventHandler (OnDataTableColumnChanged);
- dt.RowDeleted += new DataRowChangeEventHandler (OnDataTableRowDeleted);
- dt.RowChanged += new DataRowChangeEventHandler (OnDataTableRowChanged);
- }
+ if (row [args.Node.ParentNode.Name].ToString () != args.Node.InnerText) {
+ DataColumn col = row.Table.Columns [args.Node.ParentNode.Name];
+ row [col] = StringToObject (col.DataType, args.Node.InnerText);
}
+
+ } 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
+ private void OnNodeRemoved (object sender, XmlNodeChangedEventArgs args)
+ {
+ if (!raiseDocumentEvents)
+ return;
+ bool escapedRaiseDataSetEvents = raiseDataSetEvents;
+ raiseDataSetEvents = false;
+
+ try {
+ if (args.OldParent == null)
+ return;
+
+ XmlElement oldParentElem = args.OldParent as XmlElement;
+ if (oldParentElem == null)
+ return;
- // TODO: Findout what kind of exception
- if (dt == null)
- throw new Exception (); // there were no correct table
-
- // 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;
- }
+ // detach child row (if exists)
+ XmlElement childElem = args.Node as XmlElement;
+ if (childElem != null) {
+ DataRow childRow = GetRowFromElement (childElem);
+ if (childRow != null)
+ childRow.Table.Rows.Remove (childRow);
}
- // Every row must have unique id.
- tempRow.XmlRowID = dataRowID;
- dataRowIDList.Add (dataRowID);
- dt.Rows.Add (tempRow);
- dataRowID++;
-
+ DataRow row = GetRowFromElement (oldParentElem);
- } while (reader.Read ());
+ if (row == null)
+ return ;
- base.Load (textReader);
- }
-
- #endregion // overloaded Load methods
+ row [args.Node.Name] = null;
- [MonoTODO]
- public override void WriteContentTo(XmlWriter xw) {
- base.WriteContentTo (xw);
+ } finally {
+ raiseDataSetEvents = escapedRaiseDataSetEvents;
+ }
}
- [MonoTODO]
- public override void WriteTo(XmlWriter w) {
- base.WriteTo (w);
+ 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."));
+
}
+
+ 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;
+ }
- #endregion // Public Methods
-
- #region Protected Methods
+ 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;
+ }
- //FIXME: how do you handle this?
- //[MonoTODO]
- //protected internal override XPathNavigator CreateNavigator(XmlNode node) {
- // throw new NotImplementedException();
- //}
+ XmlAttribute attr = args.Node as XmlAttribute;
+ if (attr != null) { // fill attribute value
+ DataColumn col = row.Table.Columns [attr.LocalName];
+ if (col != null)
+ row [col] = StringToObject (col.DataType, 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 if (args.Node.NodeType == XmlNodeType.Element) {
+ // child element might be a column
+ DataColumn col = row.Table.Columns [args.Node.LocalName];
+ if (col != null)
+ row [col] = StringToObject (col.DataType, args.Node.InnerText);
+ } else if (args.Node is XmlCharacterData) {
+ if (args.Node.NodeType != XmlNodeType.Comment) {
+ for (int i = 0; i < row.Table.Columns.Count; i++) {
+ DataColumn col = row.Table.Columns [i];
+ if (col.ColumnMapping == MappingType.SimpleContent)
+ row [col] = StringToObject (col.DataType, args.Node.Value);
+ }
+ }
+ }
+ }
+ } finally {
+ raiseDataSetEvents = escapedRaiseDataSetEvents;
+ }
+ }
- [MonoTODO]
- public new XPathNavigator CreateNavigator() {
- throw new NotImplementedException();
+ 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);
}
- #endregion // Protected Methods
-
- #region DataSet event handlers
+ 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);
+ }
+ }
- // this changed datatable values when some of xmldocument elements is changed
- private void OnXmlDataColumnChanged (object sender, XmlNodeChangedEventArgs args)
+ private void FillRelationship (DataRow row, DataRow childRow, XmlNode parentNode, XmlNode childNode)
{
- int i = 0;
- XPathNavigator nodeNavigator = args.Node.CreateNavigator ();
- int c = GetElementsByTagName (args.Node.Name).Count;
+ 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;
+ }
+ }
+ CheckDescendantRelationship (childNode, childRow);
+ }
+ #endregion // DataSet event handlers
- // FIXME: I dont know which way it should be but this work on linux.
- // could be also GetElementsByTagName (args.OldParent.Name) []
- XmlNodeList nodeList = GetElementsByTagName (args.Node.Name);
-
+ #region DataSet event handlers
- bool isSame = false;
-
- // Find right row
- while ((i < c) && !isSame ) {
-
- XPathNavigator docNavigator = nodeList [i].CreateNavigator ();
- isSame = docNavigator.IsSamePosition (nodeNavigator);
- docNavigator = nodeList [i].CreateNavigator ();
- i++;
- }
-
- // if there wasnt such row it is just added and we dont need to care about it
- if (!isSame)
+ // If DataTable is added or removed from DataSet
+ private void OnDataTableChanged (object sender, CollectionChangeEventArgs eventArgs)
+ {
+ if (!raiseDataSetEvents)
return;
-
- // now we know rownum
- int xmlrowid = (int)dataRowIDList [i];
-
- DataTable dt = DataSet.Tables [args.OldParent.Name];
- foreach (DataRow r in dt.Rows) {
- if (xmlrowid == r.XmlRowID) {
-
- // change value only when have to.
- if ((string)r [args.Node.Name] != (string)args.Node.InnerText)
- r [args.Node.Name] = args.Node.InnerText;
+ 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;
+ }
}
- [MonoTODO]
- private void OnDataTableColumnChanged(object sender,
+ // If column has changed
+ private void OnDataTableColumnChanged (object sender,
DataColumnChangeEventArgs eventArgs)
{
- // 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.ToString ()) [dataRowIDList.IndexOf (
- eventArgs.Row.XmlRowID)].InnerText = (string)eventArgs.ProposedValue;
+ 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,
+ private void OnDataTableRowDeleted (object sender,
DataRowChangeEventArgs eventArgs)
{
- DataRow deletedRow = null;
- deletedRow = eventArgs.Row;
-
- if (eventArgs.Row.XmlRowID == 0)
+ if (!raiseDataSetEvents)
return;
+ bool escapedRaiseDocumentEvents = raiseDocumentEvents;
+ raiseDocumentEvents = false;
+
+ try {
+ // This code is obsolete XmlDataDocument one
+
+ DataRow deletedRow = null;
+ deletedRow = eventArgs.Row;
- int rowIndex = dataRowIDList.IndexOf (eventArgs.Row.XmlRowID);
+ XmlElement el = GetElementFromRow (eventArgs.Row);
+ if (el == null)
+ return;
- // Remove element from xmldocument and row indexlist
- GetElementsByTagName (deletedRow.Table.TableName) [rowIndex].RemoveAll ();
- dataRowIDList.RemoveAt (rowIndex);
+ el.ParentNode.RemoveChild (el);
+ } finally {
+ raiseDocumentEvents = escapedRaiseDocumentEvents;
+ }
}
- [MonoTODO]
- private void OnDataTableRowChanged(object sender, DataRowChangeEventArgs eventArgs)
+ [MonoTODO ("Need to handle hidden columns? - see comments on each private method")]
+ 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]
+ // Added - see FillNodeChildrenFromRow comment
private void OnDataTableRowAdded (DataRowChangeEventArgs args)
{
- // 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;
- dataRowID++;
- 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].ToString ());
- rowElement.InnerText = (string)row [i];
- 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?
+ }
}
}
// Rollback
- [MonoTODO]
+ [MonoTODO ("It does not look complete.")]
private void OnDataTableRowRollback (DataRowChangeEventArgs args)
{
- DataRow row = args.Row;
-
- int rowid = dataRowIDList.IndexOf (row.XmlRowID);
-
- // find right element in xmldocument
- XmlNode node = GetElementsByTagName (row.Table.TableName) [rowid];
-
- 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++];
-
+ if (!raiseDataSetEvents)
+ return;
+ bool escapedRaiseDocumentEvents = raiseDocumentEvents;
+ raiseDocumentEvents = false;
+
+ try {
+ DataRow r = args.Row;
+ XmlElement el = GetElementFromRow (r);
+ if (el == null)
+ return;
+ DataTable tab = r.Table;
+ ArrayList al = new ArrayList ();
+ foreach (XmlAttribute attr in el.Attributes) {
+ DataColumn col = tab.Columns [attr.LocalName];
+ if (col != null) {
+ if (r.IsNull (col))
+ // should be removed
+ al.Add (attr);
+ else
+ attr.Value = r [col].ToString ();
+ }
+ }
+ foreach (XmlAttribute attr in al)
+ el.RemoveAttributeNode (attr);
+ al.Clear ();
+ foreach (XmlNode child in el.ChildNodes) {
+ if (child.NodeType == XmlNodeType.Element) {
+ DataColumn col = tab.Columns [child.LocalName];
+ if (col != null) {
+ if (r.IsNull (col))
+ al.Add (child);
+ else
+ child.InnerText = r [col].ToString ();
+ }
+ }
+ }
+ foreach (XmlNode n in al)
+ el.RemoveChild (n);
+ } finally {
+ raiseDocumentEvents = escapedRaiseDocumentEvents;
}
}
#endregion // DataSet event handlers
#region Private methods
+ private void InitDelegateFields ()
+ {
+ columnChanged = new DataColumnChangeEventHandler (OnDataTableColumnChanged);
+ rowDeleted = new DataRowChangeEventHandler (OnDataTableRowDeleted);
+ rowChanged = new DataRowChangeEventHandler (OnDataTableRowChanged);
+ tablesChanged = new CollectionChangeEventHandler (OnDataTableChanged);
+ }
+
+ private void RemoveXmlDocumentListeners ()
+ {
+ this.NodeInserting -= new XmlNodeChangedEventHandler (OnNodeInserting);
+ this.NodeInserted -= new XmlNodeChangedEventHandler (OnNodeInserted);
+ this.NodeChanging -= new XmlNodeChangedEventHandler (OnNodeChanging);
+ this.NodeChanged -= new XmlNodeChangedEventHandler (OnNodeChanged);
+ this.NodeRemoving -= new XmlNodeChangedEventHandler (OnNodeRemoving);
+ this.NodeRemoved -= new XmlNodeChangedEventHandler (OnNodeRemoved);
+ }
- [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 AddXmlDocumentListeners ()
+ {
+ this.NodeInserting += new XmlNodeChangedEventHandler (OnNodeInserting);
+ this.NodeInserted += new XmlNodeChangedEventHandler (OnNodeInserted);
+ this.NodeChanging += new XmlNodeChangedEventHandler (OnNodeChanging);
+ this.NodeChanged += new XmlNodeChangedEventHandler (OnNodeChanged);
+ this.NodeRemoving += new XmlNodeChangedEventHandler (OnNodeRemoving);
+ this.NodeRemoved += new XmlNodeChangedEventHandler (OnNodeRemoved);
+ }
+
+ internal static object StringToObject (Type type, string value)
+ {
+ if (type == null) return value;
+
+ switch (Type.GetTypeCode (type)) {
+ case TypeCode.Boolean: return XmlConvert.ToBoolean (value);
+ case TypeCode.Byte: return XmlConvert.ToByte (value);
+ case TypeCode.Char: return (char)XmlConvert.ToInt32 (value);
+ case TypeCode.DateTime: return XmlConvert.ToDateTime (value);
+ case TypeCode.Decimal: return XmlConvert.ToDecimal (value);
+ case TypeCode.Double: return XmlConvert.ToDouble (value);
+ case TypeCode.Int16: return XmlConvert.ToInt16 (value);
+ case TypeCode.Int32: return XmlConvert.ToInt32 (value);
+ case TypeCode.Int64: return XmlConvert.ToInt64 (value);
+ case TypeCode.SByte: return XmlConvert.ToSByte (value);
+ case TypeCode.Single: return XmlConvert.ToSingle (value);
+ case TypeCode.UInt16: return XmlConvert.ToUInt16 (value);
+ case TypeCode.UInt32: return XmlConvert.ToUInt32 (value);
+ case TypeCode.UInt64: return XmlConvert.ToUInt64 (value);
}
+
+ if (type == typeof (TimeSpan)) return XmlConvert.ToTimeSpan (value);
+ if (type == typeof (Guid)) return XmlConvert.ToGuid (value);
+ if (type == typeof (byte[])) return Convert.FromBase64String (value);
+
+ return Convert.ChangeType (value, type);
}
-
#endregion // Private methods
}
}