//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // [....] // [....] //------------------------------------------------------------------------------ #pragma warning disable 618 // ignore obsolete warning about XmlDataDocument namespace System.Xml { using System; using System.Data; using System.Diagnostics; internal sealed class DataPointer : IXmlDataVirtualNode { private XmlDataDocument doc; private XmlNode node; private DataColumn column; private bool fOnValue; private bool bNeedFoliate = false; private bool _isInUse; internal DataPointer( XmlDataDocument doc, XmlNode node ) { this.doc = doc; this.node = node; this.column = null; this.fOnValue = false; bNeedFoliate = false; this._isInUse = true; AssertValid(); } internal DataPointer( DataPointer pointer ) { this.doc = pointer.doc; this.node = pointer.node; this.column = pointer.column; this.fOnValue = pointer.fOnValue; this.bNeedFoliate = false; this._isInUse = true; AssertValid(); } internal void AddPointer() { this.doc.AddPointer( (IXmlDataVirtualNode)this ); } // Returns the row element of the region that the pointer points into private XmlBoundElement GetRowElement() { //AssertValid(); XmlBoundElement rowElem; if ( this.column != null ) { rowElem = this.node as XmlBoundElement; Debug.Assert( rowElem != null ); Debug.Assert( rowElem.Row != null ); return rowElem; } doc.Mapper.GetRegion( this.node, out rowElem ); return rowElem; } private DataRow Row { get { //AssertValid(); XmlBoundElement rowElem = GetRowElement(); if ( rowElem == null ) return null; Debug.Assert( rowElem.Row != null ); return rowElem.Row; } } private static bool IsFoliated( XmlNode node ) { if (node != null && node is XmlBoundElement) return((XmlBoundElement)node).IsFoliated; return true; } internal void MoveTo( DataPointer pointer ) { AssertValid(); // You should not move outside of this document Debug.Assert( node == this.doc || node.OwnerDocument == this.doc ); this.doc = pointer.doc; this.node = pointer.node; this.column = pointer.column; this.fOnValue = pointer.fOnValue; AssertValid(); } private void MoveTo( XmlNode node ) { //AssertValid(); // You should not move outside of this document Debug.Assert( node == this.doc || node.OwnerDocument == this.doc ); this.node = node; this.column = null; this.fOnValue = false; AssertValid(); } private void MoveTo( XmlNode node, DataColumn column, bool fOnValue ) { //AssertValid(); // You should not move outside of this document Debug.Assert( node == this.doc || node.OwnerDocument == this.doc ); this.node = node; this.column = column; this.fOnValue = fOnValue; AssertValid(); } private DataColumn NextColumn( DataRow row, DataColumn col, bool fAttribute, bool fNulls ) { if (row.RowState == DataRowState.Deleted) return null; DataTable table = row.Table; DataColumnCollection columns = table.Columns; int iColumn = (col != null) ? col.Ordinal + 1 : 0; int cColumns = columns.Count; DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current; for (; iColumn < cColumns; iColumn++) { DataColumn c = columns[iColumn]; if (!doc.IsNotMapped( c ) && (c.ColumnMapping == MappingType.Attribute) == fAttribute && (fNulls || ! Convert.IsDBNull( row[c, rowVersion] ) ) ) return c; } return null; } private DataColumn NthColumn( DataRow row, bool fAttribute, int iColumn, bool fNulls ) { DataColumn c = null; while ((c = NextColumn( row, c, fAttribute, fNulls )) != null) { if (iColumn == 0) return c; iColumn = checked((int)iColumn-1); } return null; } private int ColumnCount( DataRow row, bool fAttribute, bool fNulls ) { DataColumn c = null; int count = 0; while ((c = NextColumn( row, c, fAttribute, fNulls )) != null) { count++; } return count; } internal bool MoveToFirstChild() { RealFoliate(); AssertValid(); if (node == null) return false; if (column != null) { if (fOnValue) return false; fOnValue = true; return true; } else if (!IsFoliated( node )) { // find virtual column elements first DataColumn c = NextColumn( Row, null, false, false ); if (c != null) { MoveTo( node, c, doc.IsTextOnly(c) ); return true; } } // look for anything XmlNode n = doc.SafeFirstChild( node ); if (n != null) { MoveTo(n); return true; } return false; } internal bool MoveToNextSibling() { RealFoliate(); AssertValid(); if (node != null) { if (column != null) { if (fOnValue && !doc.IsTextOnly(column)) return false; DataColumn c = NextColumn( Row, column, false, false ); if (c != null) { MoveTo( this.node, c, false ); return true; } XmlNode n = doc.SafeFirstChild( node ); if (n != null) { MoveTo( n ); return true; } } else { XmlNode n = doc.SafeNextSibling( node ); if (n != null) { MoveTo(n); return true; } } } return false; } internal bool MoveToParent() { RealFoliate(); AssertValid(); if (node != null) { if (column != null) { if (fOnValue && !doc.IsTextOnly(column)) { MoveTo( node, column, false ); return true; } if (column.ColumnMapping != MappingType.Attribute) { MoveTo( node, null, false ); return true; } } else { XmlNode n = node.ParentNode; if (n != null) { MoveTo(n); return true; } } } return false; } internal bool MoveToOwnerElement() { RealFoliate(); AssertValid(); if (node != null) { if (column != null) { if (fOnValue || doc.IsTextOnly(column) || column.ColumnMapping != MappingType.Attribute) return false; MoveTo( node, null, false ); return true; } else if (node.NodeType == XmlNodeType.Attribute) { XmlNode n = ((XmlAttribute)node).OwnerElement; if (n != null) { MoveTo( n, null, false ); return true; } } } return false; } internal int AttributeCount { get { RealFoliate(); AssertValid(); if (node != null) { if (column == null && node.NodeType == XmlNodeType.Element) { if (!IsFoliated( node )) { return ColumnCount( Row, true, false ); } else return node.Attributes.Count; } } return 0; } } internal bool MoveToAttribute( int i ) { RealFoliate(); AssertValid(); if ( i < 0 ) return false; if (node != null) { if ((column == null || column.ColumnMapping == MappingType.Attribute) && node.NodeType == XmlNodeType.Element) { if (!IsFoliated( node )) { DataColumn c = NthColumn( Row, true, i, false ); if (c != null) { MoveTo( node, c, false ); return true; } } else { XmlNode n = node.Attributes.Item(i); if (n != null) { MoveTo( n, null, false ); return true; } } } } return false; } internal XmlNodeType NodeType { get { RealFoliate(); AssertValid(); if (this.node == null) { return XmlNodeType.None; } else if (this.column == null) { return this.node.NodeType; } else if (this.fOnValue) { return XmlNodeType.Text; } else if (this.column.ColumnMapping == MappingType.Attribute) { return XmlNodeType.Attribute; } else { return XmlNodeType.Element; } } } internal string LocalName { get { RealFoliate(); AssertValid(); if (this.node == null) { return string.Empty; }else if (this.column == null) { String name = node.LocalName; Debug.Assert( name != null ); if ( IsLocalNameEmpty( this.node.NodeType ) ) return String.Empty; return name; } else if (this.fOnValue) { return String.Empty; } else { return doc.NameTable.Add(column.EncodedColumnName); } } } internal string NamespaceURI { get { RealFoliate(); AssertValid(); if (this.node == null) { return string.Empty; } else if (this.column == null) { return node.NamespaceURI; } else if (this.fOnValue) { return string.Empty; } else { return doc.NameTable.Add(column.Namespace); } } } internal string Name { get { RealFoliate(); AssertValid(); if (this.node == null) { return string.Empty; } else if (this.column == null) { String name = node.Name; //Again it could be String.Empty at null position Debug.Assert( name != null ); if ( IsLocalNameEmpty( this.node.NodeType ) ) return String.Empty; return name; } else { string prefix = Prefix; string lname = LocalName; if (prefix != null && prefix.Length > 0) { if (lname != null && lname.Length > 0) { return doc.NameTable.Add( prefix + ":" + lname ); } else { return prefix; } } else { return lname; } } } } private bool IsLocalNameEmpty ( XmlNodeType nt) { switch ( nt ) { case XmlNodeType.None : case XmlNodeType.Text : case XmlNodeType.CDATA : case XmlNodeType.Comment : case XmlNodeType.Document : case XmlNodeType.DocumentFragment : case XmlNodeType.Whitespace : case XmlNodeType.SignificantWhitespace : case XmlNodeType.EndElement : case XmlNodeType.EndEntity : return true; case XmlNodeType.Element : case XmlNodeType.Attribute : case XmlNodeType.EntityReference : case XmlNodeType.Entity : case XmlNodeType.ProcessingInstruction : case XmlNodeType.DocumentType : case XmlNodeType.Notation : case XmlNodeType.XmlDeclaration : return false; default : return true; } } internal string Prefix { get { RealFoliate(); AssertValid(); if (this.node == null) { return string.Empty; } else if (this.column == null) { return node.Prefix; } else { return string.Empty; } } } internal string Value { get { RealFoliate(); AssertValid(); if (this.node == null) { return null; } else if (this.column == null) { return this.node.Value; } else if (this.column.ColumnMapping == MappingType.Attribute || this.fOnValue) { DataRow row = this.Row; DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current; object value = row[ this.column, rowVersion ]; if ( ! Convert.IsDBNull( value ) ) return this.column.ConvertObjectToXml( value ); return null; } else { // column element has no value return null; } } } bool IXmlDataVirtualNode.IsOnNode( XmlNode nodeToCheck ) { RealFoliate(); return nodeToCheck == this.node; } bool IXmlDataVirtualNode.IsOnColumn( DataColumn col ) { RealFoliate(); return col == this.column; } internal XmlNode GetNode() { return this.node; } internal bool IsEmptyElement { get { RealFoliate(); AssertValid(); if (node != null && column == null) { // if (node.NodeType == XmlNodeType.Element) { return((XmlElement)node).IsEmpty; } } return false; } } internal bool IsDefault { get { RealFoliate(); AssertValid(); if (node != null && column == null && node.NodeType == XmlNodeType.Attribute) { return !((XmlAttribute)node).Specified; } return false; } } void IXmlDataVirtualNode.OnFoliated( XmlNode foliatedNode ) { // update the pointer if the element node has been foliated if (node == foliatedNode) { // if already on this node, nothing to do! if (column == null) return; bNeedFoliate = true; } } internal void RealFoliate() { if ( !bNeedFoliate ) return; XmlNode n = null; if (doc.IsTextOnly( column )) { n = node.FirstChild; } else { if (column.ColumnMapping == MappingType.Attribute) { n = node.Attributes.GetNamedItem( column.EncodedColumnName, column.Namespace ); } else { for (n = node.FirstChild; n != null; n = n.NextSibling) { if (n.LocalName == column.EncodedColumnName && n.NamespaceURI == column.Namespace) break; } } if (n != null && fOnValue) n = n.FirstChild; } if (n == null) throw new InvalidOperationException(Res.GetString(Res.DataDom_Foliation)); // Cannot use MoveTo( n ); b/c the initial state for MoveTo is invalid (region is foliated but this is not) this.node = n; this.column = null; this.fOnValue = false; AssertValid(); bNeedFoliate = false; } //for the 6 properties below, only when the this.column == null that the nodetype could be XmlDeclaration node internal String PublicId { get { XmlNodeType nt = NodeType; switch ( nt ) { case XmlNodeType.DocumentType : { Debug.Assert( this.column == null ); return ( ( XmlDocumentType ) (this.node)).PublicId; } case XmlNodeType.Entity : { Debug.Assert( this.column == null ); return ( ( XmlEntity ) (this.node)).PublicId; } case XmlNodeType.Notation : { Debug.Assert( this.column == null ); return ( ( XmlNotation ) (this.node)).PublicId; } } return null; } } internal String SystemId { get { XmlNodeType nt = NodeType; switch ( nt ) { case XmlNodeType.DocumentType : { Debug.Assert( this.column == null ); return ( ( XmlDocumentType ) (this.node)).SystemId; } case XmlNodeType.Entity : { Debug.Assert( this.column == null ); return ( ( XmlEntity ) (this.node)).SystemId; } case XmlNodeType.Notation : { Debug.Assert( this.column == null ); return ( ( XmlNotation ) (this.node)).SystemId; } } return null; } } internal String InternalSubset { get { if ( NodeType == XmlNodeType.DocumentType ) { Debug.Assert( this.column == null ); return ( ( XmlDocumentType ) (this.node)).InternalSubset; } return null; } } internal XmlDeclaration Declaration { get { XmlNode child = doc.SafeFirstChild(doc); if ( child != null && child.NodeType == XmlNodeType.XmlDeclaration ) return (XmlDeclaration)child; return null; } } internal String Encoding { get { if ( NodeType == XmlNodeType.XmlDeclaration ) { Debug.Assert( this.column == null ); return ( ( XmlDeclaration ) (this.node)).Encoding; } else if ( NodeType == XmlNodeType.Document ) { XmlDeclaration dec = Declaration; if ( dec != null ) return dec.Encoding; } return null; } } internal String Standalone { get { if ( NodeType == XmlNodeType.XmlDeclaration ) { Debug.Assert( this.column == null ); return ( ( XmlDeclaration ) (this.node)).Standalone; } else if ( NodeType == XmlNodeType.Document ) { XmlDeclaration dec = Declaration; if ( dec != null ) return dec.Standalone; } return null; } } internal String Version { get { if ( NodeType == XmlNodeType.XmlDeclaration ) { Debug.Assert( this.column == null ); return ( ( XmlDeclaration ) (this.node)).Version; } else if ( NodeType == XmlNodeType.Document ) { XmlDeclaration dec = Declaration; if ( dec != null ) return dec.Version; } return null; } } [System.Diagnostics.Conditional("DEBUG")] private void AssertValid() { // This pointer must be int the document list if ( this.column != null ) { // We must be on a de-foliated region XmlBoundElement rowElem = this.node as XmlBoundElement; Debug.Assert( rowElem != null ); DataRow row = rowElem.Row; Debug.Assert( row != null ); ElementState state = rowElem.ElementState; Debug.Assert( state == ElementState.Defoliated, "Region is accessed using column, but it's state is FOLIATED" ); // We cannot be on a column for which the value is DBNull DataRowVersion rowVersion = ( row.RowState == DataRowState.Detached ) ? DataRowVersion.Proposed : DataRowVersion.Current; Debug.Assert( ! Convert.IsDBNull( row[ this.column, rowVersion ] ) ); // If we are on the Text column, we should always have fOnValue == true Debug.Assert( (this.column.ColumnMapping == MappingType.SimpleContent) ? (this.fOnValue == true) : true ); } } bool IXmlDataVirtualNode.IsInUse() { return _isInUse; } internal void SetNoLongerUse() { this.node = null; this.column = null; this.fOnValue = false; this.bNeedFoliate = false; this._isInUse = false; } } }