1 //------------------------------------------------------------------------------
2 // <copyright file="Query.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
8 namespace MS.Internal.Xml.XPath {
11 using System.Xml.XPath;
13 using System.Diagnostics;
14 using System.Collections.Generic;
16 // See comments to QueryBuilder.Props
17 // Not all of them are used currently
18 internal enum QueryProps {
28 // Turn off DebuggerDisplayAttribute. in subclasses of Query.
29 // Calls to Current in the XPathNavigator.DebuggerDisplayProxy may change state or throw
30 [DebuggerDisplay("{ToString()}")]
31 internal abstract class Query : ResetableIterator {
33 protected Query(Query other) : base(other) { }
35 // -- XPathNodeIterator --
36 //public abstract XPathNodeIterator Clone();
37 //public abstract XPathNavigator Current { get; }
38 //public abstract int CurrentPosition { get; }
39 public override bool MoveNext() { return Advance() != null; }
40 public override int Count {
42 // Query can be ordered in reverse order. So we can't assume like base.Count that last node has greatest position.
44 Query clone = (Query)this.Clone();
47 while (clone.MoveNext()) count ++;
53 // ------------- ResetableIterator -----------
54 // It's importent that Query is resetable. This fact is used in several plases:
55 // 1. In LogicalExpr: "foo = bar"
56 // 2. In SelectionOperator.Reset().
57 // In all this cases Reset() means restart iterate through the same nodes.
58 // So reset shouldn't clean bufferes in query. This should be done in set context.
59 //public abstract void Reset();
61 // -------------------- Query ------------------
62 public virtual void SetXsltContext(XsltContext context) { }
64 public abstract object Evaluate(XPathNodeIterator nodeIterator);
65 public abstract XPathNavigator Advance();
67 public virtual XPathNavigator MatchNode(XPathNavigator current) {
68 throw XPathException.Create(Res.Xp_InvalidPattern);
71 public virtual double XsltDefaultPriority { get { return 0.5; } }
72 public abstract XPathResultType StaticType { get; }
73 public virtual QueryProps Properties { get { return QueryProps.Merge; } }
75 // ----------------- Helper methods -------------
76 public static Query Clone(Query input) {
78 return (Query)input.Clone();
83 protected static XPathNodeIterator Clone(XPathNodeIterator input) {
90 protected static XPathNavigator Clone(XPathNavigator input) {
97 // -----------------------------------------------------
98 // Set of methods to support insertion to sorted buffer.
99 // buffer is always sorted here
101 public bool Insert(List<XPathNavigator> buffer, XPathNavigator nav) {
103 int r = buffer.Count;
105 // In most cases nodes are already sorted.
106 // This means that nav often will be equal or after then last item in the buffer
107 // So let's check this first.
109 switch (CompareNodes(buffer[r - 1], nav)) {
110 case XmlNodeOrder.Same:
112 case XmlNodeOrder.Before:
113 buffer.Add(nav.Clone());
122 int m = GetMedian(l, r);
123 switch (CompareNodes(buffer[m], nav)) {
124 case XmlNodeOrder.Same:
126 case XmlNodeOrder.Before:
134 AssertDOD(buffer, nav, l);
135 buffer.Insert(l, nav.Clone());
139 private static int GetMedian(int l, int r) {
140 Debug.Assert(0 <= l && l < r);
141 return (int) (((uint) l + (uint) r) >> 1);
144 public static XmlNodeOrder CompareNodes(XPathNavigator l, XPathNavigator r) {
145 XmlNodeOrder cmp = l.ComparePosition(r);
146 if (cmp == XmlNodeOrder.Unknown) {
147 XPathNavigator copy = l.Clone();
149 string baseUriL = copy.BaseURI;
150 if (! copy.MoveTo(r)) {
154 string baseUriR = copy.BaseURI;
155 int cmpBase = string.CompareOrdinal(baseUriL, baseUriR);
157 cmpBase < 0 ? XmlNodeOrder.Before :
158 cmpBase > 0 ? XmlNodeOrder.After :
159 /*default*/ XmlNodeOrder.Unknown
165 [Conditional("DEBUG")]
166 private void AssertDOD(List<XPathNavigator> buffer, XPathNavigator nav, int pos) {
167 if (nav.GetType().ToString() == "Microsoft.VisualStudio.Modeling.StoreNavigator") return;
168 if (nav.GetType().ToString() == "System.Xml.DataDocumentXPathNavigator") return;
169 Debug.Assert(0 <= pos && pos <= buffer.Count, "Algorithm error: Insert()");
172 cmp = CompareNodes(buffer[pos - 1], nav);
173 Debug.Assert(cmp == XmlNodeOrder.Before, "Algorithm error: Insert()");
175 if (pos < buffer.Count) {
176 cmp = CompareNodes(nav, buffer[pos]);
177 Debug.Assert(cmp == XmlNodeOrder.Before, "Algorithm error: Insert()");
181 [Conditional("DEBUG")]
182 public static void AssertQuery(Query query) {
183 Debug.Assert(query != null, "AssertQuery(): query == null");
184 if (query is FunctionQuery) return; // Temp Fix. Functions (as document()) return now unordered sequences
185 query = Clone(query);
186 XPathNavigator last = null;
188 int querySize = query.Clone().Count;
190 while ((curr = query.Advance()) != null) {
191 if (curr.GetType().ToString() == "Microsoft.VisualStudio.Modeling.StoreNavigator") return;
192 if (curr.GetType().ToString() == "System.Xml.DataDocumentXPathNavigator") return;
193 Debug.Assert(curr == query.Current, "AssertQuery(): query.Advance() != query.Current");
195 if (last.NodeType == XPathNodeType.Namespace && curr.NodeType == XPathNodeType.Namespace) {
196 // NamespaceQuery reports namsespaces in mixed order.
197 // Ignore this for now.
198 // It seams that this doesn't breake other queries becasue NS can't have children
200 XmlNodeOrder cmp = CompareNodes(last, curr);
201 Debug.Assert(cmp == XmlNodeOrder.Before, "AssertQuery(): Wrong node order");
207 Debug.Assert(actualSize == querySize, "AssertQuery(): actualSize != querySize");
210 // =================== XPathResultType_Navigator ======================
211 // In v.1.0 and v.1.1 XPathResultType.Navigator is defined == to XPathResultType.String
212 // This is source for multiple bugs or additional type casts.
213 // To fix all of them in one change in v.2 we internaly use one more value:
214 public const XPathResultType XPathResultType_Navigator = (XPathResultType) 4;
215 // The biggest challenge in this change is preserve backward compatibility with v.1.1
216 // To achive this in all places where we accept from or report to user XPathResultType.
217 // On my best knowledge this happens only in XsltContext.ResolveFunction() / IXsltContextFunction.ReturnType
220 protected XPathResultType GetXPathType(object value) {
221 if (value is XPathNodeIterator) return XPathResultType.NodeSet;
222 if (value is string ) return XPathResultType.String;
223 if (value is double ) return XPathResultType.Number;
224 if (value is bool ) return XPathResultType.Boolean;
225 Debug.Assert(value is XPathNavigator, "Unknown value type");
226 return XPathResultType_Navigator;
229 // =================== Serialization ======================
230 public virtual void PrintQuery(XmlWriter w) {
231 w.WriteElementString(this.GetType().Name, string.Empty);