| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- //------------------------------------------------------------------------------
- // <copyright file="XmlToDatasetMap.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- // <owner current="false" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data {
- using System;
- using System.Xml;
- using System.Collections;
- using System.Diagnostics;
- using System.Globalization;
- // This is an internal helper class used during Xml load to DataSet/DataDocument.
- // XmlToDatasetMap class provides functionality for binding elemants/atributes
- // to DataTable / DataColumn
- internal sealed class XmlToDatasetMap {
- private sealed class XmlNodeIdentety {
- public string LocalName;
- public string NamespaceURI;
- public XmlNodeIdentety(string localName, string namespaceURI) {
- this.LocalName = localName;
- this.NamespaceURI = namespaceURI;
- }
- override public int GetHashCode() {
- return ((object) LocalName).GetHashCode();
- }
- override public bool Equals(object obj) {
- XmlNodeIdentety id = (XmlNodeIdentety) obj;
- return (
- (String.Compare(this.LocalName, id.LocalName, StringComparison.OrdinalIgnoreCase) == 0) &&
- (String.Compare(this.NamespaceURI, id.NamespaceURI, StringComparison.OrdinalIgnoreCase) == 0)
- );
- }
- }
- // This class exist to avoid alocatin of XmlNodeIdentety to every acces to the hash table.
- // Unfortunetely XmlNode doesn't export single identety object.
- internal sealed class XmlNodeIdHashtable : Hashtable {
- private XmlNodeIdentety id = new XmlNodeIdentety(string.Empty, string.Empty);
- public XmlNodeIdHashtable(Int32 capacity)
- : base(capacity) {}
- public object this[XmlNode node] {
- get {
- id.LocalName = node.LocalName;
- id.NamespaceURI = node.NamespaceURI;
- return this[id];
- }
- }
-
- public object this[XmlReader dataReader] {
- get {
- id.LocalName = dataReader.LocalName;
- id.NamespaceURI = dataReader.NamespaceURI;
- return this[id];
- }
- }
- public object this[DataTable table] {
- get {
- id.LocalName = table.EncodedTableName;
- id.NamespaceURI = table.Namespace;
- return this[id];
- }
- }
- public object this[string name] {
- get {
- id.LocalName = name;
- id.NamespaceURI = String.Empty;
- return this[id];
- }
- }
- }
- private sealed class TableSchemaInfo {
- public DataTable TableSchema;
- public XmlNodeIdHashtable ColumnsSchemaMap;
- public TableSchemaInfo(DataTable tableSchema) {
- this.TableSchema = tableSchema;
- this.ColumnsSchemaMap = new XmlNodeIdHashtable(tableSchema.Columns.Count);
- }
- }
- XmlNodeIdHashtable tableSchemaMap; // Holds all the tables information
- TableSchemaInfo lastTableSchemaInfo = null;
- // Used to infer schema
- public XmlToDatasetMap(DataSet dataSet, XmlNameTable nameTable) {
- Debug.Assert(dataSet != null, "DataSet can't be null");
- Debug.Assert(nameTable != null, "NameTable can't be null");
- BuildIdentityMap(dataSet, nameTable);
- }
- // Used to read data with known schema
- public XmlToDatasetMap(XmlNameTable nameTable, DataSet dataSet) {
- Debug.Assert(dataSet != null, "DataSet can't be null");
- Debug.Assert(nameTable != null, "NameTable can't be null");
- BuildIdentityMap(nameTable, dataSet);
- }
- // Used to infer schema
- public XmlToDatasetMap(DataTable dataTable, XmlNameTable nameTable) {
- Debug.Assert(dataTable != null, "DataTable can't be null");
- Debug.Assert(nameTable != null, "NameTable can't be null");
- BuildIdentityMap(dataTable, nameTable);
- }
- // Used to read data with known schema
- public XmlToDatasetMap(XmlNameTable nameTable, DataTable dataTable) {
- Debug.Assert(dataTable != null, "DataTable can't be null");
- Debug.Assert(nameTable != null, "NameTable can't be null");
- BuildIdentityMap(nameTable, dataTable);
- }
- static internal bool IsMappedColumn(DataColumn c) {
- return (c.ColumnMapping != MappingType.Hidden);
- }
- // Used to infere schema
- private TableSchemaInfo AddTableSchema(DataTable table, XmlNameTable nameTable) {
- // [....]: Because in our case reader already read the document all names that we can meet in the
- // document already has an entry in NameTable.
- // If in future we will build identity map before reading XML we can replace Get() to Add()
- // [....]: GetIdentity is called from two places: BuildIdentityMap() and LoadRows()
- // First case deals with decoded names; Second one with encoded names.
- // We decided encoded names in first case (instead of decoding them in second)
- // because it save us time in LoadRows(). We have, as usual, more data them schemas
- string tableLocalName = nameTable.Get(table.EncodedTableName);
- string tableNamespace = nameTable.Get(table.Namespace );
- if(tableLocalName == null) {
- // because name of this table isn't present in XML we don't need mapping for it.
- // Less mapping faster we work.
- return null;
- }
- TableSchemaInfo tableSchemaInfo = new TableSchemaInfo(table);
- tableSchemaMap[new XmlNodeIdentety(tableLocalName, tableNamespace)] = tableSchemaInfo;
- return tableSchemaInfo;
- }
- private TableSchemaInfo AddTableSchema(XmlNameTable nameTable, DataTable table) {
- // [....]:This is the opposite of the previous function:
- // we populate the nametable so that the hash comparison can happen as
- // object comparison instead of strings.
- // [....]: GetIdentity is called from two places: BuildIdentityMap() and LoadRows()
- // First case deals with decoded names; Second one with encoded names.
- // We decided encoded names in first case (instead of decoding them in second)
- // because it save us time in LoadRows(). We have, as usual, more data them schemas
- string _tableLocalName = table.EncodedTableName; // Table name
- string tableLocalName = nameTable.Get(_tableLocalName); // Look it up in nametable
- if(tableLocalName == null) { // If not found
- tableLocalName = nameTable.Add(_tableLocalName); // Add it
- }
- table.encodedTableName = tableLocalName; // And set it back
- string tableNamespace = nameTable.Get(table.Namespace); // Look ip table namespace
- if (tableNamespace == null) { // If not found
- tableNamespace = nameTable.Add(table.Namespace); // Add it
- }
- else {
- if (table.tableNamespace != null) // Update table namespace
- table.tableNamespace = tableNamespace;
- }
- TableSchemaInfo tableSchemaInfo = new TableSchemaInfo(table);
- // Create new table schema info
- tableSchemaMap[new XmlNodeIdentety(tableLocalName, tableNamespace)] = tableSchemaInfo;
- // And add it to the hashtable
- return tableSchemaInfo; // Return it as we have to populate
- // Column schema map and Child table
- // schema map in it
- }
- private bool AddColumnSchema(DataColumn col, XmlNameTable nameTable, XmlNodeIdHashtable columns) {
- string columnLocalName = nameTable.Get(col.EncodedColumnName );
- string columnNamespace = nameTable.Get(col.Namespace );
- if(columnLocalName == null) {
- return false;
- }
- XmlNodeIdentety idColumn = new XmlNodeIdentety(columnLocalName, columnNamespace);
- columns[idColumn] = col;
-
- if (col.ColumnName.StartsWith("xml", StringComparison.OrdinalIgnoreCase)) {
- HandleSpecialColumn(col, nameTable, columns);
- }
-
- return true;
- }
- private bool AddColumnSchema(XmlNameTable nameTable, DataColumn col, XmlNodeIdHashtable columns) {
- string _columnLocalName = XmlConvert.EncodeLocalName(col.ColumnName);
- string columnLocalName = nameTable.Get(_columnLocalName); // Look it up in a name table
- if(columnLocalName == null) { // Not found?
- columnLocalName = nameTable.Add(_columnLocalName); // Add it
- }
- col.encodedColumnName = columnLocalName; // And set it back
- string columnNamespace = nameTable.Get(col.Namespace ); // Get column namespace from nametable
- if(columnNamespace == null) { // Not found ?
- columnNamespace = nameTable.Add(col.Namespace); // Add it
- }
- else {
- if (col._columnUri != null ) // Update namespace
- col._columnUri = columnNamespace;
- }
- // Create XmlNodeIdentety
- // for this column
- XmlNodeIdentety idColumn = new XmlNodeIdentety(columnLocalName, columnNamespace);
- columns[idColumn] = col; // And add it to hashtable
- if (col.ColumnName.StartsWith("xml", StringComparison.OrdinalIgnoreCase)) {
- HandleSpecialColumn(col, nameTable, columns);
- }
-
- return true;
- }
- private void BuildIdentityMap(DataSet dataSet, XmlNameTable nameTable) {
- this.tableSchemaMap = new XmlNodeIdHashtable(dataSet.Tables.Count);
- foreach(DataTable t in dataSet.Tables) {
- TableSchemaInfo tableSchemaInfo = AddTableSchema(t, nameTable);
- if(tableSchemaInfo != null) {
- foreach( DataColumn c in t.Columns ) {
- // don't include auto-generated PK, FK and any hidden columns to be part of mapping
- if (IsMappedColumn(c)) {
- AddColumnSchema(c, nameTable, tableSchemaInfo.ColumnsSchemaMap);
- }
- }
- }
- }
- }
- // This one is used while reading data with preloaded schema
- private void BuildIdentityMap(XmlNameTable nameTable, DataSet dataSet) {
- this.tableSchemaMap = new XmlNodeIdHashtable(dataSet.Tables.Count);
- // This hash table contains
- // tables schemas as TableSchemaInfo objects
- // These objects holds reference to the table.
- // Hash tables with columns schema maps
- // and child tables schema maps
- string dsNamespace = nameTable.Get(dataSet.Namespace); // Attept to look up DataSet namespace
- // in the name table
- if (dsNamespace == null) { // Found ?
- dsNamespace = nameTable.Add(dataSet.Namespace); // Nope. Add it
- }
- dataSet.namespaceURI = dsNamespace; // Set a DataSet namespace URI
- foreach(DataTable t in dataSet.Tables) { // For each table
- TableSchemaInfo tableSchemaInfo = AddTableSchema(nameTable, t);
- // Add table schema info to hash table
- if(tableSchemaInfo != null) {
- foreach( DataColumn c in t.Columns ) { // Add column schema map
- // don't include auto-generated PK, FK and any hidden columns to be part of mapping
- if (IsMappedColumn(c)) { // If mapped column
- AddColumnSchema(nameTable, c, tableSchemaInfo.ColumnsSchemaMap);
- } // Add it to the map
- }
- // Add child nested tables to the schema
- foreach( DataRelation r in t.ChildRelations ) { // Do we have a child tables ?
- if (r.Nested) { // Is it nested?
- // don't include non nested tables
- // Handle namespaces and names as usuall
- string _tableLocalName = XmlConvert.EncodeLocalName(r.ChildTable.TableName);
- string tableLocalName = nameTable.Get(_tableLocalName);
- if(tableLocalName == null) {
- tableLocalName = nameTable.Add(_tableLocalName);
- }
- string tableNamespace = nameTable.Get(r.ChildTable.Namespace );
- if(tableNamespace == null) {
- tableNamespace = nameTable.Add(r.ChildTable.Namespace);
- }
- XmlNodeIdentety idTable = new XmlNodeIdentety(tableLocalName, tableNamespace);
- tableSchemaInfo.ColumnsSchemaMap[idTable] = r.ChildTable;
- }
- }
- }
- }
- }
- // Used for inference
- private void BuildIdentityMap(DataTable dataTable, XmlNameTable nameTable) {
- this.tableSchemaMap = new XmlNodeIdHashtable(1);
- TableSchemaInfo tableSchemaInfo = AddTableSchema(dataTable, nameTable);
- if(tableSchemaInfo != null) {
- foreach( DataColumn c in dataTable.Columns ) {
- // don't include auto-generated PK, FK and any hidden columns to be part of mapping
- if (IsMappedColumn(c)) {
- AddColumnSchema(c, nameTable, tableSchemaInfo.ColumnsSchemaMap);
- }
- }
- }
-
- }
- // This one is used while reading data with preloaded schema
- private void BuildIdentityMap(XmlNameTable nameTable, DataTable dataTable) {
- ArrayList tableList = GetSelfAndDescendants(dataTable); // Get list of tables we're loading
- // This includes our table and
- // related tables tree
- this.tableSchemaMap = new XmlNodeIdHashtable( tableList.Count );
- // Create hash table to hold all
- // tables to load.
-
- foreach (DataTable t in tableList) { // For each table
- TableSchemaInfo tableSchemaInfo = AddTableSchema(nameTable, t );
- // Create schema info
- if(tableSchemaInfo != null) {
- foreach( DataColumn c in t.Columns ) { // Add column information
- // don't include auto-generated PK, FK and any hidden columns to be part of mapping
- if (IsMappedColumn(c)) {
- AddColumnSchema(nameTable, c, tableSchemaInfo.ColumnsSchemaMap);
- }
- }
- foreach( DataRelation r in t.ChildRelations ) { // Add nested tables information
- if (r.Nested) { // Is it nested?
- // don't include non nested tables
- // Handle namespaces and names as usuall
- string _tableLocalName = XmlConvert.EncodeLocalName(r.ChildTable.TableName);
- string tableLocalName = nameTable.Get(_tableLocalName);
- if(tableLocalName == null) {
- tableLocalName = nameTable.Add(_tableLocalName);
- }
- string tableNamespace = nameTable.Get(r.ChildTable.Namespace );
- if(tableNamespace == null) {
- tableNamespace = nameTable.Add(r.ChildTable.Namespace);
- }
- XmlNodeIdentety idTable = new XmlNodeIdentety(tableLocalName, tableNamespace);
- tableSchemaInfo.ColumnsSchemaMap[idTable] = r.ChildTable;
- }
- }
- }
- }
- }
- private ArrayList GetSelfAndDescendants(DataTable dt) { // breadth-first
- ArrayList tableList = new ArrayList();
- tableList.Add(dt);
- int nCounter = 0;
-
- while (nCounter < tableList.Count) {
- foreach(DataRelation childRelations in ((DataTable)tableList[nCounter]).ChildRelations) {
- if (!tableList.Contains(childRelations.ChildTable))
- tableList.Add(childRelations.ChildTable);
- }
- nCounter++;
- }
-
- return tableList;
- }
- // Used to infer schema and top most node
- public object GetColumnSchema(XmlNode node, bool fIgnoreNamespace) {
- Debug.Assert(node != null, "Argument validation");
- TableSchemaInfo tableSchemaInfo = null;
- XmlNode nodeRegion = (node.NodeType == XmlNodeType.Attribute) ? ((XmlAttribute)node).OwnerElement : node.ParentNode;
- do {
- if(nodeRegion == null || nodeRegion.NodeType != XmlNodeType.Element) {
- return null;
- }
- tableSchemaInfo = (TableSchemaInfo) (fIgnoreNamespace ? tableSchemaMap[nodeRegion.LocalName] : tableSchemaMap[nodeRegion]);
- nodeRegion = nodeRegion.ParentNode;
- } while(tableSchemaInfo == null);
- if (fIgnoreNamespace)
- return tableSchemaInfo.ColumnsSchemaMap[node.LocalName];
- else
- return tableSchemaInfo.ColumnsSchemaMap[node];
- }
- public object GetColumnSchema(DataTable table, XmlReader dataReader, bool fIgnoreNamespace){
- if ((lastTableSchemaInfo == null) || (lastTableSchemaInfo.TableSchema != table)) {
- lastTableSchemaInfo = (TableSchemaInfo)(fIgnoreNamespace ? tableSchemaMap[table.EncodedTableName] : tableSchemaMap[table]);
- }
- if (fIgnoreNamespace)
- return lastTableSchemaInfo.ColumnsSchemaMap[dataReader.LocalName];
- return lastTableSchemaInfo.ColumnsSchemaMap[dataReader];
- }
- // Used to infer schema
- public object GetSchemaForNode(XmlNode node, bool fIgnoreNamespace) {
- TableSchemaInfo tableSchemaInfo = null;
- if (node.NodeType == XmlNodeType.Element) { // If element
- tableSchemaInfo = (TableSchemaInfo) (fIgnoreNamespace ? tableSchemaMap[node.LocalName] : tableSchemaMap[node]);
- } // Look up table schema info for it
- if (tableSchemaInfo != null) { // Got info ?
- return tableSchemaInfo.TableSchema; // Yes, Return table
- }
- return GetColumnSchema(node, fIgnoreNamespace); // Attempt to locate column
- }
- public DataTable GetTableForNode(XmlReader node, bool fIgnoreNamespace) {
- TableSchemaInfo tableSchemaInfo = (TableSchemaInfo) (fIgnoreNamespace ? tableSchemaMap[node.LocalName] : tableSchemaMap[node]);
- if (tableSchemaInfo != null) {
- lastTableSchemaInfo = tableSchemaInfo;
- return lastTableSchemaInfo.TableSchema;
- }
- return null;
- }
- private void HandleSpecialColumn(DataColumn col, XmlNameTable nameTable, XmlNodeIdHashtable columns) {
- // if column name starts with xml, we encode it manualy and add it for look up
- Debug.Assert(col.ColumnName.StartsWith("xml", StringComparison.OrdinalIgnoreCase), "column name should start with xml");
- string tempColumnName;
- if ('x' == col.ColumnName[0]) {
- tempColumnName = "_x0078_"; // lower case xml... -> _x0078_ml...
- }
- else {
- tempColumnName = "_x0058_"; // upper case Xml... -> _x0058_ml...
- }
-
- tempColumnName += col.ColumnName.Substring(1);
-
- if(nameTable.Get(tempColumnName) == null) {
- nameTable.Add(tempColumnName);
- }
- string columnNamespace = nameTable.Get(col.Namespace);
- XmlNodeIdentety idColumn = new XmlNodeIdentety(tempColumnName, columnNamespace);
- columns[idColumn] = col;
- }
- }
- }
|