File: System\Xml\Xsl\Runtime\XmlNavigatorFilter.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel;
using System.Diagnostics;
using System.Xml;
using System.Xml.XPath;
 
namespace System.Xml.Xsl.Runtime
{
    /// <summary>
    /// XmlNavigatorFilter provides a flexible filtering abstraction over XPathNavigator.  Callers do
    /// not know what type of filtering will occur; they simply call MoveToContent or MoveToSibling.
    /// The filter implementation invokes appropriate operation(s) on the XPathNavigator in order
    /// to skip over filtered nodes.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public abstract class XmlNavigatorFilter
    {
        /// <summary>
        /// Reposition the navigator to the first matching content node (inc. attributes); skip over
        /// filtered nodes.  If there are no matching nodes, then don't move navigator and return false.
        /// </summary>
        public abstract bool MoveToContent(XPathNavigator navigator);
 
        /// <summary>
        /// Reposition the navigator to the next matching content node (inc. attributes); skip over
        /// filtered nodes.  If there are no matching nodes, then don't move navigator and return false.
        /// </summary>
        public abstract bool MoveToNextContent(XPathNavigator navigator);
 
        /// <summary>
        /// Reposition the navigator to the next following sibling node (no attributes); skip over
        /// filtered nodes.  If there are no matching nodes, then don't move navigator and return false.
        /// </summary>
        public abstract bool MoveToFollowingSibling(XPathNavigator navigator);
 
        /// <summary>
        /// Reposition the navigator to the previous sibling node (no attributes); skip over filtered
        /// nodes.  If there are no matching nodes, then don't move navigator and return false.
        /// </summary>
        public abstract bool MoveToPreviousSibling(XPathNavigator navigator);
 
        /// <summary>
        /// Reposition the navigator to the next following node (inc. descendants); skip over filtered nodes.
        /// If there are no matching nodes, then return false.
        /// </summary>
        public abstract bool MoveToFollowing(XPathNavigator navigator, XPathNavigator? navigatorEnd);
 
        /// <summary>
        /// Return true if the navigator's current node matches the filter condition.
        /// </summary>
        public abstract bool IsFiltered(XPathNavigator navigator);
    }
 
 
    /// <summary>
    /// Filters any non-element and any element with a non-matching local name or namespace uri.
    /// </summary>
    internal sealed class XmlNavNameFilter : XmlNavigatorFilter
    {
        private readonly string _localName;
        private readonly string _namespaceUri;
 
        /// <summary>
        /// Return an XmlNavigatorFilter that skips over nodes that do not match the specified name.
        /// </summary>
        public static XmlNavigatorFilter Create(string localName, string namespaceUri)
        {
            return new XmlNavNameFilter(localName, namespaceUri);
        }
 
        /// <summary>
        /// Keep only elements with name = localName, namespaceUri.
        /// </summary>
        private XmlNavNameFilter(string localName, string namespaceUri)
        {
            _localName = localName;
            _namespaceUri = namespaceUri;
        }
 
        /// <summary>
        /// Reposition the navigator on the first element child with a matching name.
        /// </summary>
        public override bool MoveToContent(XPathNavigator navigator)
        {
            return navigator.MoveToChild(_localName, _namespaceUri);
        }
 
        /// <summary>
        /// Reposition the navigator on the next element child with a matching name.
        /// </summary>
        public override bool MoveToNextContent(XPathNavigator navigator)
        {
            return navigator.MoveToNext(_localName, _namespaceUri);
        }
 
        /// <summary>
        /// Reposition the navigator on the next element sibling with a matching name.
        /// </summary>
        public override bool MoveToFollowingSibling(XPathNavigator navigator)
        {
            return navigator.MoveToNext(_localName, _namespaceUri);
        }
 
        /// <summary>
        /// Reposition the navigator on the previous element sibling with a matching name.
        /// </summary>
        public override bool MoveToPreviousSibling(XPathNavigator navigator)
        {
            return navigator.MoveToPrevious(_localName, _namespaceUri);
        }
 
        /// <summary>
        /// Reposition the navigator on the next following element with a matching name.
        /// </summary>
        public override bool MoveToFollowing(XPathNavigator navigator, XPathNavigator? navEnd)
        {
            return navigator.MoveToFollowing(_localName, _namespaceUri, navEnd);
        }
 
        /// <summary>
        /// Return false if the navigator is positioned on an element with a matching name.
        /// </summary>
        public override bool IsFiltered(XPathNavigator navigator)
        {
            return navigator.LocalName != _localName || navigator.NamespaceURI != _namespaceUri;
        }
    }
 
 
    /// <summary>
    /// Filters any node not of the specified type (type may not be attribute or namespace).
    /// </summary>
    internal sealed class XmlNavTypeFilter : XmlNavigatorFilter
    {
        private static readonly XmlNavigatorFilter[] s_typeFilters = CreateTypeFilters();
        private readonly XPathNodeType _nodeType;
        private readonly int _mask;
 
        /// <summary>
        /// There are a limited number of types, so create all possible XmlNavTypeFilter objects just once.
        /// </summary>
        private static XmlNavigatorFilter[] CreateTypeFilters()
        {
            var filters = new XmlNavigatorFilter[(int)XPathNodeType.Comment + 1];
            filters[(int)XPathNodeType.Element] = new XmlNavTypeFilter(XPathNodeType.Element);
            filters[(int)XPathNodeType.Text] = new XmlNavTypeFilter(XPathNodeType.Text);
            filters[(int)XPathNodeType.ProcessingInstruction] = new XmlNavTypeFilter(XPathNodeType.ProcessingInstruction);
            filters[(int)XPathNodeType.Comment] = new XmlNavTypeFilter(XPathNodeType.Comment);
            return filters;
        }
 
        /// <summary>
        /// Return a previously constructed XmlNavigatorFilter that skips over nodes that do not match the specified type.
        /// </summary>
        public static XmlNavigatorFilter Create(XPathNodeType nodeType)
        {
            Debug.Assert(s_typeFilters[(int)nodeType] != null);
            return s_typeFilters[(int)nodeType];
        }
 
        /// <summary>
        /// Keep only nodes with XPathNodeType = nodeType, where XPathNodeType.Text selects whitespace as well.
        /// </summary>
        private XmlNavTypeFilter(XPathNodeType nodeType)
        {
            Debug.Assert(nodeType != XPathNodeType.Attribute && nodeType != XPathNodeType.Namespace);
            _nodeType = nodeType;
            _mask = XPathNavigator.GetContentKindMask(nodeType);
        }
 
        /// <summary>
        /// Reposition the navigator on the first child with a matching type.
        /// </summary>
        public override bool MoveToContent(XPathNavigator navigator)
        {
            return navigator.MoveToChild(_nodeType);
        }
 
        /// <summary>
        /// Reposition the navigator on the next child with a matching type.
        /// </summary>
        public override bool MoveToNextContent(XPathNavigator navigator)
        {
            return navigator.MoveToNext(_nodeType);
        }
 
        /// <summary>
        /// Reposition the navigator on the next non-attribute sibling with a matching type.
        /// </summary>
        public override bool MoveToFollowingSibling(XPathNavigator navigator)
        {
            return navigator.MoveToNext(_nodeType);
        }
 
        /// <summary>
        /// Reposition the navigator on the previous non-attribute sibling with a matching type.
        /// </summary>
        public override bool MoveToPreviousSibling(XPathNavigator navigator)
        {
            return navigator.MoveToPrevious(_nodeType);
        }
 
        /// <summary>
        /// Reposition the navigator on the next following element with a matching kind.
        /// </summary>
        public override bool MoveToFollowing(XPathNavigator navigator, XPathNavigator? navEnd)
        {
            return navigator.MoveToFollowing(_nodeType, navEnd);
        }
 
        /// <summary>
        /// Return false if the navigator is positioned on a node with a matching type.
        /// </summary>
        public override bool IsFiltered(XPathNavigator navigator)
        {
            return ((1 << (int)navigator.NodeType) & _mask) == 0;
        }
    }
 
 
    /// <summary>
    /// Filters all attribute nodes.
    /// </summary>
    internal sealed class XmlNavAttrFilter : XmlNavigatorFilter
    {
        private static readonly XmlNavigatorFilter s_singleton = new XmlNavAttrFilter();
 
        /// <summary>
        /// Return a singleton XmlNavigatorFilter that filters all attribute nodes.
        /// </summary>
        public static XmlNavigatorFilter Create()
        {
            return s_singleton;
        }
 
        /// <summary>
        /// Constructor.
        /// </summary>
        private XmlNavAttrFilter()
        {
        }
 
        /// <summary>
        /// Reposition the navigator on the first non-attribute child.
        /// </summary>
        public override bool MoveToContent(XPathNavigator navigator)
        {
            return navigator.MoveToFirstChild();
        }
 
        /// <summary>
        /// Reposition the navigator on the next non-attribute sibling.
        /// </summary>
        public override bool MoveToNextContent(XPathNavigator navigator)
        {
            return navigator.MoveToNext();
        }
 
        /// <summary>
        /// Reposition the navigator on the next non-attribute sibling.
        /// </summary>
        public override bool MoveToFollowingSibling(XPathNavigator navigator)
        {
            return navigator.MoveToNext();
        }
 
        /// <summary>
        /// Reposition the navigator on the previous non-attribute sibling.
        /// </summary>
        public override bool MoveToPreviousSibling(XPathNavigator navigator)
        {
            return navigator.MoveToPrevious();
        }
 
        /// <summary>
        /// Reposition the navigator on the next following non-attribute.
        /// </summary>
        public override bool MoveToFollowing(XPathNavigator navigator, XPathNavigator? navEnd)
        {
            return navigator.MoveToFollowing(XPathNodeType.All, navEnd);
        }
 
        /// <summary>
        /// Return true if the navigator is positioned on an attribute.
        /// </summary>
        public override bool IsFiltered(XPathNavigator navigator)
        {
            return navigator.NodeType == XPathNodeType.Attribute;
        }
    }
 
 
    /// <summary>
    /// Never filter nodes.
    /// </summary>
    internal sealed class XmlNavNeverFilter : XmlNavigatorFilter
    {
        private static readonly XmlNavigatorFilter s_singleton = new XmlNavNeverFilter();
 
        /// <summary>
        /// Return a singleton XmlNavigatorFilter that never filters any nodes.
        /// </summary>
        public static XmlNavigatorFilter Create()
        {
            return s_singleton;
        }
 
        /// <summary>
        /// Constructor.
        /// </summary>
        private XmlNavNeverFilter()
        {
        }
 
        /// <summary>
        /// Reposition the navigator on the first child (attribute or non-attribute).
        /// </summary>
        public override bool MoveToContent(XPathNavigator navigator)
        {
            return MoveToFirstAttributeContent(navigator);
        }
 
        /// <summary>
        /// Reposition the navigator on the next child (attribute or non-attribute).
        /// </summary>
        public override bool MoveToNextContent(XPathNavigator navigator)
        {
            return MoveToNextAttributeContent(navigator);
        }
 
        /// <summary>
        /// Reposition the navigator on the next sibling (no attributes).
        /// </summary>
        public override bool MoveToFollowingSibling(XPathNavigator navigator)
        {
            return navigator.MoveToNext();
        }
 
        /// <summary>
        /// Reposition the navigator on the previous sibling (no attributes).
        /// </summary>
        public override bool MoveToPreviousSibling(XPathNavigator navigator)
        {
            return navigator.MoveToPrevious();
        }
 
        /// <summary>
        /// Reposition the navigator on the next following node.
        /// </summary>
        public override bool MoveToFollowing(XPathNavigator navigator, XPathNavigator? navEnd)
        {
            return navigator.MoveToFollowing(XPathNodeType.All, navEnd);
        }
 
        /// <summary>
        /// Nodes are never filtered so always return false.
        /// </summary>
        public override bool IsFiltered(XPathNavigator navigator)
        {
            return false;
        }
 
        /// <summary>
        /// Move to navigator's first attribute node.  If no attribute's exist, move to the first content node.
        /// If no content nodes exist, return null.  Otherwise, return navigator.
        /// </summary>
        public static bool MoveToFirstAttributeContent(XPathNavigator navigator)
        {
            if (!navigator.MoveToFirstAttribute())
                return navigator.MoveToFirstChild();
            return true;
        }
 
        /// <summary>
        /// If navigator is positioned on an attribute, move to the next attribute node.  If there are no more
        /// attributes, move to the first content node.  If navigator is positioned on a content node, move to
        /// the next content node.  If there are no more attributes and content nodes, return null.
        /// Otherwise, return navigator.
        /// </summary>
        public static bool MoveToNextAttributeContent(XPathNavigator navigator)
        {
            if (navigator.NodeType == XPathNodeType.Attribute)
            {
                if (!navigator.MoveToNextAttribute())
                {
                    navigator.MoveToParent();
                    if (!navigator.MoveToFirstChild())
                    {
                        // No children, so reposition on original attribute
                        navigator.MoveToFirstAttribute();
                        while (navigator.MoveToNextAttribute())
                            ;
                        return false;
                    }
                }
                return true;
            }
            return navigator.MoveToNext();
        }
    }
}