use Invariant culture for string manipulations
[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 #if NET_2_0
233                                 case XmlConstants.XmlNS:
234 #endif
235                                 case XmlConstants.DiffgrNamespace:
236                                 case XmlConstants.MsdataNamespace:
237                                 case XmlConstants.MspropNamespace:
238                                 case XmlSchema.Namespace:
239                                         continue;
240                                 }
241                                 DataColumn c = Table.Columns [reader.LocalName];
242                                 if (c == null ||
243                                         c.ColumnMapping != MappingType.Attribute)                                       continue;
244                                 if (c.Namespace == null && reader.NamespaceURI == String.Empty ||
245                                         c.Namespace == reader.NamespaceURI) {
246                                         object data = XmlDataLoader.StringToObject (c.DataType, reader.Value);
247                                         if (loadType == DataRowVersion.Current)
248                                                 Row [c] = data;
249                                         else
250                                                 Row.SetOriginalValue (c.ColumnName, data);
251                                 } // otherwise just ignore as well as unknown elements.
252                         } while (reader.MoveToNextAttribute ());
253                         reader.MoveToElement ();
254                 }
255
256                 private void LoadColumnChildren (DataTable Table, DataRow Row,
257                         XmlReader reader, DataRowVersion loadType) 
258                 {
259                         // children
260                         if (reader.IsEmptyElement) {
261                                 reader.Skip ();
262                                 return;
263                         }
264                         reader.ReadStartElement ();
265                         reader.MoveToContent ();
266                         
267                         while (reader.NodeType != XmlNodeType.EndElement)
268                         {
269                                 if (reader.NodeType != XmlNodeType.Element) { reader.Read (); continue; }
270                                 
271                                 if (Table.Columns.Contains (reader.LocalName)) 
272                                 {
273                                         string colName = reader.LocalName;
274                                         object data = XmlDataLoader.StringToObject (Table.Columns[colName].DataType, reader.ReadString ());
275                                         
276                                         if (loadType == DataRowVersion.Current) Row [colName] = data;
277                                         else Row.SetOriginalValue (colName, data);
278                                         reader.Read ();
279                                 }
280                                 else 
281                                 {
282                                         DataTable t = GetTable (reader.LocalName);
283                                         if (t != null) {
284                                                 if (loadType == DataRowVersion.Original)
285                                                         LoadBeforeTable (t, reader);
286                                                 else if (loadType == DataRowVersion.Current)
287                                                         LoadCurrentTable (t, reader);
288                                         } else
289                                                 reader.Skip ();
290                                 }
291                         }
292                         
293                         reader.ReadEndElement ();
294                 }
295
296                 private void LoadBeforeTable (DataTable Table, XmlReader reader) 
297                 {
298                         string id = reader.GetAttribute ("id", XmlConstants.DiffgrNamespace);
299                         string rowOrder = reader.GetAttribute ("rowOrder", XmlConstants.MsdataNamespace);
300                         DataRow Row = (DataRow) DiffGrRows [id];
301                         
302                         if (Row == null)
303                         {
304                                 // Deleted row
305                                 Row = Table.NewRow ();
306                                 LoadColumns (Table, Row, reader, DataRowVersion.Current);
307                                 Table.Rows.InsertAt (Row, int.Parse (rowOrder));
308                                 Row.AcceptChanges ();
309                                 Row.Delete ();
310                         }
311                         else
312                         {
313                                 LoadColumns (Table, Row, reader, DataRowVersion.Original);
314                         }
315                 }
316
317                 private void LoadCurrentTable (DataTable Table, XmlReader reader) 
318                 {
319                         DataRowState state;
320                         DataRow Row = Table.NewRow ();
321
322                         string id = reader.GetAttribute  ("id", XmlConstants.DiffgrNamespace);
323                         string error = reader.GetAttribute ("hasErrors");
324                         string changes = reader.GetAttribute ("hasChanges", XmlConstants.DiffgrNamespace);
325                         
326                         if (changes != null)
327                         {
328                                 if (string.Compare (changes, "modified", true, CultureInfo.InvariantCulture) == 0) {
329                                         DiffGrRows.Add (id, Row); // for later use
330                                         state = DataRowState.Modified;
331                                 }
332                                 else if (string.Compare (changes, "inserted", true, CultureInfo.InvariantCulture) == 0) {
333                                         state = DataRowState.Added;
334                                 }
335                                 else
336                                         throw new InvalidOperationException ("Invalid row change state");
337                         }
338                         else
339                                 state = DataRowState.Unchanged;
340                         
341                         // If row had errors add row to hashtable for later use
342                         if (error != null && string.Compare (error, "true", true, CultureInfo.InvariantCulture) == 0)
343                                 ErrorRows.Add (id, Row);
344                 
345                         LoadColumns (Table, Row, reader, DataRowVersion.Current);
346                         Table.Rows.Add (Row);
347                         
348                         if (state != DataRowState.Added)
349                                 Row.AcceptChanges ();
350                 }
351
352                 DataTable GetTable (string name)
353                 {
354                         if (DSet != null) 
355                                 return DSet.Tables [name];
356                         else if (name == table.TableName) 
357                                 return table;
358                         else
359                                 return null;
360                 }
361
362
363                 #endregion // Private methods
364         }
365 }