| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300 |
- //------------------------------------------------------------------------------
- // <copyright file="XmlDataLoader.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data {
- using System;
- using System.Collections;
- using System.Data.Common;
- using System.Diagnostics;
- using System.Globalization;
- using System.Text;
- using System.Xml;
- using System.Xml.Serialization;
- internal sealed class XmlDataLoader {
- DataSet dataSet;
- XmlToDatasetMap nodeToSchemaMap = null;
- Hashtable nodeToRowMap;
- Stack childRowsStack = null;
- Hashtable htableExcludedNS = null;
- bool fIsXdr = false;
- internal bool isDiffgram = false;
- DataRow topMostRow = null;
- XmlElement topMostNode = null;
- bool ignoreSchema = false;
- DataTable dataTable;
- bool isTableLevel = false;
- private bool fromInference = false;
- internal XmlDataLoader( DataSet dataset, bool IsXdr, bool ignoreSchema) {
- // Initialization
- this.dataSet = dataset;
- this.nodeToRowMap = new Hashtable();
- this.fIsXdr = IsXdr;
- this.ignoreSchema = ignoreSchema;
- }
- internal XmlDataLoader( DataSet dataset, bool IsXdr, XmlElement topNode, bool ignoreSchema) {
- // Initialization
- this.dataSet = dataset;
- this.nodeToRowMap = new Hashtable();
- this.fIsXdr = IsXdr;
- // Allocate the stack and create the mappings
- childRowsStack = new Stack(50);
- topMostNode = topNode;
- this.ignoreSchema = ignoreSchema;
- }
- internal XmlDataLoader( DataTable datatable, bool IsXdr, bool ignoreSchema) {
- // Initialization
- this.dataSet = null;
- dataTable = datatable;
- isTableLevel = true;
- this.nodeToRowMap = new Hashtable();
- this.fIsXdr = IsXdr;
- this.ignoreSchema = ignoreSchema;
- }
- internal XmlDataLoader( DataTable datatable, bool IsXdr, XmlElement topNode, bool ignoreSchema) {
- // Initialization
- this.dataSet = null;
- dataTable = datatable;
- isTableLevel = true;
- this.nodeToRowMap = new Hashtable();
- this.fIsXdr = IsXdr;
- // Allocate the stack and create the mappings
- childRowsStack = new Stack(50);
- topMostNode = topNode;
- this.ignoreSchema = ignoreSchema;
- }
- internal bool FromInference {
- get {
- return fromInference;
- }
- set {
- fromInference = value;
- }
- }
- // after loading, all detached DataRows are attached to their tables
- private void AttachRows( DataRow parentRow, XmlNode parentElement ) {
- if (parentElement == null)
- return;
- for (XmlNode n = parentElement.FirstChild; n != null; n = n.NextSibling) {
- if (n.NodeType == XmlNodeType.Element) {
- XmlElement e = (XmlElement) n;
- DataRow r = GetRowFromElement( e );
- if (r != null && r.RowState == DataRowState.Detached) {
- if (parentRow != null)
- r.SetNestedParentRow( parentRow, /*setNonNested*/ false );
- r.Table.Rows.Add( r );
- }
- else if (r == null) {
- // n is a 'sugar element'
- AttachRows( parentRow, n );
- }
- // attach all detached rows
- AttachRows( r, n );
- }
- }
- }
- private int CountNonNSAttributes (XmlNode node) {
- int count = 0;
- for (int i = 0; i < node.Attributes.Count; i++) {
- XmlAttribute attr = node.Attributes[i];
- if (!FExcludedNamespace(node.Attributes[i].NamespaceURI))
- count++;
- }
- return count;
- }
- private string GetValueForTextOnlyColums( XmlNode n ) {
- string value = null;
- // don't consider whitespace
- while (n != null && (n.NodeType == XmlNodeType.Whitespace || !IsTextLikeNode(n.NodeType))) {
- n = n.NextSibling;
- }
-
- if (n != null) {
- if (IsTextLikeNode( n.NodeType ) && (n.NextSibling == null || !IsTextLikeNode( n.NodeType ))) {
- // don't use string builder if only one text node exists
- value = n.Value;
- n = n.NextSibling;
- }
- else {
- StringBuilder sb = new StringBuilder();
- while (n != null && IsTextLikeNode( n.NodeType )) {
- sb.Append( n.Value );
- n = n.NextSibling;
- }
- value = sb.ToString();
- }
- }
- if (value == null)
- value = String.Empty;
- return value;
- }
- private string GetInitialTextFromNodes( ref XmlNode n ) {
- string value = null;
- if (n != null) {
- // don't consider whitespace
- while (n.NodeType == XmlNodeType.Whitespace)
- n = n.NextSibling;
- if (IsTextLikeNode( n.NodeType ) && (n.NextSibling == null || !IsTextLikeNode( n.NodeType ))) {
- // don't use string builder if only one text node exists
- value = n.Value;
- n = n.NextSibling;
- }
- else {
- StringBuilder sb = new StringBuilder();
- while (n != null && IsTextLikeNode( n.NodeType )) {
- sb.Append( n.Value );
- n = n.NextSibling;
- }
- value = sb.ToString();
- }
- }
- if (value == null)
- value = String.Empty;
- return value;
- }
- private DataColumn GetTextOnlyColumn( DataRow row ) {
- DataColumnCollection columns = row.Table.Columns;
- int cCols = columns.Count;
- for (int iCol = 0; iCol < cCols; iCol++) {
- DataColumn c = columns[iCol];
- if (IsTextOnly( c ))
- return c;
- }
- return null;
- }
- internal DataRow GetRowFromElement( XmlElement e ) {
- return(DataRow) nodeToRowMap[e];
- }
- internal bool FColumnElement(XmlElement e) {
- if (nodeToSchemaMap.GetColumnSchema(e, FIgnoreNamespace(e)) == null)
- return false;
- if (CountNonNSAttributes(e) > 0)
- return false;
- for (XmlNode tabNode = e.FirstChild; tabNode != null; tabNode = tabNode.NextSibling)
- if (tabNode is XmlElement)
- return false;
- return true;
- }
-
- private bool FExcludedNamespace(string ns) {
- if (ns.Equals(Keywords.XSD_XMLNS_NS))
- return true;
-
- if (htableExcludedNS == null)
- return false;
-
- return htableExcludedNS.Contains(ns);
- }
- private bool FIgnoreNamespace(XmlNode node) {
- XmlNode ownerNode;
- if (!fIsXdr)
- return false;
- if (node is XmlAttribute)
- ownerNode = ((XmlAttribute)node).OwnerElement;
- else
- ownerNode = node;
- if (ownerNode.NamespaceURI.StartsWith("x-schema:#", StringComparison.Ordinal))
- return true;
- else
- return false;
- }
-
- private bool FIgnoreNamespace(XmlReader node) {
- if (fIsXdr && node.NamespaceURI.StartsWith("x-schema:#", StringComparison.Ordinal))
- return true;
- else
- return false;
- }
- internal bool IsTextLikeNode( XmlNodeType n ) {
- switch (n) {
- case XmlNodeType.EntityReference:
- throw ExceptionBuilder.FoundEntity();
-
- case XmlNodeType.Text:
- case XmlNodeType.Whitespace:
- case XmlNodeType.SignificantWhitespace:
- case XmlNodeType.CDATA:
- return true;
- default:
- return false;
- }
- }
- internal bool IsTextOnly( DataColumn c ) {
- if (c.ColumnMapping != MappingType.SimpleContent)
- return false;
- else
- return true;
- }
- internal void LoadData( XmlDocument xdoc ) {
- if (xdoc.DocumentElement == null)
- return;
- bool saveEnforce;
- if (isTableLevel) {
- saveEnforce = dataTable.EnforceConstraints;
- dataTable.EnforceConstraints = false;
- }
- else {
- saveEnforce = dataSet.EnforceConstraints;
- dataSet.EnforceConstraints = false;
- dataSet.fInReadXml = true;
- }
- if (isTableLevel) {
- nodeToSchemaMap = new XmlToDatasetMap(dataTable, xdoc.NameTable);
- }
- else {
- nodeToSchemaMap = new XmlToDatasetMap(dataSet, xdoc.NameTable);
- }
- /*
- // Top level table or dataset ?
- XmlElement rootElement = xdoc.DocumentElement;
- Hashtable tableAtoms = new Hashtable();
- XmlNode tabNode;
- if (CountNonNSAttributes (rootElement) > 0)
- dataSet.fTopLevelTable = true;
- else {
- for (tabNode = rootElement.FirstChild; tabNode != null; tabNode = tabNode.NextSibling) {
- if (tabNode is XmlElement && tabNode.LocalName != Keywords.XSD_SCHEMA) {
- object value = tableAtoms[QualifiedName (tabNode.LocalName, tabNode.NamespaceURI)];
- if (value == null || (bool)value == false) {
- dataSet.fTopLevelTable = true;
- break;
- }
- }
- }
- }
- */
- DataRow topRow = null;
- if (isTableLevel ||(dataSet!= null && dataSet.fTopLevelTable) ){
- XmlElement e = xdoc.DocumentElement;
- DataTable topTable = (DataTable) nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e));
- if (topTable != null) {
- topRow = topTable.CreateEmptyRow(); //enzol perf
- nodeToRowMap[ e ] = topRow;
- // get all field values.
- LoadRowData( topRow, e );
- topTable.Rows.Add(topRow);
- }
- }
- LoadRows( topRow, xdoc.DocumentElement );
- AttachRows( topRow, xdoc.DocumentElement );
- if (isTableLevel) {
- dataTable.EnforceConstraints = saveEnforce;
- }
- else {
- dataSet.fInReadXml = false;
- dataSet.EnforceConstraints = saveEnforce;
- }
- }
- private void LoadRowData(DataRow row, XmlElement rowElement) {
- XmlNode n;
- DataTable table = row.Table;
- if (FromInference)
- table.Prefix = rowElement.Prefix;
- // keep a list of all columns that get updated
- Hashtable foundColumns = new Hashtable();
- row.BeginEdit();
- // examine all children first
- n = rowElement.FirstChild;
- // Look for data to fill the TextOnly column
- DataColumn column = GetTextOnlyColumn( row );
- if (column != null) {
- foundColumns[column] = column;
- string text = GetValueForTextOnlyColums( n ) ;
- if (XMLSchema.GetBooleanAttribute(rowElement, Keywords.XSI_NIL, Keywords.XSINS, false) && Common.ADP.IsEmpty(text) )
- row[column] = DBNull.Value;
- else
- SetRowValueFromXmlText( row, column, text );
- }
- // Walk the region to find elements that map to columns
- while (n != null && n != rowElement) {
- if (n.NodeType == XmlNodeType.Element) {
- XmlElement e = (XmlElement) n;
- object schema = nodeToSchemaMap.GetSchemaForNode( e, FIgnoreNamespace(e) );
- if (schema is DataTable) {
- if (FColumnElement(e))
- schema = nodeToSchemaMap.GetColumnSchema( e, FIgnoreNamespace(e) );
- }
-
- // if element has its own table mapping, it is a separate region
- if (schema == null || schema is DataColumn) {
- // descend to examine child elements
- n = e.FirstChild;
- if (schema != null && schema is DataColumn) {
- DataColumn c = (DataColumn) schema;
- if (c.Table == row.Table && c.ColumnMapping != MappingType.Attribute && foundColumns[c] == null) {
- foundColumns[c] = c;
- string text = GetValueForTextOnlyColums( n ) ;
- if (XMLSchema.GetBooleanAttribute(e, Keywords.XSI_NIL, Keywords.XSINS, false) && Common.ADP.IsEmpty(text) )
- row[c] = DBNull.Value;
- else
- SetRowValueFromXmlText( row, c, text );
- }
- }
- else if ((schema == null) && (n!=null)) {
- continue;
- }
- // nothing left down here, continue from element
- if (n == null)
- n = e;
- }
- }
- // if no more siblings, ascend back toward original element (rowElement)
- while (n != rowElement && n.NextSibling == null) {
- n = n.ParentNode;
- }
- if (n != rowElement)
- n = n.NextSibling;
- }
- //
- // Walk the attributes to find attributes that map to columns.
- //
- foreach( XmlAttribute attr in rowElement.Attributes ) {
- object schema = nodeToSchemaMap.GetColumnSchema( attr, FIgnoreNamespace(attr) );
- if (schema != null && schema is DataColumn) {
- DataColumn c = (DataColumn) schema;
- if (c.ColumnMapping == MappingType.Attribute && foundColumns[c] == null) {
- foundColumns[c] = c;
- n = attr.FirstChild;
- SetRowValueFromXmlText( row, c, GetInitialTextFromNodes( ref n ) );
- }
- }
- }
- // Null all columns values that aren't represented in the tree
- foreach( DataColumn c in row.Table.Columns ) {
- if (foundColumns[c] == null && XmlToDatasetMap.IsMappedColumn(c)) {
- if (!c.AutoIncrement) {
- if (c.AllowDBNull) {
- row[c] = DBNull.Value;
- }
- else {
- row[c] = c.DefaultValue;
- }
- }
- else {
- c.Init(row.tempRecord);
- }
- }
- }
- row.EndEdit();
- }
-
- // load all data from tree structre into datarows
- private void LoadRows( DataRow parentRow, XmlNode parentElement ) {
- if (parentElement == null)
- return;
- // Skip schema node as well
- if (parentElement.LocalName == Keywords.XSD_SCHEMA && parentElement.NamespaceURI == Keywords.XSDNS ||
- parentElement.LocalName == Keywords.SQL_SYNC && parentElement.NamespaceURI == Keywords.UPDGNS ||
- parentElement.LocalName == Keywords.XDR_SCHEMA && parentElement.NamespaceURI == Keywords.XDRNS)
- return;
- for (XmlNode n = parentElement.FirstChild; n != null; n = n.NextSibling) {
- if (n is XmlElement) {
- XmlElement e = (XmlElement) n;
- object schema = nodeToSchemaMap.GetSchemaForNode( e, FIgnoreNamespace(e) );
- if (schema != null && schema is DataTable) {
- DataRow r = GetRowFromElement( e );
- if (r == null) {
- // skip columns which has the same name as another table
- if (parentRow != null && FColumnElement(e))
- continue;
-
- r = ((DataTable)schema).CreateEmptyRow();
- nodeToRowMap[ e ] = r;
- // get all field values.
- LoadRowData( r, e );
- }
- // recurse down to inner elements
- LoadRows( r, n );
- }
- else {
- // recurse down to inner elements
- LoadRows( null, n );
- }
- }
- }
- }
- private void SetRowValueFromXmlText( DataRow row, DataColumn col, string xmlText ) {
- row[col] = col.ConvertXmlToObject(xmlText);
- }
- internal void LoadTopMostRow(ref bool[] foundColumns) {
- // Attempt to load row from top node we backed up in DataSet.ReadXml()
- // In most cases it contains the DataSet name and no information
- // Check if DataSet object matches the top node (it won't in most cases)
- Object obj = nodeToSchemaMap.GetSchemaForNode(topMostNode,FIgnoreNamespace(topMostNode));
-
- if (obj is DataTable) { // It's a table? Load it.
- DataTable table = (DataTable) obj;
- topMostRow = table.CreateEmptyRow();
-
- foundColumns = new bool[topMostRow.Table.Columns.Count];
- //
- // Walk the attributes to find attributes that map to columns.
- //
- foreach( XmlAttribute attr in topMostNode.Attributes ) {
- object schema = nodeToSchemaMap.GetColumnSchema( attr, FIgnoreNamespace(attr) );
- if (schema != null && schema is DataColumn) {
- DataColumn c = (DataColumn) schema;
- if (c.ColumnMapping == MappingType.Attribute) {
- XmlNode n = attr.FirstChild;
- SetRowValueFromXmlText( topMostRow, c, GetInitialTextFromNodes( ref n ) );
- foundColumns[c.Ordinal] = true;
- }
- }
- }
- }
- topMostNode = null;
- }
- private XmlReader dataReader = null;
- private object XSD_XMLNS_NS;
- private object XDR_SCHEMA;
- private object XDRNS;
- private object SQL_SYNC;
- private object UPDGNS;
- private object XSD_SCHEMA;
- private object XSDNS;
- private object DFFNS;
- private object MSDNS;
- private object DIFFID;
- private object HASCHANGES;
- private object ROWORDER;
- private void InitNameTable() {
- XmlNameTable nameTable = dataReader.NameTable;
- XSD_XMLNS_NS = nameTable.Add(Keywords.XSD_XMLNS_NS);
- XDR_SCHEMA = nameTable.Add(Keywords.XDR_SCHEMA);
- XDRNS = nameTable.Add(Keywords.XDRNS);
- SQL_SYNC = nameTable.Add(Keywords.SQL_SYNC);
- UPDGNS = nameTable.Add(Keywords.UPDGNS);
- XSD_SCHEMA = nameTable.Add(Keywords.XSD_SCHEMA);
- XSDNS = nameTable.Add(Keywords.XSDNS);
- DFFNS = nameTable.Add(Keywords.DFFNS);
- MSDNS = nameTable.Add(Keywords.MSDNS);
- DIFFID = nameTable.Add(Keywords.DIFFID);
- HASCHANGES = nameTable.Add(Keywords.HASCHANGES);
- ROWORDER = nameTable.Add(Keywords.ROWORDER);
- }
- internal void LoadData(XmlReader reader) {
- dataReader = DataTextReader.CreateReader(reader);
- int entryDepth = dataReader.Depth; // Store current XML element depth so we'll read
- // correct portion of the XML and no more
- bool fEnforce = isTableLevel ? dataTable.EnforceConstraints : dataSet.EnforceConstraints;
- // Keep constraints status for datataset/table
- InitNameTable(); // Adds DataSet namespaces to reader's nametable
- if (nodeToSchemaMap == null) { // Create XML to dataset map
- nodeToSchemaMap = isTableLevel ? new XmlToDatasetMap(dataReader.NameTable, dataTable) :
- new XmlToDatasetMap(dataReader.NameTable, dataSet);
- }
- if (isTableLevel) {
- dataTable.EnforceConstraints = false; // Disable constraints
- }
- else {
- dataSet.EnforceConstraints = false; // Disable constraints
- dataSet.fInReadXml = true; // We're in ReadXml now
- }
- if (topMostNode != null) { // Do we have top node?
- if (!isDiffgram && !isTableLevel) { // Not a diffgram and not DataSet?
- DataTable table = nodeToSchemaMap.GetSchemaForNode(topMostNode, FIgnoreNamespace(topMostNode)) as DataTable;
- // Try to match table in the dataset to this node
- if (table != null) { // Got the table ?
- LoadTopMostTable(table); // Load top most node
- }
- }
- topMostNode = null; // topMostNode is no more. Good riddance.
- }
- while( !dataReader.EOF ) { // Main XML parsing loop. Check for EOF just in case.
- if (dataReader.Depth < entryDepth) // Stop if we have consumed all elements allowed
- break;
- if ( reader.NodeType != XmlNodeType.Element ) { // Read till Element is found
- dataReader.Read();
- continue;
- }
- DataTable table = nodeToSchemaMap.GetTableForNode(dataReader, FIgnoreNamespace(dataReader));
- // Try to get table for node
- if (table == null) { // Read till table is found
- if (!ProcessXsdSchema()) // Check for schemas...
- dataReader.Read(); // Not found? Read next element.
- continue;
- }
- LoadTable(table, false /* isNested */); // Here goes -- load data for this table
- // This is a root table, so it's not nested
- }
- if (isTableLevel) {
- dataTable.EnforceConstraints = fEnforce; // Restore constraints and return
- }
- else {
- dataSet.fInReadXml = false; // We're done.
- dataSet.EnforceConstraints = fEnforce; // Restore constraints and return
- }
- }
- // Loads a top most table.
- // This is neded because desktop is capable of loading almost anything into the dataset.
- // The top node could be a DataSet element or a Table element. To make things worse,
- // you could have a table with the same name as dataset.
- // Here's how we're going to dig into this mess:
- //
- // TopNode is null ?
- // / No \ Yes
- // Table matches TopNode ? Current node is the table start
- // / No \ Yes (LoadTopMostTable called in this case only)
- // Current node is the table start DataSet name matches one of the tables ?
- // TopNode is dataset node / Yes \ No
- // / TopNode is the table
- // Current node matches column or nested table in the table ? and current node
- // / No \ Yes is a column or a
- // TopNode is DataSet TopNode is table nested table
- //
- // Yes, it is terrible and I don't like it also..
-
- private void LoadTopMostTable(DataTable table) {
-
- // /------------------------------- This one is in topMostNode (backed up to XML DOM)
- // <Table> /----------------------------- We are here on entrance
- // <Column>Value</Column>
- // <AnotherColumn>Value</AnotherColumn>
- // </Table> ...
- // \------------------------------ We are here on exit
- Debug.Assert (table != null, "Table to be loaded is null on LoadTopMostTable() entry");
- Debug.Assert (topMostNode != null, "topMostNode is null on LoadTopMostTable() entry");
- Debug.Assert (!isDiffgram, "Diffgram mode is on while we have topMostNode table. This is bad." );
- bool topNodeIsTable = isTableLevel || (dataSet.DataSetName != table.TableName);
- // If table name we have matches dataset
- // name top node could be a DataSet OR a table.
- // It's a table overwise.
- DataRow row = null; // Data row we're going to add to this table
- bool matchFound = false; // Assume we found no matching elements
- int entryDepth = dataReader.Depth - 1; // Store current reader depth so we know when to stop reading
- // Adjust depth by one as we've read top most element
- // outside this method.
- string textNodeValue; // Value of a text node we might have
- Debug.Assert (entryDepth >= 0, "Wrong entry Depth for top most element." );
- int entryChild = childRowsStack.Count; // Memorize child stack level on entry
-
- DataColumn c; // Hold column here
- DataColumnCollection collection = table.Columns; // Hold column collectio here
- object[] foundColumns = new object[collection.Count];
- // This is the columns data we might find
- XmlNode n; // Need this to pass by reference
- foreach( XmlAttribute attr in topMostNode.Attributes ) {
- // Check all attributes in this node
- c = nodeToSchemaMap.GetColumnSchema( attr, FIgnoreNamespace(attr)) as DataColumn;
- // Try to mach attribute to column
- if ((c != null) && (c.ColumnMapping == MappingType.Attribute)) {
- // If it's a column with attribute mapping
- n = attr.FirstChild;
- foundColumns[c.Ordinal] = c.ConvertXmlToObject(GetInitialTextFromNodes(ref n));
- // Get value
- matchFound = true; // and note we found a matching element
- }
- }
- // Now handle elements. This could be columns or nested tables
- // We'll skip the rest as we have no idea what to do with it.
- // Note: we do not need to read first as we're already as it has been done by caller.
-
- while (entryDepth < dataReader.Depth ) {
- switch (dataReader.NodeType) { // Process nodes based on type
- case XmlNodeType.Element: // It's an element
- object o = nodeToSchemaMap.GetColumnSchema(table, dataReader, FIgnoreNamespace(dataReader));
- // Get dataset element for this XML element
- c = o as DataColumn; // Perhaps, it's a column?
- if ( c != null ) { // Do we have matched column in this table?
- // Let's load column data
- if (foundColumns[c.Ordinal] == null) {
- // If this column was not found before
- LoadColumn (c, foundColumns); // Get column value.
- matchFound = true; // Got matched row.
- }
- else {
- dataReader.Read(); // Advance to next element.
- }
- }
- else {
- DataTable nestedTable = o as DataTable;
- // Perhaps, it's a nested table ?
- if ( nestedTable != null ) { // Do we have matched table in DataSet ?
- LoadTable (nestedTable, true /* isNested */);
- // Yes. Load nested table (recursive)
- matchFound = true; // Got matched nested table
- }
- else if (ProcessXsdSchema()) { // Check for schema. Skip or load if found.
- continue; // Schema has been found. Process the next element
- // we're already at (done by schema processing).
- }
- else { // Not a table or column in this table ?
- if (!(matchFound || topNodeIsTable)) {
- // Could top node be a DataSet?
- return; // Assume top node is DataSet
- // and stop top node processing
- }
- dataReader.Read(); // Continue to the next element.
- }
- }
- break;
- // Oops. Not supported
- case XmlNodeType.EntityReference: // Oops. No support for Entity Reference
- throw ExceptionBuilder.FoundEntity();
- case XmlNodeType.Text: // It looks like a text.
- case XmlNodeType.Whitespace: // This actually could be
- case XmlNodeType.CDATA: // if we have XmlText in our table
- case XmlNodeType.SignificantWhitespace:
- textNodeValue = dataReader.ReadString();
- // Get text node value.
- c = table.xmlText; // Get XML Text column from our table
- if (c != null && foundColumns[c.Ordinal] == null) {
- // If XmlText Column is set
- // and we do not have data already
- foundColumns[c.Ordinal] = c.ConvertXmlToObject(textNodeValue);
- // Read and store the data
- }
- break;
- default:
- dataReader.Read(); // We don't process that, skip to the next element.
- break;
- }
- }
- dataReader.Read(); // Proceed to the next element.
- // It's the time to populate row with loaded data and add it to the table we'we just read to the table
- for ( int i = foundColumns.Length -1; i >= 0; --i) {
- // Check all columns
- if (null == foundColumns[i]) { // Got data for this column ?
- c = collection[i]; // No. Get column for this index
- if (c.AllowDBNull && c.ColumnMapping != MappingType.Hidden && !c.AutoIncrement) {
- foundColumns[i] = DBNull.Value; // Assign DBNull if possible
- // table.Rows.Add() below will deal
- // with default values and autoincrement
- }
- }
- }
- row = table.Rows.AddWithColumnEvents(foundColumns); // Create, populate and add row
- while (entryChild < childRowsStack.Count) { // Process child rows we might have
- DataRow childRow = (DataRow) childRowsStack.Pop();
- // Get row from the stack
- bool unchanged = (childRow.RowState == DataRowState.Unchanged);
- // Is data the same as before?
- childRow.SetNestedParentRow(row, /*setNonNested*/ false);
- // Set parent row
- if (unchanged) // Restore record if child row's unchanged
- childRow.oldRecord = childRow.newRecord;
- }
- }
- // Loads a table.
- // Yes, I know it's a big method. This is done to avoid performance penalty of calling methods
- // with many arguments and to keep recursion within one method only. To make code readable,
- // this method divided into 3 parts: attribute processing (including diffgram),
- // nested elements processing and loading data. Please keep it this way.
- private void LoadTable(DataTable table, bool isNested ) {
- // <DataSet> /--------------------------- We are here on entrance
- // <Table>
- // <Column>Value</Column>
- // <AnotherColumn>Value</AnotherColumn>
- // </Table> /-------------------------- We are here on exit
- // <AnotherTable>
- // ...
- // </AnotherTable>
- // ...
- // </DataSet>
- Debug.Assert (table != null, "Table to be loaded is null on LoadTable() entry");
- DataRow row = null; // Data row we're going to add to this table
- int entryDepth = dataReader.Depth; // Store current reader depth so we know when to stop reading
- int entryChild = childRowsStack.Count; // Memorize child stack level on entry
- DataColumn c; // Hold column here
- DataColumnCollection collection = table.Columns; // Hold column collectio here
- object[] foundColumns = new object[collection.Count];
- // This is the columns data we found
- // This is used to process diffgramms
- int rowOrder = -1; // Row to insert data to
- string diffId = String.Empty; // Diffgram ID string
- string hasChanges = null; // Changes string
- bool hasErrors = false; // Set this in case of problem
- string textNodeValue; // Value of a text node we might have
- // Process attributes first
- for ( int i = dataReader.AttributeCount -1; i >= 0; --i) {
- // Check all attributes one by one
- dataReader.MoveToAttribute(i); // Get this attribute
- c = nodeToSchemaMap.GetColumnSchema(table, dataReader, FIgnoreNamespace(dataReader)) as DataColumn;
- // Try to get column for this attribute
- if ((c != null) && (c.ColumnMapping == MappingType.Attribute)) {
- // Yep, it is a column mapped as attribute
- // Get value from XML and store it in the object array
- foundColumns[c.Ordinal] = c.ConvertXmlToObject(dataReader.Value);
- } // Oops. No column for this element
-
- //
- // else if (table.XmlText != null &&
- // dataReader.NamespaceURI == Keywords.XSINS &&
- // dataReader.LocalName == Keywords.XSI_NIL ) {
- // // Got XMLText column and it's a NIL attribute?
- // if (XmlConvert.ToBoolean(dataReader.Value)) {
- // // If NIL attribute set to true...
- // // Assign DBNull to XmlText column
- // foundColumns[table.XmlText.Ordinal] = DBNull.Value;
- // }
- // }
- if ( isDiffgram ) { // Now handle some diffgram attributes
- if ( dataReader.NamespaceURI == Keywords.DFFNS ) {
- switch (dataReader.LocalName) {
- case Keywords.DIFFID: // Is it a diffgeam ID ?
- diffId = dataReader.Value; // Store ID
- break;
- case Keywords.HASCHANGES: // Has chages attribute ?
- hasChanges = dataReader.Value; // Store value
- break;
- case Keywords.HASERRORS: // Has errors attribute ?
- hasErrors = (bool)Convert.ChangeType(dataReader.Value, typeof(bool), CultureInfo.InvariantCulture);
- // Store value
- break;
- }
- }
- else if ( dataReader.NamespaceURI == Keywords.MSDNS ) {
- if ( dataReader.LocalName == Keywords.ROWORDER ) {
- // Is it a row order attribute ?
- rowOrder = (Int32)Convert.ChangeType(dataReader.Value, typeof(Int32), CultureInfo.InvariantCulture);
- // Store it
- } else if (dataReader.LocalName.StartsWith("hidden", StringComparison.Ordinal)) {
- // Hidden column ?
- c = collection[XmlConvert.DecodeName(dataReader.LocalName.Substring(6))];
- // Let's see if we have one.
- // We have to decode name before we look it up
- // We could not use XmlToDataSet map as it contains
- // no hidden columns
- if (( c != null) && (c.ColumnMapping == MappingType.Hidden)) {
- // Got column and it is hidden ?
- foundColumns[c.Ordinal] = c.ConvertXmlToObject(dataReader.Value);
- }
- }
- }
- }
- } // Done with attributes
- // Now handle elements. This could be columns or nested tables.
- // <DataSet> /------------------- We are here after dealing with attributes
- // <Table foo="FooValue" bar="BarValue">
- // <Column>Value</Column>
- // <AnotherColumn>Value</AnotherColumn>
- // </Table>
- // </DataSet>
- if ( dataReader.Read() && entryDepth < dataReader.Depth) {
- // Read to the next element and see if we're inside
- while ( entryDepth < dataReader.Depth ) { // Get out as soon as we've processed all nested nodes.
- switch (dataReader.NodeType) { // Process nodes based on type
- case XmlNodeType.Element: // It's an element
- object o = nodeToSchemaMap.GetColumnSchema(table, dataReader, FIgnoreNamespace(dataReader));
- // Get dataset element for this XML element
- c = o as DataColumn; // Perhaps, it's a column?
- if ( c != null ) { // Do we have matched column in this table?
- // Let's load column data
- if (foundColumns[c.Ordinal] == null) {
- // If this column was not found before
- LoadColumn (c, foundColumns);
- // Get column value
- }
- else {
- dataReader.Read(); // Advance to next element.
- }
- }
- else {
- DataTable nestedTable = o as DataTable;
- // Perhaps, it's a nested table ?
- if ( nestedTable != null ) { // Do we have matched nested table in DataSet ?
- LoadTable (nestedTable, true /* isNested */);
- // Yes. Load nested table (recursive)
- } // Not a table nor column? Check if it's schema.
- else if (ProcessXsdSchema()) { // Check for schema. Skip or load if found.
- continue; // Schema has been found. Process the next element
- // we're already at (done by schema processing).
- }
- else {
- // We've got element which is not supposed to he here according to the schema.
- // That might be a table which was misplaced. We should've thrown on that,
- // but we'll try to load it so we could keep compatibility.
- // We won't try to match to columns as we have no idea
- // which table this potential column might belong to.
- DataTable misplacedTable = nodeToSchemaMap.GetTableForNode(dataReader, FIgnoreNamespace(dataReader));
- // Try to get table for node
- if (misplacedTable != null) { // Got some matching table?
- LoadTable (misplacedTable, false /* isNested */);
- // While table's XML element is nested,
- // the table itself is not. Load it this way.
- }
- else {
- dataReader.Read(); // Not a table? Try next element.
- }
- }
- }
- break;
- case XmlNodeType.EntityReference: // Oops. No support for Entity Reference
- throw ExceptionBuilder.FoundEntity();
- case XmlNodeType.Text: // It looks like a text.
- case XmlNodeType.Whitespace: // This actually could be
- case XmlNodeType.CDATA: // if we have XmlText in our table
- case XmlNodeType.SignificantWhitespace:
- textNodeValue = dataReader.ReadString();
- // Get text node value.
- c = table.xmlText; // Get XML Text column from our table
- if (c != null && foundColumns[c.Ordinal] == null) {
- // If XmlText Column is set
- // and we do not have data already
- foundColumns[c.Ordinal] = c.ConvertXmlToObject(textNodeValue);
- // Read and store the data
- }
- break;
- default:
- dataReader.Read(); // We don't process that, skip to the next element.
- break;
- }
- }
- dataReader.Read(); // We're done here, proceed to the next element.
- }
- // It's the time to populate row with loaded data and add it to the table we'we just read to the table
- if (isDiffgram) { // In case of diffgram
- row = table.NewRow(table.NewUninitializedRecord());
- // just create an empty row
- row.BeginEdit(); // and allow it's population with data
- for ( int i = foundColumns.Length - 1; i >= 0 ; --i) {
- // Check all columns
- c = collection[i]; // Get column for this index
- c[row.tempRecord] = null != foundColumns[i] ? foundColumns[i] : DBNull.Value;
- // Set column to loaded value of to
- // DBNull if value is missing.
- }
- row.EndEdit(); // Done with this row
- table.Rows.DiffInsertAt(row, rowOrder); // insert data to specific location
- // And do some diff processing
- if (hasChanges == null) { // No changes ?
- row.oldRecord = row.newRecord; // Restore old record
- }
- if ((hasChanges == Keywords.MODIFIED) || hasErrors) {
- table.RowDiffId[diffId] = row;
- }
- }
- else {
- for ( int i = foundColumns.Length -1; i >= 0 ; --i) {
- // Check all columns
- if (null == foundColumns[i]) { // Got data for this column ?
- c = collection[i]; // No. Get column for this index
- if (c.AllowDBNull && c.ColumnMapping != MappingType.Hidden && !c.AutoIncrement) {
- foundColumns[i] = DBNull.Value; // Assign DBNull if possible
- // table.Rows.Add() below will deal
- // with default values and autoincrement
- }
- }
- }
- row = table.Rows.AddWithColumnEvents(foundColumns); // Create, populate and add row
- }
- // Data is loaded into the row and row is added to the table at this point
- while (entryChild < childRowsStack.Count) { // Process child rows we might have
- DataRow childRow = (DataRow) childRowsStack.Pop();
- // Get row from the stack
- bool unchanged = (childRow.RowState == DataRowState.Unchanged);
- // Is data the same as before?
- childRow.SetNestedParentRow(row, /*setNonNested*/ false);
- // Set parent row
- if (unchanged) // Restore record if child row's unchanged
- childRow.oldRecord = childRow.newRecord;
- }
- if (isNested) // Got parent ?
- childRowsStack.Push(row); // Push row to the stack
- }
- // Returns column value
- private void LoadColumn (DataColumn column, object[] foundColumns) {
- // <DataSet> /--------------------------------- We are here on entrance
- // <Table> /
- // <Column>Value</Column>
- // <AnotherColumn>Value</AnotherColumn>
- // </Table> \------------------------------ We are here on exit
- // </DataSet>
- // <Column> If we have something like this
- // <Foo>FooVal</Foo> We would grab first text-like node
- // Value In this case it would be "FooVal"
- // <Bar>BarVal</Bar> And not "Value" as you might think
- // </Column> This is how desktop works
- string text = String.Empty; // Column text. Assume empty string
- string xsiNilString = null; // Possible NIL attribute string
- int entryDepth = dataReader.Depth; // Store depth so we won't read too much
- if (dataReader.AttributeCount > 0) // If have attributes
- xsiNilString = dataReader.GetAttribute(Keywords.XSI_NIL, Keywords.XSINS);
- // Try to get NIL attribute
- // We have to do it before we move to the next element
- if (column.IsCustomType) { // Custom type column
- object columnValue = null; // Column value we're after. Assume no value.
- string xsiTypeString = null; // XSI type name from TYPE attribute
- string typeName = null; // Type name from MSD_INSTANCETYPE attribute
- XmlRootAttribute xmlAttrib = null; // Might need this attribute for XmlSerializer
- if (dataReader.AttributeCount > 0) { // If have attributes, get attributes we'll need
- xsiTypeString = dataReader.GetAttribute(Keywords.TYPE, Keywords.XSINS);
- typeName = dataReader.GetAttribute(Keywords.MSD_INSTANCETYPE, Keywords.MSDNS);
- }
- // Check if need to use XmlSerializer. We need to do that if type does not implement IXmlSerializable.
- // We also need to do that if no polymorphism for this type allowed.
- bool useXmlSerializer = !column.ImplementsIXMLSerializable &&
- !( (column.DataType == typeof(Object)) || (typeName != null) || (xsiTypeString != null) );
- // Check if we have an attribute telling us value is null.
-
- if ((xsiNilString != null) && XmlConvert.ToBoolean(xsiNilString)) {
- if (!useXmlSerializer) { // See if need to set typed null.
- if (typeName != null && typeName.Length > 0) {
- // Got type name
- columnValue = SqlUdtStorage.GetStaticNullForUdtType(DataStorage.GetType(typeName));
- }
- }
- if (null == columnValue) { // If no value,
- columnValue = DBNull.Value; // change to DBNull;
- }
- if ( !dataReader.IsEmptyElement ) // In case element is not empty
- while (dataReader.Read() && (entryDepth < dataReader.Depth));
- // Read current elements
- dataReader.Read(); // And start reading next element.
- }
- else { // No NIL attribute. Get value
- bool skipped = false;
- if (column.Table.DataSet != null && column.Table.DataSet.UdtIsWrapped) {
- dataReader.Read(); // if UDT is wrapped, skip the wrapper
- skipped = true;
- }
- if (useXmlSerializer) { // Create an attribute for XmlSerializer
- if (skipped) {
- xmlAttrib = new XmlRootAttribute(dataReader.LocalName);
- xmlAttrib.Namespace = dataReader.NamespaceURI ;
- }
- else {
- xmlAttrib = new XmlRootAttribute(column.EncodedColumnName);
- xmlAttrib.Namespace = column.Namespace;
- }
- }
- columnValue = column.ConvertXmlToObject(dataReader, xmlAttrib);
- // Go get the value
- if (skipped) {
- dataReader.Read(); // if Wrapper is skipped, skip its end tag
- }
- }
- foundColumns[column.Ordinal] = columnValue; // Store value
- }
- else { // Not a custom type.
- if ( dataReader.Read() && entryDepth < dataReader.Depth) {
- // Read to the next element and see if we're inside.
- while (entryDepth < dataReader.Depth) {
- switch (dataReader.NodeType) { // Process nodes based on type
- case XmlNodeType.Text: // It looks like a text. And we need it.
- case XmlNodeType.Whitespace:
- case XmlNodeType.CDATA:
- case XmlNodeType.SignificantWhitespace:
- if (0 == text.Length) { // In case we do not have value already
- text = dataReader.Value; // Get value.
- // See if we have other text nodes near. In most cases this loop will not be executed.
- StringBuilder builder = null;
- while ( dataReader.Read() && entryDepth < dataReader.Depth && IsTextLikeNode(dataReader.NodeType)) {
- if (builder == null) {
- builder = new StringBuilder(text);
- }
- builder.Append(dataReader.Value); // Concatenate other sequential text like
- // nodes we might have. This is rare.
- // We're using this instead of dataReader.ReadString()
- // which would do the same thing but slower.
- }
- if (builder != null) {
- text = builder.ToString();
- }
- }
- else {
- dataReader.ReadString(); // We've got column value already. Read this one and ignore it.
- }
- break;
- case XmlNodeType.Element:
- if (ProcessXsdSchema()) { // Check for schema. Skip or load if found.
- continue; // Schema has been found. Process the next element
- // we're already at (done by schema processing).
- }
- else {
- // We've got element which is not supposed to he here.
- // That might be table which was misplaced.
- // Or it might be a column inside column (also misplaced).
- object o = nodeToSchemaMap.GetColumnSchema(column.Table, dataReader, FIgnoreNamespace(dataReader));
- // Get dataset element for this XML element
- DataColumn c = o as DataColumn; // Perhaps, it's a column?
-
- if ( c != null ) { // Do we have matched column in this table?
- // Let's load column data
-
- if (foundColumns[c.Ordinal] == null) {
- // If this column was not found before
- LoadColumn (c, foundColumns);
- // Get column value
- }
- else {
- dataReader.Read(); // Already loaded, proceed to the next element
- }
- }
- else {
- DataTable nestedTable = o as DataTable;
- // Perhaps, it's a nested table ?
- if ( nestedTable != null ) {
- // Do we have matched table in DataSet ?
- LoadTable (nestedTable, true /* isNested */);
- // Yes. Load nested table (recursive)
- }
- else { // Not a nested column nor nested table.
- // Let's try other tables in the DataSet
-
- DataTable misplacedTable = nodeToSchemaMap.GetTableForNode(dataReader, FIgnoreNamespace(dataReader));
- // Try to get table for node
- if (misplacedTable != null) {
- // Got some table to match?
- LoadTable (misplacedTable, false /* isNested */);
- // While table's XML element is nested,
- // the table itself is not. Load it this way.
- }
- else {
- dataReader.Read(); // No match? Try next element
- }
- }
- }
- }
- break;
- case XmlNodeType.EntityReference: // Oops. No support for Entity Reference
- throw ExceptionBuilder.FoundEntity();
- default:
- dataReader.Read(); // We don't process that, skip to the next element.
- break;
- }
-
- }
- dataReader.Read(); // We're done here. To the next element.
- }
- if (0 == text.Length && xsiNilString != null && XmlConvert.ToBoolean(xsiNilString)) {
- foundColumns[column.Ordinal] = DBNull.Value;
- // If no data and NIL attribute is true set value to null
- }
- else {
- foundColumns[column.Ordinal] = column.ConvertXmlToObject(text);
- }
-
- }
- }
- // Check for schema and skips or loads XSD schema if found. Returns true if schema found.
- // DataReader would be set on the first XML element after the schema of schema was found.
- // If no schema detected, reader's position will not change.
- private bool ProcessXsdSchema () {
- if (((object)dataReader.LocalName == XSD_SCHEMA && (object)dataReader.NamespaceURI == XSDNS )) {
- // Found XSD schema
- if ( ignoreSchema ) { // Should ignore it?
- dataReader.Skip(); // Yes, skip it
- }
- else { // Have to load schema.
- if ( isTableLevel ) { // Loading into the DataTable ?
- dataTable.ReadXSDSchema(dataReader, false); // Invoke ReadXSDSchema on a table
- nodeToSchemaMap = new XmlToDatasetMap(dataReader.NameTable, dataTable);
- } // Rebuild XML to DataSet map with new schema.
- else { // Loading into the DataSet ?
- dataSet.ReadXSDSchema(dataReader, false); // Invoke ReadXSDSchema on a DataSet
- nodeToSchemaMap = new XmlToDatasetMap(dataReader.NameTable, dataSet);
- } // Rebuild XML to DataSet map with new schema.
- }
- }
- else if (((object)dataReader.LocalName == XDR_SCHEMA && (object)dataReader.NamespaceURI == XDRNS ) ||
- ((object)dataReader.LocalName == SQL_SYNC && (object)dataReader.NamespaceURI == UPDGNS))
- {
- dataReader.Skip(); // Skip XDR or SQL sync
- }
- else {
- return false; // No schema found. That means reader's position
- // is unchganged. Report that to the caller.
- }
- return true; // Schema found, reader's position changed.
- }
- }
- }
|