File: System\Xml\XPath\Internal\FilterQuery.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.Diagnostics;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
 
namespace MS.Internal.Xml.XPath
{
    internal sealed class FilterQuery : BaseAxisQuery
    {
        private readonly Query _cond;
        private readonly bool _noPosition;
 
        public FilterQuery(Query qyParent, Query cond, bool noPosition) : base(qyParent)
        {
            _cond = cond;
            _noPosition = noPosition;
        }
        private FilterQuery(FilterQuery other) : base(other)
        {
            _cond = Clone(other._cond);
            _noPosition = other._noPosition;
        }
 
        public override void Reset()
        {
            _cond.Reset();
            base.Reset();
        }
 
        public Query Condition { get { return _cond; } }
 
        public override void SetXsltContext(XsltContext input)
        {
            base.SetXsltContext(input);
            _cond.SetXsltContext(input);
            if (_cond.StaticType != XPathResultType.Number && _cond.StaticType != XPathResultType.Any && _noPosition)
            {
                // BugBug: We can do such trick at Evaluate time only.
                // But to do this FilterQuery should stop inherit from BaseAxisQuery
                ReversePositionQuery? query = qyInput as ReversePositionQuery;
                if (query != null)
                {
                    qyInput = query.input;
                }
            }
        }
 
        public override XPathNavigator? Advance()
        {
            while ((currentNode = qyInput.Advance()) != null)
            {
                if (EvaluatePredicate())
                {
                    position++;
                    return currentNode;
                }
            }
            return null;
        }
 
        internal bool EvaluatePredicate()
        {
            object value = _cond.Evaluate(qyInput);
            if (value is XPathNodeIterator) return _cond.Advance() != null;
            if (value is string) return ((string)value).Length != 0;
            if (value is double) return (((double)value) == qyInput.CurrentPosition);
            if (value is bool) return (bool)value;
            Debug.Assert(value is XPathNavigator, "Unknown value type");
            return true;
        }
 
        public override XPathNavigator? MatchNode(XPathNavigator? current)
        {
            XPathNavigator? context;
            if (current == null)
            {
                return null;
            }
            context = qyInput.MatchNode(current);
 
            if (context != null)
            {
                // In this switch we process some special case in which we can calculate predicate faster then in generic case
                switch (_cond.StaticType)
                {
                    case XPathResultType.Number:
                        OperandQuery? operand = _cond as OperandQuery;
                        if (operand != null)
                        {
                            double val = (double)operand.val;
                            ChildrenQuery? childrenQuery = qyInput as ChildrenQuery;
                            if (childrenQuery != null)
                            { // foo[2], but not foo[expr][2]
                                XPathNavigator result = current.Clone();
                                result.MoveToParent();
                                int i = 0;
                                result.MoveToFirstChild();
                                do
                                {
                                    if (childrenQuery.matches(result))
                                    {
                                        i++;
                                        if (current.IsSamePosition(result))
                                        {
                                            return val == i ? context : null;
                                        }
                                    }
                                } while (result.MoveToNext());
                                return null;
                            }
                            AttributeQuery? attributeQuery = qyInput as AttributeQuery;
                            if (attributeQuery != null)
                            {// @foo[3], but not @foo[expr][2]
                                XPathNavigator result = current.Clone();
                                result.MoveToParent();
                                int i = 0;
                                result.MoveToFirstAttribute();
                                do
                                {
                                    if (attributeQuery.matches(result))
                                    {
                                        i++;
                                        if (current.IsSamePosition(result))
                                        {
                                            return val == i ? context : null;
                                        }
                                    }
                                } while (result.MoveToNextAttribute());
                                return null;
                            }
                        }
                        break;
                    case XPathResultType.NodeSet:
                        _cond.Evaluate(new XPathSingletonIterator(current, /*moved:*/true));
                        return (_cond.Advance() != null) ? context : null;
                    case XPathResultType.Boolean:
                        if (_noPosition)
                        {
                            return ((bool)_cond.Evaluate(new XPathSingletonIterator(current, /*moved:*/true))) ? context : null;
                        }
                        break;
                    case XPathResultType.String:
                        if (_noPosition)
                        {
                            return (((string)_cond.Evaluate(new XPathSingletonIterator(current, /*moved:*/true))).Length != 0) ? context : null;
                        }
                        break;
                    case XPathResultType_Navigator:
                        return context;
                    default:
                        return null;
                }
                /* Generic case */
                {
                    Evaluate(new XPathSingletonIterator(context, /*moved:*/true));
                    XPathNavigator? result;
                    while ((result = Advance()) != null)
                    {
                        if (result.IsSamePosition(current))
                        {
                            return context;
                        }
                    }
                }
            }
            return null;
        }
 
        public override QueryProps Properties
        {
            get
            {
                return QueryProps.Position | (qyInput.Properties & (QueryProps.Merge | QueryProps.Reverse));
            }
        }
 
        public override XPathNodeIterator Clone() { return new FilterQuery(this); }
    }
}