2002-12-04 Ville Palo <vi64pa@koti.soon.fi>
[mono.git] / mcs / class / System.Data / System.Xml / XmlDataDocument.cs
1 //
2 // mcs/class/System.Data/System.Xml/XmlDataDocument.cs
3 //
4 // Purpose: Provides a W3C XML DOM Document to interact with
5 //          relational data in a DataSet
6 //
7 // class: XmlDataDocument
8 // assembly: System.Data.dll
9 // namespace: System.Xml
10 //
11 // Author:
12 //     Daniel Morgan <danmorg@sc.rr.com>
13 //     Ville Palo <vi64pa@koti.soon.fi>
14 //
15 // (c)copyright 2002 Daniel Morgan
16 //
17 // XmlDataDocument is included within the Mono Class Library.
18 //
19
20 using System;
21 using System.Data;
22 using System.IO;
23 using System.Text;
24 using System.Xml.XPath;
25 using System.Collections;
26
27 namespace System.Xml {
28
29         public class XmlDataDocument : XmlDocument {
30
31                 #region Fields
32
33                 private DataSet dataSet;
34                 private bool isReadOnly = false;
35
36                 private int dataRowID = 1;
37                 private ArrayList dataRowIDList = new ArrayList ();
38
39                 #endregion // Fields
40
41                 #region Constructors
42
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);
49                 }
50
51                 public XmlDataDocument(DataSet dataset) {
52                         this.dataSet = dataset;
53                         this.NodeChanged += new XmlNodeChangedEventHandler (OnXmlDataColumnChanged);
54                 }
55
56                 #endregion // Constructors
57
58                 #region Public Properties
59
60                 public override string BaseURI {
61                         [MonoTODO]
62                         get {
63                                 // TODO: why are we overriding?
64                                 return base.BaseURI;
65                         }
66                 }
67
68                 public DataSet DataSet {
69                         [MonoTODO]
70                         get {
71                                 return dataSet;
72                         }
73                 }
74
75                 // override inheritted method from XmlDocument
76                 public override string InnerXml {
77                         [MonoTODO]
78                         get {
79                                 throw new NotImplementedException();
80                         }
81                                  
82                         [MonoTODO]
83                         set {
84                                 throw new NotImplementedException();
85                         }
86                 }
87
88                 public override bool IsReadOnly {
89                         [MonoTODO]
90                         get {
91                                 return isReadOnly;
92                         }
93
94                 }
95
96                 // Item indexer
97                 public override XmlElement this[string name] {
98                         [MonoTODO]
99                         get {
100                                 throw new NotImplementedException();
101                         }
102                 }
103
104                 // Item indexer
105                 public override XmlElement this[string localname, string ns] {
106                         [MonoTODO]
107                         get {
108                                 throw new NotImplementedException();
109                         }
110                 }
111
112                 public override string LocalName {
113                         [MonoTODO]
114                         get {
115                                 throw new NotImplementedException();
116                         }
117                 }
118
119                 public override string Name {
120                         [MonoTODO]
121                         get {
122                                 throw new NotImplementedException();
123                         }
124                 }
125
126                 public override XmlDocument OwnerDocument {
127                         [MonoTODO]
128                         get {
129                                 return null;
130                         }
131                 }
132
133                 #endregion // Public Properties
134
135                 #region Public Methods
136
137                 [MonoTODO]
138                 public override XmlNode CloneNode(bool deep) 
139                 {
140                         throw new NotImplementedException();
141                 }
142
143                 #region overloaded CreateElement methods
144
145                 [MonoTODO]
146                 public override XmlElement CreateElement(string prefix,
147                                 string localName, string namespaceURI) 
148                 {
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);
154
155                 }
156
157                 #endregion // overloaded CreateElement Methods
158                         
159                 // will not be supported
160                 public override XmlEntityReference CreateEntityReference(string name) 
161                 {
162                         throw new NotSupportedException();
163                 }
164                 
165                 // will not be supported
166                 public override XmlElement GetElementById(string elemId) 
167                 {
168                         throw new NotSupportedException();
169                 }
170
171                 // get the XmlElement associated with the DataRow
172                 public XmlElement GetElementFromRow(DataRow r) 
173                 {
174                         throw new NotImplementedException();
175                 }
176
177                 // get the DataRow associated with the XmlElement
178                 [MonoTODO]
179                 public DataRow GetRowFromElement(XmlElement e)
180                 {
181                         throw new NotImplementedException();
182                 }
183
184                 #region overload Load methods
185
186                 public override void Load(Stream inStream) {
187                         Load (new XmlTextReader (inStream));
188                 }
189
190                 public override void Load(string filename) {
191                         Load (new XmlTextReader (filename));
192                 }
193
194                 public override void Load(TextReader txtReader) {
195                         Load (new XmlTextReader (txtReader));
196                 }
197
198                 public override void Load(XmlReader reader) {
199                         
200                         DataTable dt = null;
201
202                         // For reading xml to XmlDocument
203                         XmlTextReader textReader = new XmlTextReader (
204                                 reader.BaseURI);
205
206                         if (reader.NodeType != XmlNodeType.Element)
207                                 reader.MoveToContent ();
208
209                         // TODO: Findout which exception should be throwen
210                         if (reader.NodeType != XmlNodeType.Element)
211                                 throw new Exception ();
212
213                         if (dataSet.DataSetName != reader.Name)
214                                 throw new Exception ();
215
216                         // read to next element
217                         while (reader.Read () && reader.NodeType != XmlNodeType.Element);
218
219                         do {
220
221
222                                 // Find right table from tablecollection
223                                 for (int i = 0; i < DataSet.Tables.Count && dt == null; i++) {
224
225                                         if (reader.Name == DataSet.Tables [i].TableName) {
226
227                                                 dt = DataSet.Tables [i];
228                                                 dt.ColumnChanged += new DataColumnChangeEventHandler (OnDataTableColumnChanged);
229                                                 dt.RowDeleted += new DataRowChangeEventHandler (OnDataTableRowDeleted);
230                                                 dt.RowChanged += new DataRowChangeEventHandler (OnDataTableRowChanged);
231                                         }
232                                 }
233                                 
234                                 // TODO: Findout what kind of exception 
235                                 if (dt == null) 
236                                         throw new Exception (); // there were no correct table
237
238                                 // Read rows to table
239                                 DataRow tempRow = dt.NewRow ();
240                                 while ((reader.NodeType != XmlNodeType.EndElement ||
241                                         reader.Name != dt.TableName) && reader.Read()) {
242                                         
243                                         switch (reader.NodeType) {
244                                                 
245                                         case XmlNodeType.Element:
246                                                 // Add column to DataRow
247                                                 LoadRow (reader, ref tempRow);
248                                                 break;
249                                         default:
250                                                 break;
251                                         }                       
252                                 }
253
254                                 // Every row must have unique id.
255                                 tempRow.XmlRowID = dataRowID;
256                                 dataRowIDList.Add (dataRowID);
257                                 dt.Rows.Add (tempRow);
258                                 dataRowID++;                                    
259                                 
260                                 
261                         } while (reader.Read ());
262
263                         base.Load (textReader);
264                 }
265                 
266                 #endregion // overloaded Load methods
267
268                 [MonoTODO]
269                 public override void WriteContentTo(XmlWriter xw) {
270                         base.WriteContentTo (xw);
271                 }
272
273                 [MonoTODO]
274                 public override void WriteTo(XmlWriter w) {
275                         base.WriteTo (w);
276                 }
277
278                 #endregion // Public Methods
279
280                 #region Protected Methods
281
282                 //FIXME: how do you handle this?
283                 //[MonoTODO]
284                 //protected internal override XPathNavigator CreateNavigator(XmlNode node) {
285                 //      throw new NotImplementedException();
286                 //}
287
288                 [MonoTODO]
289                 public new XPathNavigator CreateNavigator() {
290                         throw new NotImplementedException();
291                 }
292
293                 #endregion // Protected Methods
294                 
295                 #region DataSet event handlers
296
297                 // this changed datatable values when some of xmldocument elements is changed
298                 private void OnXmlDataColumnChanged (object sender, XmlNodeChangedEventArgs args)
299                 {
300                         int i = 0;
301                         XPathNavigator nodeNavigator = args.Node.CreateNavigator ();
302                         int c = GetElementsByTagName (args.Node.Name).Count;
303
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);
307                         
308
309                         bool isSame = false;
310                         
311                         // Find right row                                       
312                         while ((i < c) && !isSame ) {
313
314                                 XPathNavigator docNavigator = nodeList [i].CreateNavigator ();
315                                 isSame = docNavigator.IsSamePosition (nodeNavigator);
316                                 docNavigator = nodeList [i].CreateNavigator ();
317                                 i++;
318                         } 
319                         
320                         // if there wasnt such row it is just added and we dont need to care about it
321                         if (!isSame) 
322                                 return;
323
324                         // now we know rownum
325                         int xmlrowid = (int)dataRowIDList [i];
326                        
327                         DataTable dt = DataSet.Tables [args.OldParent.Name];                           
328                         foreach (DataRow r in dt.Rows) {
329                                 if (xmlrowid == r.XmlRowID) {
330                                         
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;
334                                 }
335                         }                       
336                 }
337
338                 [MonoTODO]
339                 private void OnDataTableColumnChanged(object sender, 
340                                                              DataColumnChangeEventArgs eventArgs)
341                 {
342                         // row is not yet in datatable
343                         if (eventArgs.Row.XmlRowID == 0)
344                                 return;
345
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;
349                 }
350         
351                 [MonoTODO]
352                 private void OnDataTableRowDeleted(object sender,
353                                                           DataRowChangeEventArgs eventArgs)
354                 {
355                         DataRow deletedRow = null;
356                         deletedRow = eventArgs.Row;
357
358                         if (eventArgs.Row.XmlRowID == 0)
359                                 return;
360
361                         int rowIndex = dataRowIDList.IndexOf (eventArgs.Row.XmlRowID);
362
363                         // Remove element from xmldocument and row indexlist
364                         GetElementsByTagName (deletedRow.Table.TableName) [rowIndex].RemoveAll ();
365                         dataRowIDList.RemoveAt (rowIndex);
366                 }
367                 
368                 [MonoTODO]
369                 private void OnDataTableRowChanged(object sender, DataRowChangeEventArgs eventArgs)
370                 {
371                         switch (eventArgs.Action) {
372
373                                 case DataRowAction.Delete:
374                                         OnDataTableRowDeleted (sender, eventArgs);
375                                         break;
376
377                                 case DataRowAction.Add:
378                                         OnDataTableRowAdded (eventArgs);
379                                         break;
380
381                                 case DataRowAction.Rollback:
382                                         OnDataTableRowRollback (eventArgs);
383                                         break;
384                                 default:
385                                         break;
386                         } 
387                 }
388
389                 // Added
390                 [MonoTODO]
391                 private void OnDataTableRowAdded (DataRowChangeEventArgs args)
392                 {
393                         // If XmlRowID is != 0 then it is already added
394                         if (args.Row.XmlRowID != 0)
395                                 return;
396                         
397                         // Create row element. Row's name same as TableName                                     
398                         DataRow row = args.Row;
399                         row.XmlRowID = dataRowID;
400                         dataRowID++;
401                         XmlElement element = CreateElement (args.Row.Table.TableName);
402                         DocumentElement.AppendChild (element);
403                         
404                         XmlElement rowElement = null;
405                         for (int i = 0; i < row.Table.Columns.Count; i++) {
406
407                                 rowElement = CreateElement (row.Table.Columns [i].ToString ());
408                                 rowElement.InnerText = (string)row [i];
409                                 element.AppendChild (rowElement);
410                         }
411                 }
412
413                 // Rollback
414                 [MonoTODO]
415                 private void OnDataTableRowRollback (DataRowChangeEventArgs args)
416                 {
417                         DataRow row = args.Row;
418                         
419                         int rowid = dataRowIDList.IndexOf (row.XmlRowID);
420                         
421                         // find right element in xmldocument
422                         XmlNode node = GetElementsByTagName (row.Table.TableName) [rowid];
423
424                         int rowValue = 0;
425                         for (int i = 0; i < node.ChildNodes.Count; i++) {
426
427                                 XmlNode child = node.ChildNodes [i];
428                                 if (child.NodeType != XmlNodeType.Whitespace)                           
429                                         child.InnerText = (string)row [rowValue++];
430                                 
431                         }
432                 }
433
434                 #endregion // DataSet event handlers
435
436                 #region Private methods
437
438                 [MonoTODO]
439                 private void LoadRow (XmlReader reader, ref DataRow row)
440                 {                       
441                         // dt.Rows.Add (LoadRow (reader, dt.NewRow ()));
442                         // This method returns DataRow filled by values
443                         // from xmldocument
444                         string rowname = reader.Name;
445                         string column = "";
446                         
447                         if (reader.NodeType == XmlNodeType.Element)
448                                 column = reader.Name;
449                         
450                         reader.Read ();
451                         
452                         if (reader.NodeType == XmlNodeType.Text) {
453                                 
454                                 string val = reader.Value;
455                                 if (row.Table.Columns.Contains (column))
456                                         row [column] = val;
457                         }
458                 }
459                 
460                 #endregion // Private methods
461         }
462 }
463