1 //------------------------------------------------------------------------------
2 // <copyright file="XMLDiffLoader.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
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 //------------------------------------------------------------------------------
10 namespace System.Data {
12 using System.Runtime.Serialization.Formatters;
13 using System.Configuration.Assemblies;
14 using System.Runtime.InteropServices;
15 using System.Diagnostics;
17 using System.Collections;
18 using System.Globalization;
19 using Microsoft.Win32;
20 using System.ComponentModel;
22 using System.Xml.Serialization;
24 internal sealed class XMLDiffLoader {
26 DataSet dataSet = null;
27 DataTable dataTable = null;
29 internal void LoadDiffGram(DataSet ds, XmlReader dataTextReader) {
30 XmlReader reader = DataTextReader.CreateReader(dataTextReader);
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
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
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) ;
54 internal void LoadDiffGram(DataTable dt, XmlReader dataTextReader) {
55 XmlReader reader = DataTextReader.CreateReader(dataTextReader);
57 tables = new ArrayList();
59 CreateTablesHierarchy(dt);
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
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
74 internal void ProcessDiffs(DataSet ds, XmlReader ssync) {
75 DataTable tableBefore;
80 int iSsyncDepth = ssync.Depth;
81 ssync.Read(); // pass the before node.
83 SkipWhitespaces(ssync);
85 while (iSsyncDepth < ssync.Depth) {
91 // the diffgramm always contains sql:before and sql:after pairs
93 int iTempDepth = ssync.Depth;
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)
101 if (tableBefore == null)
102 throw ExceptionBuilder.DiffgramMissingSQL();
104 row = (DataRow)tableBefore.RowDiffId[diffId];
106 row.oldRecord = oldRowRecord ;
107 tableBefore.recordManager[oldRowRecord] = row;
109 row = tableBefore.NewEmptyRow();
110 tableBefore.recordManager[oldRowRecord] = row;
111 row.oldRecord = oldRowRecord;
112 row.newRecord = oldRowRecord;
113 tableBefore.Rows.DiffInsertAt(row, pos);
116 tableBefore.RowDiffId[diffId] = row;
122 internal void ProcessDiffs(ArrayList tableList, XmlReader ssync) {
123 DataTable tableBefore;
128 int iSsyncDepth = ssync.Depth;
129 ssync.Read(); // pass the before node.
131 //SkipWhitespaces(ssync); for given scenario does not require this change, but in fact we should do it.
133 while (iSsyncDepth < ssync.Depth) {
135 string diffId = null;
139 // the diffgramm always contains sql:before and sql:after pairs
141 int iTempDepth = ssync.Depth;
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)
149 if (tableBefore == null)
150 throw ExceptionBuilder.DiffgramMissingSQL();
152 row = (DataRow)tableBefore.RowDiffId[diffId];
155 row.oldRecord = oldRowRecord ;
156 tableBefore.recordManager[oldRowRecord] = row;
158 row = tableBefore.NewEmptyRow();
159 tableBefore.recordManager[oldRowRecord] = row;
160 row.oldRecord = oldRowRecord;
161 row.newRecord = oldRowRecord;
162 tableBefore.Rows.DiffInsertAt(row, pos);
165 tableBefore.RowDiffId[diffId] = row;
174 internal void ProcessErrors(DataSet ds, XmlReader ssync) {
177 int iSsyncDepth = ssync.Depth;
178 ssync.Read(); // pass the before node.
180 while (iSsyncDepth < ssync.Depth) {
181 table = ds.Tables.GetTable(XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI);
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];
195 // throw exception here
196 string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS);
197 row.SetColumnError(col, colError);
202 while ((ssync.NodeType == XmlNodeType.EndElement) && (iSsyncDepth < ssync.Depth) )
210 internal void ProcessErrors(ArrayList dt, XmlReader ssync) {
213 int iSsyncDepth = ssync.Depth;
214 ssync.Read(); // pass the before node.
216 while (iSsyncDepth < ssync.Depth) {
217 table = GetTable(XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI);
219 throw ExceptionBuilder.DiffgramMissingSQL();
221 string diffId = ssync.GetAttribute(Keywords.DIFFID, Keywords.DFFNS);
223 DataRow row = (DataRow)table.RowDiffId[diffId];
225 for(int i = 0; i < dt.Count; i++) {
226 row = (DataRow)((DataTable)dt[i]).RowDiffId[diffId];
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
239 while (iRowDepth < ssync.Depth) {
240 if (XmlNodeType.Element == ssync.NodeType) {
241 DataColumn col = table.Columns[XmlConvert.DecodeName(ssync.LocalName), ssync.NamespaceURI];
243 // throw exception here
244 string colError = ssync.GetAttribute(Keywords.MSD_ERROR, Keywords.DFFNS);
245 row.SetColumnError(col, colError);
249 while ((ssync.NodeType == XmlNodeType.EndElement) && (iSsyncDepth < ssync.Depth) )
256 private DataTable GetTable(string tableName, string ns) {
258 return dataSet.Tables.GetTable(tableName, ns);
260 if (tables.Count == 0)
261 return (DataTable)tables[0];
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))
272 private int ReadOldRowData(DataSet ds, ref DataTable table, ref int pos, XmlReader row) {
273 // read table information
275 table = ds.Tables.GetTable(XmlConvert.DecodeName(row.LocalName), row.NamespaceURI);
278 table = GetTable(XmlConvert.DecodeName(row.LocalName), row.NamespaceURI);
282 row.Skip(); // need to skip this element if we dont know about it, before returning -1
286 int iRowDepth = row.Depth;
290 throw ExceptionBuilder.DiffgramMissingTable(XmlConvert.DecodeName(row.LocalName));
293 value = row.GetAttribute(Keywords.ROWORDER, Keywords.MSDNS);
294 if (!Common.ADP.IsEmpty(value)) {
295 pos = (Int32) Convert.ChangeType(value, typeof(Int32), null);
298 int record = table.NewRecord();
299 foreach (DataColumn col in table.Columns) {
300 col[record] = DBNull.Value;
303 foreach (DataColumn col in table.Columns) {
304 if ((col.ColumnMapping == MappingType.Element) ||
305 (col.ColumnMapping == MappingType.SimpleContent))
308 if (col.ColumnMapping == MappingType.Hidden) {
309 value = row.GetAttribute("hidden"+col.EncodedColumnName, Keywords.MSDNS);
312 value = row.GetAttribute(col.EncodedColumnName, col.Namespace);
319 col[record] = col.ConvertXmlToObject(value);
323 SkipWhitespaces(row);
325 int currentDepth = row.Depth;
326 if (currentDepth <= iRowDepth) {
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.
332 SkipWhitespaces(row);
337 if (table.XmlText != null) {
338 DataColumn col = table.XmlText;
339 col[record] = col.ConvertXmlToObject(row.ReadString());
342 while (row.Depth > iRowDepth) {
343 String ln =XmlConvert.DecodeName( row.LocalName) ;
344 String ns = row.NamespaceURI;
345 DataColumn column = table.Columns[ln, ns];
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!
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)) ;
360 bool skipped = false;
361 if (column.Table.DataSet != null && column.Table.DataSet.UdtIsWrapped) {
362 row.Read(); // if UDT is wrapped, skip the wrapper
366 XmlRootAttribute xmlAttrib = null;
368 if (!isPolymorphism && !column.ImplementsIXMLSerializable) { // THIS
369 // if does not implement IXLSerializable, need to go with XmlSerializer: pass XmlRootAttribute
371 xmlAttrib = new XmlRootAttribute(row.LocalName);
372 xmlAttrib.Namespace = row.NamespaceURI ;
375 xmlAttrib = new XmlRootAttribute(column.EncodedColumnName);
376 xmlAttrib.Namespace = column.Namespace;
379 // for else case xmlAttrib MUST be null
380 column[record] = column.ConvertXmlToObject(row, xmlAttrib); // you need to pass null XmlAttib here
384 row.Read(); // if Wrapper is skipped, skip its end tag
388 int iColumnDepth = row.Depth;
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);
397 row.Read(); // now points to the next column
401 // <element></element> case
402 if (column.DataType == typeof(string))
403 column[record] = string.Empty;
408 row.Read(); //now it should point to next row
409 SkipWhitespaces(row);
413 internal void SkipWhitespaces(XmlReader reader) {
414 while (reader.NodeType == XmlNodeType.Whitespace || reader.NodeType == XmlNodeType.SignificantWhitespace) {