Project

Profile

Help

Bug #6577

closed

xsl:where-populated triggers namespace fixup problem

Added by Johan Gheys 17 days ago. Updated 13 days ago.

Status:
Resolved
Priority:
Normal
Assignee:
Category:
XSLT conformance
Sprint/Milestone:
-
Start date:
2024-11-04
Due date:
% Done:

0%

Estimated time:
Legacy ID:
Applies to branch:
12, trunk
Fix Committed on Branch:
12, trunk
Fixed in Maintenance Release:
Platforms:
.NET, Java

Description

When xsl:where-populated is used to conditionally create a root element in a default namespace, the namespace declaration is not on the root element as expected, but on each of its child elements. The attached example illustrates this and gives:

<trains>
	<train xmlns="http://www.infrabel.be/Artemis/Timetable/20080711" ...>...</train>
	<train xmlns="http://www.infrabel.be/Artemis/Timetable/20080711" ...>...</train>
</trains>

while actually the following is expected:

<trains xmlns="http://www.infrabel.be/Artemis/Timetable/20080711">
	<train ...>...</train>
	<train ...>...</train>
</trains>

Files

namespace-fixup.zip (12.8 KB) namespace-fixup.zip Johan Gheys, 2024-11-04 14:58
Actions #1

Updated by Michael Kay 13 days ago

Thanks for reporting it. Problem reproduced.

Actions #2

Updated by Michael Kay 13 days ago

In the absence of the xsl:where-populated, this is what happens:

  • The xsl:element instruction is converted to a FixedElement instruction, because the name is statically known. The namespaceBindings property of the instruction is empty, because no additional namespaces (beyond the element name itself) are needed.
  • FixedElement calls ComplexContentOutputer.startElement() which does very little
  • On the first content node (a whitespace text node as it happens), CCO.startContent() is called. The inheritedNamespaces and pendingNSMap fields are both empty maps. The method CCO.checkProposedPrefix() is called, and this has the side-effect of setting pendingNSMap to a map containing the namespace binding from the element name.
  • CCO.startContent() calls nextReceiver.startElement() passing a namespace map containing the required namespace, and all is well.

When we add in the xsl:where-populated instruction, the FixedElement instruction writes to a WherePopulatedOutputter in place of the ComplexContentOutputter. The whitespace text node is notified to the WPO characters() method, which calls releaseStartTag(), which calls CCO.startElement(). It then calls CCO.characters(), but this doesn't call CCO.startContent(), because CCO.state is not equal to START_TAG. Why not?

Actions #3

Updated by Michael Kay 13 days ago

The Outputter interface has two modes of operation. In incremental mode, a start tag is delivered as a sequence of calls: startElement#4, attribute, namespace, startContent. In non-incremental mode, a start tag is delivered as a single call: startElement#6, which includes attribute and namespace information in the call. WPO.releaseStartTag is calling CCO.startElement#6, i.e. non-incremental mode, and in this mode CCO assumes that all the required namespaces (including that of the element name itself) were present in the namespace map passed on the call. This bypasses the call on checkProposedPrefix which is where the namespace would be added.

So a possible fix is that WPO.releaseStartTag() should pass the required information to CCO in incremental mode. Another possible solution is for CCO.startElement#6 to call checkProposedPrefix.

The first fix seems simpler and less likely to have side-effects. It can be achieved simply by changing WPO.releaseStartTag() to call super.startElement#6 in place of nextReceiver().startElement#6.

Actions #4

Updated by Michael Kay 13 days ago

The change fixed the supplied test case, but I simplified it to create a new XSLT4 test coco-104, which crashed:

java.lang.IllegalStateException: URI of element Q{http://www.infrabel.be/Artemis/Timetable/20080711}trains does not match declared default namespace {}
	at net.sf.saxon.event.RegularSequenceChecker.startElement(RegularSequenceChecker.java:354)
	at net.sf.saxon.event.ComplexContentOutputter

In fact the crash is in the 13.x branch whereas the fix was previously tested in 12.x. The difference is that the call on super.startElement#6 in the 13.x code now invokes the method `ProxyOutputter.startElement#6, which didn't exist in 12.x

Now fixed.

Actions #5

Updated by Michael Kay 13 days ago

  • Category set to XSLT conformance
  • Status changed from New to Resolved
  • Assignee set to Michael Kay
  • Applies to branch trunk added
  • Fix Committed on Branch 12, trunk added
  • Platforms .NET, Java added
Actions #6

Updated by Johan Gheys 13 days ago

Thank you Michael for solving this issue so quickly.

Please register to edit this issue

Also available in: Atom PDF