You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by mu...@apache.org on 2025/12/20 16:36:22 UTC
(xalan-java) branch xalan-j_xslt3.0_mvn updated: committing few improvements to xalan-j xslt 3.0 implementation
This is an automated email from the ASF dual-hosted git repository.
mukulg pushed a commit to branch xalan-j_xslt3.0_mvn
in repository https://gitbox.apache.org/repos/asf/xalan-java.git
The following commit(s) were added to refs/heads/xalan-j_xslt3.0_mvn by this push:
new d9dcaea05 committing few improvements to xalan-j xslt 3.0 implementation
d9dcaea05 is described below
commit d9dcaea058ba16d88d43b5855e33fdde8112aab4
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Sat Dec 20 22:05:53 2025 +0530
committing few improvements to xalan-j xslt 3.0 implementation
---
README | 6 +-
.../java/org/apache/xalan/templates/Constants.java | 2 +
.../org/apache/xpath/compiler/XPathParser.java | 27 ++++-
.../apache/xpath/functions/FuncArgPlaceholder.java | 133 ++++++++++++++++++++-
.../apache/xalan/tests/xpath3/XPathMapTests.java | 10 ++
.../xalan/tests/xslt3/XslPerformSortTests.java | 10 ++
src/test/resources/xpath_maps/gold/test33.out | 22 ++++
src/test/resources/xpath_maps/test33.xsl | 56 +++++++++
.../resources/xsl_perform_sort/gold/test10.out | 22 ++++
src/test/resources/xsl_perform_sort/test10.xsl | 55 +++++++++
10 files changed, 334 insertions(+), 9 deletions(-)
diff --git a/README b/README
index cd9badc62..b6513909e 100644
--- a/README
+++ b/README
@@ -17,7 +17,7 @@
Copyright 1999-2025 The Apache Software Foundation
-Document last modified : 2025-12-19
+Document last modified : 2025-12-20
Authors:
Gary Gregory <gg...@apache.org>
@@ -49,11 +49,11 @@ The Maven build results like following shall be produced:
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.apache.xalan.tests.main.AllXalanXSLT3Tests
-[INFO] Tests run: 1385, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: ..... - in org.apache.xalan.tests.main.AllXalanXSLT3Tests
+[INFO] Tests run: 1387, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: ..... - in org.apache.xalan.tests.main.AllXalanXSLT3Tests
[INFO]
[INFO] Results:
[INFO]
-[INFO] Tests run: 1385, Failures: 0, Errors: 0, Skipped: 0
+[INFO] Tests run: 1387, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- jar:3.3.0:jar (default-jar) @ xalan-java ---
diff --git a/src/main/java/org/apache/xalan/templates/Constants.java b/src/main/java/org/apache/xalan/templates/Constants.java
index 8e5bf979a..a3fa0c131 100644
--- a/src/main/java/org/apache/xalan/templates/Constants.java
+++ b/src/main/java/org/apache/xalan/templates/Constants.java
@@ -487,4 +487,6 @@ public class Constants extends org.apache.xml.utils.Constants
// This is used for Xalan-J XSLT 3.0 instruction xsl:document's implementation
public static final String XSL_DOCUMENT_INSTRUCTION_MARKER = "###xslDocument###";
+ public static final String UNARY_LOOKUP_MAP_ARRAY = "unary_lookup_map_array";
+
}
diff --git a/src/main/java/org/apache/xpath/compiler/XPathParser.java b/src/main/java/org/apache/xpath/compiler/XPathParser.java
index 97bc84d72..f71f28e59 100644
--- a/src/main/java/org/apache/xpath/compiler/XPathParser.java
+++ b/src/main/java/org/apache/xpath/compiler/XPathParser.java
@@ -2065,16 +2065,37 @@ public class XPathParser
else if (tokenIs("?")) {
// A function argument placeholder, for a function call partial
// function application.
+
+ // This is also used for, XPath map and array information lookup
int opPos1 = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
appendOp(2, OpCodes.XPath3OpCodes.OP_FUNC_ARG_PLACEHOLDER);
- consumeExpected('?');
+ consumeExpected('?');
FuncArgPlaceholder funcArgPlaceholder = new FuncArgPlaceholder();
- m_funcArgPlaceHolderList.add(funcArgPlaceholder);
+
+ if (m_token != null) {
+ List<String> argList = new ArrayList<>();
+ argList.add(m_token);
+
+ nextToken();
+ XPathDynamicFunctionCall xpathDynamicFunctionCall = new XPathDynamicFunctionCall();
+ xpathDynamicFunctionCall.setFuncRefVarName(Constants.UNARY_LOOKUP_MAP_ARRAY);
+ xpathDynamicFunctionCall.setIsFromUnaryLookupEvaluation(true);
+ xpathDynamicFunctionCall.setArgList(argList);
+
+ funcArgPlaceholder.setXPathDynamicFunctionCall(xpathDynamicFunctionCall);
+ }
+
+ m_funcArgPlaceHolderList.add(funcArgPlaceholder);
+
+ if (m_token != null) {
+ EqualityExpr(-1);
+ }
+
m_ops.setOp(opPos1 + OpMap.MAPINDEX_LENGTH,
m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos1);
}
@@ -4919,7 +4940,7 @@ public class XPathParser
// XPath literal map expression as, function argument
mapFuncArg();
}
- else if (tokenIs("?")) {
+ else if (tokenIs('?')) {
// A function argument placeholder, for a function call partial
// function application.
appendOp(2, OpCodes.XPath3OpCodes.OP_FUNC_ARG_PLACEHOLDER);
diff --git a/src/main/java/org/apache/xpath/functions/FuncArgPlaceholder.java b/src/main/java/org/apache/xpath/functions/FuncArgPlaceholder.java
index 151081912..d488e49d7 100644
--- a/src/main/java/org/apache/xpath/functions/FuncArgPlaceholder.java
+++ b/src/main/java/org/apache/xpath/functions/FuncArgPlaceholder.java
@@ -1,18 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
package org.apache.xpath.functions;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.Vector;
+import javax.xml.transform.SourceLocator;
import javax.xml.transform.TransformerException;
+import org.apache.xalan.templates.Constants;
import org.apache.xpath.Expression;
import org.apache.xpath.ExpressionOwner;
import org.apache.xpath.XPathContext;
import org.apache.xpath.XPathVisitor;
+import org.apache.xpath.objects.ResultSequence;
import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XPathArray;
+import org.apache.xpath.objects.XPathMap;
+
+import xml.xpath31.processor.types.XSDouble;
+import xml.xpath31.processor.types.XSString;
/**
* A class definition, to help implement XPath partial function
- * application using placeholder function argument expression ?.
+ * application using placeholder function argument expression ?,
+ * and also XPath map and array unary lookup expressions.
*
* @author Mukul Gandhi <mu...@apache.org>
*
@@ -22,6 +51,12 @@ public class FuncArgPlaceholder extends Expression {
private static final long serialVersionUID = 684157463367484125L;
+ /**
+ * This class field is used to, evaluate XPath map and array
+ * unary lookup expressions.
+ */
+ private XPathDynamicFunctionCall m_xpathDynamicFunctionCall;
+
/**
* Class constructor.
*/
@@ -31,8 +66,92 @@ public class FuncArgPlaceholder extends Expression {
@Override
public XObject execute(XPathContext xctxt) throws TransformerException {
- // NO OP
- return null;
+
+ XObject result = null;
+
+ SourceLocator srcLocator = xctxt.getSAXLocator();
+
+ if (m_xpathDynamicFunctionCall != null) {
+ String funcRefVarName = m_xpathDynamicFunctionCall.getFuncRefVarName();
+ if ((Constants.UNARY_LOOKUP_MAP_ARRAY).equals(funcRefVarName)) {
+ List<String> argList = m_xpathDynamicFunctionCall.getArgList();
+ XObject xObj = xctxt.getXPath3ContextItem();
+ if (xObj instanceof XPathMap) {
+ String mapKeyStr = argList.get(0);
+ XPathMap xpathMap = (XPathMap)xObj;
+ if (!"*".equals(mapKeyStr)) {
+ result = xpathMap.get(new XSString(mapKeyStr));
+ if (result == null) {
+ try {
+ // Validate the string value mapKeyStr, for it to be an integer
+ int keyIntValue = Integer.valueOf(mapKeyStr);
+
+ result = xpathMap.get(new XSDouble(mapKeyStr));
+ }
+ catch (NumberFormatException ex) {
+ result = new ResultSequence();
+ }
+ }
+ }
+ else {
+ // An xdm map lookup expression is ?*
+ Map<XObject,XObject> nativeMap = xpathMap.getNativeMap();
+ Set<XObject> keySet = nativeMap.keySet();
+ ResultSequence rSeq = new ResultSequence();
+ for (Iterator<XObject> iter = keySet.iterator(); iter.hasNext(); ) {
+ XObject key1 = iter.next();
+ rSeq.add(nativeMap.get(key1));
+ }
+
+ result = rSeq;
+ }
+
+ return result;
+ }
+ else if (xObj instanceof XPathArray) {
+ String arrayKeyStr = argList.get(0);
+ if (!"*".equals(arrayKeyStr)) {
+ try {
+ int keyIntValue = Integer.valueOf(arrayKeyStr);
+ if (keyIntValue > 0) {
+ XPathArray xpathArr = (XPathArray)xObj;
+ result = xpathArr.get(keyIntValue - 1);
+ }
+ else {
+ throw new TransformerException("XPTY0004 : For an XPath array information lookup, the key specifier "
+ + "doesn't evaluate to an integer "
+ + "value greater than zero.", srcLocator);
+ }
+ }
+ catch (NumberFormatException ex) {
+ throw new TransformerException("XPTY0004 : For an XPath array information lookup, the key specifier "
+ + "doesn't evaluate to an integer "
+ + "value.", srcLocator);
+ }
+ }
+ else {
+ // An xdm map lookup expression is ?*
+ XPathArray xpathArr = (XPathArray)xObj;
+ int arraySize = xpathArr.size();
+ ResultSequence rSeq = new ResultSequence();
+ for (int idx = 0; idx < arraySize; idx++) {
+ rSeq.add(xpathArr.get(idx));
+ }
+
+ result = rSeq;
+ }
+
+ return result;
+ }
+ else {
+ throw new TransformerException("XPTY0004 : For an XPath map or array lookup expression ?KeySpecifier's "
+ + "evaluation, the context item "
+ + "is not an XPath map or array.", srcLocator);
+ }
+ }
+ }
+
+ return result;
}
@Override
@@ -51,4 +170,12 @@ public class FuncArgPlaceholder extends Expression {
return false;
}
+ public XPathDynamicFunctionCall getXPathDynamicFunctionCall() {
+ return m_xpathDynamicFunctionCall;
+ }
+
+ public void setXPathDynamicFunctionCall(XPathDynamicFunctionCall xpathDynamicFunctionCall) {
+ this.m_xpathDynamicFunctionCall = xpathDynamicFunctionCall;
+ }
+
}
diff --git a/src/test/java/org/apache/xalan/tests/xpath3/XPathMapTests.java b/src/test/java/org/apache/xalan/tests/xpath3/XPathMapTests.java
index 9b90787ce..62c3fc0e2 100644
--- a/src/test/java/org/apache/xalan/tests/xpath3/XPathMapTests.java
+++ b/src/test/java/org/apache/xalan/tests/xpath3/XPathMapTests.java
@@ -369,5 +369,15 @@ public class XPathMapTests extends XslTransformTestsUtil {
runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
}
+
+ @Test
+ public void xslMapTest33() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test33.xsl";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test33.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test33.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
}
diff --git a/src/test/java/org/apache/xalan/tests/xslt3/XslPerformSortTests.java b/src/test/java/org/apache/xalan/tests/xslt3/XslPerformSortTests.java
index 93029066f..ddac47e56 100644
--- a/src/test/java/org/apache/xalan/tests/xslt3/XslPerformSortTests.java
+++ b/src/test/java/org/apache/xalan/tests/xslt3/XslPerformSortTests.java
@@ -138,5 +138,15 @@ public class XslPerformSortTests extends XslTransformTestsUtil {
runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
}
+
+ @Test
+ public void xslPerformSortTest10() {
+ String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test10.xsl";
+ String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test10.xsl";
+
+ String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test10.out";
+
+ runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+ }
}
diff --git a/src/test/resources/xpath_maps/gold/test33.out b/src/test/resources/xpath_maps/gold/test33.out
new file mode 100644
index 000000000..db2b5a160
--- /dev/null
+++ b/src/test/resources/xpath_maps/gold/test33.out
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+ <map>
+ <one>5</one>
+ <two>6</two>
+ <three>hello</three>
+ <allValues>hello 5 6</allValues>
+ </map>
+ <map>
+ <one>2</one>
+ <two>3</two>
+ <three>hello1</three>
+ <allValues>hello1 2 3</allValues>
+ </map>
+ <array>
+ <item>10</item>
+ <allValues>5 10 7 8</allValues>
+ </array>
+ <array>
+ <item>12</item>
+ <allValues>2 12</allValues>
+ </array>
+</result>
diff --git a/src/test/resources/xpath_maps/test33.xsl b/src/test/resources/xpath_maps/test33.xsl
new file mode 100644
index 000000000..04d6de5d7
--- /dev/null
+++ b/src/test/resources/xpath_maps/test33.xsl
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ exclude-result-prefixes="xs"
+ expand-text="yes"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- An XSL stylesheet test case, to test XPath 3.1 map
+ and array unary lookup expressions. -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:variable name="map-sequence1" select="(map {'one' : 5, 'two' : 6, 1 : 'hello'}, map {'one' : 2, 'two' : 3, 1 : 'hello1'})" as="map(*)*"/>
+
+ <xsl:variable name="array-sequence1" select="([5, 10, 7, 8], [2, 12])" as="array(xs:integer)*"/>
+
+ <xsl:template match="/">
+ <result>
+ <xsl:for-each select="$map-sequence1">
+ <map>
+ <one>{?one}</one>
+ <two>{?two}</two>
+ <three>{?1}</three>
+ <allValues>{?*}</allValues>
+ </map>
+ </xsl:for-each>
+ <xsl:for-each select="$array-sequence1">
+ <array>
+ <item>{?2}</item>
+ <allValues>{?*}</allValues>
+ </array>
+ </xsl:for-each>
+ </result>
+ </xsl:template>
+
+ <!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ -->
+
+</xsl:transform>
diff --git a/src/test/resources/xsl_perform_sort/gold/test10.out b/src/test/resources/xsl_perform_sort/gold/test10.out
new file mode 100644
index 000000000..a83541d52
--- /dev/null
+++ b/src/test/resources/xsl_perform_sort/gold/test10.out
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+ <item>
+ <name>hello</name>
+ <value>1</value>
+ </item>
+ <item>
+ <name>bar</name>
+ <value>2</value>
+ </item>
+ <item>
+ <name>foo</name>
+ <value>3</value>
+ </item>
+ <item>
+ <name>how are you</name>
+ <value>4</value>
+ </item>
+ <item>
+ <name>there</name>
+ <value>5</value>
+ </item>
+</result>
diff --git a/src/test/resources/xsl_perform_sort/test10.xsl b/src/test/resources/xsl_perform_sort/test10.xsl
new file mode 100644
index 000000000..e0c1b6a19
--- /dev/null
+++ b/src/test/resources/xsl_perform_sort/test10.xsl
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:map="http://www.w3.org/2005/xpath-functions/map"
+ exclude-result-prefixes="map"
+ expand-text="yes"
+ version="3.0">
+
+ <!-- Author: mukulg@apache.org -->
+
+ <!-- An XSL 3 stylesheet test case to test, xsl:perform-sort
+ instruction, to sort a sequence of xdm map items. -->
+
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:variable name="map-sequence1" select="(map { 'name' : 'foo', 'value' : 3 },
+ map { 'name': 'bar', 'value' : 2 },
+ map { 'name': 'hello', 'value' : 1 },
+ map { 'name': 'there', 'value' : 5 },
+ map { 'name': 'how are you', 'value' : 4 })" as="map(*)*"/>
+
+ <xsl:template match="/">
+ <result>
+ <xsl:variable name="sorted-map-sequence1" as="map(*)*">
+ <xsl:perform-sort select="$map-sequence1">
+ <xsl:sort select="?value"/>
+ </xsl:perform-sort>
+ </xsl:variable>
+ <xsl:for-each select="$sorted-map-sequence1">
+ <item>
+ <name>{?name}</name>
+ <value>{?value}</value>
+ </item>
+ </xsl:for-each>
+ </result>
+ </xsl:template>
+
+ <!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ -->
+
+</xsl:stylesheet>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xalan.apache.org
For additional commands, e-mail: commits-help@xalan.apache.org