Project

Profile

Help

Bug #2530

closed

Ant XSLT task - top-level variables always refer to first input document

Added by Priscilla Walmsley over 8 years ago. Updated over 8 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
JAXP Java API
Sprint/Milestone:
-
Start date:
2015-12-04
Due date:
% Done:

100%

Estimated time:
Legacy ID:
Applies to branch:
9.7
Fix Committed on Branch:
9.7
Fixed in Maintenance Release:
Platforms:

Description

Using 9.7, if I use an Ant XSLT task on multiple files (via @basedir/@destdir), and there are top-level variables in the XSLT, they use the first input document in the entire set as their context rather than the current input document. Local variables do not have this issue.

See attached zip - the top-level variables in test.xsl always use file1.xml as the context, even when processing file2.xml and file3.xml.

I'm using SaxonHE 9.7.0.1 with Ant 1.9.2 on Windows 7. When I switch back in the 9.6.0.5 jar, it works as expected.


Files

saxonbug.zip (2.09 KB) saxonbug.zip Priscilla Walmsley, 2015-12-04 17:21
Actions #1

Updated by Michael Kay over 8 years ago

  • Category set to Ant Transform Task
  • Assignee set to Michael Kay
  • Priority changed from High to Normal

Thanks. Will investigate. First thoughts are that Ant is probably re-using the JAXP Transformer for multiple transformations, which is a common bad habit in the JAXP world, and which doesn't play at all well with the XSLT 3.0 initiation concept that you prime a stylesheet once to establish values for global variables, and then invoke it repeatedly e.g. to execute different templates or functions. Now that Saxon's JAXP Transformer is layered on top of the s9api XsltTransformer, I'm wondering if it doesn't make sense to create a new XsltTransformer every time the JAXP Transformer.transform() method is called.

Actions #2

Updated by Michael Kay over 8 years ago

Confirmed that Ant is reusing the same Transformer to transform each source document, and that Saxon isn't setting the global context item on transformations after the first; the Controller is assuming that if globalContextItem is non-null, then Controller.setGlobalContextItem must have been called to supply a context item other than the document built from the supplied SAXSource.

It's not difficult to find some kind of solution that will work for this case (for example, the JAXP Transformer could call Controller.reset() after each transformation); but finding a good solution that works for all cases requires careful thought. We need to think about compatibility not only at the JAXP Transformer level, but also at the level of the s9api XsltTransformer and the Controller itself. There's now a confusing mix of different ways of supplying both the global context item and the initial match selection (to use XSLT 3.0 terminology) and we need to be clear about the exact effect of each of these different interfaces.

(There are some odd things going on as well. Why does XQueryExpression invoke Controller.setInitialMatchSelection()? Surely setInitialMatchSelection should only affect XSLT?)

Actions #3

Updated by Michael Kay over 8 years ago

Two other observations about this particular Ant task:

  1. It is not calling Transformer.reset() between transformations. It wouldn't make much difference if it did, because Saxon's implementation of reset() in TransformerImpl doesn't call Controller.reset() - but it's worth noting that we're not going to solve this problem by relying on reset() being called.

  2. There are three files in the input directory, and Ant appears to process each of them twice: file1, file2, file3, file1, file2, file3. No idea what's going on here, but we have to assume Ant knows what it's doing.

Actions #4

Updated by Michael Kay over 8 years ago

  • Category changed from Ant Transform Task to JAXP Java API

I'm reclassifying this as "JAXP" rather than "Ant" because any JAXP client could have the same problems. It probably also affects some s9api paths.

I explored the possibility of doing a radical restructuring of the Controller class, because the design is very exposed to this kind of problem: the variables held by the Controller mix options explicitly set by the user with data maintained internally during the course of a transformation, and the interactions of the many methods and their effect on these variables is poorly documented. But I decided this was not a good time to be doing this; we need to get 9.7 stable, so I've gone for a much more cautious approach that only fixes the immediate problems.

The immediate problem is that various methods like transform() don't overwrite the value of globalContextItem() if it has been set externally, and do overwrite it otherwise; and if it has been set by a previous call on transform(), that counts the same as being set externally. I've changed transform() to overwrite globalContextItem unconditionally, and to document this behaviour. Essentially setGlobalContextItem() is there for use only in conjunction with the new 3.0 methods like applyTemplates and callTemplate, and this will be documented.

Longer-term, it would be good to split the Controller up. The part that contains run-time data specific to a transformation should be encapsulated as something like TransformationData, and should be entirely private, accessible only via the Context. This should be instantiated anew for each transformation.

Actions #5

Updated by Michael Kay over 8 years ago

  • Status changed from New to Resolved

Patch submitted on the 9.7 branch and tested both against this case, and against relevant unit tests.

Actions #6

Updated by O'Neil Delpratt over 8 years ago

  • Applies to branch 9.7 added
  • Fix Committed on Branch 9.7 added
Actions #7

Updated by O'Neil Delpratt over 8 years ago

  • % Done changed from 0 to 100
  • Fixed in Maintenance Release 9.7.0.2 added

Bug fix applied in the Saxon 9.7.0.2 maintenance release

Actions #8

Updated by O'Neil Delpratt over 8 years ago

  • Status changed from Resolved to Closed

Please register to edit this issue

Also available in: Atom PDF