| File: System\Xml\RegionIterator.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.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; #pragma warning disable 618 // ignore obsolete warning about XmlDataDocument namespace System.Xml { internal abstract class BaseRegionIterator : BaseTreeIterator { internal BaseRegionIterator(DataSetMapper mapper) : base(mapper) { } } // Iterates over non-attribute nodes internal sealed class RegionIterator : BaseRegionIterator { private readonly XmlBoundElement _rowElement; private XmlNode _currentNode; internal RegionIterator(XmlBoundElement rowElement) : base(((XmlDataDocument)(rowElement.OwnerDocument)).Mapper) { Debug.Assert(rowElement != null && rowElement.Row != null); _rowElement = rowElement; _currentNode = rowElement; } internal override XmlNode? CurrentNode => _currentNode; [MemberNotNullWhen(true, nameof(CurrentNode))] internal override bool Next() { XmlNode? nextNode; ElementState oldState = _rowElement.ElementState; // We do not want to cause any foliation w/ this iterator or use this iterator once the region was defoliated Debug.Assert(oldState != ElementState.None); // Try to move to the first child nextNode = _currentNode.FirstChild; // No children, try next sibling if (nextNode != null) { _currentNode = nextNode; Debug.Assert(CurrentNode != null); // If we have been defoliated, we should have stayed that way Debug.Assert((oldState == ElementState.Defoliated) ? (_rowElement.ElementState == ElementState.Defoliated) : true); // Rollback foliation _rowElement.ElementState = oldState; return true; } return NextRight(); } [MemberNotNullWhen(true, nameof(CurrentNode))] internal override bool NextRight() { // Make sure we do not get past the rowElement if we call NextRight on a just initialized iterator and rowElement has no children if (_currentNode == _rowElement) { _currentNode = null!; return false; } ElementState oldState = _rowElement.ElementState; // We do not want to cause any foliation w/ this iterator or use this iterator once the region was defoliated Debug.Assert(oldState != ElementState.None); XmlNode? nextNode = _currentNode.NextSibling; if (nextNode != null) { _currentNode = nextNode; // If we have been defoliated, we should have stayed that way Debug.Assert((oldState == ElementState.Defoliated) ? (_rowElement.ElementState == ElementState.Defoliated) : true); // Rollback foliation _rowElement.ElementState = oldState; Debug.Assert(CurrentNode != null); return true; } // No next sibling, try the first sibling of from the parent chain nextNode = _currentNode; while (nextNode != _rowElement && nextNode!.NextSibling == null) { nextNode = nextNode.ParentNode; } if (nextNode == _rowElement) { _currentNode = null!; // If we have been defoliated, we should have stayed that way Debug.Assert((oldState == ElementState.Defoliated) ? (_rowElement.ElementState == ElementState.Defoliated) : true); // Rollback foliation _rowElement.ElementState = oldState; return false; } Debug.Assert(nextNode.NextSibling != null); _currentNode = nextNode.NextSibling; // If we have been defoliated, we should have stayed that way Debug.Assert((oldState == ElementState.Defoliated) ? (_rowElement.ElementState == ElementState.Defoliated) : true); // Rollback foliation _rowElement.ElementState = oldState; Debug.Assert(CurrentNode != null); return true; } // Get the initial text value for the current node. You should be positioned on the node (element) for // which to get the initial text value, not on the text node. [MemberNotNullWhen(true, nameof(CurrentNode))] internal bool NextInitialTextLikeNodes(out string value) { Debug.Assert(CurrentNode != null); Debug.Assert(CurrentNode.NodeType == XmlNodeType.Element); #if DEBUG // It's not OK to try to read the initial text value for sub-regions, because we do not know how to revert their initial state if (CurrentNode.NodeType == XmlNodeType.Element && DataSetMapper.GetTableSchemaForElement((XmlElement)(CurrentNode)) != null) { if (CurrentNode != _rowElement) { Debug.Fail("Reading the initial text value for sub-regions."); } } #endif ElementState oldState = _rowElement.ElementState; // We do not want to cause any foliation w/ this iterator or use this iterator once the region was defoliated Debug.Assert(oldState != ElementState.None); XmlNode? n = CurrentNode.FirstChild; value = GetInitialTextFromNodes(ref n); if (n == null) { // If we have been defoliated, we should have stayed that way Debug.Assert((oldState == ElementState.Defoliated) ? (_rowElement.ElementState == ElementState.Defoliated) : true); // Rollback eventual foliation _rowElement.ElementState = oldState; return NextRight(); } Debug.Assert(!XmlDataDocument.Helpers.IsTextLikeNode(n)); _currentNode = n; // If we have been defoliated, we should have stayed that way Debug.Assert((oldState == ElementState.Defoliated) ? (_rowElement.ElementState == ElementState.Defoliated) : true); // Rollback eventual foliation _rowElement.ElementState = oldState; return true; } private static string GetInitialTextFromNodes(ref XmlNode? n) { string? value = null; if (n != null) { // don't consider whitespace while (n.NodeType == XmlNodeType.Whitespace) { n = n.NextSibling; if (n == null) { return string.Empty; } } if (XmlDataDocument.Helpers.IsTextLikeNode(n) && (n.NextSibling == null || !XmlDataDocument.Helpers.IsTextLikeNode(n.NextSibling))) { // don't use string builder if only one text node exists value = n.Value; n = n.NextSibling; } else { StringBuilder sb = new StringBuilder(); while (n != null && XmlDataDocument.Helpers.IsTextLikeNode(n)) { // Ignore non-significant whitespace nodes if (n.NodeType != XmlNodeType.Whitespace) { sb.Append(n.Value); } n = n.NextSibling; } value = sb.ToString(); } } return value ?? string.Empty; } } }