| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973 |
- //------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------
- namespace System.ServiceModel.Dispatcher
- {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Runtime;
- using System.ServiceModel.Channels;
- using System.ServiceModel.Diagnostics;
- using System.Xml;
- using System.Xml.XPath;
- using System.Xml.Xsl;
- internal enum QueryCompilerFlags
- {
- None = 0x00000000,
- InverseQuery = 0x00000001
- }
- internal struct FilterResult
- {
- QueryProcessor processor;
- bool result;
- internal FilterResult(QueryProcessor processor)
- {
- this.processor = processor;
- this.result = this.processor.Result;
- }
- internal FilterResult(bool result)
- {
- this.processor = null;
- this.result = result;
- }
- #if NO
- internal ICollection<MessageFilter> Matches
- {
- get
- {
- return this.processor.ResultSet;
- }
- }
- #endif
- internal QueryProcessor Processor
- {
- get
- {
- return this.processor;
- }
- }
- internal bool Result
- {
- get
- {
- return this.result;
- }
- }
- internal MessageFilter GetSingleMatch()
- {
- Collection<MessageFilter> matches = processor.MatchList;
- MessageFilter match;
- switch (matches.Count)
- {
- default:
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, matches));
- case 0:
- match = null;
- break;
- case 1:
- match = matches[0];
- break;
- }
- return match;
- }
- }
- // XPathResult.GetResultAsString and XPathResult.GetResultAsBoolean,
- // drive knowledge of TResult into the engine.
- internal class QueryResult<TResult> : IEnumerable<KeyValuePair<MessageQuery, TResult>>
- {
- bool evalBody;
- QueryMatcher matcher;
- Message message;
- internal QueryResult(QueryMatcher matcher, Message message, bool evalBody)
- {
- this.matcher = matcher;
- this.message = message;
- this.evalBody = evalBody;
- }
- public TResult GetSingleResult()
- {
- QueryProcessor processor = this.matcher.CreateProcessor();
- XPathResult result;
- try
- {
- processor.Eval(this.matcher.RootOpcode, this.message, this.evalBody);
- }
- catch (XPathNavigatorException e)
- {
- throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
- }
- catch (NavigatorInvalidBodyAccessException e)
- {
- throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
- }
- finally
- {
- if (this.evalBody)
- {
- this.message.Close();
- }
- result = processor.QueryResult;
- this.matcher.ReleaseProcessor(processor);
- }
- if (typeof(TResult) == typeof(XPathResult) || typeof(TResult) == typeof(object))
- {
- return (TResult)(object)result;
- }
- else if (typeof(TResult) == typeof(string))
- {
- return (TResult)(object)result.GetResultAsString();
- }
- else if (typeof(TResult) == typeof(bool))
- {
- return (TResult)(object)result.GetResultAsBoolean();
- }
- else
- {
- throw Fx.AssertAndThrowFatal("unsupported type");
- }
- }
- public IEnumerator<KeyValuePair<MessageQuery, TResult>> GetEnumerator()
- {
- QueryProcessor processor = this.matcher.CreateProcessor();
- Collection<KeyValuePair<MessageQuery, XPathResult>> results =
- new Collection<KeyValuePair<MessageQuery, XPathResult>>();
- processor.ResultSet = results;
- try
- {
- processor.Eval(this.matcher.RootOpcode, this.message, this.evalBody);
- if (typeof(TResult) == typeof(XPathResult))
- {
- return (IEnumerator<KeyValuePair<MessageQuery, TResult>>)(object)results.GetEnumerator();
- }
- else if (typeof(TResult) == typeof(string) ||
- typeof(TResult) == typeof(bool) ||
- typeof(TResult) == typeof(object))
- {
- Collection<KeyValuePair<MessageQuery, TResult>> typedResults =
- new Collection<KeyValuePair<MessageQuery, TResult>>();
- foreach (var result in results)
- {
- if (typeof(TResult) == typeof(string))
- {
- typedResults.Add(
- new KeyValuePair<MessageQuery, TResult>(
- result.Key, (TResult)(object)result.Value.GetResultAsString()));
- }
- else if (typeof(TResult) == typeof(bool))
- {
- typedResults.Add(
- new KeyValuePair<MessageQuery, TResult>(
- result.Key, (TResult)(object)result.Value.GetResultAsBoolean()));
- }
- else
- {
- typedResults.Add(new KeyValuePair<MessageQuery, TResult>(
- result.Key, (TResult)(object)result.Value));
- }
- }
- return (IEnumerator<KeyValuePair<MessageQuery, TResult>>)typedResults.GetEnumerator();
- }
- else
- {
- throw Fx.AssertAndThrowFatal("unsupported type");
- }
- }
- catch (XPathNavigatorException e)
- {
- throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
- }
- catch (NavigatorInvalidBodyAccessException e)
- {
- throw TraceUtility.ThrowHelperError(e.Process(this.matcher.RootOpcode), this.message);
- }
- finally
- {
- if (this.evalBody)
- {
- this.message.Close();
- }
- this.matcher.ReleaseProcessor(processor);
- }
- }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return this.GetEnumerator();
- }
- }
- /// <summary>
- ///
- /// </summary>
- internal abstract class QueryMatcher
- {
- static IFunctionLibrary[] defaultFunctionLibs; // The set of function libraries that our XPath compiler will link to
- static XPathNavigator fxCompiler; // fx compiler
- protected int maxNodes; // Maximum # of nodes that we will process while performing any individual match
- protected Opcode query; // root opcode - this is where query evaluation starts
- protected int subExprVars; // the number of subexpr node sequences the processing context must hold
- // Processor Pool
- protected WeakReference processorPool;
- internal class QueryProcessorPool
- {
- QueryProcessor processor;
- internal QueryProcessorPool()
- {
- }
- internal QueryProcessor Pop()
- {
- QueryProcessor p = this.processor;
- if (null != p)
- {
- this.processor = (QueryProcessor)p.next;
- p.next = null;
- return p;
- }
- return null;
- }
- internal void Push(QueryProcessor p)
- {
- p.next = this.processor;
- this.processor = p;
- }
- }
- static QueryMatcher()
- {
- QueryMatcher.defaultFunctionLibs = new IFunctionLibrary[] { new XPathFunctionLibrary() };
- // For some incomprehensible reason, the Framework XPath compiler requires an instance of an XPath navigator
- // to compile an xpath. This compiler uses a dummy xml document to create a navigator
- XmlDocument doc = new XmlDocument();
- doc.LoadXml("<a/>");
- QueryMatcher.fxCompiler = doc.CreateNavigator();
- }
- internal QueryMatcher()
- {
- this.maxNodes = int.MaxValue;
- this.query = null;
- this.processorPool = new WeakReference(null);
- this.subExprVars = 0;
- }
- #if NO
- internal QueryMatcher(QueryMatcher matcher)
- {
- this.processorPool = new WeakReference(null);
- this.maxNodes = matcher.maxNodes;
- this.query = matcher.query;
- this.subExprVars = matcher.subExprVars;
- }
- #endif
- internal bool IsCompiled
- {
- get
- {
- return (null != this.query);
- }
- }
- internal int NodeQuota
- {
- get
- {
- return this.maxNodes;
- }
- set
- {
- Fx.Assert(value > 0, "");
- this.maxNodes = value;
- }
- }
- internal Opcode RootOpcode
- {
- get
- {
- return this.query;
- }
- }
- internal int SubExprVarCount
- {
- get
- {
- return this.subExprVars;
- }
- }
- /// <summary>
- /// Compile the given filter to run on an external (fx) xpath engine
- /// </summary>
- internal static OpcodeBlock CompileForExternalEngine(string expression, XmlNamespaceManager namespaces, object item, bool match)
- {
- // Compile...
- XPathExpression xpathExpr = QueryMatcher.fxCompiler.Compile(expression);
- // Fx will bind prefixes and functions here.
- if (namespaces != null)
- {
- // There's a bug in System.Xml.XPath. If we pass an XsltContext to SetContext it won't throw if there's
- // an undefined prefix.
- if (namespaces is XsltContext)
- {
- // Lex the xpath to find all prefixes used
- XPathLexer lexer = new XPathLexer(expression, false);
- while (lexer.MoveNext())
- {
- string prefix = lexer.Token.Prefix;
- if (prefix.Length > 0 && namespaces.LookupNamespace(prefix) == null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XsltException(SR.GetString(SR.FilterUndefinedPrefix, prefix)));
- }
- }
- }
- xpathExpr.SetContext(namespaces);
- }
- //
- // FORCE the function to COMPILE - they won't bind namespaces unless we check the return type
- //
- if (XPathResultType.Error == xpathExpr.ReturnType)
- {
- // This should never be reached. The above property should throw if there's an error
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XPathException(SR.GetString(SR.FilterCouldNotCompile, expression)));
- }
- OpcodeBlock codeBlock = new OpcodeBlock();
- SingleFxEngineResultOpcode op;
- if (!match)
- {
- op = new QuerySingleFxEngineResultOpcode();
- }
- else
- {
- op = new MatchSingleFxEngineResultOpcode();
- }
- op.XPath = xpathExpr;
- op.Item = item;
- codeBlock.Append(op);
- return codeBlock;
- }
- /// <summary>
- /// Compile the given filter for evaluation using the internal engine.
- /// </summary>
- /// <param name="flags">Caller customizes optimizations via the flags parameter</param>
- /// <param name="returnType">Every xpath expression has a return type</param>
- /// <returns>The opcode block we execute to evaluate</returns>
- internal static OpcodeBlock CompileForInternalEngine(XPathMessageFilter filter, QueryCompilerFlags flags, IFunctionLibrary[] functionLibs, out ValueDataType returnType)
- {
- return QueryMatcher.CompileForInternalEngine(filter.XPath.Trim(), filter.namespaces, flags, functionLibs, out returnType);
- }
- internal static OpcodeBlock CompileForInternalEngine(string xpath, XmlNamespaceManager nsManager, QueryCompilerFlags flags, IFunctionLibrary[] functionLibs, out ValueDataType returnType)
- {
- OpcodeBlock codeBlock;
- returnType = ValueDataType.None;
- if (0 == xpath.Length)
- {
- // 0 length XPaths always match
- codeBlock = new OpcodeBlock();
- codeBlock.Append(new PushBooleanOpcode(true)); // Always match by pushing true on the eval stack
- }
- else
- {
- // Try to parse the xpath. Bind to default function libraries
- // The parser returns an expression tree
- XPathParser parser = new XPathParser(xpath, nsManager, functionLibs);
- XPathExpr parseTree = parser.Parse();
- if (null == parseTree)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.CouldNotParseExpression));
- }
- returnType = parseTree.ReturnType;
- // Compile the expression tree
- XPathCompiler compiler = new XPathCompiler(flags);
- codeBlock = compiler.Compile(parseTree);
- }
- return codeBlock;
- }
- internal static OpcodeBlock CompileForInternalEngine(string xpath, XmlNamespaceManager ns, QueryCompilerFlags flags, out ValueDataType returnType)
- {
- return QueryMatcher.CompileForInternalEngine(xpath, ns, flags, QueryMatcher.defaultFunctionLibs, out returnType);
- }
- internal SeekableXPathNavigator CreateMessageNavigator(Message message, bool matchBody)
- {
- SeekableXPathNavigator nav = message.GetNavigator(matchBody, this.maxNodes);
- // Position the navigator at the root element
- // This allows a caller to run relative XPaths on message
- nav.MoveToRoot();
- return nav;
- }
- /// <summary>
- /// Checks the context pool for a generic navigator first. If none is available, creates a new one
- /// </summary>
- internal SeekableXPathNavigator CreateSeekableNavigator(XPathNavigator navigator)
- {
- return new GenericSeekableNavigator(navigator);
- }
- internal SeekableXPathNavigator CreateSafeNavigator(SeekableXPathNavigator navigator)
- {
- INodeCounter counter = navigator as INodeCounter;
- if (counter != null)
- {
- counter.CounterMarker = this.maxNodes;
- counter.MaxCounter = this.maxNodes;
- }
- else
- {
- navigator = new SafeSeekableNavigator(navigator, this.maxNodes);
- }
- return navigator;
- }
- /// <summary>
- /// Checks the context pool for a processor first. If none is available, creates a new one
- /// </summary>
- internal QueryProcessor CreateProcessor()
- {
- QueryProcessor p = null;
- lock (this.processorPool)
- {
- QueryProcessorPool pool = this.processorPool.Target as QueryProcessorPool;
- if (null != pool)
- {
- p = pool.Pop();
- }
- }
- if (null != p)
- {
- p.ClearProcessor();
- }
- else
- {
- p = new QueryProcessor(this);
- }
- p.AddRef();
- return p;
- }
- internal FilterResult Match(MessageBuffer messageBuffer, ICollection<MessageFilter> matches)
- {
- Message message = messageBuffer.CreateMessage();
- FilterResult result;
- try
- {
- result = this.Match(message, true, matches);
- }
- finally
- {
- message.Close();
- }
- return result;
- }
- internal FilterResult Match(Message message, bool matchBody, ICollection<MessageFilter> matches)
- {
- QueryProcessor processor = this.CreateProcessor();
- processor.MatchSet = matches;
- processor.EnsureFilterCollection();
- try
- {
- processor.Eval(this.query, message, matchBody);
- }
- catch (XPathNavigatorException e)
- {
- throw TraceUtility.ThrowHelperError(e.Process(this.query), message);
- }
- catch (NavigatorInvalidBodyAccessException e)
- {
- throw TraceUtility.ThrowHelperError(e.Process(this.query), message);
- }
- return new FilterResult(processor);
- }
- internal QueryResult<TResult> Evaluate<TResult>(MessageBuffer messageBuffer)
- {
- Message message = messageBuffer.CreateMessage();
- return this.Evaluate<TResult>(message, true);
- }
- internal QueryResult<TResult> Evaluate<TResult>(Message message, bool matchBody)
- {
- return new QueryResult<TResult>(this, message, matchBody);
- }
- /// <summary>
- /// Execute matches over the given seekable navigator. If the navigator is not safe, wrap it with one that is
- /// </summary>
- internal FilterResult Match(SeekableXPathNavigator navigator, ICollection<MessageFilter> matches)
- {
- // If the matcher places restrictions on the # of nodes we will inspect, and the navigator passed does
- // not do any nodecounting itself, we must make that navigator safe by wrapping it
- if (this.maxNodes < int.MaxValue)
- {
- navigator = this.CreateSafeNavigator(navigator);
- }
- QueryProcessor processor = this.CreateProcessor();
- processor.MatchSet = matches;
- processor.EnsureFilterCollection();
- try
- {
- processor.Eval(this.query, navigator);
- }
- catch (XPathNavigatorException e)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
- }
- catch (NavigatorInvalidBodyAccessException e)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
- }
- return new FilterResult(processor);
- }
- /// <summary>
- /// Execute matches over the given navigator by wrapping it with a Seekable Navigator
- /// </summary>
- internal FilterResult Match(XPathNavigator navigator, ICollection<MessageFilter> matches)
- {
- SeekableXPathNavigator nav = this.CreateSeekableNavigator(navigator);
- return this.Match(nav, matches);
- }
- /// <summary>
- /// Release the given processor and place it back in the context pool
- /// </summary>
- internal void ReleaseProcessor(QueryProcessor processor)
- {
- if (!processor.ReleaseRef())
- {
- return;
- }
- lock (this.processorPool)
- {
- QueryProcessorPool pool = this.processorPool.Target as QueryProcessorPool;
- if (null == pool)
- {
- pool = new QueryProcessorPool();
- this.processorPool.Target = pool;
- }
- pool.Push(processor);
- }
- }
- internal void ReleaseResult(FilterResult result)
- {
- if (null != result.Processor)
- {
- result.Processor.MatchSet = null;
- this.ReleaseProcessor(result.Processor);
- }
- }
- /// <summary>
- /// Trim all pool
- /// </summary>
- internal virtual void Trim()
- {
- if (this.query != null)
- {
- this.query.Trim();
- }
- }
- }
- internal enum XPathFilterFlags
- {
- None = 0x00,
- AlwaysMatch = 0x01, // filter always matches
- IsFxFilter = 0x02, // filter is matched using the framework engine
- }
- /// <summary>
- /// A matcher used to evalute single XPath expressions
- /// </summary>
- internal class XPathQueryMatcher : QueryMatcher
- {
- XPathFilterFlags flags;
- bool match;
- static PushBooleanOpcode matchAlwaysFilter; // used for compiling xpaths that always match - i.e. xpath.Length == 0
- static OpcodeBlock rootFilter; // used for compiling "/"
- static XPathQueryMatcher()
- {
- XPathQueryMatcher.matchAlwaysFilter = new PushBooleanOpcode(true); //dummy
- ValueDataType returnType;
- XPathQueryMatcher.rootFilter = QueryMatcher.CompileForInternalEngine("/", null, QueryCompilerFlags.None, out returnType);
- XPathQueryMatcher.rootFilter.Append(new MatchResultOpcode());
- }
- internal XPathQueryMatcher(bool match)
- : base()
- {
- this.flags = XPathFilterFlags.None;
- this.match = match;
- }
- #if NO
- internal XPathFilterMatcher(XPathFilterMatcher matcher)
- : base(matcher)
- {
- this.flags = matcher.flags;
- }
- #endif
- internal bool IsAlwaysMatch
- {
- get
- {
- return (0 != (this.flags & XPathFilterFlags.AlwaysMatch));
- }
- }
- internal bool IsFxFilter
- {
- get
- {
- return (0 != (this.flags & XPathFilterFlags.IsFxFilter));
- }
- }
- /// <summary>
- /// If the xpath is an empty string, there is nothing to compile and the filter always matches
- /// If not, try to compile the filter for execution within the filter engine's own query processor
- /// If that query processor cannot accept the filter (it doesn't fall within the class of xpaths it can handle),
- /// then revert to the fall-back solution - the slower Fx engine
- /// </summary>
- internal void Compile(string expression, XmlNamespaceManager namespaces)
- {
- if (null == this.query)
- {
- // Try to compile for the internal engine first
- try
- {
- this.CompileForInternal(expression, namespaces);
- }
- catch (QueryCompileException)
- {
- }
- if (null == this.query)
- {
- // Try for an external engine that might work..
- this.CompileForExternal(expression, namespaces);
- }
- }
- }
- /// <summary>
- /// Compile this xpath to run on an external (fx) xpath engine
- /// </summary>
- internal void CompileForExternal(string xpath, XmlNamespaceManager names)
- {
- Opcode op = QueryMatcher.CompileForExternalEngine(xpath, names, null, this.match).First;
- this.query = op;
- this.flags |= XPathFilterFlags.IsFxFilter;
- }
- /// <summary>
- /// Compile for the internal engine with default flags
- /// By defalt, we compile an xpath to run stand alone, with standard optimizations
- /// </summary>
- internal void CompileForInternal(string xpath, XmlNamespaceManager names)
- {
- this.query = null;
- xpath = xpath.Trim();
- if (0 == xpath.Length)
- {
- // Empty xpaths always match. Same for xpaths that refer to the root only
- // We will evaluate such filters with minimal overhead. However, we
- // don't want a null value for this.query, so we stick a dummy value in there
- this.query = XPathQueryMatcher.matchAlwaysFilter;
- this.flags |= (XPathFilterFlags.AlwaysMatch);
- }
- else if (1 == xpath.Length && '/' == xpath[0])
- {
- this.query = XPathQueryMatcher.rootFilter.First;
- this.flags |= (XPathFilterFlags.AlwaysMatch);
- }
- else
- {
- ValueDataType returnType;
- OpcodeBlock codeBlock = QueryMatcher.CompileForInternalEngine(xpath, names, QueryCompilerFlags.None, out returnType);
- // Inject a final opcode that will place the query result on the query context
- // This query is now ready for execution STAND ALONE
- if (this.match)
- {
- codeBlock.Append(new MatchResultOpcode());
- }
- else
- {
- codeBlock.Append(new QueryResultOpcode());
- }
- this.query = codeBlock.First;
- }
- this.flags &= ~XPathFilterFlags.IsFxFilter;
- }
- internal FilterResult Match(MessageBuffer messageBuffer)
- {
- Message message = messageBuffer.CreateMessage();
- FilterResult result;
- try
- {
- result = this.Match(message, true);
- }
- finally
- {
- message.Close();
- }
- return result;
- }
- internal FilterResult Match(Message message, bool matchBody)
- {
- if (this.IsAlwaysMatch)
- {
- // No need to do any expensive query evaluation if we know that the query will always match
- return new FilterResult(true);
- }
- return base.Match(message, matchBody, null);
- }
- internal FilterResult Match(SeekableXPathNavigator navigator)
- {
- if (this.IsAlwaysMatch)
- {
- // No need to do any expensive query evaluation if we know that the query will always match
- return new FilterResult(true);
- }
- // Is it a filter that we will evaluate using the framework engine?
- // We can evaluate that without having to allocate a query processor
- if (this.IsFxFilter)
- {
- return new FilterResult(this.MatchFx(navigator));
- }
- return base.Match(navigator, null);
- }
- internal FilterResult Match(XPathNavigator navigator)
- {
- Fx.Assert(null != this.query, "");
- if (this.IsAlwaysMatch)
- {
- return new FilterResult(true);
- }
- // Is it a filter that we will evaluate using the framework engine?
- // We can evaluate that without having to allocate a query processor
- if (this.IsFxFilter)
- {
- return new FilterResult(this.MatchFx(navigator));
- }
- return base.Match(navigator, null);
- }
- /// <summary>
- /// Evaluates the filter over infosets surfaced via the given navigator by using the Fx engine
- /// We assume that the filter was pre-compiled using the framework engine
- /// </summary>
- internal bool MatchFx(XPathNavigator navigator)
- {
- INodeCounter counter = navigator as INodeCounter;
- if (counter == null)
- {
- navigator = new SafeSeekableNavigator(new GenericSeekableNavigator(navigator), this.NodeQuota);
- }
- else
- {
- counter.CounterMarker = this.NodeQuota;
- counter.MaxCounter = this.NodeQuota;
- }
- Fx.Assert(null != this.query && OpcodeID.MatchSingleFx == this.query.ID, "");
- try
- {
- return ((MatchSingleFxEngineResultOpcode)this.query).Match(navigator);
- }
- catch (XPathNavigatorException e)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
- }
- catch (NavigatorInvalidBodyAccessException e)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(e.Process(this.query));
- }
- }
- }
- internal class InverseQueryMatcher : QueryMatcher
- {
- SubExprEliminator elim;
- Dictionary<object, Opcode> lastLookup;
- bool match;
- internal InverseQueryMatcher(bool match)
- : base()
- {
- this.elim = new SubExprEliminator();
- this.lastLookup = new Dictionary<object, Opcode>();
- this.match = match;
- }
- internal void Add(string expression, XmlNamespaceManager names, object item, bool forceExternal)
- {
- Fx.Assert(null != item, "");
- // Compile the new filter
- bool compiled = false;
- OpcodeBlock codeBlock = new OpcodeBlock();
- codeBlock.Append(new NoOpOpcode(OpcodeID.QueryTree));
- if (!forceExternal)
- {
- try
- {
- ValueDataType returnType = ValueDataType.None;
- // Try to compile and merge the compiled query into the query tree
- codeBlock.Append(QueryMatcher.CompileForInternalEngine(expression, names, QueryCompilerFlags.InverseQuery, out returnType));
- MultipleResultOpcode opcode;
- if (!this.match)
- {
- opcode = new QueryMultipleResultOpcode();
- }
- else
- {
- opcode = new MatchMultipleResultOpcode();
- }
- opcode.AddItem(item);
- codeBlock.Append(opcode);
- compiled = true;
- // Perform SubExpression Elimination
- codeBlock = new OpcodeBlock(this.elim.Add(item, codeBlock.First));
- this.subExprVars = this.elim.VariableCount;
- }
- catch (QueryCompileException)
- {
- // If the filter couldn't be compiled, we drop down to the framework engine
- }
- }
- if (!compiled)
- {
- codeBlock.Append(QueryMatcher.CompileForExternalEngine(expression, names, item, this.match));
- }
- // Merge the compiled query into the query tree
- QueryTreeBuilder builder = new QueryTreeBuilder();
- this.query = builder.Build(this.query, codeBlock);
- // To de-merge this filter from the tree, we'll have to walk backwards up the tree... so we
- // have to remember the last opcode that is executed on behalf of this filter
- this.lastLookup[item] = builder.LastOpcode;
- }
- internal void Clear()
- {
- foreach (object item in this.lastLookup.Keys)
- {
- this.Remove(this.lastLookup[item], item);
- this.elim.Remove(item);
- }
- this.subExprVars = this.elim.VariableCount;
- this.lastLookup.Clear();
- }
- internal void Remove(object item)
- {
- Fx.Assert(this.lastLookup.ContainsKey(item), "");
- this.Remove(this.lastLookup[item], item);
- this.lastLookup.Remove(item);
- // Remove filter from subexpr eliminator
- this.elim.Remove(item);
- this.subExprVars = this.elim.VariableCount;
- }
- void Remove(Opcode opcode, object item)
- {
- MultipleResultOpcode multiOpcode = opcode as MultipleResultOpcode;
- if (multiOpcode != null)
- {
- multiOpcode.RemoveItem(item);
- }
- else
- {
- opcode.Remove();
- }
- }
- internal override void Trim()
- {
- base.Trim();
- elim.Trim();
- }
- }
- }
|