JDK-6689809 : XSLT transformer ignores XPath predicates in xsl:key elements
  • Type: Bug
  • Component: xml
  • Sub-Component: javax.xml.transform
  • Affected Version: 5.0,6u18
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux,solaris_10
  • CPU: x86,sparc
  • Submitted: 2008-04-16
  • Updated: 2012-04-25
  • Resolved: 2010-02-11
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6 JDK 7
1.4.0 1.4Fixed 6u19-revFixed 7Fixed
Description
FULL PRODUCT VERSION :
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_15-b04)
Java HotSpot(TM) Client VM (build 1.5.0_15-b04, mixed mode)
java version "1.5.0_14"

Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_14-b03)
Java HotSpot(TM) Client VM (build 1.5.0_14-b03, mixed mode)
java version "1.6.0_05"

Java(TM) SE Runtime Environment (build 1.6.0_05-b13)
Java HotSpot(TM) Client VM (build 10.0-b19, mixed mode)
java version "1.6.0_04"

Java(TM) SE Runtime Environment (build 1.6.0_04-b12)
Java HotSpot(TM) Client VM (build 10.0-b19, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Linux 2.6.24.3


A DESCRIPTION OF THE PROBLEM :
The Xalan XSLT compiler (XSLTC) ignores XPath predicates in xsl:key
elements since the class 'org.apache.xalan.xsltc.compiler.Stylesheet'
was rearranged in august 2003 to reorder the compilation of top level
XSLT elements (including keys) to respect dependencies between global
XSLT variables and keys. Method 'compileTopLevel' was changed to emit
code also for key elements and not emit code calling the method generated
by 'compileBuildKeys'. For this reason the byte code for each key element
is generated twice: First time into generated method 'buildKeys' from
'compileBuildKeys' and second time into generated method 'topLevel'
from 'compileTopLevel'. Method 'buildKeys' is still necessary, because
it is called by the XSLT 'document' function, if additional input
documents are loaded later.

Unfortunately the translate method of some XPath elements expected to
be called only once and they remove sub elements while their first execution.
So all XPath predicates get lost in class 'org.apache.xalan.xsltc.compiler.FilterExpr'
and 'org.apache.xalan.xsltc.compiler.Step' by a remove operation on
the '_predicates' container while the execution from 'compileBuildKeys'.
So 'compileTopLevel' generates wrong code for all key elements containing
predicates in their XPath expressions.

The attached patch changes the 'FilterExpr' and 'Step' class to use an
index variable to determine the current predicate and to not remove them.
This patch was tested with the current Subversion version of Xalan
(last change of Xalan tree in revision 584164) and with Sun JDK 1.5.0_14,
1.5.0_15, 1.6.0_04 and 1.6.0_05.

This bug exists also in Sun JRE 1.6 (1.6.0 up to 1.6.0_05) and JRE 1.5
(since 1.5.0_12 up to 1.5.0_15) in classes
'com.sun.org.apache.xalan.internal.xsltc.compiler.FilterExpr' and
'com.sun.org.apache.xalan.internal.xsltc.compiler.Step'. The attached
test JAR file contains also patches for these versions and fixes in form
of JAR files to be installed into '../jre/lib/endorsed' directories of
Sun JRE installations. The last Sun JRE version with correct handling
of xls:key elements is 1.5.0_11.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Download JAR file 'PredicateInKey-XSLT-Test.jar' from

http://issues.apache.org/jira/browse/XALANJ-2438

and execute

java -jar PredicateInKey-XSLT-Test.jar

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
[nothing]
ACTUAL -
XSLT Error: Function xsl:key is broken, because nodes in result of key('para1-key', 1) are missed or surplus.
  Text content of 14 surplus nodes: 04 05 06 07 08 09 10 11 12 13 14 15 16 17

XSLT Error: Function xsl:key is broken, because nodes in result of key('para2-key', 2) are missed or surplus.
  Text content of 11 surplus nodes: 02 03 07 08 09 13 14 15 18 19 20

XSLT Error: Function xsl:key is broken, because nodes in result of key('level-key', 1) are missed or surplus.
  Text content of 14 surplus nodes: 04 05 06 07 08 09 10 11 12 13 14 15 16 17

XSLT Error: Function xsl:key is broken, because nodes in result of key('level-key', 2) are missed or surplus.
  Text content of 9 surplus nodes: 07 08 09 13 14 15 18 19 20

XSLT Error: Function xsl:key is broken, because nodes in result of key('level-key', 3) are missed or surplus.
  Text content of 9 surplus nodes: 10 11 12 14 16 17 18 19 20

XSLT Error: Function xsl:key is broken, because nodes in result of key('level-key', 4) are missed or surplus.
  Text content of 6 surplus nodes: 15 16 17 18 19 20

XSLT Error: Function xsl:key seems to be broken, because line 4 of transformation result differs from expected result.
  Expected text line '<p>02</p>', but found '</div>'.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * PredicateInKey.java - Test XSLT with predicate in xsl:key element
 *
 * Project OpenSHORE Xalan Predicate in Key Fix
 *
 * Copyright (C) 2008 Helge Schulz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * $Id: $
 */

package org.openshore.test.xslt;

import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * Class <code>PredicateInKey</code> tests XPath predicates in xsl:key
 * elements by transforming the resource file <code>INPUT_RES_NAME</code>
 * with the XSLT file <code>STYLESHEET_RES_NAME</code> and compares the
 * result line by line with <code>OUTPUT_RES_NAME</code>. If the main
 * methods gets any arguments, the first one is interpreted as a file
 * name, the result is written to this file and no comparison is done.
 *
 * @author Helge Schulz
 */
public class PredicateInKey {
   
   static protected final String INPUT_RES_NAME = "PredicateInKey.xml";
   static protected final String STYLESHEET_RES_NAME = "PredicateInKey.xsl";
   static protected final String OUTPUT_RES_NAME = "PredicateInKey.out";

   static private void compareLines(
      BufferedReader expected, BufferedReader result) throws IOException {
      
      try {
         int line = 1;
         String expectedLine = expected.readLine();
         String resultLine = result.readLine();

         while (expectedLine != null) {
            if (resultLine != null) {
               if (!expectedLine.equals(resultLine)) {
                  System.err.println("XSLT Error: Function xsl:key seems"
                     + " to be broken, because line " + line + " of"
                     + " transformation result differs from expected"
                     + " result.");
                  System.err.println("  Expected text line '"
                     + expectedLine + "', but found '" + resultLine + "'.");
                  return;
               }
            } else {
               System.err.println("XSLT Error: Function xsl:key seems"
                  + " to be broken, because transformation result has"
                  + " to less text lines (missing line " + line + ").");
            }
            line++;
            expectedLine = expected.readLine();
            resultLine = result.readLine();
         }
         if (resultLine != null) {
            System.err.println("XSLT Error: Function xsl:key seems"
               + " to be broken, because transformation result has"
               + " to many text lines (surplus line " + line + ").");
         }
      } finally {
         if (expected != null) {
            try {
               expected.close();
            } catch (IOException e) {
               // ignore
            }
         }
         if (result != null) {
            try {
               result.close();
            } catch (IOException e) {
               // ignore
            }
         }
      }
      
   }
   
   static public void main(String[] args) throws
      IOException,
      TransformerException,
      TransformerConfigurationException {
      
      StreamSource input = new StreamSource(
         PredicateInKey.class.getResourceAsStream(INPUT_RES_NAME));
      StreamSource stylesheet = new StreamSource(
         PredicateInKey.class.getResourceAsStream(STYLESHEET_RES_NAME));
      StreamResult output;
      CharArrayWriter buffer = null;
         
      if (args.length > 0) {
         output = new StreamResult(new File(args[0]));
      } else {
         buffer = new CharArrayWriter();
         output = new StreamResult(buffer);
      }
      
      TransformerFactory.newInstance().newTransformer(stylesheet)
         .transform(input, output);
      
      if (buffer != null) {
         compareLines(
            new BufferedReader(new InputStreamReader(
               PredicateInKey.class.getResourceAsStream(OUTPUT_RES_NAME))),
            new BufferedReader(new CharArrayReader(buffer.toCharArray())));
      }
   }
   
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Install JAR fix files from inside PredicateInKey-XSLT-Test.jar into '.../jre/lib/endorsed'

Comments
EVALUATION Refer to jaxp issue 60: https://jaxp.dev.java.net/issues/show_bug.cgi?id=60 Quote from Helge Schulz: The first version of my patch submitted in March 2008 caused an infinite recursion with certain XPath expressions. The JAXP regression tests 'bug6175602/Test.java' and 'bug6537167/Test.java' failed for this reason (see <https://jaxp-sources.dev.java.net/source/browse/jaxp-sources/jaxp-ri/src/unit-test/>). The new version from January 2010 solve this problem. I have applied the patch to jaxp source and verified that the new patch passed all of our regression tests. The new patch is now in JAXP 1.4 sources. Thanks again for your contribution!
11-02-2010