6fec13b5cb67a895cfc581d788d4873a9ac848b4
[mono.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Dispatcher / QueryMatcher.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Dispatcher
5 {
6     using System;
7     using System.Collections;
8     using System.Collections.Generic;
9     using System.Collections.ObjectModel;
10     using System.Runtime;
11     using System.ServiceModel.Channels;
12     using System.ServiceModel.Diagnostics;
13     using System.Xml;
14     using System.Xml.XPath;
15     using System.Xml.Xsl;
16
17     internal enum QueryCompilerFlags
18     {
19         None = 0x00000000,
20         InverseQuery = 0x00000001
21     }
22
23     internal struct FilterResult
24     {
25         QueryProcessor processor;
26         bool result;
27
28         internal FilterResult(QueryProcessor processor)
29         {
30             this.processor = processor;
31             this.result = this.processor.Result;
32         }
33
34         internal FilterResult(bool result)
35         {
36             this.processor = null;
37             this.result = result;
38         }
39
40 #if NO
41         internal ICollection<MessageFilter> Matches
42         {
43             get
44             {
45                 return this.processor.ResultSet;
46             }            
47         }
48 #endif
49         internal QueryProcessor Processor
50         {
51             get
52             {
53                 return this.processor;
54             }
55         }
56
57         internal bool Result
58         {
59             get
60             {
61                 return this.result;
62             }
63         }
64
65         internal MessageFilter GetSingleMatch()
66         {
67             Collection<MessageFilter> matches = processor.MatchList;
68             MessageFilter match;
69             switch (matches.Count)
70             {
71                 default:
72                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, matches));
73
74                 case 0:
75                     match = null;
76                     break;
77
78                 case 1:
79                     match = matches[0];
80                     break;
81             }
82
83             return match;
84         }
85     }
86
87     // XPathResult.GetResultAsString and XPathResult.GetResultAsBoolean,
88     // drive knowledge of TResult into the engine.
89     internal class QueryResult<TResult> : IEnumerable<KeyValuePair<MessageQuery, TResult>>
90     {
91         bool evalBody;
92         QueryMatcher matcher;
93         Message message;
94
95         internal QueryResult(QueryMatcher matcher, Message message, bool evalBody)
96         {
97             this.matcher = matcher;
98             this.message = message;
99             this.evalBody = evalBody;
100         }
101
102         public TResult GetSingleResult()
103         {
104             QueryProcessor processor = this.matcher.CreateProcessor();
105             XPathResult result;
106
107             try
108             {
109                 processor.Eval(this.matcher.RootOpcode, this.message, this.evalBody);
110             }
111             catch (XPathNavigatorException e)
112             {
113                 throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
114             }
115             catch (NavigatorInvalidBodyAccessException e)
116             {
117                 throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
118             }
119             finally
120             {
121                 if (this.evalBody)
122                 {
123                     this.message.Close();
124                 }
125
126                 result = processor.QueryResult;
127                 this.matcher.ReleaseProcessor(processor);
128             }
129
130             if (typeof(TResult) == typeof(XPathResult) || typeof(TResult) == typeof(object))
131             {
132                 return (TResult)(object)result;
133             }
134             else if (typeof(TResult) == typeof(string))
135             {
136                 return (TResult)(object)result.GetResultAsString();
137             }
138             else if (typeof(TResult) == typeof(bool))
139             {
140                 return (TResult)(object)result.GetResultAsBoolean();
141             }
142             else
143             {
144                 throw Fx.AssertAndThrowFatal("unsupported type");
145             }
146         }
147
148         public IEnumerator<KeyValuePair<MessageQuery, TResult>> GetEnumerator()
149         {
150             QueryProcessor processor = this.matcher.CreateProcessor();
151             Collection<KeyValuePair<MessageQuery, XPathResult>> results =
152                 new Collection<KeyValuePair<MessageQuery, XPathResult>>();
153             processor.ResultSet = results;
154
155             try
156             {
157                 processor.Eval(this.matcher.RootOpcode, this.message, this.evalBody);
158
159                 if (typeof(TResult) == typeof(XPathResult))
160                 {
161                     return (IEnumerator<KeyValuePair<MessageQuery, TResult>>)(object)results.GetEnumerator();
162                 }
163                 else if (typeof(TResult) == typeof(string) ||
164                     typeof(TResult) == typeof(bool) ||
165                     typeof(TResult) == typeof(object))
166                 {
167                     Collection<KeyValuePair<MessageQuery, TResult>> typedResults =
168                         new Collection<KeyValuePair<MessageQuery, TResult>>();
169
170                     foreach (var result in results)
171                     {
172                         if (typeof(TResult) == typeof(string))
173                         {
174                             typedResults.Add(
175                                 new KeyValuePair<MessageQuery, TResult>(
176                                     result.Key, (TResult)(object)result.Value.GetResultAsString()));
177                         }
178                         else if (typeof(TResult) == typeof(bool))
179                         {
180                             typedResults.Add(
181                                 new KeyValuePair<MessageQuery, TResult>(
182                                     result.Key, (TResult)(object)result.Value.GetResultAsBoolean()));
183                         }
184                         else
185                         {
186                             typedResults.Add(new KeyValuePair<MessageQuery, TResult>(
187                                 result.Key, (TResult)(object)result.Value));
188                         }
189                     }
190
191                     return (IEnumerator<KeyValuePair<MessageQuery, TResult>>)typedResults.GetEnumerator();
192                 }
193                 else
194                 {
195                     throw Fx.AssertAndThrowFatal("unsupported type");
196                 }
197             }
198             catch (XPathNavigatorException e)
199             {
200                 throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
201             }
202             catch (NavigatorInvalidBodyAccessException e)
203             {
204                 throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
205             }
206             finally
207             {
208                 if (this.evalBody)
209                 {
210                     this.message.Close();
211                 }
212
213                 this.matcher.ReleaseProcessor(processor);
214             }
215         }
216
217         IEnumerator IEnumerable.GetEnumerator()
218         {
219             return this.GetEnumerator();
220         }
221     }
222
223     /// <summary>
224     /// 
225     /// </summary>
226     internal abstract class QueryMatcher
227     {
228         static IFunctionLibrary[] defaultFunctionLibs;  // The set of function libraries that our XPath compiler will link to
229         static XPathNavigator fxCompiler;       // fx compiler
230
231         protected int maxNodes;     // Maximum # of nodes that we will process while performing any individual match
232         protected Opcode query;     // root opcode - this is where query evaluation starts
233         protected int subExprVars;  // the number of subexpr node sequences the processing context must hold
234
235         // Processor Pool
236         protected WeakReference processorPool;
237
238         internal class QueryProcessorPool
239         {
240             QueryProcessor processor;
241
242             internal QueryProcessorPool()
243             {
244             }
245
246             internal QueryProcessor Pop()
247             {
248                 QueryProcessor p = this.processor;
249                 if (null != p)
250                 {
251                     this.processor = (QueryProcessor)p.next;
252                     p.next = null;
253                     return p;
254                 }
255                 return null;
256             }
257
258             internal void Push(QueryProcessor p)
259             {
260                 p.next = this.processor;
261                 this.processor = p;
262             }
263         }
264
265         static QueryMatcher()
266         {
267             QueryMatcher.defaultFunctionLibs = new IFunctionLibrary[] { new XPathFunctionLibrary() };
268
269             // For some incomprehensible reason, the Framework XPath compiler requires an instance of an XPath navigator
270             // to compile an xpath. This compiler uses a dummy xml document to create a navigator
271             XmlDocument doc = new XmlDocument();
272             doc.LoadXml("<a/>");
273             QueryMatcher.fxCompiler = doc.CreateNavigator();
274         }
275
276         internal QueryMatcher()
277         {
278             this.maxNodes = int.MaxValue;
279             this.query = null;
280             this.processorPool = new WeakReference(null);
281             this.subExprVars = 0;
282         }
283 #if NO       
284         internal QueryMatcher(QueryMatcher matcher)
285         {
286             this.processorPool = new WeakReference(null); 
287             this.maxNodes = matcher.maxNodes;
288             this.query = matcher.query;
289             this.subExprVars = matcher.subExprVars;
290         }
291 #endif
292         internal bool IsCompiled
293         {
294             get
295             {
296                 return (null != this.query);
297             }
298         }
299
300         internal int NodeQuota
301         {
302             get
303             {
304                 return this.maxNodes;
305             }
306             set
307             {
308                 Fx.Assert(value > 0, "");
309                 this.maxNodes = value;
310             }
311         }
312
313         internal Opcode RootOpcode
314         {
315             get
316             {
317                 return this.query;
318             }
319         }
320
321         internal int SubExprVarCount
322         {
323             get
324             {
325                 return this.subExprVars;
326             }
327         }
328
329         /// <summary>
330         /// Compile the given filter to run on an external (fx) xpath engine
331         /// </summary>
332         internal static OpcodeBlock CompileForExternalEngine(string expression, XmlNamespaceManager namespaces, object item, bool match)
333         {
334             // Compile...            
335             XPathExpression xpathExpr = QueryMatcher.fxCompiler.Compile(expression);
336
337             // Fx will bind prefixes and functions here.
338             if (namespaces != null)
339             {
340                 // There's a 
341
342                 if (namespaces is XsltContext)
343                 {
344                     // Lex the xpath to find all prefixes used
345                     XPathLexer lexer = new XPathLexer(expression, false);
346                     while (lexer.MoveNext())
347                     {
348                         string prefix = lexer.Token.Prefix;
349
350                         if (prefix.Length > 0 && namespaces.LookupNamespace(prefix) == null)
351                         {
352                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XsltException(SR.GetString(SR.FilterUndefinedPrefix, prefix)));
353                         }
354                     }
355                 }
356
357                 xpathExpr.SetContext(namespaces);
358             }
359
360             //
361             // FORCE the function to COMPILE - they won't bind namespaces unless we check the return type
362             //
363             if (XPathResultType.Error == xpathExpr.ReturnType)
364             {
365                 // This should never be reached.  The above property should throw if there's an error
366                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathException(SR.GetString(SR.FilterCouldNotCompile, expression)));
367             }
368
369             OpcodeBlock codeBlock = new OpcodeBlock();
370             SingleFxEngineResultOpcode op;
371
372             if (!match)
373             {
374                 op = new QuerySingleFxEngineResultOpcode();
375             }
376             else
377             {
378                 op = new MatchSingleFxEngineResultOpcode();
379             }
380
381             op.XPath = xpathExpr;
382             op.Item = item;
383
384             codeBlock.Append(op);
385             return codeBlock;
386         }
387
388         /// <summary>
389         /// Compile the given filter for evaluation using the internal engine. 
390         /// </summary>
391         /// <param name="flags">Caller customizes optimizations via the flags parameter</param>
392         /// <param name="returnType">Every xpath expression has a return type</param>
393         /// <returns>The opcode block we execute to evaluate</returns>
394         internal static OpcodeBlock CompileForInternalEngine(XPathMessageFilter filter, QueryCompilerFlags flags, IFunctionLibrary[] functionLibs, out ValueDataType returnType)
395         {
396             return QueryMatcher.CompileForInternalEngine(filter.XPath.Trim(), filter.namespaces, flags, functionLibs, out returnType);
397         }
398
399         internal static OpcodeBlock CompileForInternalEngine(string xpath, XmlNamespaceManager nsManager, QueryCompilerFlags flags, IFunctionLibrary[] functionLibs, out ValueDataType returnType)
400         {
401             OpcodeBlock codeBlock;
402
403             returnType = ValueDataType.None;
404             if (0 == xpath.Length)
405             {
406                 // 0 length XPaths always match
407                 codeBlock = new OpcodeBlock();
408                 codeBlock.Append(new PushBooleanOpcode(true)); // Always match by pushing true on the eval stack
409             }
410             else
411             {
412                 // Try to parse the xpath. Bind to default function libraries
413                 // The parser returns an expression tree
414                 XPathParser parser = new XPathParser(xpath, nsManager, functionLibs);
415                 XPathExpr parseTree = parser.Parse();
416
417                 if (null == parseTree)
418                 {
419                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.CouldNotParseExpression));
420                 }
421
422                 returnType = parseTree.ReturnType;
423
424                 // Compile the expression tree
425                 XPathCompiler compiler = new XPathCompiler(flags);
426
427                 codeBlock = compiler.Compile(parseTree);
428             }
429
430             return codeBlock;
431         }
432
433         internal static OpcodeBlock CompileForInternalEngine(string xpath, XmlNamespaceManager ns, QueryCompilerFlags flags, out ValueDataType returnType)
434         {
435             return QueryMatcher.CompileForInternalEngine(xpath, ns, flags, QueryMatcher.defaultFunctionLibs, out returnType);
436         }
437
438         internal SeekableXPathNavigator CreateMessageNavigator(Message message, bool matchBody)
439         {
440             SeekableXPathNavigator nav = message.GetNavigator(matchBody, this.maxNodes);
441
442             // Position the navigator at the root element
443             // This allows a caller to run relative XPaths on message
444             nav.MoveToRoot();
445             return nav;
446         }
447
448         /// <summary>
449         /// Checks the context pool for a generic navigator first. If none is available, creates a new one
450         /// </summary>
451         internal SeekableXPathNavigator CreateSeekableNavigator(XPathNavigator navigator)
452         {
453             return new GenericSeekableNavigator(navigator);
454         }
455
456         internal SeekableXPathNavigator CreateSafeNavigator(SeekableXPathNavigator navigator)
457         {
458             INodeCounter counter = navigator as INodeCounter;
459             if (counter != null)
460             {
461                 counter.CounterMarker = this.maxNodes;
462                 counter.MaxCounter = this.maxNodes;
463             }
464             else
465             {
466                 navigator = new SafeSeekableNavigator(navigator, this.maxNodes);
467             }
468             return navigator;
469         }
470
471         /// <summary>
472         /// Checks the context pool for a processor first. If none is available, creates a new one
473         /// </summary>
474         internal QueryProcessor CreateProcessor()
475         {
476             QueryProcessor p = null;
477
478             lock (this.processorPool)
479             {
480                 QueryProcessorPool pool = this.processorPool.Target as QueryProcessorPool;
481                 if (null != pool)
482                 {
483                     p = pool.Pop();
484                 }
485             }
486
487             if (null != p)
488             {
489                 p.ClearProcessor();
490             }
491             else
492             {
493                 p = new QueryProcessor(this);
494             }
495
496             p.AddRef();
497             return p;
498         }
499
500         internal FilterResult Match(MessageBuffer messageBuffer, ICollection<MessageFilter> matches)
501         {
502             Message message = messageBuffer.CreateMessage();
503             FilterResult result;
504             try
505             {
506                 result = this.Match(message, true, matches);
507             }
508             finally
509             {
510                 message.Close();
511             }
512
513             return result;
514         }
515
516         internal FilterResult Match(Message message, bool matchBody, ICollection<MessageFilter> matches)
517         {
518             QueryProcessor processor = this.CreateProcessor();
519             processor.MatchSet = matches;
520             processor.EnsureFilterCollection();
521             try
522             {
523                 processor.Eval(this.query, message, matchBody);
524             }
525             catch (XPathNavigatorException e)
526             {
527                 throw TraceUtility.ThrowHelperError(e.Process(this.query), message);
528             }
529             catch (NavigatorInvalidBodyAccessException e)
530             {
531                 throw TraceUtility.ThrowHelperError(e.Process(this.query), message);
532             }
533
534             return new FilterResult(processor);
535         }
536
537         internal QueryResult<TResult> Evaluate<TResult>(MessageBuffer messageBuffer)
538         {
539             Message message = messageBuffer.CreateMessage();
540             return this.Evaluate<TResult>(message, true);
541         }
542
543         internal QueryResult<TResult> Evaluate<TResult>(Message message, bool matchBody)
544         {
545             return new QueryResult<TResult>(this, message, matchBody);
546         }
547
548         /// <summary>
549         /// Execute matches over the given seekable navigator. If the navigator is not safe, wrap it with one that is
550         /// </summary>
551         internal FilterResult Match(SeekableXPathNavigator navigator, ICollection<MessageFilter> matches)
552         {
553             // If the matcher places restrictions on the # of nodes we will inspect, and the navigator passed does
554             // not do any nodecounting itself, we must make that navigator safe by wrapping it
555             if (this.maxNodes < int.MaxValue)
556             {
557                 navigator = this.CreateSafeNavigator(navigator);
558             }
559
560             QueryProcessor processor = this.CreateProcessor();
561             processor.MatchSet = matches;
562             processor.EnsureFilterCollection();
563             try
564             {
565                 processor.Eval(this.query, navigator);
566             }
567             catch (XPathNavigatorException e)
568             {
569                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
570             }
571             catch (NavigatorInvalidBodyAccessException e)
572             {
573                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
574             }
575
576             return new FilterResult(processor);
577         }
578
579         /// <summary>
580         /// Execute matches over the given navigator by wrapping it with a Seekable Navigator
581         /// </summary>
582         internal FilterResult Match(XPathNavigator navigator, ICollection<MessageFilter> matches)
583         {
584             SeekableXPathNavigator nav = this.CreateSeekableNavigator(navigator);
585             return this.Match(nav, matches);
586         }
587
588         /// <summary>
589         /// Release the given processor and place it back in the context pool
590         /// </summary>
591         internal void ReleaseProcessor(QueryProcessor processor)
592         {
593             if (!processor.ReleaseRef())
594             {
595                 return;
596             }
597
598             lock (this.processorPool)
599             {
600                 QueryProcessorPool pool = this.processorPool.Target as QueryProcessorPool;
601                 if (null == pool)
602                 {
603                     pool = new QueryProcessorPool();
604                     this.processorPool.Target = pool;
605                 }
606                 pool.Push(processor);
607             }
608         }
609
610         internal void ReleaseResult(FilterResult result)
611         {
612             if (null != result.Processor)
613             {
614                 result.Processor.MatchSet = null;
615                 this.ReleaseProcessor(result.Processor);
616             }
617         }
618
619         /// <summary>
620         /// Trim all pool
621         /// </summary>
622         internal virtual void Trim()
623         {
624             if (this.query != null)
625             {
626                 this.query.Trim();
627             }
628         }
629     }
630
631     internal enum XPathFilterFlags
632     {
633         None = 0x00,
634         AlwaysMatch = 0x01,     // filter always matches
635         IsFxFilter = 0x02,      // filter is matched using the framework engine
636     }
637
638     /// <summary>
639     /// A matcher used to evalute single XPath expressions
640     /// </summary>    
641     internal class XPathQueryMatcher : QueryMatcher
642     {
643         XPathFilterFlags flags;
644         bool match;
645         static PushBooleanOpcode matchAlwaysFilter; // used for compiling xpaths that always match - i.e. xpath.Length == 0
646         static OpcodeBlock rootFilter;        // used for compiling "/"
647
648         static XPathQueryMatcher()
649         {
650             XPathQueryMatcher.matchAlwaysFilter = new PushBooleanOpcode(true); //dummy
651
652             ValueDataType returnType;
653             XPathQueryMatcher.rootFilter = QueryMatcher.CompileForInternalEngine("/", null, QueryCompilerFlags.None, out returnType);
654             XPathQueryMatcher.rootFilter.Append(new MatchResultOpcode());
655         }
656
657         internal XPathQueryMatcher(bool match)
658             : base()
659         {
660             this.flags = XPathFilterFlags.None;
661             this.match = match;
662         }
663 #if NO        
664         internal XPathFilterMatcher(XPathFilterMatcher matcher)
665             : base(matcher)
666         {
667             this.flags = matcher.flags;
668         }
669 #endif
670         internal bool IsAlwaysMatch
671         {
672             get
673             {
674                 return (0 != (this.flags & XPathFilterFlags.AlwaysMatch));
675             }
676         }
677
678         internal bool IsFxFilter
679         {
680             get
681             {
682                 return (0 != (this.flags & XPathFilterFlags.IsFxFilter));
683             }
684         }
685
686         /// <summary>
687         /// If the xpath is an empty string, there is nothing to compile and the filter always matches
688         /// If not, try to compile the filter for execution within the filter engine's own query processor
689         /// If that query processor cannot accept the filter (it doesn't fall within the class of xpaths it can handle),
690         /// then revert to the fall-back solution - the slower Fx engine
691         /// </summary>
692         internal void Compile(string expression, XmlNamespaceManager namespaces)
693         {
694             if (null == this.query)
695             {
696                 // Try to compile for the internal engine first
697                 try
698                 {
699                     this.CompileForInternal(expression, namespaces);
700                 }
701                 catch (QueryCompileException)
702                 {
703                 }
704                 if (null == this.query)
705                 {
706                     // Try for an external engine that might work..
707                     this.CompileForExternal(expression, namespaces);
708                 }
709             }
710         }
711
712         /// <summary>
713         /// Compile this xpath to run on an external (fx) xpath engine
714         /// </summary>
715         internal void CompileForExternal(string xpath, XmlNamespaceManager names)
716         {
717             Opcode op = QueryMatcher.CompileForExternalEngine(xpath, names, null, this.match).First;
718             this.query = op;
719             this.flags |= XPathFilterFlags.IsFxFilter;
720         }
721
722         /// <summary>
723         /// Compile for the internal engine with default flags
724         /// By defalt, we compile an xpath to run stand alone, with standard optimizations
725         /// </summary>
726         internal void CompileForInternal(string xpath, XmlNamespaceManager names)
727         {
728             this.query = null;
729             xpath = xpath.Trim();
730
731             if (0 == xpath.Length)
732             {
733                 // Empty xpaths always match. Same for xpaths that refer to the root only
734                 // We will evaluate such filters with minimal overhead. However, we
735                 // don't want a null value for this.query, so we stick a dummy value in there
736                 this.query = XPathQueryMatcher.matchAlwaysFilter;
737                 this.flags |= (XPathFilterFlags.AlwaysMatch);
738             }
739             else if (1 == xpath.Length && '/' == xpath[0])
740             {
741                 this.query = XPathQueryMatcher.rootFilter.First;
742                 this.flags |= (XPathFilterFlags.AlwaysMatch);
743             }
744             else
745             {
746                 ValueDataType returnType;
747                 OpcodeBlock codeBlock = QueryMatcher.CompileForInternalEngine(xpath, names, QueryCompilerFlags.None, out returnType);
748                 // Inject a final opcode that will place the query result on the query context
749                 // This query is now ready for execution STAND ALONE
750                 if (this.match)
751                 {
752                     codeBlock.Append(new MatchResultOpcode());
753                 }
754                 else
755                 {
756                     codeBlock.Append(new QueryResultOpcode());
757                 }
758
759                 this.query = codeBlock.First;
760             }
761
762             this.flags &= ~XPathFilterFlags.IsFxFilter;
763         }
764
765         internal FilterResult Match(MessageBuffer messageBuffer)
766         {
767             Message message = messageBuffer.CreateMessage();
768             FilterResult result;
769
770             try
771             {
772                 result = this.Match(message, true);
773             }
774             finally
775             {
776                 message.Close();
777             }
778             return result;
779         }
780
781         internal FilterResult Match(Message message, bool matchBody)
782         {
783             if (this.IsAlwaysMatch)
784             {
785                 // No need to do any expensive query evaluation if we know that the query will always match
786                 return new FilterResult(true);
787             }
788
789             return base.Match(message, matchBody, null);
790         }
791
792         internal FilterResult Match(SeekableXPathNavigator navigator)
793         {
794             if (this.IsAlwaysMatch)
795             {
796                 // No need to do any expensive query evaluation if we know that the query will always match
797                 return new FilterResult(true);
798             }
799
800             // Is it a filter that we will evaluate using the framework engine?
801             // We can evaluate that without having to allocate a query processor
802             if (this.IsFxFilter)
803             {
804                 return new FilterResult(this.MatchFx(navigator));
805             }
806
807             return base.Match(navigator, null);
808         }
809
810         internal FilterResult Match(XPathNavigator navigator)
811         {
812             Fx.Assert(null != this.query, "");
813             if (this.IsAlwaysMatch)
814             {
815                 return new FilterResult(true);
816             }
817             // Is it a filter that we will evaluate using the framework engine?
818             // We can evaluate that without having to allocate a query processor
819             if (this.IsFxFilter)
820             {
821                 return new FilterResult(this.MatchFx(navigator));
822             }
823
824             return base.Match(navigator, null);
825         }
826
827         /// <summary>
828         /// Evaluates the filter over infosets surfaced via the given navigator by using the Fx engine
829         /// We assume that the filter was pre-compiled using the framework engine
830         /// </summary>
831         internal bool MatchFx(XPathNavigator navigator)
832         {
833             INodeCounter counter = navigator as INodeCounter;
834             if (counter == null)
835             {
836                 navigator = new SafeSeekableNavigator(new GenericSeekableNavigator(navigator), this.NodeQuota);
837             }
838             else
839             {
840                 counter.CounterMarker = this.NodeQuota;
841                 counter.MaxCounter = this.NodeQuota;
842             }
843             Fx.Assert(null != this.query && OpcodeID.MatchSingleFx == this.query.ID, "");
844             try
845             {
846                 return ((MatchSingleFxEngineResultOpcode)this.query).Match(navigator);
847             }
848             catch (XPathNavigatorException e)
849             {
850                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
851             }
852             catch (NavigatorInvalidBodyAccessException e)
853             {
854                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
855             }
856         }
857     }
858
859     internal class InverseQueryMatcher : QueryMatcher
860     {
861         SubExprEliminator elim;
862         Dictionary<object, Opcode> lastLookup;
863         bool match;
864
865         internal InverseQueryMatcher(bool match)
866             : base()
867         {
868             this.elim = new SubExprEliminator();
869             this.lastLookup = new Dictionary<object, Opcode>();
870             this.match = match;
871         }
872
873         internal void Add(string expression, XmlNamespaceManager names, object item, bool forceExternal)
874         {
875             Fx.Assert(null != item, "");
876
877             // Compile the new filter
878
879             bool compiled = false;
880             OpcodeBlock codeBlock = new OpcodeBlock();
881
882             codeBlock.Append(new NoOpOpcode(OpcodeID.QueryTree));
883             if (!forceExternal)
884             {
885                 try
886                 {
887                     ValueDataType returnType = ValueDataType.None;
888
889                     // Try to compile and merge the compiled query into the query tree
890                     codeBlock.Append(QueryMatcher.CompileForInternalEngine(expression, names, QueryCompilerFlags.InverseQuery, out returnType));
891
892                     MultipleResultOpcode opcode;
893
894                     if (!this.match)
895                     {
896                         opcode = new QueryMultipleResultOpcode();
897                     }
898                     else
899                     {
900                         opcode = new MatchMultipleResultOpcode();
901                     }
902
903                     opcode.AddItem(item);
904                     codeBlock.Append(opcode);
905                     compiled = true;
906
907                     // Perform SubExpression Elimination
908                     codeBlock = new OpcodeBlock(this.elim.Add(item, codeBlock.First));
909                     this.subExprVars = this.elim.VariableCount;
910                 }
911                 catch (QueryCompileException)
912                 {
913                     // If the filter couldn't be compiled, we drop down to the framework engine
914                 }
915             }
916
917             if (!compiled)
918             {
919                 codeBlock.Append(QueryMatcher.CompileForExternalEngine(expression, names, item, this.match));
920             }
921
922             // Merge the compiled query into the query tree
923             QueryTreeBuilder builder = new QueryTreeBuilder();
924             this.query = builder.Build(this.query, codeBlock);
925             // To de-merge this filter from the tree, we'll have to walk backwards up the tree... so we
926             // have to remember the last opcode that is executed on behalf of this filter
927             this.lastLookup[item] = builder.LastOpcode;
928         }
929
930         internal void Clear()
931         {
932             foreach (object item in this.lastLookup.Keys)
933             {
934                 this.Remove(this.lastLookup[item], item);
935                 this.elim.Remove(item);
936             }
937             this.subExprVars = this.elim.VariableCount;
938             this.lastLookup.Clear();
939         }
940
941         internal void Remove(object item)
942         {
943             Fx.Assert(this.lastLookup.ContainsKey(item), "");
944
945             this.Remove(this.lastLookup[item], item);
946             this.lastLookup.Remove(item);
947
948             // Remove filter from subexpr eliminator
949             this.elim.Remove(item);
950             this.subExprVars = this.elim.VariableCount;
951         }
952
953         void Remove(Opcode opcode, object item)
954         {
955             MultipleResultOpcode multiOpcode = opcode as MultipleResultOpcode;
956
957             if (multiOpcode != null)
958             {
959                 multiOpcode.RemoveItem(item);
960             }
961             else
962             {
963                 opcode.Remove();
964             }
965         }
966
967         internal override void Trim()
968         {
969             base.Trim();
970             elim.Trim();
971         }
972     }
973 }