188 lines
7.5 KiB
C#
188 lines
7.5 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="CompiledXpathExpr.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</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
|
||
|
}
|