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