04b9e74274618e7d73dbcf3acf388f529dcc4140
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / XPath / Internal / FilterQuery.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="FilterQuery.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
7
8 namespace MS.Internal.Xml.XPath {
9     using System;
10     using System.Xml;
11     using System.Xml.XPath;
12     using System.Diagnostics;
13     using System.Globalization;
14     using System.Xml.Xsl;
15
16     internal sealed class FilterQuery : BaseAxisQuery {
17         private Query cond;
18         private bool noPosition;
19         
20         public FilterQuery(Query qyParent, Query cond, bool noPosition) : base(qyParent) {
21             this.cond = cond;
22             this.noPosition = noPosition;
23         }
24         private FilterQuery(FilterQuery other) : base(other) {
25             this.cond       = Clone(other.cond);
26             this.noPosition = other.noPosition;
27         }        
28
29         public override void Reset() {
30             cond.Reset();
31             base.Reset();
32         }
33
34         public Query Condition { get { return cond; } }
35
36         public override void SetXsltContext(XsltContext input) {
37             base.SetXsltContext(input);
38             cond.SetXsltContext(input);
39             if (cond.StaticType != XPathResultType.Number && cond.StaticType != XPathResultType.Any && noPosition) {
40                 // BugBug: We can do such trick at Evaluate time only.
41                 // But to do this FilterQuery should stop inherit from BaseAxisQuery
42                 ReversePositionQuery query = qyInput as ReversePositionQuery;
43                 if (query != null) {
44                     qyInput = query.input;
45                 }
46             }
47         }
48
49         public override XPathNavigator Advance() {
50             while ((currentNode = qyInput.Advance()) != null) {
51                 if (EvaluatePredicate()) {
52                     position++;
53                     return currentNode;
54                 }
55             }
56             return null;
57         }
58
59         internal bool EvaluatePredicate() {
60             object value = cond.Evaluate(qyInput);
61             if (value is XPathNodeIterator) return cond.Advance() != null;
62             if (value is string           ) return ((string)value).Length != 0;
63             if (value is double           ) return (((double)value) == qyInput.CurrentPosition);
64             if (value is bool             ) return (bool)value;
65             Debug.Assert(value is XPathNavigator, "Unknown value type");
66             return true;
67         }
68
69         public override XPathNavigator MatchNode(XPathNavigator current) {
70             XPathNavigator context;
71             if (current == null) {
72                 return null;
73             }
74             context = qyInput.MatchNode(current);
75
76             if (context != null) {
77                 // In this switch we process some special case in wich we can calculate predicate faster then in generic case
78                 switch (cond.StaticType) {
79                 case XPathResultType.Number:
80                     OperandQuery operand = cond as OperandQuery;
81                     if (operand != null) {
82                         double val = (double)operand.val;
83                         ChildrenQuery childrenQuery = qyInput as ChildrenQuery;
84                         if (childrenQuery != null) { // foo[2], but not foo[expr][2]
85                             XPathNavigator result = current.Clone();
86                             result.MoveToParent();
87                             int i = 0;
88                             result.MoveToFirstChild();
89                             do {
90                                 if (childrenQuery.matches(result)) {
91                                     i++;
92                                     if (current.IsSamePosition(result)) {
93                                         return val == i ? context : null;
94                                     }
95                                 }
96                             } while (result.MoveToNext());
97                             return null;
98                         }
99                         AttributeQuery attributeQuery = qyInput as AttributeQuery;
100                         if (attributeQuery != null) {// @foo[3], but not @foo[expr][2]
101                             XPathNavigator result = current.Clone();
102                             result.MoveToParent();
103                             int i = 0;
104                             result.MoveToFirstAttribute();
105                             do {
106                                 if (attributeQuery.matches(result)) {
107                                     i++;
108                                     if (current.IsSamePosition(result)) {
109                                         return val == i ? context : null;
110                                     }
111                                 }
112                             } while (result.MoveToNextAttribute());
113                             return null;
114                         }
115                     }
116                     break;
117                 case XPathResultType.NodeSet:
118                     cond.Evaluate(new XPathSingletonIterator(current, /*moved:*/true));
119                     return (cond.Advance() != null) ? context : null;
120                 case XPathResultType.Boolean:
121                     if (noPosition) {
122                         return ((bool)cond.Evaluate(new XPathSingletonIterator(current, /*moved:*/true))) ? context : null;
123                     }
124                     break;
125                 case XPathResultType.String:
126                     if (noPosition) {
127                         return (((string)cond.Evaluate(new XPathSingletonIterator(current, /*moved:*/true))).Length != 0) ? context : null;
128                     }
129                     break;
130                 case XPathResultType_Navigator:
131                     return context;
132                 default:
133                     return null;
134                 }
135                 /* Generic case */ {
136                     Evaluate(new XPathSingletonIterator(context, /*moved:*/true));
137                     XPathNavigator result;
138                     while ((result = Advance()) != null) {
139                         if (result.IsSamePosition(current)) {
140                             return context;
141                         }
142                     }
143                 }
144             }
145             return null;
146         }
147
148         public override QueryProps Properties { 
149             get { 
150                 return QueryProps.Position | (qyInput.Properties & (QueryProps.Merge | QueryProps.Reverse)); 
151             } 
152         }
153
154         public override XPathNodeIterator Clone() { return new FilterQuery(this); }
155
156         public override void PrintQuery(XmlWriter w) {
157             w.WriteStartElement(this.GetType().Name);
158             if (! noPosition
159                 ) {
160                 w.WriteAttributeString("position", "yes");
161             }
162             qyInput.PrintQuery(w);
163             cond.PrintQuery(w);
164             w.WriteEndElement();
165         }
166     }
167 }