c240bd1ed33a520a23fe2a5676ed62d36e3ff843
[mono.git] / mcs / class / referencesource / System.Data / System / Data / XMLDiffLoader.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XMLDiffLoader.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 // <owner current="false" primary="false">Microsoft</owner>
8 //------------------------------------------------------------------------------
9
10 namespace System.Data {
11     using System;
12     using System.Runtime.Serialization.Formatters;
13     using System.Configuration.Assemblies;
14     using System.Runtime.InteropServices;
15     using System.Diagnostics;
16     using System.IO;
17     using System.Collections;
18     using System.Globalization;
19     using Microsoft.Win32;
20     using System.ComponentModel;
21     using System.Xml;
22     using System.Xml.Serialization;
23     
24     internal sealed class XMLDiffLoader {
25         ArrayList tables;
26         DataSet dataSet = null;
27         DataTable dataTable = null;
28
29         internal void LoadDiffGram(DataSet ds, XmlReader dataTextReader) {
30             XmlReader reader = DataTextReader.CreateReader(dataTextReader);
31             dataSet = ds;
32             while (reader.LocalName == Keywords.SQL_BEFORE && reader.NamespaceURI==Keywords.DFFNS)  {
33                 ProcessDiffs(ds, reader);
34                 reader.Read(); // now the reader points to the error section
35             }
36
37             while (reader.LocalName == Keywords.MSD_ERRORS && reader.NamespaceURI==Keywords.DFFNS) {
38                 ProcessErrors(ds, reader);
39                 Debug.Assert(reader.LocalName == Keywords.MSD_ERRORS && reader.NamespaceURI==Keywords.DFFNS, "something fishy");
40                 reader.Read(); // pass the end of errors tag
41             }
42         }
43
44
45         private void CreateTablesHierarchy(DataTable dt) {
46             foreach( DataRelation r in dt.ChildRelations ) {
47                 if (! tables.Contains((DataTable)r.ChildTable)) {
48                     tables.Add((DataTable)r.ChildTable);
49                     CreateTablesHierarchy(r.ChildTable)     ;
50                 }
51             }
52         }
53             
54         internal void LoadDiffGram(DataTable dt, XmlReader dataTextReader) {
55             XmlReader reader = DataTextReader.CreateReader(dataTextReader);
56             dataTable = dt;
57             tables = new ArrayList();
58             tables.Add(dt);
59             CreateTablesHierarchy(dt);
60                 
61             while (reader.LocalName == Keywords.SQL_BEFORE && reader.NamespaceURI==Keywords.DFFNS)  {
62                 ProcessDiffs(tables, reader);
63                 reader.Read(); // now the reader points to the error section
64             }
65
66             while (reader.LocalName == Keywords.MSD_ERRORS && reader.NamespaceURI==Keywords.DFFNS) {
67                 ProcessErrors(tables, reader);
68                 Debug.Assert(reader.LocalName == Keywords.MSD_ERRORS && reader.NamespaceURI==Keywords.DFFNS, "something fishy");
69                 reader.Read(); // pass the end of errors tag
70             }
71
72         }
73
74         internal void ProcessDiffs(DataSet ds, XmlReader ssync) {
75             DataTable tableBefore;
76             DataRow row;
77             int oldRowRecord;
78             int pos = -1;
79
80             int iSsyncDepth = ssync.Depth; 
81             ssync.Read(); // pass the before node.
82
83             SkipWhitespaces(ssync);
84
85             while (iSsyncDepth < ssync.Depth) {
86                 tableBefore = null;
87                 string diffId = null;
88
89                 oldRowRecord = -1;
90
91                 // the diffgramm always contains sql:before and sql:after pairs
92
93                 int iTempDepth = ssync.Depth;
94
95                 diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS);
96                 bool hasErrors = (bool) (ssync.GetAttribute(Keywords.HASERRORS, Keywords.DFFNS) == Keywords.TRUE);
97                 oldRowRecord = ReadOldRowData(ds, ref tableBefore, ref pos, ssync);
98                 if (oldRowRecord == -1)
99                     continue;
100  
101                 if (tableBefore == null) 
102                     throw ExceptionBuilder.DiffgramMissingSQL();
103
104                 row = (DataRow)tableBefore.RowDiffId[diffId];
105                 if (row != null) {
106                     row.oldRecord = oldRowRecord ;
107                     tableBefore.recordManager[oldRowRecord] = row;
108                 } else {
109                     row = tableBefore.NewEmptyRow();
110                     tableBefore.recordManager[oldRowRecord] = row;
111                     row.oldRecord = oldRowRecord;
112                     row.newRecord = oldRowRecord;
113                     tableBefore.Rows.DiffInsertAt(row, pos);
114                     row.Delete();
115                     if (hasErrors)
116                         tableBefore.RowDiffId[diffId] = row;
117                 }
118             }
119
120             return; 
121         }
122         internal void ProcessDiffs(ArrayList tableList, XmlReader ssync) {
123             DataTable tableBefore;
124             DataRow row;
125             int oldRowRecord;
126             int pos = -1;
127
128             int iSsyncDepth = ssync.Depth; 
129             ssync.Read(); // pass the before node.
130
131             //SkipWhitespaces(ssync); for given scenario does not require this change, but in fact we should do it.
132
133             while (iSsyncDepth < ssync.Depth) {
134                 tableBefore = null;
135                 string diffId = null;
136
137                 oldRowRecord = -1;
138
139                 // the diffgramm always contains sql:before and sql:after pairs
140
141                 int iTempDepth = ssync.Depth;
142
143                 diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS);
144                 bool hasErrors = (bool) (ssync.GetAttribute(Keywords.HASERRORS, Keywords.DFFNS) == Keywords.TRUE);
145                 oldRowRecord = ReadOldRowData(dataSet, ref tableBefore, ref pos, ssync);
146                 if (oldRowRecord == -1)
147                     continue;
148  
149                 if (tableBefore == null) 
150                     throw ExceptionBuilder.DiffgramMissingSQL();
151
152                 row = (DataRow)tableBefore.RowDiffId[diffId];
153
154                 if (row != null) {
155                     row.oldRecord = oldRowRecord ;
156                     tableBefore.recordManager[oldRowRecord] = row;
157                 } else {
158                     row = tableBefore.NewEmptyRow();
159                     tableBefore.recordManager[oldRowRecord] = row;
160                     row.oldRecord = oldRowRecord;
161                     row.newRecord = oldRowRecord;
162                     tableBefore.Rows.DiffInsertAt(row, pos);
163                     row.Delete();
164                     if (hasErrors)
165                         tableBefore.RowDiffId[diffId] = row;
166                 }
167             }
168
169             return; 
170
171         }
172
173         
174         internal void ProcessErrors(DataSet ds, XmlReader ssync) {
175             DataTable table;
176
177             int iSsyncDepth = ssync.Depth;
178             ssync.Read(); // pass the before node.
179
180             while (iSsyncDepth < ssync.Depth) {
181                 table = ds.Tables.GetTable(XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI);
182                 if (table == null) 
183                     throw ExceptionBuilder.DiffgramMissingSQL();
184                 string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS);
185                 DataRow row = (DataRow)table.RowDiffId[diffId];
186                 string rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS);
187                 if (rowError != null)
188                     row.RowError = rowError;
189                 int iRowDepth = ssync.Depth;
190                 ssync.Read(); // we may be inside a column
191                 while (iRowDepth < ssync.Depth) {
192                     if (XmlNodeType.Element == ssync.NodeType) {
193                         DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI];
194                         //if (col == null)
195                         // throw exception here
196                         string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS);
197                         row.SetColumnError(col, colError);
198                     }
199
200                     ssync.Read();
201                 }
202                 while ((ssync.NodeType == XmlNodeType.EndElement) && (iSsyncDepth < ssync.Depth) )
203                     ssync.Read();
204
205             }
206
207             return; 
208         }
209
210         internal void ProcessErrors(ArrayList dt, XmlReader ssync) {
211             DataTable table;
212
213             int iSsyncDepth = ssync.Depth;
214             ssync.Read(); // pass the before node.
215
216             while (iSsyncDepth < ssync.Depth) {
217                 table = GetTable(XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI);
218                 if (table == null) 
219                     throw ExceptionBuilder.DiffgramMissingSQL();
220
221                 string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS);
222
223                 DataRow row = (DataRow)table.RowDiffId[diffId];
224                 if (row  == null) {
225                     for(int i = 0; i < dt.Count; i++) {
226                         row = (DataRow)((DataTable)dt[i]).RowDiffId[diffId];
227                         if (row != null) {
228                             table = row.Table;
229                             break;
230                         }                       
231                     }
232                 }
233                 string rowError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS);
234                 if (rowError != null)
235                     row.RowError = rowError;
236                 int iRowDepth = ssync.Depth;
237                 ssync.Read(); // we may be inside a column
238
239                 while (iRowDepth < ssync.Depth) {
240                     if (XmlNodeType.Element == ssync.NodeType) {
241                         DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI];
242                         //if (col == null)
243                         // throw exception here
244                         string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS);
245                         row.SetColumnError(col, colError);
246                     }
247                     ssync.Read();
248                 }
249                 while ((ssync.NodeType == XmlNodeType.EndElement) && (iSsyncDepth < ssync.Depth) )
250                     ssync.Read();
251
252             }
253
254             return; 
255         }
256         private DataTable GetTable(string tableName, string ns) {
257             if (tables == null)
258                 return dataSet.Tables.GetTable(tableName, ns);
259
260             if (tables.Count == 0)
261                 return (DataTable)tables[0];
262             
263             for(int i = 0; i < tables.Count; i++) {
264                 DataTable dt = (DataTable)tables[i];
265                 if ((string.Compare(dt.TableName, tableName, StringComparison.Ordinal) == 0)
266                     && (string.Compare(dt.Namespace, ns, StringComparison.Ordinal) == 0))
267                     return dt;
268             }
269             return null;
270         }
271         
272         private int ReadOldRowData(DataSet ds, ref DataTable table, ref int pos, XmlReader row) {
273             // read table information
274             if (ds != null) {
275                 table = ds.Tables.GetTable(XmlConvert.DecodeName(row.LocalName), row.NamespaceURI);
276             }
277             else {
278                 table = GetTable(XmlConvert.DecodeName(row.LocalName), row.NamespaceURI);
279             }
280
281             if (table == null) {
282                 row.Skip(); // need to skip this element if we dont know about it, before returning -1
283                 return -1;
284             }
285             
286             int iRowDepth = row.Depth;
287             string value = null;
288
289             if (table == null)
290                 throw ExceptionBuilder.DiffgramMissingTable(XmlConvert.DecodeName(row.LocalName));
291
292             
293             value = row.GetAttribute(Keywords.ROWORDER, Keywords.MSDNS);
294             if (!Common.ADP.IsEmpty(value)) {
295                 pos = (Int32) Convert.ChangeType(value, typeof(Int32), null);
296             }
297
298             int record = table.NewRecord();
299             foreach (DataColumn col in table.Columns) {
300                 col[record] = DBNull.Value;
301             }
302
303             foreach (DataColumn col in table.Columns) {
304                 if ((col.ColumnMapping == MappingType.Element) ||
305                     (col.ColumnMapping == MappingType.SimpleContent))
306                     continue;
307
308                 if (col.ColumnMapping == MappingType.Hidden) {
309                     value = row.GetAttribute("hidden"+col.EncodedColumnName, Keywords.MSDNS);
310                 }
311                 else {
312                     value = row.GetAttribute(col.EncodedColumnName, col.Namespace);
313                 }
314
315                 if (value == null) {
316                     continue;
317                 }
318
319                 col[record] = col.ConvertXmlToObject(value);
320             }
321
322             row.Read();
323             SkipWhitespaces(row);
324
325             int currentDepth = row.Depth;
326             if (currentDepth <= iRowDepth) {
327                 // the node is empty
328                 if (currentDepth == iRowDepth && row.NodeType == XmlNodeType.EndElement) {
329                     // VSTFDEVDIV 764390: read past the EndElement of the current row
330                     // note: (currentDepth == iRowDepth) check is needed so we do not skip elements on parent rows.
331                     row.Read();
332                     SkipWhitespaces(row);
333                 }
334                 return record;
335             }
336
337             if (table.XmlText != null) {
338                 DataColumn col = table.XmlText;
339                 col[record] = col.ConvertXmlToObject(row.ReadString());
340             }
341             else {
342                 while (row.Depth > iRowDepth)  {
343                     String ln =XmlConvert.DecodeName( row.LocalName) ;
344                     String ns = row.NamespaceURI;
345                     DataColumn column = table.Columns[ln, ns];
346
347                     if (column == null) {
348                         while((row.NodeType != XmlNodeType.EndElement) && (row.LocalName!=ln) && (row.NamespaceURI!=ns))
349                             row.Read(); // consume the current node
350                         row.Read(); // now points to the next column
351                         //SkipWhitespaces(row); seems no need, just in case if we see other issue , this will be here as hint
352                         continue;// add a read here!
353                     }
354
355                     if (column.IsCustomType) {
356                         // if column's type is object or column type does not implement IXmlSerializable
357                         bool isPolymorphism = (column.DataType == typeof(Object)|| (row.GetAttribute(Keywords.MSD_INSTANCETYPE, Keywords.MSDNS) != null) ||
358                         (row.GetAttribute(Keywords.TYPE, Keywords.XSINS) != null)) ;
359
360                         bool skipped = false;
361                         if (column.Table.DataSet != null && column.Table.DataSet.UdtIsWrapped) {
362                             row.Read(); // if UDT is wrapped, skip the wrapper
363                             skipped = true;
364                         }
365
366                         XmlRootAttribute xmlAttrib = null;
367
368                         if (!isPolymorphism && !column.ImplementsIXMLSerializable) { // THIS 
369                             // if does not implement IXLSerializable, need to go with XmlSerializer: pass XmlRootAttribute
370                             if (skipped) {
371                                 xmlAttrib = new XmlRootAttribute(row.LocalName);
372                                 xmlAttrib.Namespace = row.NamespaceURI ;
373                             }
374                             else {
375                                 xmlAttrib = new XmlRootAttribute(column.EncodedColumnName);
376                                 xmlAttrib.Namespace = column.Namespace;
377                             }
378                         }
379                         // for else case xmlAttrib MUST be null
380                         column[record] = column.ConvertXmlToObject(row, xmlAttrib); // you need to pass null XmlAttib here
381
382                         
383                         if (skipped) {
384                             row.Read(); // if Wrapper is skipped, skip its end tag
385                         }
386                     }
387                     else {
388                         int iColumnDepth = row.Depth;
389                         row.Read();
390                         
391                         // SkipWhitespaces(row);seems no need, just in case if we see other issue , this will be here as hint
392                         if (row.Depth > iColumnDepth) { //we are inside the column
393                             if (row.NodeType == XmlNodeType.Text || row.NodeType == XmlNodeType.Whitespace || row.NodeType == XmlNodeType.SignificantWhitespace) {
394                                 String text = row.ReadString();
395                                 column[record] = column.ConvertXmlToObject(text);
396
397                                 row.Read(); // now points to the next column
398                             }
399                         }
400                         else {
401                             // <element></element> case
402                             if (column.DataType == typeof(string))
403                                 column[record] = string.Empty;
404                         }
405                     }
406                 }
407             }
408             row.Read(); //now it should point to next row
409             SkipWhitespaces(row);
410             return record;
411         }
412
413         internal void SkipWhitespaces(XmlReader reader) {
414             while (reader.NodeType == XmlNodeType.Whitespace || reader.NodeType == XmlNodeType.SignificantWhitespace) {
415                 reader.Read();
416             }    
417         }
418     }
419 }