StaticContext.Processor in SaxonCS ExtensionFunctionCall: only available in XPath but not XSLT or XQuery?
Added by Martin Honnen over 2 years ago
With SaxonCS 11.4.1, I have some extension functions that need to access the current Processor
object in its Call
method, I thought the way to do that would be by storing e.g.
public class GrammarUriFunctionCall : ExtensionFunctionCall
{
Processor processor;
public override void SupplyStaticContext(StaticContext context)
{
base.SupplyStaticContext(context);
processor = context.Processor;
}
...
but when I do that my extension function works fine in XPath but not in XQuery or XSLT, there I am told processor
is null.
System.NullReferenceException
HResult=0x80004003
Nachricht = Object reference not set to an instance of an object.
Quelle = CoffeeSacksNETLib
Stapelüberwachung:
bei CoffeeSacksNETLib.GrammarUriFunctionCall.Call(XdmValue[] arguments, DynamicContext context) in C:\Users\marti\source\repos\CoffeeSacksNETLib\CoffeeSacksNETLib\GrammarUriFunctionCall.cs: Zeile16
The DynamicContext https://www.saxonica.com/html/documentation11/dotnetdoc/Saxon/Api/DynamicContext.html doesn't have a Processor
property and its Implementation
leads to lower level Saxon APIs.
Is the above null
expected for XSLT/XQuery?
What is the right way in an extension function called from XQuery or XSLT to access the Processor
in SaxonCS?
using Saxon.Api;
using org.nineml.coffeefilter;
namespace CoffeeSacksNETLib
{
public class GrammarUriFunctionCall : ExtensionFunctionCall
{
Processor processor;
public override XdmValue Call(XdmValue[] arguments, DynamicContext context)
{
string uri = arguments[0][0].StringValue;
InvisibleXmlParser parser = new InvisibleXml().getParser(new java.net.URI(uri));
if (parser.constructed())
{
return processor.NewDocumentBuilder().Build(new StringReader(parser.getCompiledParser()));
}
else
{
return processor.NewDocumentBuilder().Build(new StringReader(parser.getFailedParse().getTree()));
}
}
public override void SupplyStaticContext(StaticContext context)
{
base.SupplyStaticContext(context);
processor = context.Processor;
}
}
}
Replies (7)
Please register to reply
RE: StaticContext.Processor in SaxonCS ExtensionFunctionCall: only available in XPath but not XSLT or XQuery? - Added by Martin Honnen over 2 years ago
Found a way to get at the Processor
from the dynamic context:
if (processor == null)
{
processor = context.Implementation.getConfiguration().getProcessor() as Processor;
}
Still wondering why the first approach with the static context works with XPath only but not XSLT or XQuery.
RE: StaticContext.Processor in SaxonCS ExtensionFunctionCall: only available in XPath but not XSLT or XQuery? - Added by Michael Kay over 2 years ago
We've got a unit test that is doing this from XSLT and it's working just fine.
My guess would be that Configuration.getProcessor()
for some reason is set to the underlying Saxon.Hej.a9api.Processor
, in which case the StaticContext.Processor property will be returned as null. But I don't know why that would happen.
RE: StaticContext.Processor in SaxonCS ExtensionFunctionCall: only available in XPath but not XSLT or XQuery? - Added by Martin Honnen over 2 years ago
I have tried to isolate the problem in a small test project but there it didn't occur, the Processor
from the StaticContext
, captured in SupplyStaticContext
, seemed to be available both in XPath and XQuery.
But now have tried to proceed with the main project and have also tried to store e.g.
private Uri baseUri;
public override void SupplyStaticContext(StaticContext context)
{
base.SupplyStaticContext(context);
processor = context.Processor;
baseUri = context.BaseUri;
}
to use the baseUri
field in the Call
as well, to run into the same problem, now debugged in more details: the SupplyStaticContext
runs and sets the processor
and baseUri
fields correctly but when the Call
method is called it seems to be called on a different object instance which has the fields as null
values.
Now on reading up on https://www.saxonica.com/html/documentation11/dotnetdoc/Saxon/Api/ExtensionFunctionDefinition.html#DependsOnFocus saying
It should also return true (despite the property name) if the function makes use of parts of the static context that vary from one part of the query or stylesheet to another. Setting the property to true inhibits certain Saxon optimizations, such as extracting the call from a loop, or moving it into a global variable.
and https://www.saxonica.com/html/documentation11/dotnetdoc/Saxon/Api/ExtensionFunctionCall.html#CopyLocalData(ExtensionFunctionCall) I guess I have to do some more work on the function.
RE: StaticContext.Processor in SaxonCS ExtensionFunctionCall: only available in XPath but not XSLT or XQuery? - Added by Martin Honnen over 2 years ago
I am now trying e.g.
public override void CopyLocalData(ExtensionFunctionCall destination)
{
base.CopyLocalData(destination);
var destinationCasted = (GrammarUriFunctionCall)destination;
destinationCasted.baseUri = baseUri;
destinationCasted.processor = processor;
}
but a breakpoint inside of that CopyLocalData
method is never hit while the Call
method is run on a different instance than the one where SupplyStaticContext
set the baseUri
and processor
fields.
I need to look at that with some rest, I guess, so far it looks as if I haven't understood what the functions are supposed to do.
RE: StaticContext.Processor in SaxonCS ExtensionFunctionCall: only available in XPath but not XSLT or XQuery? - Added by Martin Honnen over 2 years ago
I have now reproduced the problem in my smaller sample project and have a test case that fails, the processor
is null:
System.NullReferenceException : Object reference not set to an instance of an object.
Stapelüberwachung:
Example1FunctionCall.Call(XdmValue[] arguments, DynamicContext context) Zeile 13
ExtensionFunctionCallWrapper.call(XPathContext context, Sequence[] arguments)
IntegratedFunctionCall.iterate(XPathContext context)
Expression.evaluateItem(XPathContext context)
SimpleStepExpression.iterate(XPathContext context)
FilterExpression.iterate(XPathContext context)
Expression.process(Outputter output, XPathContext context)
ElementCreator.processLeavingTail(Outputter out, XPathContext context, NodeInfo copiedNode)
ElementCreator.processLeavingTail(Outputter output, XPathContext context)
Instruction.process(Outputter output, XPathContext context)
ExpressionTool.getIteratorFromProcessMethod(Expression exp, XPathContext context)
Instruction.iterate(XPathContext context)
XQueryExpression.iterator(DynamicQueryContext env)
XQueryEvaluator.evaluateSingle()
XQueryEvaluator.EvaluateSingle()
Tests.XQueryTest3() Zeile 65
I need some advice whether the code of the extension function is still incomplete and that CopyLocalData
is wrong or an additional function or property needs to be set or whether that is some quirk with SaxonCS.
Ignore the fact that the sample function is duplicating existing fn:parse-xml
functionality, in my real use case I need the processor to create a DocumentBuilder
and later the baseUri from the static context and can't access them with the values being null although the SupplyStaticContext
initializes them and the CopyLocalData
copies them (but doesn't seem to be called).
Test case:
using Saxon.Api;
using SaxonCSExtensionStaticContextTestLib;
namespace SaxonCSExtensionStaticContextTests
{
public class Tests
{
static Processor processor;
[SetUp]
public void Setup()
{
processor = new();
processor.RegisterExtensionFunction(new Example1Function());
}
//[snip]
[Test]
public void XQueryTest3()
{
var xqueryCompiler = processor.NewXQueryCompiler();
var xqueryCode = @"
declare namespace mf = 'http://example.com/mf';
let $doc1 := mf:parse-xml('<root><item>a</item><item>b</item><item>1</item></root>')
return
<root>{ $doc1//item[matches(., '\p{L}+')] }</root>
";
var xquery = xqueryCompiler.Compile(xqueryCode).Load();
var result = xquery.EvaluateSingle();
Assert.IsInstanceOf<XdmNode>(result);
}
}
}
Example extension function code
using Saxon.Api;
namespace SaxonCSExtensionStaticContextTestLib
{
public class Example1Function : ExtensionFunctionDefinition
{
public override QName FunctionName => new QName("http://example.com/mf", "parse-xml");
public override int MinimumNumberOfArguments => 1;
public override int MaximumNumberOfArguments => 1;
public override XdmSequenceType[] ArgumentTypes => new XdmSequenceType[] {
new XdmSequenceType(XdmAtomicType.BuiltInAtomicType(QName.XS_STRING), XdmSequenceType.ONE)
};
public override ExtensionFunctionCall MakeFunctionCall()
{
return new Example1FunctionCall();
}
public override XdmSequenceType ResultType(XdmSequenceType[] ArgumentTypes)
{
return new XdmSequenceType(XdmAnyNodeType.Instance, XdmSequenceType.ONE);
}
}
}
using Saxon.Api;
namespace SaxonCSExtensionStaticContextTestLib
{
public class Example1FunctionCall : ExtensionFunctionCall
{
Processor processor;
public override XdmValue Call(XdmValue[] arguments, DynamicContext context)
{
string inputString = arguments[0][0].StringValue;
return processor.NewDocumentBuilder().Build(new StringReader(inputString));
}
public override void CopyLocalData(ExtensionFunctionCall destination)
{
base.CopyLocalData(destination);
var castedDestination = (Example1FunctionCall)destination;
castedDestination.processor = processor;
}
public override void SupplyStaticContext(StaticContext context)
{
base.SupplyStaticContext(context);
processor = context.Processor;
}
}
}
Example1Function.cs (864 Bytes) Example1Function.cs | |||
Example1FunctionCall.cs (909 Bytes) Example1FunctionCall.cs |
RE: StaticContext.Processor in SaxonCS ExtensionFunctionCall: only available in XPath but not XSLT or XQuery? - Added by Martin Honnen over 2 years ago
Further debugging of the sample .NET projects shows that the CopyLocalData
doesn't seem to be called at all but the Call()
call is done on a separate instance than the SupplyStaticContext()
happened on.
I have tried a similar example with SaxonJ EE 11.4 and run it through the debugger, the the copyLocalData
is called (and that way the code works as wanted).
Some current summary is that SaxonCS fails to call CopyLocalData
after creating a new ExtensionFunctionCall
of some concrete subclass implementation.
RE: StaticContext.Processor in SaxonCS ExtensionFunctionCall: only available in XPath but not XSLT or XQuery? - Added by Martin Honnen over 2 years ago
Is that class ExtensionFunctionCallWrapper
in https://saxonica.plan.io/projects/saxonmirrorhe/repository/he/revisions/he_mirror_saxon_11_4/entry/src/main/saxon-cs/engine/Saxon/Api/Support/ExtensionFunctionDefinitionWrapper.cs#L52 simply lacking a method
public override void copyLocalData(..) {
apiCall.CopyLocalData(..);
}
?
Please register to reply