1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Xml;
7 using System.Xml.XPath;
8 using System.Xml.Xsl;
9 
10 namespace MS.Internal.Xml.XPath
11 {
12     internal sealed class LogicalExpr : ValueQuery
13     {
14         private Operator.Op _op;
15         private Query _opnd1;
16         private Query _opnd2;
17 
LogicalExpr(Operator.Op op, Query opnd1, Query opnd2)18         public LogicalExpr(Operator.Op op, Query opnd1, Query opnd2)
19         {
20             Debug.Assert(
21                 Operator.Op.LT == op || Operator.Op.GT == op ||
22                 Operator.Op.LE == op || Operator.Op.GE == op ||
23                 Operator.Op.EQ == op || Operator.Op.NE == op
24             );
25             _op = op;
26             _opnd1 = opnd1;
27             _opnd2 = opnd2;
28         }
LogicalExpr(LogicalExpr other)29         private LogicalExpr(LogicalExpr other) : base(other)
30         {
31             _op = other._op;
32             _opnd1 = Clone(other._opnd1);
33             _opnd2 = Clone(other._opnd2);
34         }
35 
SetXsltContext(XsltContext context)36         public override void SetXsltContext(XsltContext context)
37         {
38             _opnd1.SetXsltContext(context);
39             _opnd2.SetXsltContext(context);
40         }
41 
Evaluate(XPathNodeIterator nodeIterator)42         public override object Evaluate(XPathNodeIterator nodeIterator)
43         {
44             Operator.Op op = _op;
45             object val1 = _opnd1.Evaluate(nodeIterator);
46             object val2 = _opnd2.Evaluate(nodeIterator);
47             int type1 = (int)GetXPathType(val1);
48             int type2 = (int)GetXPathType(val2);
49             if (type1 < type2)
50             {
51                 op = Operator.InvertOperator(op);
52                 object valTemp = val1;
53                 val1 = val2;
54                 val2 = valTemp;
55                 int typeTmp = type1;
56                 type1 = type2;
57                 type2 = typeTmp;
58             }
59 
60             if (op == Operator.Op.EQ || op == Operator.Op.NE)
61             {
62                 return s_CompXsltE[type1][type2](op, val1, val2);
63             }
64             else
65             {
66                 return s_CompXsltO[type1][type2](op, val1, val2);
67             }
68         }
69 
cmpXslt(Operator.Op op, object val1, object val2)70         private delegate bool cmpXslt(Operator.Op op, object val1, object val2);
71 
72         //                              Number,                       String,                        Boolean,                     NodeSet,                      Navigator
73         private static readonly cmpXslt[][] s_CompXsltE = {
74             new cmpXslt[] { new cmpXslt(cmpNumberNumber), null                         , null                       , null                        , null                    },
75             new cmpXslt[] { new cmpXslt(cmpStringNumber), new cmpXslt(cmpStringStringE), null                       , null                        , null                    },
76             new cmpXslt[] { new cmpXslt(cmpBoolNumberE ), new cmpXslt(cmpBoolStringE  ), new cmpXslt(cmpBoolBoolE  ), null                        , null                    },
77             new cmpXslt[] { new cmpXslt(cmpQueryNumber ), new cmpXslt(cmpQueryStringE ), new cmpXslt(cmpQueryBoolE ), new cmpXslt(cmpQueryQueryE ), null                    },
78             new cmpXslt[] { new cmpXslt(cmpRtfNumber   ), new cmpXslt(cmpRtfStringE   ), new cmpXslt(cmpRtfBoolE   ), new cmpXslt(cmpRtfQueryE   ), new cmpXslt(cmpRtfRtfE) },
79         };
80         private static readonly cmpXslt[][] s_CompXsltO = {
81             new cmpXslt[] { new cmpXslt(cmpNumberNumber), null                         , null                       , null                        , null                    },
82             new cmpXslt[] { new cmpXslt(cmpStringNumber), new cmpXslt(cmpStringStringO), null                       , null                        , null                    },
83             new cmpXslt[] { new cmpXslt(cmpBoolNumberO ), new cmpXslt(cmpBoolStringO  ), new cmpXslt(cmpBoolBoolO  ), null                        , null                    },
84             new cmpXslt[] { new cmpXslt(cmpQueryNumber ), new cmpXslt(cmpQueryStringO ), new cmpXslt(cmpQueryBoolO ), new cmpXslt(cmpQueryQueryO ), null                    },
85             new cmpXslt[] { new cmpXslt(cmpRtfNumber   ), new cmpXslt(cmpRtfStringO   ), new cmpXslt(cmpRtfBoolO   ), new cmpXslt(cmpRtfQueryO   ), new cmpXslt(cmpRtfRtfO) },
86         };
87 
88         /*cmpXslt:*/
cmpQueryQueryE(Operator.Op op, object val1, object val2)89         private static bool cmpQueryQueryE(Operator.Op op, object val1, object val2)
90         {
91             Debug.Assert(op == Operator.Op.EQ || op == Operator.Op.NE);
92             bool isEQ = (op == Operator.Op.EQ);
93 
94             NodeSet n1 = new NodeSet(val1);
95             NodeSet n2 = new NodeSet(val2);
96 
97             while (true)
98             {
99                 if (!n1.MoveNext())
100                 {
101                     return false;
102                 }
103                 if (!n2.MoveNext())
104                 {
105                     return false;
106                 }
107 
108                 string str1 = n1.Value;
109 
110                 do
111                 {
112                     if ((str1 == n2.Value) == isEQ)
113                     {
114                         return true;
115                     }
116                 } while (n2.MoveNext());
117                 n2.Reset();
118             }
119         }
120 
121         /*cmpXslt:*/
cmpQueryQueryO(Operator.Op op, object val1, object val2)122         private static bool cmpQueryQueryO(Operator.Op op, object val1, object val2)
123         {
124             Debug.Assert(
125                 op == Operator.Op.LT || op == Operator.Op.GT ||
126                 op == Operator.Op.LE || op == Operator.Op.GE
127             );
128 
129             NodeSet n1 = new NodeSet(val1);
130             NodeSet n2 = new NodeSet(val2);
131 
132             while (true)
133             {
134                 if (!n1.MoveNext())
135                 {
136                     return false;
137                 }
138                 if (!n2.MoveNext())
139                 {
140                     return false;
141                 }
142 
143                 double num1 = NumberFunctions.Number(n1.Value);
144 
145                 do
146                 {
147                     if (cmpNumberNumber(op, num1, NumberFunctions.Number(n2.Value)))
148                     {
149                         return true;
150                     }
151                 } while (n2.MoveNext());
152                 n2.Reset();
153             }
154         }
cmpQueryNumber(Operator.Op op, object val1, object val2)155         private static bool cmpQueryNumber(Operator.Op op, object val1, object val2)
156         {
157             NodeSet n1 = new NodeSet(val1);
158             double n2 = (double)val2;
159 
160             while (n1.MoveNext())
161             {
162                 if (cmpNumberNumber(op, NumberFunctions.Number(n1.Value), n2))
163                 {
164                     return true;
165                 }
166             }
167             return false;
168         }
169 
cmpQueryStringE(Operator.Op op, object val1, object val2)170         private static bool cmpQueryStringE(Operator.Op op, object val1, object val2)
171         {
172             NodeSet n1 = new NodeSet(val1);
173             string n2 = (string)val2;
174 
175             while (n1.MoveNext())
176             {
177                 if (cmpStringStringE(op, n1.Value, n2))
178                 {
179                     return true;
180                 }
181             }
182             return false;
183         }
184 
cmpQueryStringO(Operator.Op op, object val1, object val2)185         private static bool cmpQueryStringO(Operator.Op op, object val1, object val2)
186         {
187             NodeSet n1 = new NodeSet(val1);
188             double n2 = NumberFunctions.Number((string)val2);
189 
190             while (n1.MoveNext())
191             {
192                 if (cmpNumberNumberO(op, NumberFunctions.Number(n1.Value), n2))
193                 {
194                     return true;
195                 }
196             }
197             return false;
198         }
199 
cmpRtfQueryE(Operator.Op op, object val1, object val2)200         private static bool cmpRtfQueryE(Operator.Op op, object val1, object val2)
201         {
202             string n1 = Rtf(val1);
203             NodeSet n2 = new NodeSet(val2);
204 
205             while (n2.MoveNext())
206             {
207                 if (cmpStringStringE(op, n1, n2.Value))
208                 {
209                     return true;
210                 }
211             }
212             return false;
213         }
214 
cmpRtfQueryO(Operator.Op op, object val1, object val2)215         private static bool cmpRtfQueryO(Operator.Op op, object val1, object val2)
216         {
217             double n1 = NumberFunctions.Number(Rtf(val1));
218             NodeSet n2 = new NodeSet(val2);
219 
220             while (n2.MoveNext())
221             {
222                 if (cmpNumberNumberO(op, n1, NumberFunctions.Number(n2.Value)))
223                 {
224                     return true;
225                 }
226             }
227             return false;
228         }
229 
cmpQueryBoolE(Operator.Op op, object val1, object val2)230         private static bool cmpQueryBoolE(Operator.Op op, object val1, object val2)
231         {
232             NodeSet n1 = new NodeSet(val1);
233             bool b1 = n1.MoveNext();
234             bool b2 = (bool)val2;
235             return cmpBoolBoolE(op, b1, b2);
236         }
237 
cmpQueryBoolO(Operator.Op op, object val1, object val2)238         private static bool cmpQueryBoolO(Operator.Op op, object val1, object val2)
239         {
240             NodeSet n1 = new NodeSet(val1);
241             double d1 = n1.MoveNext() ? 1.0 : 0;
242             double d2 = NumberFunctions.Number((bool)val2);
243             return cmpNumberNumberO(op, d1, d2);
244         }
245 
cmpBoolBoolE(Operator.Op op, bool n1, bool n2)246         private static bool cmpBoolBoolE(Operator.Op op, bool n1, bool n2)
247         {
248             Debug.Assert(op == Operator.Op.EQ || op == Operator.Op.NE,
249                 "Unexpected Operator.op code in cmpBoolBoolE()"
250             );
251             return (op == Operator.Op.EQ) == (n1 == n2);
252         }
cmpBoolBoolE(Operator.Op op, object val1, object val2)253         private static bool cmpBoolBoolE(Operator.Op op, object val1, object val2)
254         {
255             bool n1 = (bool)val1;
256             bool n2 = (bool)val2;
257             return cmpBoolBoolE(op, n1, n2);
258         }
259 
cmpBoolBoolO(Operator.Op op, object val1, object val2)260         private static bool cmpBoolBoolO(Operator.Op op, object val1, object val2)
261         {
262             double n1 = NumberFunctions.Number((bool)val1);
263             double n2 = NumberFunctions.Number((bool)val2);
264             return cmpNumberNumberO(op, n1, n2);
265         }
266 
cmpBoolNumberE(Operator.Op op, object val1, object val2)267         private static bool cmpBoolNumberE(Operator.Op op, object val1, object val2)
268         {
269             bool n1 = (bool)val1;
270             bool n2 = BooleanFunctions.toBoolean((double)val2);
271             return cmpBoolBoolE(op, n1, n2);
272         }
273 
cmpBoolNumberO(Operator.Op op, object val1, object val2)274         private static bool cmpBoolNumberO(Operator.Op op, object val1, object val2)
275         {
276             double n1 = NumberFunctions.Number((bool)val1);
277             double n2 = (double)val2;
278             return cmpNumberNumberO(op, n1, n2);
279         }
280 
cmpBoolStringE(Operator.Op op, object val1, object val2)281         private static bool cmpBoolStringE(Operator.Op op, object val1, object val2)
282         {
283             bool n1 = (bool)val1;
284             bool n2 = BooleanFunctions.toBoolean((string)val2);
285             return cmpBoolBoolE(op, n1, n2);
286         }
287 
cmpRtfBoolE(Operator.Op op, object val1, object val2)288         private static bool cmpRtfBoolE(Operator.Op op, object val1, object val2)
289         {
290             bool n1 = BooleanFunctions.toBoolean(Rtf(val1));
291             bool n2 = (bool)val2;
292             return cmpBoolBoolE(op, n1, n2);
293         }
294 
cmpBoolStringO(Operator.Op op, object val1, object val2)295         private static bool cmpBoolStringO(Operator.Op op, object val1, object val2)
296         {
297             return cmpNumberNumberO(op,
298                 NumberFunctions.Number((bool)val1),
299                 NumberFunctions.Number((string)val2)
300             );
301         }
302 
cmpRtfBoolO(Operator.Op op, object val1, object val2)303         private static bool cmpRtfBoolO(Operator.Op op, object val1, object val2)
304         {
305             return cmpNumberNumberO(op,
306                 NumberFunctions.Number(Rtf(val1)),
307                 NumberFunctions.Number((bool)val2)
308             );
309         }
310 
cmpNumberNumber(Operator.Op op, double n1, double n2)311         private static bool cmpNumberNumber(Operator.Op op, double n1, double n2)
312         {
313             switch (op)
314             {
315                 case Operator.Op.LT: return (n1 < n2);
316                 case Operator.Op.GT: return (n1 > n2);
317                 case Operator.Op.LE: return (n1 <= n2);
318                 case Operator.Op.GE: return (n1 >= n2);
319                 case Operator.Op.EQ: return (n1 == n2);
320                 case Operator.Op.NE: return (n1 != n2);
321             }
322             Debug.Fail("Unexpected Operator.op code in cmpNumberNumber()");
323             return false;
324         }
cmpNumberNumberO(Operator.Op op, double n1, double n2)325         private static bool cmpNumberNumberO(Operator.Op op, double n1, double n2)
326         {
327             switch (op)
328             {
329                 case Operator.Op.LT: return (n1 < n2);
330                 case Operator.Op.GT: return (n1 > n2);
331                 case Operator.Op.LE: return (n1 <= n2);
332                 case Operator.Op.GE: return (n1 >= n2);
333             }
334             Debug.Fail("Unexpected Operator.op code in cmpNumberNumber()");
335             return false;
336         }
cmpNumberNumber(Operator.Op op, object val1, object val2)337         private static bool cmpNumberNumber(Operator.Op op, object val1, object val2)
338         {
339             double n1 = (double)val1;
340             double n2 = (double)val2;
341             return cmpNumberNumber(op, n1, n2);
342         }
343 
cmpStringNumber(Operator.Op op, object val1, object val2)344         private static bool cmpStringNumber(Operator.Op op, object val1, object val2)
345         {
346             double n2 = (double)val2;
347             double n1 = NumberFunctions.Number((string)val1);
348             return cmpNumberNumber(op, n1, n2);
349         }
350 
cmpRtfNumber(Operator.Op op, object val1, object val2)351         private static bool cmpRtfNumber(Operator.Op op, object val1, object val2)
352         {
353             double n2 = (double)val2;
354             double n1 = NumberFunctions.Number(Rtf(val1));
355             return cmpNumberNumber(op, n1, n2);
356         }
357 
cmpStringStringE(Operator.Op op, string n1, string n2)358         private static bool cmpStringStringE(Operator.Op op, string n1, string n2)
359         {
360             Debug.Assert(op == Operator.Op.EQ || op == Operator.Op.NE,
361                 "Unexpected Operator.op code in cmpStringStringE()"
362             );
363             return (op == Operator.Op.EQ) == (n1 == n2);
364         }
cmpStringStringE(Operator.Op op, object val1, object val2)365         private static bool cmpStringStringE(Operator.Op op, object val1, object val2)
366         {
367             string n1 = (string)val1;
368             string n2 = (string)val2;
369             return cmpStringStringE(op, n1, n2);
370         }
cmpRtfStringE(Operator.Op op, object val1, object val2)371         private static bool cmpRtfStringE(Operator.Op op, object val1, object val2)
372         {
373             string n1 = Rtf(val1);
374             string n2 = (string)val2;
375             return cmpStringStringE(op, n1, n2);
376         }
cmpRtfRtfE(Operator.Op op, object val1, object val2)377         private static bool cmpRtfRtfE(Operator.Op op, object val1, object val2)
378         {
379             string n1 = Rtf(val1);
380             string n2 = Rtf(val2);
381             return cmpStringStringE(op, n1, n2);
382         }
383 
cmpStringStringO(Operator.Op op, object val1, object val2)384         private static bool cmpStringStringO(Operator.Op op, object val1, object val2)
385         {
386             double n1 = NumberFunctions.Number((string)val1);
387             double n2 = NumberFunctions.Number((string)val2);
388             return cmpNumberNumberO(op, n1, n2);
389         }
390 
cmpRtfStringO(Operator.Op op, object val1, object val2)391         private static bool cmpRtfStringO(Operator.Op op, object val1, object val2)
392         {
393             double n1 = NumberFunctions.Number(Rtf(val1));
394             double n2 = NumberFunctions.Number((string)val2);
395             return cmpNumberNumberO(op, n1, n2);
396         }
397 
cmpRtfRtfO(Operator.Op op, object val1, object val2)398         private static bool cmpRtfRtfO(Operator.Op op, object val1, object val2)
399         {
400             double n1 = NumberFunctions.Number(Rtf(val1));
401             double n2 = NumberFunctions.Number(Rtf(val2));
402             return cmpNumberNumberO(op, n1, n2);
403         }
404 
Clone()405         public override XPathNodeIterator Clone() { return new LogicalExpr(this); }
406 
407         private struct NodeSet
408         {
409             private Query _opnd;
410             private XPathNavigator _current;
411 
NodeSetMS.Internal.Xml.XPath.LogicalExpr.NodeSet412             public NodeSet(object opnd)
413             {
414                 _opnd = (Query)opnd;
415                 _current = null;
416             }
MoveNextMS.Internal.Xml.XPath.LogicalExpr.NodeSet417             public bool MoveNext()
418             {
419                 _current = _opnd.Advance();
420                 return _current != null;
421             }
422 
ResetMS.Internal.Xml.XPath.LogicalExpr.NodeSet423             public void Reset()
424             {
425                 _opnd.Reset();
426             }
427 
428             public string Value { get { return _current.Value; } }
429         }
430 
Rtf(object o)431         private static string Rtf(object o) { return ((XPathNavigator)o).Value; }
432 
433         public override XPathResultType StaticType { get { return XPathResultType.Boolean; } }
434     }
435 }
436