1 //------------------------------------------------------------------------------ 2 // <copyright file="DataPointer.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // <owner current="true" primary="true">Microsoft</owner> 6 // <owner current="true" primary="false">Microsoft</owner> 7 //------------------------------------------------------------------------------ 8 #pragma warning disable 618 // ignore obsolete warning about XmlDataDocument 9 namespace System.Xml { 10 using System; 11 using System.Data; 12 using System.Diagnostics; 13 14 internal sealed class DataPointer : IXmlDataVirtualNode { 15 private XmlDataDocument doc; 16 private XmlNode node; 17 private DataColumn column; 18 private bool fOnValue; 19 private bool bNeedFoliate = false; 20 private bool _isInUse; 21 DataPointer( XmlDataDocument doc, XmlNode node )22 internal DataPointer( XmlDataDocument doc, XmlNode node ) { 23 this.doc = doc; 24 this.node = node; 25 this.column = null; 26 this.fOnValue = false; 27 bNeedFoliate = false; 28 this._isInUse = true; 29 AssertValid(); 30 } 31 DataPointer( DataPointer pointer )32 internal DataPointer( DataPointer pointer ) { 33 this.doc = pointer.doc; 34 this.node = pointer.node; 35 this.column = pointer.column; 36 this.fOnValue = pointer.fOnValue; 37 this.bNeedFoliate = false; 38 this._isInUse = true; 39 AssertValid(); 40 } 41 AddPointer()42 internal void AddPointer() { 43 this.doc.AddPointer( (IXmlDataVirtualNode)this ); 44 } 45 46 // Returns the row element of the region that the pointer points into GetRowElement()47 private XmlBoundElement GetRowElement() { 48 //AssertValid(); 49 50 XmlBoundElement rowElem; 51 if ( this.column != null ) { 52 rowElem = this.node as XmlBoundElement; 53 Debug.Assert( rowElem != null ); 54 Debug.Assert( rowElem.Row != null ); 55 return rowElem; 56 } 57 58 doc.Mapper.GetRegion( this.node, out rowElem ); 59 return rowElem; 60 } 61 62 private DataRow Row { 63 get { 64 //AssertValid(); 65 XmlBoundElement rowElem = GetRowElement(); 66 if ( rowElem == null ) 67 return null; 68 69 Debug.Assert( rowElem.Row != null ); 70 return rowElem.Row; 71 } 72 } 73 IsFoliated( XmlNode node )74 private static bool IsFoliated( XmlNode node ) { 75 if (node != null && node is XmlBoundElement) 76 return((XmlBoundElement)node).IsFoliated; 77 return true; 78 } 79 MoveTo( DataPointer pointer )80 internal void MoveTo( DataPointer pointer ) { 81 AssertValid(); 82 // You should not move outside of this document 83 Debug.Assert( node == this.doc || node.OwnerDocument == this.doc ); 84 85 this.doc = pointer.doc; 86 this.node = pointer.node; 87 this.column = pointer.column; 88 this.fOnValue = pointer.fOnValue; 89 AssertValid(); 90 } MoveTo( XmlNode node )91 private void MoveTo( XmlNode node ) { 92 //AssertValid(); 93 // You should not move outside of this document 94 Debug.Assert( node == this.doc || node.OwnerDocument == this.doc ); 95 96 this.node = node; 97 this.column = null; 98 this.fOnValue = false; 99 AssertValid(); 100 } 101 MoveTo( XmlNode node, DataColumn column, bool fOnValue )102 private void MoveTo( XmlNode node, DataColumn column, bool fOnValue ) { 103 //AssertValid(); 104 // You should not move outside of this document 105 Debug.Assert( node == this.doc || node.OwnerDocument == this.doc ); 106 107 this.node = node; 108 this.column = column; 109 this.fOnValue = fOnValue; 110 AssertValid(); 111 } 112 NextColumn( DataRow row, DataColumn col, bool fAttribute, bool fNulls )113 private DataColumn NextColumn( DataRow row, DataColumn col, bool fAttribute, bool fNulls ) { 114 if (row.RowState == DataRowState.Deleted) 115 return null; 116 117 DataTable table = row.Table; 118 DataColumnCollection columns = table.Columns; 119 int iColumn = (col != null) ? col.Ordinal + 1 : 0; 120 int cColumns = columns.Count; 121 DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current; 122 123 for (; iColumn < cColumns; iColumn++) { 124 DataColumn c = columns[iColumn]; 125 if (!doc.IsNotMapped( c ) && (c.ColumnMapping == MappingType.Attribute) == fAttribute && (fNulls || ! Convert.IsDBNull( row[c, rowVersion] ) ) ) 126 return c; 127 } 128 129 return null; 130 } 131 NthColumn( DataRow row, bool fAttribute, int iColumn, bool fNulls )132 private DataColumn NthColumn( DataRow row, bool fAttribute, int iColumn, bool fNulls ) { 133 DataColumn c = null; 134 while ((c = NextColumn( row, c, fAttribute, fNulls )) != null) { 135 if (iColumn == 0) 136 return c; 137 138 iColumn = checked((int)iColumn-1); 139 } 140 return null; 141 } 142 ColumnCount( DataRow row, bool fAttribute, bool fNulls )143 private int ColumnCount( DataRow row, bool fAttribute, bool fNulls ) { 144 DataColumn c = null; 145 int count = 0; 146 while ((c = NextColumn( row, c, fAttribute, fNulls )) != null) { 147 count++; 148 } 149 return count; 150 } 151 MoveToFirstChild()152 internal bool MoveToFirstChild() { 153 RealFoliate(); 154 AssertValid(); 155 if (node == null) 156 return false; 157 158 if (column != null) { 159 if (fOnValue) 160 return false; 161 162 fOnValue = true; 163 return true; 164 } 165 else if (!IsFoliated( node )) { 166 // find virtual column elements first 167 DataColumn c = NextColumn( Row, null, false, false ); 168 if (c != null) { 169 MoveTo( node, c, doc.IsTextOnly(c) ); 170 return true; 171 } 172 } 173 174 // look for anything 175 XmlNode n = doc.SafeFirstChild( node ); 176 if (n != null) { 177 MoveTo(n); 178 return true; 179 } 180 181 return false; 182 } 183 MoveToNextSibling()184 internal bool MoveToNextSibling() { 185 RealFoliate(); 186 AssertValid(); 187 if (node != null) { 188 if (column != null) { 189 if (fOnValue && !doc.IsTextOnly(column)) 190 return false; 191 192 DataColumn c = NextColumn( Row, column, false, false ); 193 if (c != null) { 194 MoveTo( this.node, c, false ); 195 return true; 196 } 197 198 XmlNode n = doc.SafeFirstChild( node ); 199 if (n != null) { 200 MoveTo( n ); 201 return true; 202 } 203 } 204 else { 205 XmlNode n = doc.SafeNextSibling( node ); 206 if (n != null) { 207 MoveTo(n); 208 return true; 209 } 210 } 211 } 212 213 return false; 214 } 215 MoveToParent()216 internal bool MoveToParent() { 217 RealFoliate(); 218 AssertValid(); 219 if (node != null) { 220 if (column != null) { 221 if (fOnValue && !doc.IsTextOnly(column)) { 222 MoveTo( node, column, false ); 223 return true; 224 } 225 226 if (column.ColumnMapping != MappingType.Attribute) { 227 MoveTo( node, null, false ); 228 return true; 229 } 230 } 231 else { 232 XmlNode n = node.ParentNode; 233 if (n != null) { 234 MoveTo(n); 235 return true; 236 } 237 } 238 } 239 return false; 240 } 241 MoveToOwnerElement()242 internal bool MoveToOwnerElement() { 243 RealFoliate(); 244 AssertValid(); 245 if (node != null) { 246 if (column != null) { 247 if (fOnValue || doc.IsTextOnly(column) || column.ColumnMapping != MappingType.Attribute) 248 return false; 249 250 MoveTo( node, null, false ); 251 return true; 252 } 253 else if (node.NodeType == XmlNodeType.Attribute) { 254 XmlNode n = ((XmlAttribute)node).OwnerElement; 255 if (n != null) { 256 MoveTo( n, null, false ); 257 return true; 258 } 259 } 260 } 261 262 return false; 263 } 264 265 266 internal int AttributeCount { 267 get { 268 RealFoliate(); 269 AssertValid(); 270 if (node != null) { 271 if (column == null && node.NodeType == XmlNodeType.Element) { 272 if (!IsFoliated( node )) { 273 return ColumnCount( Row, true, false ); 274 } 275 else 276 return node.Attributes.Count; 277 } 278 } 279 return 0; 280 } 281 } 282 MoveToAttribute( int i )283 internal bool MoveToAttribute( int i ) { 284 RealFoliate(); 285 AssertValid(); 286 if ( i < 0 ) 287 return false; 288 if (node != null) { 289 if ((column == null || column.ColumnMapping == MappingType.Attribute) && node.NodeType == XmlNodeType.Element) { 290 if (!IsFoliated( node )) { 291 DataColumn c = NthColumn( Row, true, i, false ); 292 if (c != null) { 293 MoveTo( node, c, false ); 294 return true; 295 } 296 } 297 else { 298 XmlNode n = node.Attributes.Item(i); 299 if (n != null) { 300 MoveTo( n, null, false ); 301 return true; 302 } 303 } 304 } 305 } 306 return false; 307 } 308 309 internal XmlNodeType NodeType { 310 get { 311 RealFoliate(); 312 AssertValid(); 313 if (this.node == null) { 314 return XmlNodeType.None; 315 } 316 else if (this.column == null) { 317 return this.node.NodeType; 318 } 319 else if (this.fOnValue) { 320 return XmlNodeType.Text; 321 } 322 else if (this.column.ColumnMapping == MappingType.Attribute) { 323 return XmlNodeType.Attribute; 324 } 325 else { 326 return XmlNodeType.Element; 327 } 328 } 329 } 330 331 internal string LocalName { 332 get { 333 RealFoliate(); 334 AssertValid(); 335 if (this.node == null) { 336 return string.Empty; 337 }else if (this.column == null) { 338 String name = node.LocalName; 339 Debug.Assert( name != null ); 340 if ( IsLocalNameEmpty( this.node.NodeType ) ) 341 return String.Empty; 342 return name; 343 } 344 else if (this.fOnValue) { 345 return String.Empty; 346 } 347 else { 348 return doc.NameTable.Add(column.EncodedColumnName); 349 } 350 } 351 } 352 353 internal string NamespaceURI { 354 get { 355 RealFoliate(); 356 AssertValid(); 357 if (this.node == null) { 358 return string.Empty; 359 } 360 else if (this.column == null) { 361 return node.NamespaceURI; 362 } 363 else if (this.fOnValue) { 364 return string.Empty; 365 } 366 else { 367 return doc.NameTable.Add(column.Namespace); 368 } 369 } 370 } 371 372 internal string Name { 373 get { 374 RealFoliate(); 375 AssertValid(); 376 if (this.node == null) { 377 return string.Empty; 378 } 379 else if (this.column == null) { 380 String name = node.Name; 381 //Again it could be String.Empty at null position 382 Debug.Assert( name != null ); 383 if ( IsLocalNameEmpty( this.node.NodeType ) ) 384 return String.Empty; 385 return name; 386 } 387 else { 388 string prefix = Prefix; 389 string lname = LocalName; 390 if (prefix != null && prefix.Length > 0) { 391 if (lname != null && lname.Length > 0) { 392 return doc.NameTable.Add( prefix + ":" + lname ); 393 } 394 else { 395 return prefix; 396 } 397 } 398 else { 399 return lname; 400 } 401 } 402 } 403 } 404 405 IsLocalNameEmpty( XmlNodeType nt)406 private bool IsLocalNameEmpty ( XmlNodeType nt) { 407 switch ( nt ) { 408 case XmlNodeType.None : 409 case XmlNodeType.Text : 410 case XmlNodeType.CDATA : 411 case XmlNodeType.Comment : 412 case XmlNodeType.Document : 413 case XmlNodeType.DocumentFragment : 414 case XmlNodeType.Whitespace : 415 case XmlNodeType.SignificantWhitespace : 416 case XmlNodeType.EndElement : 417 case XmlNodeType.EndEntity : 418 return true; 419 case XmlNodeType.Element : 420 case XmlNodeType.Attribute : 421 case XmlNodeType.EntityReference : 422 case XmlNodeType.Entity : 423 case XmlNodeType.ProcessingInstruction : 424 case XmlNodeType.DocumentType : 425 case XmlNodeType.Notation : 426 case XmlNodeType.XmlDeclaration : 427 return false; 428 default : 429 return true; 430 } 431 } 432 433 internal string Prefix { 434 get { 435 RealFoliate(); 436 AssertValid(); 437 if (this.node == null) { 438 return string.Empty; 439 } 440 else if (this.column == null) { 441 return node.Prefix; 442 } 443 else { 444 return string.Empty; 445 } 446 } 447 } 448 449 internal string Value { 450 get { 451 RealFoliate(); 452 AssertValid(); 453 if (this.node == null) { 454 return null; 455 } 456 else if (this.column == null) { 457 return this.node.Value; 458 } 459 else if (this.column.ColumnMapping == MappingType.Attribute || this.fOnValue) { 460 DataRow row = this.Row; 461 DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current; 462 object value = row[ this.column, rowVersion ]; 463 if ( ! Convert.IsDBNull( value ) ) 464 return this.column.ConvertObjectToXml( value ); 465 return null; 466 } 467 else { 468 // column element has no value 469 return null; 470 } 471 } 472 } 473 IXmlDataVirtualNode.IsOnNode( XmlNode nodeToCheck )474 bool IXmlDataVirtualNode.IsOnNode( XmlNode nodeToCheck ) { 475 RealFoliate(); 476 return nodeToCheck == this.node; 477 } 478 IXmlDataVirtualNode.IsOnColumn( DataColumn col )479 bool IXmlDataVirtualNode.IsOnColumn( DataColumn col ) { 480 RealFoliate(); 481 return col == this.column; 482 } 483 GetNode()484 internal XmlNode GetNode() { 485 return this.node; 486 } 487 488 internal bool IsEmptyElement { 489 get { 490 RealFoliate(); 491 AssertValid(); 492 if (node != null && column == null) { 493 // 494 if (node.NodeType == XmlNodeType.Element) { 495 return((XmlElement)node).IsEmpty; 496 } 497 } 498 return false; 499 } 500 } 501 502 internal bool IsDefault { 503 get { 504 RealFoliate(); 505 AssertValid(); 506 if (node != null && column == null && node.NodeType == XmlNodeType.Attribute) { 507 return !((XmlAttribute)node).Specified; 508 } 509 510 return false; 511 } 512 } 513 IXmlDataVirtualNode.OnFoliated( XmlNode foliatedNode )514 void IXmlDataVirtualNode.OnFoliated( XmlNode foliatedNode ) { 515 // update the pointer if the element node has been foliated 516 if (node == foliatedNode) { 517 // if already on this node, nothing to do! 518 if (column == null) 519 return; 520 bNeedFoliate = true; 521 } 522 } 523 RealFoliate()524 internal void RealFoliate() { 525 if ( !bNeedFoliate ) 526 return; 527 528 XmlNode n = null; 529 530 if (doc.IsTextOnly( column )) { 531 n = node.FirstChild; 532 } 533 else { 534 if (column.ColumnMapping == MappingType.Attribute) { 535 n = node.Attributes.GetNamedItem( column.EncodedColumnName, column.Namespace ); 536 } 537 else { 538 for (n = node.FirstChild; n != null; n = n.NextSibling) { 539 if (n.LocalName == column.EncodedColumnName && n.NamespaceURI == column.Namespace) 540 break; 541 } 542 } 543 544 if (n != null && fOnValue) 545 n = n.FirstChild; 546 } 547 548 if (n == null) 549 throw new InvalidOperationException(Res.GetString(Res.DataDom_Foliation)); 550 551 // Cannot use MoveTo( n ); b/c the initial state for MoveTo is invalid (region is foliated but this is not) 552 this.node = n; 553 this.column = null; 554 this.fOnValue = false; 555 AssertValid(); 556 557 bNeedFoliate = false; 558 } 559 560 //for the 6 properties below, only when the this.column == null that the nodetype could be XmlDeclaration node 561 internal String PublicId { 562 get { 563 XmlNodeType nt = NodeType; 564 switch ( nt ) { 565 case XmlNodeType.DocumentType : { 566 Debug.Assert( this.column == null ); 567 return ( ( XmlDocumentType ) (this.node)).PublicId; 568 } 569 case XmlNodeType.Entity : { 570 Debug.Assert( this.column == null ); 571 return ( ( XmlEntity ) (this.node)).PublicId; 572 } 573 case XmlNodeType.Notation : { 574 Debug.Assert( this.column == null ); 575 return ( ( XmlNotation ) (this.node)).PublicId; 576 } 577 } 578 return null; 579 } 580 } 581 582 internal String SystemId { 583 get { 584 XmlNodeType nt = NodeType; 585 switch ( nt ) { 586 case XmlNodeType.DocumentType : { 587 Debug.Assert( this.column == null ); 588 return ( ( XmlDocumentType ) (this.node)).SystemId; 589 } 590 case XmlNodeType.Entity : { 591 Debug.Assert( this.column == null ); 592 return ( ( XmlEntity ) (this.node)).SystemId; 593 } 594 case XmlNodeType.Notation : { 595 Debug.Assert( this.column == null ); 596 return ( ( XmlNotation ) (this.node)).SystemId; 597 } 598 } 599 return null; 600 } 601 } 602 603 internal String InternalSubset { 604 get { 605 if ( NodeType == XmlNodeType.DocumentType ) { 606 Debug.Assert( this.column == null ); 607 return ( ( XmlDocumentType ) (this.node)).InternalSubset; 608 } 609 return null; 610 } 611 } 612 613 internal XmlDeclaration Declaration { 614 get { 615 XmlNode child = doc.SafeFirstChild(doc); 616 if ( child != null && child.NodeType == XmlNodeType.XmlDeclaration ) 617 return (XmlDeclaration)child; 618 return null; 619 } 620 } 621 622 internal String Encoding { 623 get { 624 if ( NodeType == XmlNodeType.XmlDeclaration ) { 625 Debug.Assert( this.column == null ); 626 return ( ( XmlDeclaration ) (this.node)).Encoding; 627 } else if ( NodeType == XmlNodeType.Document ) { 628 XmlDeclaration dec = Declaration; 629 if ( dec != null ) 630 return dec.Encoding; 631 } 632 return null; 633 } 634 } 635 636 internal String Standalone { 637 get { 638 if ( NodeType == XmlNodeType.XmlDeclaration ) { 639 Debug.Assert( this.column == null ); 640 return ( ( XmlDeclaration ) (this.node)).Standalone; 641 } else if ( NodeType == XmlNodeType.Document ) { 642 XmlDeclaration dec = Declaration; 643 if ( dec != null ) 644 return dec.Standalone; 645 } 646 return null; 647 } 648 } 649 650 internal String Version { 651 get { 652 if ( NodeType == XmlNodeType.XmlDeclaration ) { 653 Debug.Assert( this.column == null ); 654 return ( ( XmlDeclaration ) (this.node)).Version; 655 } else if ( NodeType == XmlNodeType.Document ) { 656 XmlDeclaration dec = Declaration; 657 if ( dec != null ) 658 return dec.Version; 659 } 660 return null; 661 } 662 } 663 664 [System.Diagnostics.Conditional("DEBUG")] AssertValid()665 private void AssertValid() { 666 // This pointer must be int the document list 667 if ( this.column != null ) { 668 // We must be on a de-foliated region 669 XmlBoundElement rowElem = this.node as XmlBoundElement; 670 Debug.Assert( rowElem != null ); 671 672 DataRow row = rowElem.Row; 673 Debug.Assert( row != null ); 674 675 ElementState state = rowElem.ElementState; 676 Debug.Assert( state == ElementState.Defoliated, "Region is accessed using column, but it's state is FOLIATED" ); 677 678 // We cannot be on a column for which the value is DBNull 679 DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current; 680 Debug.Assert( ! Convert.IsDBNull( row[ this.column, rowVersion ] ) ); 681 682 // If we are on the Text column, we should always have fOnValue == true 683 Debug.Assert( (this.column.ColumnMapping == MappingType.SimpleContent) ? (this.fOnValue == true) : true ); 684 } 685 } 686 IXmlDataVirtualNode.IsInUse()687 bool IXmlDataVirtualNode.IsInUse() { 688 return _isInUse; 689 } 690 SetNoLongerUse()691 internal void SetNoLongerUse() { 692 this.node = null; 693 this.column = null; 694 this.fOnValue = false; 695 this.bNeedFoliate = false; 696 this._isInUse = false; 697 } 698 699 } 700 } 701