Eric, thank you for your interest in this subject.
The issue tracker appears to be inactive since 2011, and it’s not possible to 
register an account there.
Your XSLT files didn’t work for me with an error “An attempt was made to insert 
a node where it is not permitted”, apparently because they are attempting to 
create multiple elements “first”, “emitted”, “Output” and “last” at the root 
level. I added a new wrapping element to the “Root” template and it started 
working a bit better.
For the “TestNotOK.xsl” file, all three XSLT engines (JDK-Xalan, Xalan 2.7.2, 
Saxon) give the same result:

<OutputRoot xmlns:exslt="http://exslt.org/common"; 
xmlns:str="http://exslt.org/strings";>
 <first>foo</first>
 <emitted>foo</emitted>
 <Output>
   <Display></Display>
   <Usage></Usage>
   <Display></Display>
 </Output>
 <emitted>foo</emitted>
  <last>foo</last>
</OutputRoot>

The parameter isn’t being passed to the “Created” template.
For the “TestOK.xsl” file, Xalan 2.7.2 gives the same result, but JDK-Xalan and 
Saxon work better:

<OutputRoot xmlns:exslt="http://exslt.org/common"; 
xmlns:str="http://exslt.org/strings";>
 <first>foo</first>
 <emitted>foo</emitted>
 <Output>
   <Display>foo</Display>
   <Usage></Usage>
   <Display>foo</Display>
 </Output>
 <emitted>foo</emitted>
  <last>foo</last>
</OutputRoot>

Here the parameter is being passed successfully. What’s the difference? 
“TestNotOK” and “TestOK” differ in having the “/” template.
Probably there is something to do with calling “apply-templates” on a 
result-tree-fragment. I have read somewhere that in such cases the 
result-tree-fragment is being treated as a separate document with its own 
“document element”, and the templates should be called on that document element 
first, but, since I don’t have a dedicated template for that element, a 
“default template” is being called, and at this moment the parameter is being 
forgotten. Probably the “/” template changes this behavior in some way. But I 
can’t understand why because your “/” template also ignores the parameter, and 
anyway it appears identical to the “default template” so I can’t see why it can 
make any difference.
I have modified your “/” template by adding a “prm” parameter to it and passing 
this parameter to “apply-templates”. Please find attached 
“TestDocTemplatePrm.xsl”. Now it works! The parameter is being passed in all 
three engines including Xalan 2.7.2:

<OutputRoot xmlns:exslt="http://exslt.org/common"; 
xmlns:str="http://exslt.org/strings";>
  <first>foo</first>
 <emitted>foo</emitted>
 <Output>
   <Display>foo</Display>
   <Usage></Usage>
   <Display>foo</Display>
 </Output>
 <emitted>foo</emitted>
 <last>foo</last>
</OutputRoot>

So indeed the result-tree-fragment is being processed starting with an 
imaginary “document element”, and if I supply a suitable template which passes 
the parameter through, it is being passed to the next “Created” template I 
actually need.
Then I uncommented the “key” stuff and finally got working what I initially 
needed. I had to make another modification. Apparently at some stage of these 
nested templates the context is lost and the “key” function can’t find the 
required key. To restore the context, I have added a DOCUMENT_ROOT variable and 
wrapped the “key” invocation inside a “for-each”. Also I modified the key and 
the source XML file so that it could find something meaningful. And it finally 
worked! Please find attached “TestEverythingWorks.xml” and 
“TestEverythingWorks.xsl”. Xalan 2.7.2 and Saxon give the correct result:

<OutputRoot xmlns:exslt="http://exslt.org/common"; 
xmlns:str="http://exslt.org/strings";>
  <first>foo</first>
 <emitted>foo</emitted>
 <Output>
   <Display>foo</Display>
   <Usage>bar</Usage>
   <Display>foo</Display>
 </Output>
 <emitted>foo</emitted>
 <last>foo</last>
</OutputRoot>

The element is being found in the key and from it the value “bar” is being 
read. Hooray!
I still don’t know why so many twists are required to pass the parameter. Is it 
correct anyway? Probably it’s all towards the stringent implementation of the 
XSLT standard. In the JDK-Xalan the parameter was being passed even in the 
absence of the “/” template, probably that was a bug which has been fixed since 
then. On the other hand, the JDK-Xalan was losing the parameter which was an 
obvious bug; now I see that it has been fixed in Xalan 2.7.2. Thank you!

Sergey Bederov
Senior Developer
Cortona3D
www.cortona3D.com<http://www.cortona3d.com/>




From: Eric J. Schwarzenbach [mailto:eric.schwarzenb...@wrycan.com]
Sent: Saturday, March 11, 2023 11:28 PM
To: Bederov, Sergey <bede...@cortona3d.com>
Cc: j-users@xalan.apache.org
Subject: Re: Xalan bug: key( ) clears variable


The issue tracker is here: 
https://issues.apache.org/jira/projects/XALANJ/issues/XALANJ-2357?filter=allopenissues

However, I'm seeing even more weirdness with this xsl. It seems I can make it 
work by making changes that have no reason to make a difference. I'd made a 
number of changes to my copy of your xsl, to try to make it work, and to test 
that certain things were working property and were not the cause of the 
problem. I started to clean that up in case it might be useful for the bug 
submission. But some change I made caused the variable to be passed and make 
<Display>foo</Display> appear in the output. Trying to backtrack I  saw the 
problem come and go unexpectedly.

I isolated one change that causes the problem to go away. My attached 
testOK.xsl works properly. testNotOK.xsl exhibits the problem. The only 
difference is testNotOK.xsl having a "/" template (which was not even in your 
version and should not be having any effect). I've also gotten other changes 
that should be unrelated to cause the problem to come and go. It is as if 
various changes "stir the pot" and activate or deactivate the problem. I 
realize this makes no sense and makes me sound like a crazy person, but I urge 
you to try my two attached versions of the stylesheet.

I suspect that issue you had with the key usage using the JDK version was this 
same "stir the pot" issue and nothing particularly to do with keys, and it 
probably behaves the same way in both xalan and the JDK version of it.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; xmlns:str="http://exslt.org/strings"; xmlns:exslt="http://exslt.org/common";>

  <!--  <xsl:key name="keyname" match="Foo" use="@id"/>-->
  
  <xsl:template match="/">
    <xsl:param name="prm"/>
    <xsl:apply-templates>
        <xsl:with-param name="prm" select="$prm" />
    </xsl:apply-templates>
  </xsl:template>
  
  <xsl:template match="Root">
    <xsl:variable name="created">
      <Created />
    </xsl:variable>
	<OutputRoot>
      <first>
        <xsl:value-of select="@attr" />
      </first>
      <xsl:call-template name="emitVar">
        <xsl:with-param name="prm" select="@attr" />
      </xsl:call-template>
      <xsl:variable name="wtf" select="@attr" />
      <xsl:apply-templates select="exslt:node-set($created)">
        <xsl:with-param name="prm" select="@attr" />
        <!-- note that substituting a literal, like 'foo' for @attr makes no difference -->
      </xsl:apply-templates>
      <xsl:call-template name="emitVar">
        <xsl:with-param name="prm" select="@attr" />
      </xsl:call-template>
      <last>
        <xsl:value-of select="@attr" />
      </last>
	</OutputRoot>
  </xsl:template>
  
  <xsl:template name="emitVar">
    <xsl:param name="prm" select="'default'" />
    <emitted>
      <xsl:value-of select="$prm" />
    </emitted>
  </xsl:template>
  
  <xsl:template match="Created">
    <xsl:param name="prm" />
    <Output>
      <Display>
        <xsl:value-of select="$prm" />
      </Display>
      <Usage>
        <!--        <xsl:value-of select="key('keyname',$prm)"/>-->
      </Usage>
      <Display>
        <xsl:value-of select="$prm" />
      </Display>
    </Output>
  </xsl:template>
  
</xsl:stylesheet>

Attachment: TestEverythingWorks.xml
Description: TestEverythingWorks.xml

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; xmlns:str="http://exslt.org/strings"; xmlns:exslt="http://exslt.org/common";>

  <xsl:key name="keyname" match="Root" use="@attr"/>
  
  <xsl:variable name="DOCUMENT_ROOT" select="/"/>
  
  <xsl:template match="/">
    <xsl:param name="prm"/>
    <xsl:apply-templates>
        <xsl:with-param name="prm" select="$prm" />
    </xsl:apply-templates>
  </xsl:template>
  
  <xsl:template match="Root">
    <xsl:variable name="created">
      <Created />
    </xsl:variable>
	<OutputRoot>
      <first>
        <xsl:value-of select="@attr" />
      </first>
      <xsl:call-template name="emitVar">
        <xsl:with-param name="prm" select="@attr" />
      </xsl:call-template>
      <xsl:variable name="wtf" select="@attr" />
      <xsl:apply-templates select="exslt:node-set($created)">
        <xsl:with-param name="prm" select="@attr" />
        <!-- note that substituting a literal, like 'foo' for @attr makes no difference -->
      </xsl:apply-templates>
      <xsl:call-template name="emitVar">
        <xsl:with-param name="prm" select="@attr" />
      </xsl:call-template>
      <last>
        <xsl:value-of select="@attr" />
      </last>
	</OutputRoot>
  </xsl:template>
  
  <xsl:template name="emitVar">
    <xsl:param name="prm" select="'default'" />
    <emitted>
      <xsl:value-of select="$prm" />
    </emitted>
  </xsl:template>
  
  <xsl:template match="Created">
    <xsl:param name="prm" />
    <Output>
      <Display>
        <xsl:value-of select="$prm" />
      </Display>
	  <xsl:for-each select="$DOCUMENT_ROOT">
        <Usage>
          <xsl:value-of select="key('keyname',$prm)/@data"/>
        </Usage>
	  </xsl:for-each>
      <Display>
        <xsl:value-of select="$prm" />
      </Display>
    </Output>
  </xsl:template>
  
</xsl:stylesheet>

Reply via email to