| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- //------------------------------------------------------------------------------
- // <copyright file="DataSetMapper.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">Microsoft</owner>
- // <owner current="true" primary="false">Microsoft</owner>
- //------------------------------------------------------------------------------
- #pragma warning disable 618 // ignore obsolete warning about XmlDataDocument
- namespace System.Xml {
- using System.Collections;
- using System.Data;
- using System.Diagnostics;
- //
- // Maps XML nodes to schema
- //
- // With the exception of some functions (the most important is SearchMatchingTableSchema) all functions expect that each region rowElem is already associated
- // w/ it's DataRow (basically the test to determine a rowElem is based on a != null associated DataRow). As a result of this, some functions will NOT work properly
- // when they are used on a tree for which rowElem's are not associated w/ a DataRow.
- //
-
- internal sealed class DataSetMapper {
- Hashtable tableSchemaMap; // maps an string (currently this is localName:nsURI) to a DataTable. Used to quickly find if a bound-elem matches any data-table metadata..
- Hashtable columnSchemaMap; // maps a string (table localName:nsURI) to a Hashtable. The 2nd hastable (the one that is stored as data in columnSchemaMap, maps a string to a DataColumn.
- XmlDataDocument doc; // The document this mapper is related to
- DataSet dataSet; // The dataset this mapper is related to
- internal const string strReservedXmlns = "http://www.w3.org/2000/xmlns/";
- internal DataSetMapper() {
- Debug.Assert( this.dataSet == null );
- this.tableSchemaMap = new Hashtable();
- this.columnSchemaMap = new Hashtable();
- }
- internal void SetupMapping( XmlDataDocument xd, DataSet ds ) {
- // If are already mapped, forget about our current mapping and re-do it again.
- if ( IsMapped() ) {
- this.tableSchemaMap = new Hashtable();
- this.columnSchemaMap = new Hashtable();
- }
- doc = xd;
- dataSet = ds;
- foreach( DataTable t in dataSet.Tables ) {
- AddTableSchema( t );
- foreach( DataColumn c in t.Columns ) {
- // don't include auto-generated PK & FK to be part of mapping
- if ( ! IsNotMapped(c) ) {
- AddColumnSchema( c );
- }
- }
- }
- }
- internal bool IsMapped() {
- return dataSet != null;
- }
- internal DataTable SearchMatchingTableSchema( string localName, string namespaceURI ) {
- object tid = GetIdentity( localName, namespaceURI );
- return (DataTable)(tableSchemaMap[ tid ]);
-
- }
- // SearchMatchingTableSchema function works only when the elem has not been bound to a DataRow. If you want to get the table associated w/ an element after
- // it has been associated w/ a DataRow use GetTableSchemaForElement function.
- // rowElem is the parent region rowElem or null if there is no parent region (in case elem is a row elem, then rowElem will be the parent region; if elem is not
- // mapped to a DataRow, then rowElem is the region elem is part of)
- //
- // Those are the rules for determing if elem is a row element:
- // 1. node is an element (already meet, since elem is of type XmlElement)
- // 2. If the node is already associated w/ a DataRow, then the node is a row element - not applicable, b/c this function is intended to be called on a
- // to find out if the node s/b associated w/ a DataRow (see XmlDataDocument.LoadRows)
- // 3. If the node localName/ns matches a DataTable then
- // 3.1 Take the parent region DataTable (in our case rowElem.Row.DataTable)
- // 3.2 If no parent region, then the node is associated w/ a DataTable
- // 3.3 If there is a parent region
- // 3.3.1 If the node has no elem children and no attr other than namespace declaration, and the node can match
- // a column from the parent region table, then the node is NOT associated w/ a DataTable (it is a potential DataColumn in the parent region)
- // 3.3.2 Else the node is a row-element (and associated w/ a DataTable / DataRow )
- //
- internal DataTable SearchMatchingTableSchema( XmlBoundElement rowElem, XmlBoundElement elem ) {
- Debug.Assert( elem != null );
- DataTable t = SearchMatchingTableSchema( elem.LocalName, elem.NamespaceURI );
- if ( t == null )
- return null;
- if ( rowElem == null )
- return t;
- // Currently we expect we map things from top of the tree to the bottom
- Debug.Assert( rowElem.Row != null );
- DataColumn col = GetColumnSchemaForNode( rowElem, elem );
- if ( col == null )
- return t;
- foreach ( XmlAttribute a in elem.Attributes ) {
- #if DEBUG
- // Some sanity check to catch errors like namespace attributes have the right localName/namespace value, but a wrong atomized namespace value
- if ( a.LocalName == "xmlns" ) {
- Debug.Assert( a.Prefix != null && a.Prefix.Length == 0 );
- Debug.Assert( (object)a.NamespaceURI == (object)strReservedXmlns );
- }
- if ( a.Prefix == "xmlns" ) {
- Debug.Assert( (object)a.NamespaceURI == (object)strReservedXmlns );
- }
- if ( a.NamespaceURI == strReservedXmlns )
- Debug.Assert( (object)a.NamespaceURI == (object)strReservedXmlns );
- #endif
- // No namespace attribute found, so elem cannot be a potential DataColumn, therefore is a row-elem
- if ( (object)(a.NamespaceURI) != (object)strReservedXmlns )
- return t;
- }
- for ( XmlNode n = elem.FirstChild; n != null; n = n.NextSibling ) {
- if ( n.NodeType == XmlNodeType.Element ) {
- // elem has an element child, so elem cannot be a potential DataColumn, therefore is a row-elem
- return t;
- }
- }
- // Node is a potential DataColumn in rowElem region
- return null;
- }
- internal DataColumn GetColumnSchemaForNode( XmlBoundElement rowElem, XmlNode node ) {
- //
- Debug.Assert( rowElem != null );
- // The caller must make sure that node is not a row-element
- Debug.Assert( (node is XmlBoundElement) ? ((XmlBoundElement)node).Row == null : true );
- object tid = GetIdentity( rowElem.LocalName, rowElem.NamespaceURI );
- object cid = GetIdentity( node.LocalName, node.NamespaceURI );
- Hashtable columns = (Hashtable) columnSchemaMap[ tid ];
- if ( columns != null ) {
- DataColumn col = (DataColumn)(columns[ cid ]);
- if ( col == null )
- return null;
- MappingType mt = col.ColumnMapping;
- if ( node.NodeType == XmlNodeType.Attribute && mt == MappingType.Attribute )
- return col;
- if ( node.NodeType == XmlNodeType.Element && mt == MappingType.Element )
- return col;
- // node's (localName, ns) matches a column, but the MappingType is different (i.e. node is elem, MT is attr)
- return null;
- }
- return null;
- }
- internal DataTable GetTableSchemaForElement( XmlElement elem ) {
- //
- XmlBoundElement be = elem as XmlBoundElement;
- if ( be == null )
- return null;
- return GetTableSchemaForElement( be );
- }
- internal DataTable GetTableSchemaForElement( XmlBoundElement be ) {
- // if bound to a row, must be a table.
- DataRow row = be.Row;
- if ( row != null )
- return row.Table;
- return null;
- }
- internal static bool IsNotMapped( DataColumn c ) {
- return c.ColumnMapping == MappingType.Hidden;
- }
- // ATTENTION: GetRowFromElement( XmlElement ) and GetRowFromElement( XmlBoundElement ) should have the same functionality and side effects.
- // See this code fragment for why:
- // XmlBoundElement be = ...;
- // XmlElement e = be;
- // GetRowFromElement( be ); // Calls GetRowFromElement( XmlBoundElement )
- // GetRowFromElement( e ); // Calls GetRowFromElement( XmlElement ), in spite of e beeing an instance of XmlBoundElement
- internal DataRow GetRowFromElement( XmlElement e ) {
- XmlBoundElement be = e as XmlBoundElement;
- if ( be != null )
- return be.Row;
- return null;
- }
- internal DataRow GetRowFromElement( XmlBoundElement be ) {
- return be.Row;
- }
- // Get the row-elem associatd w/ the region node is in.
- // If node is in a region not mapped (like document element node) the function returns false and sets elem to null)
- // This function does not work if the region is not associated w/ a DataRow (it uses DataRow association to know what is the row element associated w/ the region)
- internal bool GetRegion( XmlNode node, out XmlBoundElement rowElem ) {
- while ( node != null ) {
- XmlBoundElement be = node as XmlBoundElement;
- // Break if found a region
- if ( be != null && GetRowFromElement( be ) != null ) {
- rowElem = be;
- return true;
- }
- if ( node.NodeType == XmlNodeType.Attribute )
- node = ((XmlAttribute)node).OwnerElement;
- else
- node = node.ParentNode;
- }
- rowElem = null;
- return false;
- }
- internal bool IsRegionRadical( XmlBoundElement rowElem ) {
- // You must pass a row element (which s/b associated w/ a DataRow)
- Debug.Assert( rowElem.Row != null );
- if ( rowElem.ElementState == ElementState.Defoliated )
- return true;
- DataTable table = GetTableSchemaForElement( rowElem );
- DataColumnCollection columns = table.Columns;
- int iColumn = 0;
- // check column attributes...
- int cAttrs = rowElem.Attributes.Count;
- for ( int iAttr = 0; iAttr < cAttrs; iAttr++ ) {
- XmlAttribute attr = rowElem.Attributes[iAttr];
- // only specified attributes are radical
- if ( !attr.Specified )
- return false;
- // only mapped attrs are valid
- DataColumn schema = GetColumnSchemaForNode( rowElem, attr );
- if ( schema == null ) {
- //Console.WriteLine("Region has unmapped attribute");
- return false;
- }
- // check to see if column is in order
- if ( !IsNextColumn( columns, ref iColumn, schema ) ) {
- //Console.WriteLine("Region has attribute columns out of order or duplicate");
- return false;
- }
- // must have exactly one text node (XmlNodeType.Text) child
- //
- XmlNode fc = attr.FirstChild;
- if ( fc == null || fc.NodeType != XmlNodeType.Text || fc.NextSibling != null ) {
- //Console.WriteLine("column element has other than a single child text node");
- return false;
- }
- }
- // check column elements
- iColumn = 0;
- XmlNode n = rowElem.FirstChild;
- for ( ; n != null; n = n.NextSibling ) {
- // only elements can exist in radically structured data
- if ( n.NodeType != XmlNodeType.Element ) {
- //Console.WriteLine("Region has non-element child");
- return false;
- }
- XmlElement e = n as XmlElement;
- // only checking for column mappings in this loop
- if ( GetRowFromElement( e ) != null )
- break;
- // element's must have schema to be radically structured
- DataColumn schema = GetColumnSchemaForNode( rowElem, e );
- if ( schema == null ) {
- //Console.WriteLine("Region has unmapped child element");
- return false;
- }
- // check to see if column is in order
- if ( !IsNextColumn( columns, ref iColumn, schema ) ) {
- //Console.WriteLine("Region has element columns out of order or duplicate");
- return false;
- }
- // must have no attributes
- if ( e.HasAttributes )
- return false;
- // must have exactly one text node child
- XmlNode fc = e.FirstChild;
- if ( fc == null || fc.NodeType != XmlNodeType.Text || fc.NextSibling != null ) {
- //Console.WriteLine("column element has other than a single child text node");
- return false;
- }
- }
- // check for remaining sub-regions
- for (; n != null; n = n.NextSibling ) {
- // only elements can exist in radically structured data
- if ( n.NodeType != XmlNodeType.Element ) {
- //Console.WriteLine("Region has non-element child");
- return false;
- }
- // element's must be regions in order to be radially structured
- DataRow row = GetRowFromElement( (XmlElement)n );
- if ( row == null ) {
- //Console.WriteLine("Region has unmapped element");
- return false;
- }
- }
- return true;
- }
- private void AddTableSchema( DataTable table ) {
- object idTable = GetIdentity( table.EncodedTableName, table.Namespace );
- tableSchemaMap[ idTable ] = table;
- }
- private void AddColumnSchema( DataColumn col ) {
- DataTable table = col.Table;
- object idTable = GetIdentity( table.EncodedTableName, table.Namespace );
- object idColumn = GetIdentity( col.EncodedColumnName, col.Namespace );
- Hashtable columns = (Hashtable) columnSchemaMap[ idTable ];
- if ( columns == null ) {
- columns = new Hashtable();
- columnSchemaMap[ idTable ] = columns;
- }
- columns[ idColumn ] = col;
- }
- private static object GetIdentity( string localName, string namespaceURI ) {
- // we need access to XmlName to make this faster
- return localName+":"+namespaceURI;
- }
- private bool IsNextColumn( DataColumnCollection columns, ref int iColumn, DataColumn col ) {
- for ( ; iColumn < columns.Count; iColumn++ ) {
- if ( columns[iColumn] == col ) {
- iColumn++; // advance before we return...
- return true;
- }
- }
- return false;
- }
- }
- }
|