Project

Profile

Help

Bug #4219

StreamingTransformerImpl fails to run xsl but same xsl works on commandline

Added by Stefan Fritz 4 months ago. Updated 3 months ago.

Status:
Resolved
Priority:
Normal
Assignee:
Category:
JAXP Java API
Sprint/Milestone:
-
Start date:
2019-05-14
Due date:
% Done:

0%

Legacy ID:
Applies to branch:
9.8
Fix Committed on Branch:
9.8
Fixed in Maintenance Release:

Description

Saxon EE 9.8.0.15

XSL runs fine via commandline (see working.cmd) but fails with below error when XSL is run via StreamingTransformerImpl (see fails.cmd)

XSL error:

SAXON parallel threads per test: 1 Warning at char 1 in xsl:template/@match on line 24 column 178 of CleanUp.xsl: XPTY0004: An error occurred matching pattern {element()[string-length(zero-or-one(convertUntyped(data(child::text())))) eq 0]}: A sequence of more than one item is not allowed as the first argument of fn:string-length() ("", "") Type error at char 22 in xsl:value-of/@select on line 16 column 77 of CleanUp.xsl: XPTY0004: A sequence of more than one item is not allowed as the first argument of fn:replace() ("", "") in built-in template rule for /test in mode cleanup at xsl:apply-templates (file:/C:/dev/workspace/java/SaxonEETests2/./resources/Relationship_EBI.xsl#15) processing / A sequence of more than one item is not allowed as the first argument of fn:replace() ("", "") xsl done

Java stack:

Caused by: net.sf.saxon.trans.XPathException: A sequence of more than one item is not allowed as the first argument of fn:replace() ("", "") at net.sf.saxon.expr.Expression.typeError(Expression.java:1455) at net.sf.saxon.expr.CardinalityChecker.evaluateItem(CardinalityChecker.java:297) at net.sf.saxon.expr.parser.Evaluator$4.evaluate(Evaluator.java:85) at net.sf.saxon.expr.SystemFunctionCall.evaluateArguments(SystemFunctionCall.java:451) at net.sf.saxon.expr.FunctionCall.iterate(FunctionCall.java:545) at net.sf.saxon.expr.Expression.evaluateItem(Expression.java:849) at net.sf.saxon.expr.parser.Evaluator$4.evaluate(Evaluator.java:85) at net.sf.saxon.expr.SystemFunctionCall.evaluateArguments(SystemFunctionCall.java:451) at net.sf.saxon.expr.FunctionCall.iterate(FunctionCall.java:545) at net.sf.saxon.expr.Expression.evaluateItem(Expression.java:849) at net.sf.saxon.expr.Expression.evaluateAsString(Expression.java:908) at net.sf.saxon.expr.instruct.SimpleNodeConstructor.processLeavingTail(SimpleNodeConstructor.java:218) at net.sf.saxon.expr.instruct.ValueOf.processLeavingTail(ValueOf.java:287) at net.sf.saxon.expr.instruct.Block.processLeavingTail(Block.java:687) at net.sf.saxon.expr.instruct.Instruction.process(Instruction.java:151) at net.sf.saxon.expr.instruct.ElementCreator.processLeavingTail(ElementCreator.java:337) at net.sf.saxon.expr.instruct.Copy.processLeavingTail(Copy.java:415) at net.sf.saxon.expr.instruct.TemplateRule.applyLeavingTail(TemplateRule.java:347) at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:505) at net.sf.saxon.trans.TextOnlyCopyRuleSet.process(TextOnlyCopyRuleSet.java:69) at net.sf.saxon.trans.Mode.applyTemplates(Mode.java:481) at net.sf.saxon.expr.instruct.ApplyTemplates$ApplyTemplatesPackage.processLeavingTail(ApplyTemplates.java:553) at net.sf.saxon.Controller.transformDocument(Controller.java:2425) at net.sf.saxon.Controller.transform(Controller.java:1979) at net.sf.saxon.s9api.Xslt30Transformer.applyTemplates(Xslt30Transformer.java:592) at net.sf.saxon.jaxp.StreamingTransformerImpl.transform(StreamingTransformerImpl.java:76)

Attached is a project to reproduce this.

SaxonEETests2.zip (1.1 MB) Stefan Fritz, 2019-05-14 11:04 SaxonEETests2.zip

History

#1 Updated by Michael Kay 4 months ago

I've started by running this on 9.9 and I get a different error:

Error in xsl:copy-of/@select on line 10 column 29 of Relationship_EBI.xsl:
  XPDY0002: The context item is absent
at procedure setEnrichedMessage on line 9 of Relationship_EBI.xsl:
     invoked by global xsl:variable
  In template rule with match="/" on line 13 of Relationship_EBI.xsl

This is because the global variable contains a reference to the global context item ".", and StreamingTransformerFactory explicitly doesn't allow that: the Javadoc forStreamingTransformerFactory states: "Because it is designed for streaming, the source document does not become the global context item (specifically, if a global variable references the value of the context item ".", a dynamic error XPDY0002 occurs)."

So on 9.9, this is working as designed.

#2 Updated by Michael Kay 4 months ago

I have reproduced the error you are seeing on the 9.8 branch.

Although the Javadoc for StreamingTransformerImpl in 9.8 states the same restriction on use of the context item as in 9.9, it does not seem to be enforced as rigorously; in fact, on the non-streaming path being executed, the context item is explicitly set to the source node of the transformation. And in fact, this doesn't seem to be related to the failure, because we get the same failure if we substitute an EnterpriseTransformerFactory for the StreamingTransformerFactory.

What actually seems to be happening here is that the input document contains whitespace text nodes. The element causing the failure is:

<test>
    <a>12
3</a>
    <b>456</b>
</test>

which fails because text() selects more than one child text node (there are three whitespace text node children).

The stylesheet specifies xsl:strip-space so this shouldn't be happening

#3 Updated by Stefan Fritz 4 months ago

The idea of the customer was to replace &#xA and &#xD with a semicolon.

Their main argument is that this works fine on commandline and not in the way we apply the XSL.

#4 Updated by Michael Kay 4 months ago

There seems to be a clear error on the 9.8 branch that has been corrected in 9.9. In Controller.makeSourceTree(), 9.8 has

if (config.isStripsAllWhiteSpace() || isStylesheetContainingStripSpace() ||
                validationMode == Validation.STRICT || validationMode == Validation.LAX) {
            Stripper stripper = makeStripper(sourceBuilder);
            spaceStrippingRule = stripper.getSpaceStrippingRule();
        }

which has been changed in 9.9 to

if (config.isStripsAllWhiteSpace() || isStylesheetContainingStripSpace() ||
                validationMode == Validation.STRICT || validationMode == Validation.LAX) {
            r = makeStripper(sourceBuilder);
            spaceStrippingRule = getSpaceStrippingRule();
        }

So 9.8 is creating the stripper (which does the whitespace stripping) but isn't inserting it into the document-building pipeline.

Looking at the change history, 9.9 has had this change since it was first released.

The error appears to have been introduced into the 9.8 branch with commit 2588, which was attempting to fix bug 3885, and first hit the field with maintenance release 9.8.0.15.

#5 Updated by Michael Kay 4 months ago

  • Category set to JAXP Java API
  • Status changed from New to In Progress
  • Assignee set to Michael Kay
  • Applies to branch 9.8 added
  • Fix Committed on Branch 9.8 added

Confirmed that if we change the 9.8 code to be the same as 9.9, this test case works.

So it looks as if we might have to plan for another maintenance release on the 9.8 branch (which means looking at what other patches are also outstanding).

Meanwhile, I have committed the change.

#6 Updated by Michael Kay 4 months ago

Also confirmed that the test works on 9.9 if we change the StreamingTransformerFactory to an EnterpriseTransformerFactory.

(I also had to delete inactive code using an old MessageEmitter interface to get it to compile under 9.9)

#7 Updated by Michael Kay 4 months ago

Confirmed also that the method in question (Controller.makeSourceTree()) is not being used when the transformation is run from the command line. For some reason the command line has its own way of doing the source document building, which differs from the code used by JAXP.

#8 Updated by Stefan Fritz 4 months ago

As workaround for our customer: Is there a XSL level change that we can do to get this XSL working the way it does on commandline?

#9 Updated by Michael Kay 4 months ago

I think at the XSLT level the simplest is probably to strip whitespace text nodes explicitly:

<xsl:mode name="strip-space" on-no-match="shallow-copy"/>
<xsl:template match="text()[.='']" mode="strip-space"/>
<xsl:function name="f:strip-space" as="document-node()">
  <xsl:param name="in" as="document-node()"/>
  <xsl:apply-templates select="$in" mode="strip-space"/.
<xsl:function>

#10 Updated by Stefan Fritz 4 months ago

Sorry I don't get how to use this. The function is not invoked from what I see. And the requirement is to replace the and whitespace characters with ';' (semicolon). This is part of the Cleanup.xsl

#11 Updated by Michael Kay 4 months ago

You're currently doing:

    <!-- Perform Enrichments -->
    <xsl:variable name="setEnrichedMessage">
        <xsl:copy-of select="." />
    </xsl:variable>

    <xsl:template match="/">
        <!-- Perform Clean Up -->
        <xsl:apply-templates select="$setEnrichedMessage" mode="CleanUp"/>
    </xsl:template>

I'm not sure I understand the logic of this (why is setEnrichedMessage a copy of the input document, why can't it use the original? Perhaps there's some overriding stylesheet somewhere?) but as written you could simply change the logic to

<xsl:apply-templates select="f:strip-space(.)" mode="CleanUp"/>

or you could change the variable to

        <xsl:variable name="setEnrichedMessage">
        <xsl:apply-templates select="." mode="strip-space" />
    </xsl:variable>

#12 Updated by Martin Honnen 4 months ago

If the aim is to replace those two characters in text nodes then I would simply match on text() and perform the replacement e.g. instead of

    <xsl:template match="*[text()[contains(.,'&#xA;') or contains(.,'&#xD;')]]" mode="CleanUp">
        <xsl:copy>
            <xsl:apply-templates select="@*" mode="CleanUp"/>
            <xsl:value-of select="replace(replace(text(),'&#xA;',';'),'&#xD;',';')"/>
        </xsl:copy>
    </xsl:template>

you could simply use

        <xsl:template match="text()" mode="CleanUp">
            <xsl:value-of select="replace(replace(.,'&#xA;',';'),'&#xD;',';')"/>
        </xsl:template>

As you already have an identity transformation template the element node copying is taken care of.

#13 Updated by Michael Kay 3 months ago

  • Status changed from In Progress to Resolved

I'm going to mark this resolved, which is the status we use when we believe we have identified the fault and committed a patch (the status moves to "Closed" when we put out a maintenance release containing the patch).

There's no certainty that there will be another 9.8 maintenance release. I think I've given you a workaround that will keep you going; if that's not the case and this patch is urgently required then please let us know.

Please register to edit this issue

Also available in: Atom PDF