You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,350 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="DataSetMapper.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
// <owner current="true" primary="false">[....]</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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user