Welcome to mirror list, hosted at ThFree Co, Russian Federation.

XmlToDatasetMap.cs « Data « System « System.Data « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: a1aab9cd5645377e5770b26e8a2e04a0b7970588 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
//------------------------------------------------------------------------------
// <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;
        }

    }
}