File: System\Xml\XPath\Internal\CompiledXPathExpr.cs
Project: ndp\fx\src\Xml\System.Xml.csproj (System.Xml)
//------------------------------------------------------------------------------
// <copyright file="CompiledXpathExpr.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
 
namespace MS.Internal.Xml.XPath {
    using System;
    using System.Xml;
    using System.Xml.XPath;
    using System.Diagnostics;
    using System.Globalization;
    using System.Collections;
    using System.Xml.Xsl;
 
    internal class CompiledXpathExpr : XPathExpression {
        Query query;
        string expr;
        bool needContext;
 
        internal CompiledXpathExpr(Query query, string expression, bool needContext) {
            this.query = query;
            this.expr = expression;
            this.needContext = needContext;
        }
 
        internal Query QueryTree {
            get {
                if (needContext) {
                    throw XPathException.Create(Res.Xp_NoContext);
                }
                return query;
            }
        }
 
        public override string Expression {
            get { return expr; }
        }
 
        public virtual void CheckErrors() {
            Debug.Assert(query != null, "In case of error in XPath we create ErrorXPathExpression");
        }
 
        public override void AddSort(object expr, IComparer comparer) {
            // sort makes sense only when we are dealing with a query that
            // returns a nodeset.
	        Query evalExpr;
            if (expr is string) {
                evalExpr = new QueryBuilder().Build((string)expr, out needContext); // this will throw if expr is invalid
            } else if (expr is CompiledXpathExpr) {
                evalExpr = ((CompiledXpathExpr)expr).QueryTree;
	        } else {
                throw XPathException.Create(Res.Xp_BadQueryObject);
            }
            SortQuery sortQuery = query as SortQuery;
            if (sortQuery == null) {
                query = sortQuery = new SortQuery(query);
            }
            sortQuery.AddSort(evalExpr, comparer);
        }
 
        public override void AddSort(object expr, XmlSortOrder order, XmlCaseOrder caseOrder, string lang, XmlDataType dataType) {
            AddSort(expr, new XPathComparerHelper(order, caseOrder, lang, dataType));
        }
 
        public override XPathExpression Clone() {
            return new CompiledXpathExpr(Query.Clone(query), expr, needContext);
        }
 
        public override void SetContext(XmlNamespaceManager nsManager) {
            SetContext((IXmlNamespaceResolver)nsManager);
        }
 
        public override void SetContext(IXmlNamespaceResolver nsResolver) {
            XsltContext xsltContext = nsResolver as XsltContext;
            if(xsltContext == null) {
                if(nsResolver == null) {
                    nsResolver = new XmlNamespaceManager(new NameTable());
                }
                xsltContext = new UndefinedXsltContext(nsResolver);
            }
            query.SetXsltContext(xsltContext);
 
            needContext = false;
        }
 
        public override XPathResultType ReturnType { get { return query.StaticType; } }
 
        private class UndefinedXsltContext : XsltContext {
            private IXmlNamespaceResolver nsResolver;
 
            public UndefinedXsltContext(IXmlNamespaceResolver nsResolver) : base(/*dummy*/false) {
                this.nsResolver = nsResolver;
            }
            //----- Namespace support -----
            public override string DefaultNamespace {
                get { return string.Empty; }
            }
            public override string LookupNamespace(string prefix) {
                Debug.Assert(prefix != null);
                if (prefix.Length == 0) {
                    return string.Empty;
                }
                string ns = this.nsResolver.LookupNamespace(prefix);
                if (ns == null) {
                    throw XPathException.Create(Res.XmlUndefinedAlias, prefix);
                }
                return ns;
            }
            //----- XsltContext support -----
            public override IXsltContextVariable ResolveVariable(string prefix, string name) {
                throw XPathException.Create(Res.Xp_UndefinedXsltContext);
            }
            public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] ArgTypes) {
                throw XPathException.Create(Res.Xp_UndefinedXsltContext);
            }
            public override bool Whitespace { get{ return false; } }
            public override bool PreserveWhitespace(XPathNavigator node) { return false; }
            public override int CompareDocument (string baseUri, string nextbaseUri) {
                return string.CompareOrdinal(baseUri, nextbaseUri);
            }
        }
    }
 
    internal sealed class XPathComparerHelper : IComparer {
        private XmlSortOrder order;
        private XmlCaseOrder caseOrder;
        private CultureInfo  cinfo;
        private XmlDataType  dataType;
 
        public XPathComparerHelper(XmlSortOrder order, XmlCaseOrder caseOrder, string lang, XmlDataType dataType) {
            if (lang == null) {
                this.cinfo = System.Threading.Thread.CurrentThread.CurrentCulture;
            } else {
                try {
                    this.cinfo = new CultureInfo(lang);
                }
                catch (System.ArgumentException) {
                    throw;  // Throwing an XsltException would be a breaking change
				}
            }
 
            if (order == XmlSortOrder.Descending) {
                if (caseOrder == XmlCaseOrder.LowerFirst) {
                    caseOrder = XmlCaseOrder.UpperFirst;
                }
                else if (caseOrder == XmlCaseOrder.UpperFirst) {
                    caseOrder = XmlCaseOrder.LowerFirst;
                }
            }
 
            this.order     = order;
            this.caseOrder = caseOrder;
            this.dataType  = dataType;
        }
 
        public int Compare(object x, object y) {
            switch (this.dataType) {
            case XmlDataType.Text:
                string s1 = Convert.ToString(x, this.cinfo);
                string s2 = Convert.ToString(y, this.cinfo);
                int result = string.Compare(s1, s2, /*ignoreCase:*/ this.caseOrder != XmlCaseOrder.None, this.cinfo);
 
                if (result != 0 || this.caseOrder == XmlCaseOrder.None)
                    return (this.order == XmlSortOrder.Ascending) ? result : -result;
 
                // If we came this far, it means that strings s1 and s2 are
                // equal to each other when case is ignored. Now it's time to check
                // and see if they differ in case only and take into account the user
                // requested case order for sorting purposes.
                result = string.Compare(s1, s2, /*ignoreCase:*/ false, this.cinfo);
                return (this.caseOrder == XmlCaseOrder.LowerFirst) ? result : -result;
 
            case XmlDataType.Number:
                double r1 = XmlConvert.ToXPathDouble(x);
                double r2 = XmlConvert.ToXPathDouble(y);
                result = r1.CompareTo(r2);
                return (this.order == XmlSortOrder.Ascending) ? result : -result;
 
            default:
                // dataType doesn't support any other value
                throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
            }
        } // Compare ()
    } // class XPathComparerHelper
}