| File: System\Xml\XPath\Internal\Query.cs | Web Access |
| Project: src\runtime\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; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Xml; using System.Xml.XPath; using System.Xml.Xsl; namespace MS.Internal.Xml.XPath { // See comments to QueryBuilder.Props // Not all of them are used currently internal enum QueryProps { None = 0x00, Position = 0x01, Count = 0x02, Cached = 0x04, Reverse = 0x08, Merge = 0x10, }; // Turn off DebuggerDisplayAttribute. in subclasses of Query. // Calls to Current in the XPathNavigator.DebuggerDisplayProxy may change state or throw [DebuggerDisplay("{ToString()}")] internal abstract class Query : ResettableIterator { public Query() { } protected Query(Query other) : base(other) { } // -- XPathNodeIterator -- public override bool MoveNext() { return Advance() != null; } public override int Count { get { // Query can be ordered in reverse order. So we can't assume like base.Count that last node has greatest position. if (count == -1) { Query clone = (Query)this.Clone(); clone.Reset(); count = 0; while (clone.MoveNext()) count++; } return count; } } // -------------------- Query ------------------ public virtual void SetXsltContext(XsltContext context) { } public abstract object Evaluate(XPathNodeIterator nodeIterator); public abstract XPathNavigator? Advance(); public virtual XPathNavigator? MatchNode(XPathNavigator? current) { throw XPathException.Create(SR.Xp_InvalidPattern); } public virtual double XsltDefaultPriority { get { return 0.5; } } public abstract XPathResultType StaticType { get; } public virtual QueryProps Properties { get { return QueryProps.Merge; } } // ----------------- Helper methods ------------- [return: NotNullIfNotNull(nameof(input))] public static Query? Clone(Query? input) { if (input != null) { return (Query)input.Clone(); } return null; } [return: NotNullIfNotNull(nameof(input))] protected static XPathNodeIterator? Clone(XPathNodeIterator? input) { if (input != null) { return input.Clone(); } return null; } [return: NotNullIfNotNull(nameof(input))] protected static XPathNavigator? Clone(XPathNavigator? input) { if (input != null) { return input.Clone(); } return null; } // ----------------------------------------------------- // Set of methods to support insertion to sorted buffer. // buffer is always sorted here public static bool Insert(List<XPathNavigator> buffer, XPathNavigator nav) { int l = 0; int r = buffer.Count; // In most cases nodes are already sorted. // This means that nav often will be equal or after then last item in the buffer // So let's check this first. if (r != 0) { switch (CompareNodes(buffer[r - 1], nav)) { case XmlNodeOrder.Same: return false; case XmlNodeOrder.Before: buffer.Add(nav.Clone()); return true; default: r--; break; } } while (l < r) { int m = GetMedian(l, r); switch (CompareNodes(buffer[m], nav)) { case XmlNodeOrder.Same: return false; case XmlNodeOrder.Before: l = m + 1; break; default: r = m; break; } } buffer.Insert(l, nav.Clone()); return true; } private static int GetMedian(int l, int r) { Debug.Assert(0 <= l && l < r); return (int)(((uint)l + (uint)r) >> 1); } public static XmlNodeOrder CompareNodes(XPathNavigator l, XPathNavigator r) { XmlNodeOrder cmp = l.ComparePosition(r); if (cmp == XmlNodeOrder.Unknown) { XPathNavigator copy = l.Clone(); copy.MoveToRoot(); string baseUriL = copy.BaseURI; if (!copy.MoveTo(r)) { copy = r.Clone(); } copy.MoveToRoot(); string baseUriR = copy.BaseURI; int cmpBase = string.CompareOrdinal(baseUriL, baseUriR); cmp = ( cmpBase < 0 ? XmlNodeOrder.Before : cmpBase > 0 ? XmlNodeOrder.After : /*default*/ XmlNodeOrder.Unknown ); } return cmp; } // =================== XPathResultType_Navigator ====================== // In v.1.0 and v.1.1 XPathResultType.Navigator is defined == to XPathResultType.String // This is source for multiple bugs or additional type casts. // To fix all of them in one change in v.2 we internally use one more value: public const XPathResultType XPathResultType_Navigator = (XPathResultType)4; // The biggest challenge in this change is preserve backward compatibility with v.1.1 // To achieve this in all places where we accept from or report to user XPathResultType. // On my best knowledge this happens only in XsltContext.ResolveFunction() / IXsltContextFunction.ReturnType protected static XPathResultType GetXPathType(object value) { if (value is XPathNodeIterator) return XPathResultType.NodeSet; if (value is string) return XPathResultType.String; if (value is double) return XPathResultType.Number; if (value is bool) return XPathResultType.Boolean; Debug.Assert(value is XPathNavigator, "Unknown value type"); return XPathResultType_Navigator; } } }