2016-08-03 10:59:49 +00:00
|
|
|
//------------------------------------------------------------
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
2017-08-21 15:34:15 +00:00
|
|
|
// REFACTOR, Microsoft, make this a function on QueryValueModel
|
2016-08-03 10:59:49 +00:00
|
|
|
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
|
|
|
|
{
|
2017-08-21 15:34:15 +00:00
|
|
|
// PERF, Microsoft, see if we can cache these arrays to avoid allocations
|
2016-08-03 10:59:49 +00:00
|
|
|
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];
|
|
|
|
|
2017-08-21 15:34:15 +00:00
|
|
|
// PERF, Microsoft, this is really slow.
|
2016-08-03 10:59:49 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|