2 // mcs/class/System.Data/System.Xml/XmlDataDocument.cs
4 // Purpose: Provides a W3C XML DOM Document to interact with
5 // relational data in a DataSet
7 // class: XmlDataDocument
8 // assembly: System.Data.dll
9 // namespace: System.Xml
12 // Daniel Morgan <danmorg@sc.rr.com>
13 // Ville Palo <vi64pa@koti.soon.fi>
15 // (c)copyright 2002 Daniel Morgan
17 // XmlDataDocument is included within the Mono Class Library.
24 using System.Xml.XPath;
25 using System.Collections;
27 namespace System.Xml {
29 public class XmlDataDocument : XmlDocument {
33 private DataSet dataSet;
34 private bool isReadOnly = false;
36 private int dataRowID = 1;
37 private ArrayList dataRowIDList = new ArrayList ();
43 public XmlDataDocument() {
44 dataSet = new DataSet();
45 this.NodeChanged += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
46 //this.NodeChanging += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
47 //this.NodeInserted += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
48 //this.NodeRemoved += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
51 public XmlDataDocument(DataSet dataset) {
52 this.dataSet = dataset;
53 this.NodeChanged += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
56 #endregion // Constructors
58 #region Public Properties
60 public override string BaseURI {
63 // TODO: why are we overriding?
68 public DataSet DataSet {
75 // override inheritted method from XmlDocument
76 public override string InnerXml {
79 throw new NotImplementedException();
84 throw new NotImplementedException();
88 public override bool IsReadOnly {
97 public override XmlElement this[string name] {
100 throw new NotImplementedException();
105 public override XmlElement this[string localname, string ns] {
108 throw new NotImplementedException();
112 public override string LocalName {
115 throw new NotImplementedException();
119 public override string Name {
122 throw new NotImplementedException();
126 public override XmlDocument OwnerDocument {
133 #endregion // Public Properties
135 #region Public Methods
138 public override XmlNode CloneNode(bool deep)
140 throw new NotImplementedException();
143 #region overloaded CreateElement methods
146 public override XmlElement CreateElement(string prefix,
147 string localName, string namespaceURI)
149 if ((localName == null) || (localName == String.Empty))
150 throw new ArgumentException ("The local name for elements or attributes cannot be null" +
151 "or an empty string.");
152 string pref = prefix != null ? prefix : String.Empty;
153 return base.CreateElement (pref, localName, namespaceURI != null ? namespaceURI : String.Empty);
157 #endregion // overloaded CreateElement Methods
159 // will not be supported
160 public override XmlEntityReference CreateEntityReference(string name)
162 throw new NotSupportedException();
165 // will not be supported
166 public override XmlElement GetElementById(string elemId)
168 throw new NotSupportedException();
171 // get the XmlElement associated with the DataRow
172 public XmlElement GetElementFromRow(DataRow r)
174 throw new NotImplementedException();
177 // get the DataRow associated with the XmlElement
179 public DataRow GetRowFromElement(XmlElement e)
181 throw new NotImplementedException();
184 #region overload Load methods
186 public override void Load(Stream inStream) {
187 Load (new XmlTextReader (inStream));
190 public override void Load(string filename) {
191 Load (new XmlTextReader (filename));
194 public override void Load(TextReader txtReader) {
195 Load (new XmlTextReader (txtReader));
198 public override void Load(XmlReader reader) {
202 // For reading xml to XmlDocument
203 XmlTextReader textReader = new XmlTextReader (
206 if (reader.NodeType != XmlNodeType.Element)
207 reader.MoveToContent ();
209 // TODO: Findout which exception should be throwen
210 if (reader.NodeType != XmlNodeType.Element)
211 throw new Exception ();
213 if (dataSet.DataSetName != reader.Name)
214 throw new Exception ();
216 // read to next element
217 while (reader.Read () && reader.NodeType != XmlNodeType.Element);
222 // Find right table from tablecollection
223 for (int i = 0; i < DataSet.Tables.Count && dt == null; i++) {
225 if (reader.Name == DataSet.Tables [i].TableName) {
227 dt = DataSet.Tables [i];
228 dt.ColumnChanged += new DataColumnChangeEventHandler (OnDataTableColumnChanged);
229 dt.RowDeleted += new DataRowChangeEventHandler (OnDataTableRowDeleted);
230 dt.RowChanged += new DataRowChangeEventHandler (OnDataTableRowChanged);
234 // TODO: Findout what kind of exception
236 throw new Exception (); // there were no correct table
238 // Read rows to table
239 DataRow tempRow = dt.NewRow ();
240 while ((reader.NodeType != XmlNodeType.EndElement ||
241 reader.Name != dt.TableName) && reader.Read()) {
243 switch (reader.NodeType) {
245 case XmlNodeType.Element:
246 // Add column to DataRow
247 LoadRow (reader, ref tempRow);
254 // Every row must have unique id.
255 tempRow.XmlRowID = dataRowID;
256 dataRowIDList.Add (dataRowID);
257 dt.Rows.Add (tempRow);
261 } while (reader.Read ());
263 base.Load (textReader);
266 #endregion // overloaded Load methods
269 public override void WriteContentTo(XmlWriter xw) {
270 base.WriteContentTo (xw);
274 public override void WriteTo(XmlWriter w) {
278 #endregion // Public Methods
280 #region Protected Methods
282 //FIXME: how do you handle this?
284 //protected internal override XPathNavigator CreateNavigator(XmlNode node) {
285 // throw new NotImplementedException();
289 public new XPathNavigator CreateNavigator() {
290 throw new NotImplementedException();
293 #endregion // Protected Methods
295 #region DataSet event handlers
297 // this changed datatable values when some of xmldocument elements is changed
298 private void OnXmlDataColumnChanged (object sender, XmlNodeChangedEventArgs args)
301 XPathNavigator nodeNavigator = args.Node.CreateNavigator ();
302 int c = GetElementsByTagName (args.Node.Name).Count;
304 // FIXME: I dont know which way it should be but this work on linux.
305 // could be also GetElementsByTagName (args.OldParent.Name) []
306 XmlNodeList nodeList = GetElementsByTagName (args.Node.Name);
312 while ((i < c) && !isSame ) {
314 XPathNavigator docNavigator = nodeList [i].CreateNavigator ();
315 isSame = docNavigator.IsSamePosition (nodeNavigator);
316 docNavigator = nodeList [i].CreateNavigator ();
320 // if there wasnt such row it is just added and we dont need to care about it
324 // now we know rownum
325 int xmlrowid = (int)dataRowIDList [i];
327 DataTable dt = DataSet.Tables [args.OldParent.Name];
328 foreach (DataRow r in dt.Rows) {
329 if (xmlrowid == r.XmlRowID) {
331 // change value only when have to.
332 if ((string)r [args.Node.Name] != (string)args.Node.InnerText)
333 r [args.Node.Name] = args.Node.InnerText;
339 private void OnDataTableColumnChanged(object sender,
340 DataColumnChangeEventArgs eventArgs)
342 // row is not yet in datatable
343 if (eventArgs.Row.XmlRowID == 0)
346 // TODO: Here should be some kind of error checking.
347 GetElementsByTagName (eventArgs.Column.ToString ()) [dataRowIDList.IndexOf (
348 eventArgs.Row.XmlRowID)].InnerText = (string)eventArgs.ProposedValue;
352 private void OnDataTableRowDeleted(object sender,
353 DataRowChangeEventArgs eventArgs)
355 DataRow deletedRow = null;
356 deletedRow = eventArgs.Row;
358 if (eventArgs.Row.XmlRowID == 0)
361 int rowIndex = dataRowIDList.IndexOf (eventArgs.Row.XmlRowID);
363 // Remove element from xmldocument and row indexlist
364 GetElementsByTagName (deletedRow.Table.TableName) [rowIndex].RemoveAll ();
365 dataRowIDList.RemoveAt (rowIndex);
369 private void OnDataTableRowChanged(object sender, DataRowChangeEventArgs eventArgs)
371 switch (eventArgs.Action) {
373 case DataRowAction.Delete:
374 OnDataTableRowDeleted (sender, eventArgs);
377 case DataRowAction.Add:
378 OnDataTableRowAdded (eventArgs);
381 case DataRowAction.Rollback:
382 OnDataTableRowRollback (eventArgs);
391 private void OnDataTableRowAdded (DataRowChangeEventArgs args)
393 // If XmlRowID is != 0 then it is already added
394 if (args.Row.XmlRowID != 0)
397 // Create row element. Row's name same as TableName
398 DataRow row = args.Row;
399 row.XmlRowID = dataRowID;
401 XmlElement element = CreateElement (args.Row.Table.TableName);
402 DocumentElement.AppendChild (element);
404 XmlElement rowElement = null;
405 for (int i = 0; i < row.Table.Columns.Count; i++) {
407 rowElement = CreateElement (row.Table.Columns [i].ToString ());
408 rowElement.InnerText = (string)row [i];
409 element.AppendChild (rowElement);
415 private void OnDataTableRowRollback (DataRowChangeEventArgs args)
417 DataRow row = args.Row;
419 int rowid = dataRowIDList.IndexOf (row.XmlRowID);
421 // find right element in xmldocument
422 XmlNode node = GetElementsByTagName (row.Table.TableName) [rowid];
425 for (int i = 0; i < node.ChildNodes.Count; i++) {
427 XmlNode child = node.ChildNodes [i];
428 if (child.NodeType != XmlNodeType.Whitespace)
429 child.InnerText = (string)row [rowValue++];
434 #endregion // DataSet event handlers
436 #region Private methods
439 private void LoadRow (XmlReader reader, ref DataRow row)
441 // dt.Rows.Add (LoadRow (reader, dt.NewRow ()));
442 // This method returns DataRow filled by values
444 string rowname = reader.Name;
447 if (reader.NodeType == XmlNodeType.Element)
448 column = reader.Name;
452 if (reader.NodeType == XmlNodeType.Text) {
454 string val = reader.Value;
455 if (row.Table.Columns.Contains (column))
460 #endregion // Private methods