QueryMatcher.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / ServiceModel / System / ServiceModel / Dispatcher / QueryMatcher.cs / 1 / QueryMatcher.cs

                            //------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------
namespace System.ServiceModel.Dispatcher
{ 
    using System;
    using System.ServiceModel.Channels; 
    using System.Collections.Generic; 
    using System.Collections.ObjectModel;
    using System.Diagnostics; 
    using System.Xml;
    using System.Xml.XPath;
    using System.Xml.Xsl;
    using System.ServiceModel.Diagnostics; 

    internal enum QueryCompilerFlags 
    { 
        None = 0x00000000,
        InverseQuery = 0x00000001 
    }

    internal struct QueryResult
    { 
        QueryProcessor processor;
        bool result; 
 
        internal QueryResult(QueryProcessor processor)
        { 
            this.processor = processor;
            this.result = this.processor.Result;
        }
 
        internal QueryResult(bool result)
        { 
            this.processor = null; 
            this.result = result;
        } 

#if NO
        internal ICollection 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 matches = processor.ResultList;
            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; 
        }
    }

    ///  
    ///
    ///  
    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("");
            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 
            { 
                DiagnosticUtility.DebugAssert(value > 0, "");
                this.maxNodes = value; 
            }
        }

#if NO 
        internal Opcode RootOpcode
        { 
            get 
            {
                return this.query; 
            }
            set
            {
                DiagnosticUtility.DebugAssert(null != value, ""); 
                this.query = value;
            } 
        } 
#endif
 
        internal int SubExprVarCount
        {
            get
            { 
                return this.subExprVars;
            } 
        } 

        ///  
        /// Compile the given filter to run on an external (fx) xpath engine
        /// 
        internal static OpcodeBlock CompileForExternalEngine(XPathMessageFilter filter)
        { 
            // Compile...
            XPathExpression xpathExpr = QueryMatcher.fxCompiler.Compile(filter.XPath); 
 
            // Fx will bind prefixes and functions here.
            if (null != filter.namespaces) 
            {
                // 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(filter.namespaces is XsltContext) 
                {
                    // Lex the xpath to find all prefixes used 
                    XPathLexer lexer = new XPathLexer(filter.XPath, false); 
                    while (lexer.MoveNext())
                    { 
                        string prefix = lexer.Token.Prefix;

                        if (prefix.Length > 0 && filter.namespaces.LookupNamespace(prefix) == null)
                        { 
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XsltException(SR.GetString(SR.FilterUndefinedPrefix, prefix)));
                        } 
                    } 
                }
 
                xpathExpr.SetContext(filter.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, filter.XPath)));
            }

            OpcodeBlock codeBlock = new OpcodeBlock(); 
            MatchSingleFxEngineOpcode op = new MatchSingleFxEngineOpcode();
            op.XPath = xpathExpr; 
            op.Filter = filter; 

            codeBlock.Append(op); 
            return codeBlock;
        }

        ///  
        /// Compile the given filter for evaluation using the internal engine.
        ///  
        /// Caller customizes optimizations via the flags parameter 
        /// Every xpath expression has a return type
        /// The opcode block we execute to evaluate 
        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;
        }

        ///  
        /// Compile for the internal engine - use default function libraries
        ///  
        internal static OpcodeBlock CompileForInternalEngine(XPathMessageFilter filter, QueryCompilerFlags flags, out ValueDataType returnType) 
        {
            return QueryMatcher.CompileForInternalEngine(filter, flags, QueryMatcher.defaultFunctionLibs, out returnType); 
        }

        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; 
        } 

        ///  
        /// Checks the context pool for a generic navigator first. If none is available, creates a new one
        /// 
        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;
        }
 
        /// 
        /// Checks the context pool for a processor first. If none is available, creates a new one 
        ///  
        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();
                return p; 
            } 
            return new QueryProcessor(this);
        } 

        internal QueryResult Match(MessageBuffer messageBuffer, ICollection matches)
        {
            Message message = messageBuffer.CreateMessage(); 
            QueryResult result;
            try 
            { 
                result = this.Match(message, true, matches);
            } 
            finally
            {
                message.Close();
            } 

            return result; 
        } 

        internal QueryResult Match(Message message, bool matchBody, ICollection matches) 
        {
            QueryProcessor processor = this.CreateProcessor();
            processor.ResultSet = 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 QueryResult(processor); 
        }

        /// 
        /// Execute matches over the given seekable navigator. If the navigator is not safe, wrap it with one that is 
        /// 
        internal QueryResult Match(SeekableXPathNavigator navigator, ICollection 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.ResultSet = 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 QueryResult(processor); 
        }
 
        ///  
        /// Execute matches over the given navigator by wrapping it with a Seekable Navigator
        ///  
        internal QueryResult Match(XPathNavigator navigator, ICollection matches)
        {
            SeekableXPathNavigator nav = this.CreateSeekableNavigator(navigator);
            return this.Match(nav, matches); 
        }
#if FILTER_SELECT 
        internal string Select(Message message, bool selectBody) 
        {
            QueryProcessor processor = this.CreateProcessor(); 
            processor.IsSelect = true;
            processor.Eval(this.query, message, selectBody);
            string result = processor.SelectResults;
 
            this.ReleaseProcessor(processor);
            return result; 
        } 

        internal string Select(MessageBuffer messageBuffer) 
        {
            Message message = messageBuffer.CreateMessage();
            string result = null;
            try 
            {
                result = this.Select(message, true); 
            } 
            finally
            { 
                message.Close();
            }
            return result;
        } 
#endif
        ///  
        /// Release the given processor and place it back in the context pool 
        /// 
        internal void ReleaseProcessor(QueryProcessor processor) 
        {
            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(QueryResult result)
        { 
            if (null != result.Processor) 
            {
                result.Processor.ResultSet = null; 
                this.ReleaseProcessor(result.Processor);
            }
        }
 
        /// 
        /// Trim all pool 
        ///  
        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
    } 

    ///  
    /// A matcher used to evalute single XPath filters 
    /// 
    internal class XPathFilterMatcher : QueryMatcher 
    {
        XPathFilterFlags flags;
        static PushBooleanOpcode matchAlwaysFilter; // used for compiling xpaths that always match - i.e. xpath.Length == 0
        static OpcodeBlock rootFilter;        // used for compiling "/" 

        static XPathFilterMatcher() 
        { 
            XPathFilterMatcher.matchAlwaysFilter = new PushBooleanOpcode(true); //dummy
 
            ValueDataType returnType;
            XPathFilterMatcher.rootFilter = QueryMatcher.CompileForInternalEngine("/", null, QueryCompilerFlags.None, out returnType);
            XPathFilterMatcher.rootFilter.Append(new MatchQueryResultOpcode());
        } 

        internal XPathFilterMatcher() 
            : base() 
        {
            this.flags = XPathFilterFlags.None; 
        }
#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));
            } 
        }
 
        ///  
        /// 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
        /// 
        internal void Compile(XPathMessageFilter filter) 
        {
            if (null == this.query) 
            { 
                // Try to compile for the internal engine first
                try 
                {
                    this.CompileForInternal(filter);
                }
                catch (QueryCompileException) 
                {
                    // Catch and eat compilation errors. We respond to internal compilation errors by dropping down 
                    // to the framework and letting it try 
                }
                if (null == this.query) 
                {
                    // Try for an external engine that might work..
                    this.CompileForExternal(filter);
                } 
            }
        } 
 
        /// 
        /// Compile this xpath to run on an external (fx) xpath engine 
        /// 
        internal void CompileForExternal(XPathMessageFilter filter)
        {
            MatchSingleFxEngineOpcode op = (MatchSingleFxEngineOpcode)QueryMatcher.CompileForExternalEngine(filter).First; 
            this.query = op;
            this.flags |= XPathFilterFlags.IsFxFilter; 
        } 

        ///  
        /// Compile for the internal engine with default flags
        /// By defalt, we compile an xpath to run stand alone, with standard optimizations
        /// 
        internal void CompileForInternal(XPathMessageFilter filter) 
        {
            this.query = null; 
            string xpath = filter.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 = XPathFilterMatcher.matchAlwaysFilter;
                this.flags |= (XPathFilterFlags.AlwaysMatch); 
            } 
            else if (1 == xpath.Length && '/' == xpath[0])
            { 
                this.query = XPathFilterMatcher.rootFilter.First;
                this.flags |= (XPathFilterFlags.AlwaysMatch);
            }
            else 
            {
                ValueDataType returnType; 
                OpcodeBlock codeBlock = QueryMatcher.CompileForInternalEngine(filter, 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 
                codeBlock.Append(new MatchQueryResultOpcode());
                this.query = codeBlock.First;
            }
 
            this.flags &= ~XPathFilterFlags.IsFxFilter;
        } 
 
        internal QueryResult Match(MessageBuffer messageBuffer)
        { 
            Message message = messageBuffer.CreateMessage();
            QueryResult result;

            try 
            {
                result = this.Match(message, true); 
            } 
            finally
            { 
                message.Close();
            }
            return result;
        } 

        internal QueryResult 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 QueryResult(true);
            }
 
            return base.Match(message, matchBody, null);
        } 
 
        internal QueryResult 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 QueryResult(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 QueryResult(this.MatchFx(navigator));
            }
 
            return base.Match(navigator, null);
        } 
 
        internal QueryResult Match(XPathNavigator navigator)
        { 
            DiagnosticUtility.DebugAssert(null != this.query, "");
            if (this.IsAlwaysMatch)
            {
               return new QueryResult(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 QueryResult(this.MatchFx(navigator));
            }

            return base.Match(navigator, null); 
        }
 
        ///  
        /// 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 
        /// 
        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;
            } 
            DiagnosticUtility.DebugAssert(null != this.query && OpcodeID.MatchSingleFx == this.query.ID, "");
            try 
            { 
                return ((MatchSingleFxEngineOpcode)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 lastLookup; 
 
        internal InverseQueryMatcher()
            : base() 
        {
            this.elim = new SubExprEliminator();
            this.lastLookup = new Dictionary();
        } 

        internal void Add(XPathMessageFilter filter, bool forceExternal) 
        { 
            DiagnosticUtility.DebugAssert(null != filter, "");
 
            // 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(filter, QueryCompilerFlags.InverseQuery, out returnType));
                    codeBlock.Append(new MatchMultipleFilterResultOpcode(filter)); 
                    compiled = true; 

                    // Perform SubExpression Elimination 
                    codeBlock = new OpcodeBlock(this.elim.Add(filter, 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)
            {
                // Compilation failed. Try the framework
                codeBlock.Append(QueryMatcher.CompileForExternalEngine(filter)); 
            }
 
            // 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[filter] = builder.LastOpcode;
        } 

        internal void Clear() 
        { 
            foreach(XPathMessageFilter filter in this.lastLookup.Keys)
            { 
                this.Remove(this.lastLookup[filter], filter);
                this.elim.Remove(filter);
            }
            this.subExprVars = this.elim.VariableCount; 
            this.lastLookup.Clear();
        } 
 
        internal void Remove(XPathMessageFilter filter)
        { 
            DiagnosticUtility.DebugAssert(this.lastLookup.ContainsKey(filter), "");

            this.Remove(this.lastLookup[filter], filter);
            this.lastLookup.Remove(filter); 

            // Remove filter from subexpr eliminator 
            this.elim.Remove(filter); 
            this.subExprVars = this.elim.VariableCount;
        } 

        void Remove(Opcode opcode, XPathMessageFilter filter)
        {
            switch (opcode.ID) 
            {
                default: 
                    opcode.Remove(); 
                    break;
 
                case OpcodeID.MatchMultipleFilterResult:
                    ((MatchMultipleFilterResultOpcode)opcode).Remove(filter);
                    break;
            } 
        }
 
        internal override void Trim() 
        {
            base.Trim(); 
            elim.Trim();
        }
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK