| File: System\Xml\DataPointer.cs | Web Access |
| Project: src\runtime\src\libraries\System.Data.Common\src\System.Data.Common.csproj (System.Data.Common) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Data; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; #pragma warning disable 0618 // ignore obsolete warning about XmlDataDocument namespace System.Xml { internal sealed class DataPointer : IXmlDataVirtualNode { private XmlDataDocument _doc; private XmlNode _node; private DataColumn? _column; private bool _fOnValue; private bool _bNeedFoliate; private bool _isInUse; internal DataPointer(XmlDataDocument doc, XmlNode node) { _doc = doc; _node = node; _column = null; _fOnValue = false; _bNeedFoliate = false; _isInUse = true; AssertValid(); } internal DataPointer(DataPointer pointer) { _doc = pointer._doc; _node = pointer._node; _column = pointer._column; _fOnValue = pointer._fOnValue; _bNeedFoliate = false; _isInUse = true; AssertValid(); } internal void AddPointer() => _doc.AddPointer(this); // Returns the row element of the region that the pointer points into private XmlBoundElement? GetRowElement() { XmlBoundElement? rowElem; if (_column != null) { rowElem = _node as XmlBoundElement; Debug.Assert(rowElem != null); Debug.Assert(rowElem.Row != null); return rowElem; } DataSetMapper.GetRegion(_node, out rowElem); return rowElem; } private DataRow? Row { get { XmlBoundElement? rowElem = GetRowElement(); if (rowElem == null) { return null; } Debug.Assert(rowElem.Row != null); return rowElem.Row; } } private static bool IsFoliated(XmlNode node) => node != null && node is XmlBoundElement ? ((XmlBoundElement)node).IsFoliated : true; internal void MoveTo(DataPointer pointer) { AssertValid(); // You should not move outside of this document Debug.Assert(_node == _doc || _node.OwnerDocument == _doc); _doc = pointer._doc; _node = pointer._node; _column = pointer._column; _fOnValue = pointer._fOnValue; AssertValid(); } private void MoveTo(XmlNode node) { // You should not move outside of this document Debug.Assert(node == _doc || node.OwnerDocument == _doc); _node = node; _column = null; _fOnValue = false; AssertValid(); } private void MoveTo(XmlNode node, DataColumn? column, bool fOnValue) { // You should not move outside of this document Debug.Assert(node == _doc || node.OwnerDocument == _doc); _node = node; _column = column; _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((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(_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 (_node == null) { return XmlNodeType.None; } else if (_column == null) { return _node.NodeType; } else if (_fOnValue) { return XmlNodeType.Text; } else if (_column.ColumnMapping == MappingType.Attribute) { return XmlNodeType.Attribute; } else { return XmlNodeType.Element; } } } internal string LocalName { get { RealFoliate(); AssertValid(); if (_node == null) { return string.Empty; } else if (_column == null) { string name = _node.LocalName; Debug.Assert(name != null); if (IsLocalNameEmpty(_node.NodeType)) { return string.Empty; } return name; } else if (_fOnValue) { return string.Empty; } else { return _doc.NameTable.Add(_column.EncodedColumnName); } } } internal string NamespaceURI { get { RealFoliate(); AssertValid(); if (_node == null) { return string.Empty; } else if (_column == null) { return _node.NamespaceURI; } else if (_fOnValue) { return string.Empty; } else { return _doc.NameTable.Add(_column.Namespace); } } } internal string Name { get { RealFoliate(); AssertValid(); if (_node == null) { return string.Empty; } else if (_column == null) { string name = _node.Name; //Again it could be String.Empty at null position Debug.Assert(name != null); if (IsLocalNameEmpty(_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 static 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 (_node == null) { return string.Empty; } else if (_column == null) { return _node.Prefix; } else { return string.Empty; } } } internal string? Value { [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)] [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)] get { RealFoliate(); AssertValid(); if (_node == null) { return null; } else if (_column == null) { return _node.Value; } else if (_column.ColumnMapping == MappingType.Attribute || _fOnValue) { DataRow row = Row!; DataRowVersion rowVersion = (row.RowState == DataRowState.Detached) ? DataRowVersion.Proposed : DataRowVersion.Current; object value = row[_column, rowVersion]; if (!Convert.IsDBNull(value)) { return _column.ConvertObjectToXml(value); } return null; } else { // column element has no value return null; } } } bool IXmlDataVirtualNode.IsOnNode(XmlNode nodeToCheck) { RealFoliate(); return nodeToCheck == _node; } bool IXmlDataVirtualNode.IsOnColumn(DataColumn? col) { RealFoliate(); return col == _column; } internal XmlNode GetNode() => _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; 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(SR.DataDom_Foliation); } // Cannot use MoveTo( n ); b/c the initial state for MoveTo is invalid (region is foliated but this is not) _node = n; _column = null; _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(_column == null); return ((XmlDocumentType)(_node)).PublicId; } case XmlNodeType.Entity: { Debug.Assert(_column == null); return ((XmlEntity)(_node)).PublicId; } case XmlNodeType.Notation: { Debug.Assert(_column == null); return ((XmlNotation)(_node)).PublicId; } } return null; } } internal string? SystemId { get { XmlNodeType nt = NodeType; switch (nt) { case XmlNodeType.DocumentType: { Debug.Assert(_column == null); return ((XmlDocumentType)(_node)).SystemId; } case XmlNodeType.Entity: { Debug.Assert(_column == null); return ((XmlEntity)(_node)).SystemId; } case XmlNodeType.Notation: { Debug.Assert(_column == null); return ((XmlNotation)(_node)).SystemId; } } return null; } } internal string? InternalSubset { get { if (NodeType == XmlNodeType.DocumentType) { Debug.Assert(_column == null); return ((XmlDocumentType)(_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(_column == null); return ((XmlDeclaration)(_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(_column == null); return ((XmlDeclaration)(_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(_column == null); return ((XmlDeclaration)(_node)).Version; } else if (NodeType == XmlNodeType.Document) { XmlDeclaration? dec = Declaration; if (dec != null) return dec.Version; } return null; } } [Conditional("DEBUG")] private void AssertValid() { // This pointer must be int the document list if (_column != null) { // We must be on a de-foliated region XmlBoundElement? rowElem = _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[_column, rowVersion])); // If we are on the Text column, we should always have fOnValue == true Debug.Assert((_column.ColumnMapping == MappingType.SimpleContent) ? _fOnValue : true); } } bool IXmlDataVirtualNode.IsInUse() => _isInUse; internal void SetNoLongerUse() { _node = null!; _column = null; _fOnValue = false; _bNeedFoliate = false; _isInUse = false; } } }