merging the Mainsoft branch to the trunk
[mono.git] / mcs / class / System.Data / System.Data / XmlDiffLoader.cs
1 //
2 // mcs/class/System.Data/System.Data/XmlDiffLoader.cs
3 //
4 // Purpose: Loads XmlDiffGrams to DataSet 
5 //
6 // class: XmlDiffLoader
7 // assembly: System.Data.dll
8 // namespace: System.Data
9 //
10 // Author:
11 //     Ville Palo <vi64pa@koti.soon.fi>
12 //     Lluis Sanchez Gual (lluis@ximian.com)
13 //
14 // (c)copyright 2003 Ville Palo
15 //
16
17 //
18 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
19 //
20 // Permission is hereby granted, free of charge, to any person obtaining
21 // a copy of this software and associated documentation files (the
22 // "Software"), to deal in the Software without restriction, including
23 // without limitation the rights to use, copy, modify, merge, publish,
24 // distribute, sublicense, and/or sell copies of the Software, and to
25 // permit persons to whom the Software is furnished to do so, subject to
26 // the following conditions:
27 // 
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
30 // 
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 //
39 using System;
40 using System.Data;
41 using System.Xml;
42 using System.Xml.Schema;
43 using System.Xml.XPath;
44 using System.Collections;
45 using System.Globalization;
46
47 namespace System.Data {
48
49         internal class XmlDiffLoader 
50         {
51
52                 #region Fields
53                 private DataSet DSet;
54                 private DataTable table;
55                 private Hashtable DiffGrRows = new Hashtable ();
56                 private Hashtable ErrorRows = new Hashtable ();
57
58                 #endregion // Fields
59
60                 #region ctors
61
62                 public XmlDiffLoader (DataSet DSet) 
63                 {
64                         this.DSet = DSet;
65                 }
66
67                 public XmlDiffLoader (DataTable table) 
68                 {
69                         this.table = table;
70                 }
71
72                 #endregion //ctors
73
74                 #region Public methods
75
76                 public void Load (XmlReader reader) 
77                 {
78                         bool origEnforceConstraint = false;
79                         if (DSet != null) {
80                                 origEnforceConstraint = DSet.EnforceConstraints;
81                                 DSet.EnforceConstraints = false;
82                         }
83                         
84                         reader.MoveToContent ();
85                         if (reader.IsEmptyElement) {
86                                 reader.Skip ();
87                                 return;
88                         }
89                         
90                         reader.ReadStartElement ("diffgram", XmlConstants.DiffgrNamespace);
91                         reader.MoveToContent ();
92                         
93                         while (reader.NodeType != XmlNodeType.EndElement)
94                         {
95                                 if (reader.NodeType == XmlNodeType.Element)
96                                 {
97                                         if (reader.LocalName == "before" && reader.NamespaceURI == XmlConstants.DiffgrNamespace)
98                                                 LoadBefore (reader);
99                                         else if (reader.LocalName == "errors" && reader.NamespaceURI == XmlConstants.DiffgrNamespace)
100                                                 LoadErrors (reader);
101                                         else
102                                                 LoadCurrent (reader);
103                                 }
104                                 else
105                                         reader.Skip ();
106                         }
107                         
108                         reader.ReadEndElement ();
109                         
110                         if (DSet != null)
111                                 DSet.EnforceConstraints = origEnforceConstraint;
112                 }
113
114                 #endregion // Public methods
115
116                 #region Private methods
117
118                 private void LoadCurrent (XmlReader reader) 
119                 {
120                         if (reader.IsEmptyElement) {
121                                 reader.Skip();
122                                 return;
123                         }
124                         reader.ReadStartElement ();             // Dataset root
125                         reader.MoveToContent ();
126
127                         while (reader.NodeType != XmlNodeType.EndElement)
128                         {
129                                 if (reader.NodeType == XmlNodeType.Element)
130                                 {
131                                         DataTable t = GetTable (reader.LocalName);
132                                         if (t != null)
133                                                 LoadCurrentTable (t, reader);
134 #if true
135                                         else
136                                                 reader.Skip ();
137 #else
138                                         else 
139                                                 throw new DataException (Locale.GetText ("Cannot load diffGram. Table '" + reader.LocalName + "' is missing in the destination dataset"));      
140 #endif
141                                 }
142                                 else
143                                         reader.Skip ();
144                         }
145                         
146                         reader.ReadEndElement ();
147                 }
148
149                 private void LoadBefore (XmlReader reader) 
150                 {
151                         if (reader.IsEmptyElement) {
152                                 reader.Skip ();
153                                 return;
154                         }
155                         reader.ReadStartElement ();
156                         reader.MoveToContent ();
157
158                         while (reader.NodeType != XmlNodeType.EndElement)
159                         {
160                                 if (reader.NodeType == XmlNodeType.Element)
161                                 {
162                                         DataTable t = GetTable (reader.LocalName);
163                                         if (t != null)
164                                                 LoadBeforeTable(t, reader);
165                                         else
166                                                 throw new DataException (Locale.GetText ("Cannot load diffGram. Table '" + reader.LocalName + "' is missing in the destination dataset"));
167                                 }
168                                 else
169                                         reader.Skip ();
170                         }
171                         
172                         reader.ReadEndElement ();
173                 }                                
174                                 
175                                            
176                 private void LoadErrors (XmlReader reader) 
177                 {
178                         if (reader.IsEmptyElement) {
179                                 reader.Skip ();
180                                 return;
181                         }
182                         reader.ReadStartElement ();
183                         reader.MoveToContent ();
184
185                         while (reader.NodeType != XmlNodeType.EndElement)
186                         {
187                                 if (reader.NodeType == XmlNodeType.Element)
188                                 {
189                                         DataRow Row = null;
190                 
191                                         // find the row in 'current' section
192                                         
193                                         string id = reader.GetAttribute ("id", XmlConstants.DiffgrNamespace);
194                                         
195                                         if (id != null)
196                                                 Row = (DataRow) ErrorRows [id];
197                 
198                                         if (reader.IsEmptyElement) continue;
199                                         reader.ReadStartElement ();
200                                         while (reader.NodeType != XmlNodeType.EndElement)
201                                         {
202                                                 if (reader.NodeType == XmlNodeType.Element) {
203                                                         string error = reader.GetAttribute ("Error", XmlConstants.DiffgrNamespace);
204                                                         Row.SetColumnError (reader.LocalName, error);
205                                                 }
206                                                 reader.Read ();
207                                         }
208                                 }
209                                 else
210                                         reader.Skip ();
211                         }
212                         reader.ReadEndElement ();
213                 }
214
215                 private void LoadColumns (DataTable Table, DataRow Row, 
216                         XmlReader reader, DataRowVersion loadType)
217                 {
218                         // attributes
219                         LoadColumnAttributes (Table, Row, reader, loadType);
220                         LoadColumnChildren (Table, Row, reader, loadType);
221                 }
222
223                 private void LoadColumnAttributes (DataTable Table, DataRow Row,
224                         XmlReader reader, DataRowVersion loadType)
225                 {
226                         if (!reader.HasAttributes // this check will be faster
227                                 || !reader.MoveToFirstAttribute ())
228                                 return;
229                         do {
230                                 switch (reader.NamespaceURI) {
231                                 case XmlConstants.XmlnsNS:
232                                 case XmlConstants.DiffgrNamespace:
233                                 case XmlConstants.MsdataNamespace:
234                                 case XmlConstants.MspropNamespace:
235                                 case XmlSchema.Namespace:
236                                         continue;
237                                 }
238                                 DataColumn c = Table.Columns [reader.LocalName];
239                                 if (c == null ||
240                                         c.ColumnMapping != MappingType.Attribute)                                       continue;
241                                 if (c.Namespace == null && reader.NamespaceURI == String.Empty ||
242                                         c.Namespace == reader.NamespaceURI) {
243                                         object data = XmlDataLoader.StringToObject (c.DataType, reader.Value);
244                                         if (loadType == DataRowVersion.Current)
245                                                 Row [c] = data;
246                                         else
247                                                 Row.SetOriginalValue (c.ColumnName, data);
248                                 } // otherwise just ignore as well as unknown elements.
249                         } while (reader.MoveToNextAttribute ());
250                         reader.MoveToElement ();
251                 }
252
253                 private void LoadColumnChildren (DataTable Table, DataRow Row,
254                         XmlReader reader, DataRowVersion loadType) 
255                 {
256                         // children
257                         if (reader.IsEmptyElement) {
258                                 reader.Skip ();
259                                 return;
260                         }
261                         reader.ReadStartElement ();
262                         reader.MoveToContent ();
263                         
264                         while (reader.NodeType != XmlNodeType.EndElement)
265                         {
266                                 if (reader.NodeType != XmlNodeType.Element) { reader.Read (); continue; }
267                                 
268                                 if (Table.Columns.Contains (reader.LocalName)) 
269                                 {
270                                         string colName = reader.LocalName;
271                                         object data = XmlDataLoader.StringToObject (Table.Columns[colName].DataType, reader.ReadString ());
272                                         
273                                         if (loadType == DataRowVersion.Current) Row [colName] = data;
274                                         else Row.SetOriginalValue (colName, data);
275                                         reader.Read ();
276                                 }
277                                 else 
278                                 {
279                                         DataTable t = GetTable (reader.LocalName);
280                                         if (t != null) {
281                                                 if (loadType == DataRowVersion.Original)
282                                                         LoadBeforeTable (t, reader);
283                                                 else if (loadType == DataRowVersion.Current)
284                                                         LoadCurrentTable (t, reader);
285                                         }
286                                 }
287                         }
288                         
289                         reader.ReadEndElement ();
290                 }
291
292                 private void LoadBeforeTable (DataTable Table, XmlReader reader) 
293                 {
294                         string id = reader.GetAttribute ("id", XmlConstants.DiffgrNamespace);
295                         string rowOrder = reader.GetAttribute ("rowOrder", XmlConstants.MsdataNamespace);
296                         DataRow Row = (DataRow) DiffGrRows [id];
297                         
298                         if (Row == null)
299                         {
300                                 // Deleted row
301                                 Row = Table.NewRow ();
302                                 LoadColumns (Table, Row, reader, DataRowVersion.Current);
303                                 Table.Rows.InsertAt (Row, int.Parse (rowOrder));
304                                 Row.AcceptChanges ();
305                                 Row.Delete ();
306                         }
307                         else
308                         {
309                                 LoadColumns (Table, Row, reader, DataRowVersion.Original);
310                         }
311                 }
312
313                 private void LoadCurrentTable (DataTable Table, XmlReader reader) 
314                 {
315                         DataRowState state;
316                         DataRow Row = Table.NewRow ();
317
318                         string id = reader.GetAttribute  ("id", XmlConstants.DiffgrNamespace);
319                         string error = reader.GetAttribute ("hasErrors");
320                         string changes = reader.GetAttribute ("hasChanges", XmlConstants.DiffgrNamespace);
321                         
322                         if (changes != null)
323                         {
324                                 if (string.Compare (changes, "modified", true) == 0) {
325                                         DiffGrRows.Add (id, Row); // for later use
326                                         state = DataRowState.Modified;
327                                 }
328                                 else if (string.Compare (changes, "inserted", true) == 0) {
329                                         state = DataRowState.Added;
330                                 }
331                                 else
332                                         throw new InvalidOperationException ("Invalid row change state");
333                         }
334                         else
335                                 state = DataRowState.Unchanged;
336                         
337                         // If row had errors add row to hashtable for later use
338                         if (error != null && string.Compare (error, "true", true) == 0)
339                                 ErrorRows.Add (id, Row);
340                 
341                         LoadColumns (Table, Row, reader, DataRowVersion.Current);
342                         Table.Rows.Add (Row);
343                         
344                         if (state != DataRowState.Added)
345                                 Row.AcceptChanges ();
346                 }
347
348                 DataTable GetTable (string name)
349                 {
350                         if (DSet != null) 
351                                 return DSet.Tables [name];
352                         else if (name == table.TableName) 
353                                 return table;
354                         else
355                                 return null;
356                 }
357
358
359                 #endregion // Private methods
360         }
361 }