|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Dispatcher
{
using System.Collections.Generic;
using System.Runtime;
using System.Text;
using System.Xml.XPath;
using System.Xml.Xsl;
internal class FunctionCallOpcode : Opcode
{
QueryFunction function;
internal FunctionCallOpcode(QueryFunction function)
: base(OpcodeID.Function)
{
Fx.Assert(null != function, "");
this.function = function;
}
internal override bool Equals(Opcode op)
{
if (base.Equals(op))
{
FunctionCallOpcode functionCall = (FunctionCallOpcode)op;
return functionCall.function.Equals(this.function);
}
return false;
}
internal override Opcode Eval(ProcessingContext context)
{
this.function.Eval(context);
return this.next;
}
#if DEBUG_FILTER
public override string ToString()
{
return string.Format("{0} {1}", base.ToString(), this.function.ToString());
}
#endif
}
internal class XsltFunctionCallOpcode : Opcode
{
static object[] NullArgs = new object[0];
int argCount;
XsltContext xsltContext;
IXsltContextFunction function;
List<NodeSequenceIterator> iterList;
// REFACTOR, Microsoft, make this a function on QueryValueModel
internal XsltFunctionCallOpcode(XsltContext context, IXsltContextFunction function, int argCount)
: base(OpcodeID.XsltFunction)
{
Fx.Assert(null != context && null != function, "");
this.xsltContext = context;
this.function = function;
this.argCount = argCount;
for (int i = 0; i < function.Maxargs; ++i)
{
if (function.ArgTypes[i] == XPathResultType.NodeSet)
{
this.iterList = new List<NodeSequenceIterator>();
break;
}
}
// Make sure the return type is valid
switch (this.function.ReturnType)
{
case XPathResultType.String:
case XPathResultType.Number:
case XPathResultType.Boolean:
case XPathResultType.NodeSet:
break;
default:
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryCompileException(QueryCompileError.InvalidType, SR.GetString(SR.QueryFunctionTypeNotSupported, this.function.ReturnType.ToString())));
}
}
internal override bool Equals(Opcode op)
{
// We have no way of knowing if an Xslt function is stateless and can be merged
return false;
}
internal override Opcode Eval(ProcessingContext context)
{
XPathNavigator nav = context.Processor.ContextNode;
if (nav != null && context.Processor.ContextMessage != null)
{
((SeekableMessageNavigator)nav).Atomize();
}
if (this.argCount == 0)
{
context.PushFrame();
int count = context.IterationCount;
if (count > 0)
{
object ret = this.function.Invoke(this.xsltContext, NullArgs, nav);
switch (this.function.ReturnType)
{
case XPathResultType.String:
context.Push((string)ret, count);
break;
case XPathResultType.Number:
context.Push((double)ret, count);
break;
case XPathResultType.Boolean:
context.Push((bool)ret, count);
break;
case XPathResultType.NodeSet:
NodeSequence seq = context.CreateSequence();
XPathNodeIterator iter = (XPathNodeIterator)ret;
seq.Add(iter);
context.Push(seq, count);
break;
default:
// This should never be reached
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected, SR.GetString(SR.QueryFunctionTypeNotSupported, this.function.ReturnType.ToString())));
}
}
}
else
{
// PERF, Microsoft, see if we can cache these arrays to avoid allocations
object[] xsltArgs = new object[this.argCount];
int iterationCount = context.TopArg.Count;
for (int iteration = 0; iteration < iterationCount; ++iteration)
{
for (int i = 0; i < this.argCount; ++i)
{
StackFrame arg = context[i];
Fx.Assert(iteration < arg.Count, "");
switch (this.function.ArgTypes[i])
{
case XPathResultType.String:
xsltArgs[i] = context.PeekString(arg[iteration]);
break;
case XPathResultType.Number:
xsltArgs[i] = context.PeekDouble(arg[iteration]);
break;
case XPathResultType.Boolean:
xsltArgs[i] = context.PeekBoolean(arg[iteration]);
break;
case XPathResultType.NodeSet:
NodeSequenceIterator iter = new NodeSequenceIterator(context.PeekSequence(arg[iteration]));
xsltArgs[i] = iter;
this.iterList.Add(iter);
break;
default:
// This should never be reached
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected, SR.GetString(SR.QueryFunctionTypeNotSupported, this.function.ArgTypes[i].ToString())));
}
}
object ret = this.function.Invoke(this.xsltContext, xsltArgs, nav);
if (this.iterList != null)
{
for (int i = 0; i < this.iterList.Count; ++i)
{
this.iterList[i].Clear();
}
this.iterList.Clear();
}
switch (this.function.ReturnType)
{
case XPathResultType.String:
context.SetValue(context, context[this.argCount - 1][iteration], (string)ret);
break;
case XPathResultType.Number:
context.SetValue(context, context[this.argCount - 1][iteration], (double)ret);
break;
case XPathResultType.Boolean:
context.SetValue(context, context[this.argCount - 1][iteration], (bool)ret);
break;
case XPathResultType.NodeSet:
NodeSequence seq = context.CreateSequence();
XPathNodeIterator iter = (XPathNodeIterator)ret;
seq.Add(iter);
context.SetValue(context, context[this.argCount - 1][iteration], seq);
break;
default:
// This should never be reached
throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.Unexpected, SR.GetString(SR.QueryFunctionTypeNotSupported, this.function.ReturnType.ToString())));
}
}
for (int i = 0; i < this.argCount - 1; ++i)
{
context.PopFrame();
}
}
return this.next;
}
#if DEBUG_FILTER
public override string ToString()
{
return string.Format("{0} IXsltContextFunction", base.ToString());
}
#endif
}
internal enum QueryFunctionFlag
{
None = 0x00000000,
UsesContextNode = 0x00000001
}
internal abstract class QueryFunction
{
static ValueDataType[] emptyParams = new ValueDataType[0];
QueryFunctionFlag flags;
protected string name;
ValueDataType[] paramTypes;
ValueDataType returnType;
internal QueryFunction(string name, ValueDataType returnType)
: this(name, returnType, QueryFunction.emptyParams, QueryFunctionFlag.None)
{
}
internal QueryFunction(string name, ValueDataType returnType, QueryFunctionFlag flags)
: this(name, returnType, QueryFunction.emptyParams, flags)
{
}
internal QueryFunction(string name, ValueDataType returnType, ValueDataType[] paramTypes)
: this(name, returnType, paramTypes, QueryFunctionFlag.None)
{
}
internal QueryFunction(string name, ValueDataType returnType, ValueDataType[] paramTypes, QueryFunctionFlag flags)
{
Fx.Assert(null != paramTypes, "");
Fx.Assert(null != name, "");
this.name = name;
this.returnType = returnType;
this.paramTypes = paramTypes;
this.flags = flags;
}
internal ValueDataType[] ParamTypes
{
get
{
return this.paramTypes;
}
}
internal ValueDataType ReturnType
{
get
{
return this.returnType;
}
}
internal bool Bind(string name, XPathExprList args)
{
Fx.Assert(null != name && null != args, "");
if (
0 != string.CompareOrdinal(this.name, name)
|| this.paramTypes.Length != args.Count
)
{
return false;
}
return (this.paramTypes.Length == args.Count);
}
internal abstract bool Equals(QueryFunction function);
internal abstract void Eval(ProcessingContext context);
internal bool TestFlag(QueryFunctionFlag flag)
{
return (0 != (this.flags & flag));
}
#if DEBUG_FILTER
public override string ToString()
{
StringBuilder text = new StringBuilder();
text.Append(this.name);
text.Append('(');
for (int i = 0; i < this.paramTypes.Length; ++i)
{
if (i > 0)
{
text.Append(',');
}
text.Append(this.paramTypes[i].ToString());
}
text.Append(')');
return text.ToString();
}
#endif
}
internal interface IFunctionLibrary
{
QueryFunction Bind(string functionName, string functionNamespace, XPathExprList args);
}
internal enum XPathFunctionID
{
// Set
IterateSequences,
Count,
Position,
Last,
LocalName,
LocalNameDefault,
Name,
NameDefault,
NamespaceUri,
NamespaceUriDefault,
// Boolean
Boolean,
Not,
True,
False,
Lang,
// Number
Number,
NumberDefault,
Ceiling,
Floor,
Round,
Sum,
// String
String,
StringDefault,
StartsWith,
ConcatTwo,
ConcatThree,
ConcatFour,
Contains,
NormalizeSpace,
NormalizeSpaceDefault,
StringLength,
StringLengthDefault,
SubstringBefore,
SubstringAfter,
Substring,
SubstringLimit,
Translate
}
internal class XPathFunctionLibrary : IFunctionLibrary
{
static XPathFunction[] functionTable;
static XPathFunctionLibrary()
{
XPathFunctionLibrary.functionTable = new XPathFunction[] {
new XPathFunction(XPathFunctionID.Boolean, "boolean", ValueDataType.Boolean, new ValueDataType[] { ValueDataType.None }),
new XPathFunction(XPathFunctionID.False, "false", ValueDataType.Boolean),
new XPathFunction(XPathFunctionID.True, "true", ValueDataType.Boolean),
new XPathFunction(XPathFunctionID.Not, "not", ValueDataType.Boolean, new ValueDataType[] { ValueDataType.Boolean }),
new XPathFunction(XPathFunctionID.Lang, "lang", ValueDataType.Boolean, new ValueDataType[] { ValueDataType.String }),
new XPathFunction(XPathFunctionID.Number, "number", ValueDataType.Double, new ValueDataType[] { ValueDataType.None }),
new XPathFunction(XPathFunctionID.NumberDefault, "number", ValueDataType.Double),
new XPathFunction(XPathFunctionID.Sum, "sum", ValueDataType.Double, new ValueDataType[] { ValueDataType.Sequence }),
new XPathFunction(XPathFunctionID.Floor, "floor", ValueDataType.Double, new ValueDataType[] { ValueDataType.Double }),
new XPathFunction(XPathFunctionID.Ceiling, "ceiling", ValueDataType.Double, new ValueDataType[] { ValueDataType.Double }),
new XPathFunction(XPathFunctionID.Round, "round", ValueDataType.Double, new ValueDataType[] { ValueDataType.Double }),
new XPathFunction(XPathFunctionID.String, "string", ValueDataType.String, new ValueDataType[] { ValueDataType.None }),
new XPathFunction(XPathFunctionID.StringDefault, "string", ValueDataType.String, QueryFunctionFlag.UsesContextNode),
new XPathFunction(XPathFunctionID.ConcatTwo, "concat", ValueDataType.String, new ValueDataType[] { ValueDataType.String, ValueDataType.String }),
new XPathFunction(XPathFunctionID.ConcatThree, "concat", ValueDataType.String, new ValueDataType[] { ValueDataType.String, ValueDataType.String, ValueDataType.String }),
new XPathFunction(XPathFunctionID.ConcatFour, "concat", ValueDataType.String, new ValueDataType[] { ValueDataType.String, ValueDataType.String, ValueDataType.String, ValueDataType.String }),
new XPathFunction(XPathFunctionID.StartsWith, "starts-with", ValueDataType.Boolean, new ValueDataType[] { ValueDataType.String, ValueDataType.String }),
new XPathFunction(XPathFunctionID.NormalizeSpace, "normalize-space", ValueDataType.String, new ValueDataType[] { ValueDataType.String }),
new XPathFunction(XPathFunctionID.NormalizeSpaceDefault, "normalize-space", ValueDataType.String, QueryFunctionFlag.UsesContextNode),
new XPathFunction(XPathFunctionID.Contains, "contains", ValueDataType.Boolean, new ValueDataType[] { ValueDataType.String, ValueDataType.String }),
new XPathFunction(XPathFunctionID.SubstringBefore, "substring-before", ValueDataType.String, new ValueDataType[] { ValueDataType.String, ValueDataType.String }),
new XPathFunction(XPathFunctionID.SubstringAfter, "substring-after", ValueDataType.String, new ValueDataType[] { ValueDataType.String, ValueDataType.String }),
new XPathFunction(XPathFunctionID.Substring, "substring", ValueDataType.String, new ValueDataType[] { ValueDataType.String, ValueDataType.Double }),
new XPathFunction(XPathFunctionID.SubstringLimit, "substring", ValueDataType.String, new ValueDataType[] { ValueDataType.String, ValueDataType.Double, ValueDataType.Double }),
new XPathFunction(XPathFunctionID.StringLength, "string-length", ValueDataType.Double, new ValueDataType[] { ValueDataType.String }),
new XPathFunction(XPathFunctionID.StringLengthDefault, "string-length", ValueDataType.Double, QueryFunctionFlag.UsesContextNode),
new XPathFunction(XPathFunctionID.Translate, "translate", ValueDataType.String, new ValueDataType[] { ValueDataType.String, ValueDataType.String, ValueDataType.String }),
new XPathFunction(XPathFunctionID.Last, "last", ValueDataType.Double, QueryFunctionFlag.UsesContextNode),
new XPathFunction(XPathFunctionID.Position, "position", ValueDataType.Double, QueryFunctionFlag.UsesContextNode),
new XPathFunction(XPathFunctionID.Count, "count", ValueDataType.Double, new ValueDataType[] { ValueDataType.Sequence }),
new XPathFunction(XPathFunctionID.LocalName, "local-name", ValueDataType.String, new ValueDataType[] { ValueDataType.Sequence }),
new XPathFunction(XPathFunctionID.LocalNameDefault, "local-name", ValueDataType.String, QueryFunctionFlag.UsesContextNode),
new XPathFunction(XPathFunctionID.Name, "name", ValueDataType.String, new ValueDataType[] { ValueDataType.Sequence }),
new XPathFunction(XPathFunctionID.NameDefault, "name", ValueDataType.String, QueryFunctionFlag.UsesContextNode),
new XPathFunction(XPathFunctionID.NamespaceUri, "namespace-uri", ValueDataType.String, new ValueDataType[] { ValueDataType.Sequence }),
new XPathFunction(XPathFunctionID.NamespaceUriDefault, "namespace-uri", ValueDataType.String, QueryFunctionFlag.UsesContextNode)
};
}
internal XPathFunctionLibrary()
{
}
public QueryFunction Bind(string functionName, string functionNamespace, XPathExprList args)
{
Fx.Assert(null != functionName && null != args, "");
// Variable length argument list requires a special case here
if (functionName == "concat" && args.Count > 4)
{
ConcatFunction f = new ConcatFunction(args.Count);
if (f.Bind(functionName, args))
{
return f;
}
}
else
{
for (int i = 0; i < XPathFunctionLibrary.functionTable.Length; ++i)
{
// XPath functions are typeless, so don't check types
if (XPathFunctionLibrary.functionTable[i].Bind(functionName, args))
{
return XPathFunctionLibrary.functionTable[i];
}
}
}
return null;
}
}
internal class ConcatFunction : QueryFunction
{
int argCount;
internal ConcatFunction(int argCount)
: base("concat", ValueDataType.String, ConcatFunction.MakeTypes(argCount))
{
Fx.Assert(argCount >= 2, "");
this.argCount = argCount;
}
internal override bool Equals(QueryFunction function)
{
ConcatFunction f = function as ConcatFunction;
if (f != null && this.argCount == f.argCount)
{
return true;
}
return false;
}
internal override void Eval(ProcessingContext context)
{
Fx.Assert(context != null, "");
StackFrame[] args = new StackFrame[argCount];
for (int i = 0; i < this.argCount; ++i)
{
args[i] = context[i];
}
StringBuilder builder = new StringBuilder();
while (args[0].basePtr <= args[0].endPtr)
{
builder.Length = 0;
for (int i = 0; i < this.argCount; ++i)
{
builder.Append(context.PeekString(args[i].basePtr));
}
context.SetValue(context, args[this.argCount - 1].basePtr, builder.ToString());
for (int i = 0; i < this.argCount; ++i)
{
args[i].basePtr++;
}
}
for (int i = 0; i < this.argCount - 1; ++i)
{
context.PopFrame();
}
}
internal static ValueDataType[] MakeTypes(int size)
{
ValueDataType[] t = new ValueDataType[size];
for (int i = 0; i < size; ++i)
{
t[i] = ValueDataType.String;
}
return t;
}
}
internal class XPathFunction : QueryFunction
{
XPathFunctionID functionID;
internal XPathFunction(XPathFunctionID functionID, string name, ValueDataType returnType)
: base(name, returnType)
{
this.functionID = functionID;
}
internal XPathFunction(XPathFunctionID functionID, string name, ValueDataType returnType, QueryFunctionFlag flags)
: base(name, returnType, flags)
{
this.functionID = functionID;
}
internal XPathFunction(XPathFunctionID functionID, string name, ValueDataType returnType, ValueDataType[] argTypes)
: base(name, returnType, argTypes)
{
this.functionID = functionID;
}
internal XPathFunctionID ID
{
get
{
return this.functionID;
}
}
internal override bool Equals(QueryFunction function)
{
XPathFunction xpathFunction = function as XPathFunction;
if (null == xpathFunction)
{
return false;
}
return (xpathFunction.ID == this.ID);
}
static void ConvertFirstArg(ProcessingContext context, ValueDataType type)
{
StackFrame arg = context.TopArg;
Value[] values = context.Values;
while (arg.basePtr <= arg.endPtr)
{
values[arg.basePtr++].ConvertTo(context, type);
}
}
internal override void Eval(ProcessingContext context)
{
Fx.Assert(null != context, "");
switch (this.functionID)
{
default:
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.GetString(SR.QueryNotImplemented, this.name)));
case XPathFunctionID.IterateSequences:
XPathFunction.IterateAndPushSequences(context);
break;
case XPathFunctionID.Count:
XPathFunction.NodesetCount(context);
break;
case XPathFunctionID.Position:
XPathFunction.NodesetPosition(context);
break;
case XPathFunctionID.Last:
XPathFunction.NodesetLast(context);
break;
case XPathFunctionID.LocalName:
XPathFunction.NodesetLocalName(context);
break;
case XPathFunctionID.LocalNameDefault:
XPathFunction.NodesetLocalNameDefault(context);
break;
case XPathFunctionID.Name:
XPathFunction.NodesetName(context);
break;
case XPathFunctionID.NameDefault:
XPathFunction.NodesetNameDefault(context);
break;
case XPathFunctionID.NamespaceUri:
XPathFunction.NodesetNamespaceUri(context);
break;
case XPathFunctionID.NamespaceUriDefault:
XPathFunction.NodesetNamespaceUriDefault(context);
break;
case XPathFunctionID.Boolean:
XPathFunction.BooleanBoolean(context);
break;
case XPathFunctionID.False:
XPathFunction.BooleanFalse(context);
break;
case XPathFunctionID.True:
XPathFunction.BooleanTrue(context);
break;
case XPathFunctionID.Not:
XPathFunction.BooleanNot(context);
break;
case XPathFunctionID.Lang:
XPathFunction.BooleanLang(context);
break;
case XPathFunctionID.Contains:
XPathFunction.StringContains(context);
break;
case XPathFunctionID.Number:
XPathFunction.NumberNumber(context);
break;
case XPathFunctionID.NumberDefault:
XPathFunction.NumberNumberDefault(context);
break;
case XPathFunctionID.Ceiling:
XPathFunction.NumberCeiling(context);
break;
case XPathFunctionID.Floor:
XPathFunction.NumberFloor(context);
break;
case XPathFunctionID.Round:
XPathFunction.NumberRound(context);
break;
case XPathFunctionID.Sum:
XPathFunction.NumberSum(context);
break;
case XPathFunctionID.String:
XPathFunction.StringString(context);
break;
case XPathFunctionID.StringDefault:
XPathFunction.StringStringDefault(context);
break;
case XPathFunctionID.ConcatTwo:
XPathFunction.StringConcatTwo(context);
break;
case XPathFunctionID.ConcatThree:
XPathFunction.StringConcatThree(context);
break;
case XPathFunctionID.ConcatFour:
XPathFunction.StringConcatFour(context);
break;
case XPathFunctionID.StartsWith:
XPathFunction.StringStartsWith(context);
break;
case XPathFunctionID.StringLength:
XPathFunction.StringLength(context);
break;
case XPathFunctionID.StringLengthDefault:
XPathFunction.StringLengthDefault(context);
break;
case XPathFunctionID.SubstringBefore:
XPathFunction.SubstringBefore(context);
break;
case XPathFunctionID.SubstringAfter:
XPathFunction.SubstringAfter(context);
break;
case XPathFunctionID.Substring:
XPathFunction.Substring(context);
break;
case XPathFunctionID.SubstringLimit:
XPathFunction.SubstringLimit(context);
break;
case XPathFunctionID.Translate:
XPathFunction.Translate(context);
break;
case XPathFunctionID.NormalizeSpace:
XPathFunction.NormalizeSpace(context);
break;
case XPathFunctionID.NormalizeSpaceDefault:
XPathFunction.NormalizeSpaceDefault(context);
break;
}
}
internal static void BooleanBoolean(ProcessingContext context)
{
StackFrame arg = context.TopArg;
Value[] values = context.Values;
while (arg.basePtr <= arg.endPtr)
{
values[arg.basePtr++].ConvertTo(context, ValueDataType.Boolean);
}
}
internal static void BooleanFalse(ProcessingContext context)
{
context.PushFrame();
int count = context.IterationCount;
if (count > 0)
{
context.Push(false, count);
}
}
internal static void BooleanNot(ProcessingContext context)
{
StackFrame arg = context.TopArg;
Value[] values = context.Values;
while (arg.basePtr <= arg.endPtr)
{
values[arg.basePtr++].Not();
}
}
internal static void BooleanTrue(ProcessingContext context)
{
context.PushFrame();
int count = context.IterationCount;
if (count > 0)
{
context.Push(true, count);
}
}
internal static void BooleanLang(ProcessingContext context)
{
StackFrame langArg = context.TopArg;
StackFrame sequences = context.TopSequenceArg;
Value[] sequenceBuffer = context.Sequences;
while (sequences.basePtr <= sequences.endPtr)
{
NodeSequence sourceSeq = sequenceBuffer[sequences.basePtr++].Sequence;
for (int item = 0; item < sourceSeq.Count; ++item)
{
string lang = context.PeekString(langArg.basePtr).ToUpperInvariant();
QueryNode node = sourceSeq.Items[item].Node;
long pos = node.Node.CurrentPosition;
node.Node.CurrentPosition = node.Position;
string docLang = node.Node.XmlLang.ToUpperInvariant();
node.Node.CurrentPosition = pos;
if (lang.Length == docLang.Length && string.CompareOrdinal(lang, docLang) == 0)
{
context.SetValue(context, langArg.basePtr++, true);
}
else if (docLang.Length > 0 && lang.Length < docLang.Length && docLang.StartsWith(lang, StringComparison.Ordinal) && docLang[lang.Length] == '-')
{
context.SetValue(context, langArg.basePtr++, true);
}
else
{
context.SetValue(context, langArg.basePtr++, false);
}
}
sequences.basePtr++;
}
}
internal static void IterateAndPushSequences(ProcessingContext context)
{
StackFrame sequences = context.TopSequenceArg;
Value[] sequenceBuffer = context.Sequences;
context.PushFrame();
while (sequences.basePtr <= sequences.endPtr)
{
NodeSequence sourceSeq = sequenceBuffer[sequences.basePtr++].Sequence;
int count = sourceSeq.Count;
if (count == 0)
{
context.PushSequence(NodeSequence.Empty);
}
else
{
for (int item = 0; item < sourceSeq.Count; ++item)
{
NodeSequence newSequence = context.CreateSequence();
newSequence.StartNodeset();
newSequence.Add(ref sourceSeq.Items[item]);
newSequence.StopNodeset();
context.Push(newSequence);
}
}
}
}
internal static void NodesetCount(ProcessingContext context)
{
StackFrame arg = context.TopArg;
while (arg.basePtr <= arg.endPtr)
{
context.SetValue(context, arg.basePtr, context.PeekSequence(arg.basePtr).Count);
arg.basePtr++;
}
}
internal static void NodesetLast(ProcessingContext context)
{
context.TransferSequenceSize();
}
internal static void NodesetLocalName(ProcessingContext context)
{
StackFrame arg = context.TopArg;
while (arg.basePtr <= arg.endPtr)
{
NodeSequence sequence = context.PeekSequence(arg.basePtr);
context.SetValue(context, arg.basePtr, sequence.LocalName);
arg.basePtr++;
}
}
internal static void NodesetLocalNameDefault(ProcessingContext context)
{
XPathFunction.IterateAndPushSequences(context);
XPathFunction.NodesetLocalName(context);
}
internal static void NodesetName(ProcessingContext context)
{
StackFrame arg = context.TopArg;
while (arg.basePtr <= arg.endPtr)
{
NodeSequence sequence = context.PeekSequence(arg.basePtr);
context.SetValue(context, arg.basePtr, sequence.Name);
arg.basePtr++;
}
}
internal static void NodesetNameDefault(ProcessingContext context)
{
XPathFunction.IterateAndPushSequences(context);
XPathFunction.NodesetName(context);
}
internal static void NodesetNamespaceUri(ProcessingContext context)
{
StackFrame arg = context.TopArg;
while (arg.basePtr <= arg.endPtr)
{
NodeSequence sequence = context.PeekSequence(arg.basePtr);
context.SetValue(context, arg.basePtr, sequence.Namespace);
arg.basePtr++;
}
}
internal static void NodesetNamespaceUriDefault(ProcessingContext context)
{
XPathFunction.IterateAndPushSequences(context);
XPathFunction.NodesetNamespaceUri(context);
}
internal static void NodesetPosition(ProcessingContext context)
{
context.TransferSequencePositions();
}
internal static void NumberCeiling(ProcessingContext context)
{
StackFrame arg = context.TopArg;
while (arg.basePtr <= arg.endPtr)
{
context.SetValue(context, arg.basePtr, Math.Ceiling(context.PeekDouble(arg.basePtr)));
arg.basePtr++;
}
}
internal static void NumberNumber(ProcessingContext context)
{
StackFrame arg = context.TopArg;
Value[] values = context.Values;
while (arg.basePtr <= arg.endPtr)
{
values[arg.basePtr++].ConvertTo(context, ValueDataType.Double);
}
}
internal static void NumberNumberDefault(ProcessingContext context)
{
XPathFunction.IterateAndPushSequences(context);
XPathFunction.NumberNumber(context);
}
internal static void NumberFloor(ProcessingContext context)
{
StackFrame arg = context.TopArg;
while (arg.basePtr <= arg.endPtr)
{
context.SetValue(context, arg.basePtr, Math.Floor(context.PeekDouble(arg.basePtr)));
arg.basePtr++;
}
}
internal static void NumberRound(ProcessingContext context)
{
StackFrame arg = context.TopArg;
while (arg.basePtr <= arg.endPtr)
{
double val = context.PeekDouble(arg.basePtr);
context.SetValue(context, arg.basePtr, QueryValueModel.Round(context.PeekDouble(arg.basePtr)));
arg.basePtr++;
}
}
internal static void NumberSum(ProcessingContext context)
{
StackFrame arg = context.TopArg;
while (arg.basePtr <= arg.endPtr)
{
NodeSequence sequence = context.PeekSequence(arg.basePtr);
double sum = 0.0;
for (int item = 0; item < sequence.Count; ++item)
{
sum += QueryValueModel.Double(sequence[item].StringValue());
}
context.SetValue(context, arg.basePtr, sum);
arg.basePtr++;
}
}
internal static void StringString(ProcessingContext context)
{
StackFrame arg = context.TopArg;
Value[] values = context.Values;
while (arg.basePtr <= arg.endPtr)
{
values[arg.basePtr++].ConvertTo(context, ValueDataType.String);
}
}
internal static void StringStringDefault(ProcessingContext context)
{
XPathFunction.IterateAndPushSequences(context);
XPathFunction.StringString(context);
}
internal static void StringConcatTwo(ProcessingContext context)
{
StackFrame arg1 = context[0];
StackFrame arg2 = context[1];
while (arg1.basePtr <= arg1.endPtr)
{
string str1 = context.PeekString(arg1.basePtr);
string str2 = context.PeekString(arg2.basePtr);
context.SetValue(context, arg2.basePtr, str1 + str2);
arg1.basePtr++;
arg2.basePtr++;
}
context.PopFrame();
}
internal static void StringConcatThree(ProcessingContext context)
{
StackFrame arg1 = context[0];
StackFrame arg2 = context[1];
StackFrame arg3 = context[2];
while (arg1.basePtr <= arg1.endPtr)
{
string str1 = context.PeekString(arg1.basePtr);
string str2 = context.PeekString(arg2.basePtr);
string str3 = context.PeekString(arg3.basePtr);
context.SetValue(context, arg3.basePtr, str1 + str2 + str3);
arg1.basePtr++;
arg2.basePtr++;
arg3.basePtr++;
}
context.PopFrame();
context.PopFrame();
}
internal static void StringConcatFour(ProcessingContext context)
{
StackFrame arg1 = context[0];
StackFrame arg2 = context[1];
StackFrame arg3 = context[2];
StackFrame arg4 = context[3];
while (arg1.basePtr <= arg1.endPtr)
{
string str1 = context.PeekString(arg1.basePtr);
string str2 = context.PeekString(arg2.basePtr);
string str3 = context.PeekString(arg3.basePtr);
string str4 = context.PeekString(arg4.basePtr);
context.SetValue(context, arg4.basePtr, str1 + str2 + str3 + str4);
arg1.basePtr++;
arg2.basePtr++;
arg3.basePtr++;
arg4.basePtr++;
}
context.PopFrame();
context.PopFrame();
context.PopFrame();
}
internal static void StringContains(ProcessingContext context)
{
StackFrame arg1 = context.TopArg;
StackFrame arg2 = context.SecondArg;
Fx.Assert(arg1.Count == arg2.Count, "");
while (arg1.basePtr <= arg1.endPtr)
{
string leftString = context.PeekString(arg1.basePtr);
string rightString = context.PeekString(arg2.basePtr);
context.SetValue(context, arg2.basePtr, (-1 != leftString.IndexOf(rightString, StringComparison.Ordinal)));
arg1.basePtr++;
arg2.basePtr++;
}
context.PopFrame();
}
internal static void StringLength(ProcessingContext context)
{
StackFrame arg = context.TopArg;
while (arg.basePtr <= arg.endPtr)
{
context.SetValue(context, arg.basePtr, context.PeekString(arg.basePtr).Length);
arg.basePtr++;
}
}
internal static void StringLengthDefault(ProcessingContext context)
{
XPathFunction.IterateAndPushSequences(context);
XPathFunction.ConvertFirstArg(context, ValueDataType.String);
XPathFunction.StringLength(context);
}
internal static void StringStartsWith(ProcessingContext context)
{
StackFrame arg1 = context.TopArg;
StackFrame arg2 = context.SecondArg;
Fx.Assert(arg1.Count == arg2.Count, "");
while (arg1.basePtr <= arg1.endPtr)
{
string leftString = context.PeekString(arg1.basePtr);
string rightString = context.PeekString(arg2.basePtr);
context.SetValue(context, arg2.basePtr, leftString.StartsWith(rightString, StringComparison.Ordinal));
arg1.basePtr++;
arg2.basePtr++;
}
context.PopFrame();
}
internal static void SubstringBefore(ProcessingContext context)
{
StackFrame arg1 = context.TopArg;
StackFrame arg2 = context.SecondArg;
Fx.Assert(arg1.Count == arg2.Count, "");
while (arg1.basePtr <= arg1.endPtr)
{
string str1 = context.PeekString(arg1.basePtr);
string str2 = context.PeekString(arg2.basePtr);
int idx = str1.IndexOf(str2, StringComparison.Ordinal);
context.SetValue(context, arg2.basePtr, idx == -1 ? string.Empty : str1.Substring(0, idx));
arg1.basePtr++;
arg2.basePtr++;
}
context.PopFrame();
}
internal static void SubstringAfter(ProcessingContext context)
{
StackFrame arg1 = context.TopArg;
StackFrame arg2 = context.SecondArg;
Fx.Assert(arg1.Count == arg2.Count, "");
while (arg1.basePtr <= arg1.endPtr)
{
string str1 = context.PeekString(arg1.basePtr);
string str2 = context.PeekString(arg2.basePtr);
int idx = str1.IndexOf(str2, StringComparison.Ordinal);
context.SetValue(context, arg2.basePtr, idx == -1 ? string.Empty : str1.Substring(idx + str2.Length));
arg1.basePtr++;
arg2.basePtr++;
}
context.PopFrame();
}
internal static void Substring(ProcessingContext context)
{
StackFrame arg1 = context.TopArg;
StackFrame arg2 = context.SecondArg;
Fx.Assert(arg1.Count == arg2.Count, "");
while (arg1.basePtr <= arg1.endPtr)
{
string str = context.PeekString(arg1.basePtr);
int startAt = ((int)Math.Round(context.PeekDouble(arg2.basePtr))) - 1;
if (startAt < 0)
{
startAt = 0;
}
context.SetValue(context, arg2.basePtr, (startAt >= str.Length) ? string.Empty : str.Substring(startAt));
arg1.basePtr++;
arg2.basePtr++;
}
context.PopFrame();
}
internal static void SubstringLimit(ProcessingContext context)
{
StackFrame argString = context.TopArg;
StackFrame argStartAt = context.SecondArg;
StackFrame argLimit = context[2];
Fx.Assert(argString.Count == argStartAt.Count, "");
Fx.Assert(argString.Count == argLimit.Count, "");
while (argString.basePtr <= argString.endPtr)
{
string str = context.PeekString(argString.basePtr);
int startAt = ((int)Math.Round(context.PeekDouble(argStartAt.basePtr))) - 1;
if (startAt < 0)
{
startAt = 0;
}
int length = (int)Math.Round(context.PeekDouble(argLimit.basePtr));
string substr;
if (length < 1 || ((startAt + length) >= str.Length))
{
substr = string.Empty;
}
else
{
substr = str.Substring(startAt, length);
}
context.SetValue(context, argLimit.basePtr, substr);
argStartAt.basePtr++;
argString.basePtr++;
argLimit.basePtr++;
}
context.PopFrame();
context.PopFrame();
}
internal static void Translate(ProcessingContext context)
{
StackFrame argSource = context.TopArg;
StackFrame argKeys = context.SecondArg;
StackFrame argValues = context[2];
// PERF, Microsoft, this is really slow.
StringBuilder builder = new StringBuilder();
while (argSource.basePtr <= argSource.endPtr)
{
builder.Length = 0;
string source = context.PeekString(argSource.basePtr);
string keys = context.PeekString(argKeys.basePtr);
string values = context.PeekString(argValues.basePtr);
for (int i = 0; i < source.Length; ++i)
{
char c = source[i];
int idx = keys.IndexOf(c);
if (idx < 0)
{
builder.Append(c);
}
else if (idx < values.Length)
{
builder.Append(values[idx]);
}
}
context.SetValue(context, argValues.basePtr, builder.ToString());
argSource.basePtr++;
argKeys.basePtr++;
argValues.basePtr++;
}
context.PopFrame();
context.PopFrame();
}
internal static void NormalizeSpace(ProcessingContext context)
{
StackFrame argStr = context.TopArg;
StringBuilder builder = new StringBuilder();
while (argStr.basePtr <= argStr.endPtr)
{
char[] whitespace = new char[] { ' ', '\t', '\r', '\n' };
string str = context.PeekString(argStr.basePtr).Trim(whitespace);
bool eatingWhitespace = false;
builder.Length = 0;
for (int i = 0; i < str.Length; ++i)
{
char c = str[i];
if (XPathCharTypes.IsWhitespace(c))
{
if (!eatingWhitespace)
{
builder.Append(' ');
eatingWhitespace = true;
}
}
else
{
builder.Append(c);
eatingWhitespace = false;
}
}
context.SetValue(context, argStr.basePtr, builder.ToString());
argStr.basePtr++;
}
}
internal static void NormalizeSpaceDefault(ProcessingContext context)
{
XPathFunction.IterateAndPushSequences(context);
XPathFunction.ConvertFirstArg(context, ValueDataType.String);
XPathFunction.NormalizeSpace(context);
}
#if NO
internal static bool IsWhitespace(char c)
{
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
}
#endif
}
}
|