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 2024/05/30 12:39:11 UTC

(xalan-java) branch xalan-j_xslt3.0 updated: committing implementation of xpath3.1 function map:for-each, and few related working test cases

This is an automated email from the ASF dual-hosted git repository.

mukulg pushed a commit to branch xalan-j_xslt3.0
in repository https://gitbox.apache.org/repos/asf/xalan-java.git


The following commit(s) were added to refs/heads/xalan-j_xslt3.0 by this push:
     new c7cba932 committing implementation of xpath3.1 function map:for-each, and few related working test cases
c7cba932 is described below

commit c7cba932240223105dbff260b6ee2b38342eafac
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Thu May 30 18:08:57 2024 +0530

    committing implementation of xpath3.1 function map:for-each, and few related working test cases
---
 src/org/apache/xpath/compiler/FunctionTable.java   |  11 +-
 src/org/apache/xpath/compiler/Keywords.java        |   3 +
 src/org/apache/xpath/compiler/XPathParserImpl.java |   3 +
 .../apache/xpath/functions/map/FuncMapForEach.java | 222 +++++++++++++++++++++
 tests/org/apache/xalan/xpath3/XPathMapTests.java   |  20 ++
 tests/xpath_maps/gold/test15.out                   |   5 +
 tests/xpath_maps/gold/test16.out                   |   5 +
 tests/xpath_maps/test15.xsl                        |  76 +++++++
 tests/xpath_maps/test16.xsl                        |  76 +++++++
 9 files changed, 419 insertions(+), 2 deletions(-)

diff --git a/src/org/apache/xpath/compiler/FunctionTable.java b/src/org/apache/xpath/compiler/FunctionTable.java
index 0433ea4e..0161cb64 100644
--- a/src/org/apache/xpath/compiler/FunctionTable.java
+++ b/src/org/apache/xpath/compiler/FunctionTable.java
@@ -468,6 +468,9 @@ public class FunctionTable
   
   /** The 'array:reverse()' id. */
   public static final int FUNC_ARRAY_REVERSE = 150;
+  
+  /** The 'map:for-each()' id. */
+  public static final int FUNC_MAP_FOREACH = 151;
 
   // Proprietary
 
@@ -529,7 +532,8 @@ public class FunctionTable
     
   static final Integer[] XPATH_MAP_FUNC_IDS = new Integer[] { new Integer(FUNC_MAP_SIZE), new Integer(FUNC_MAP_KEYS),
 		                                                                         new Integer(FUNC_MAP_CONTAINS), new Integer(FUNC_MAP_GET), 
-		                                                                         new Integer(FUNC_MAP_PUT), new Integer(FUNC_MAP_ENTRY)};
+		                                                                         new Integer(FUNC_MAP_PUT), new Integer(FUNC_MAP_ENTRY),
+		                                                                         new Integer(FUNC_MAP_FOREACH) };
   
   static final List<Integer> XPATH_MAP_FUNC_IDS_ARR = Arrays.asList(XPATH_MAP_FUNC_IDS);
 
@@ -555,7 +559,7 @@ public class FunctionTable
    * Number of built in functions. Be sure to update this as
    * built-in functions are added.
    */
-  private static final int NUM_BUILT_IN_FUNCS = 151;
+  private static final int NUM_BUILT_IN_FUNCS = 152;
 
   /**
    * Number of built-in functions that may be added.
@@ -810,6 +814,7 @@ public class FunctionTable
     m_functions[FUNC_MAP_GET] = org.apache.xpath.functions.map.FuncMapGet.class;
     m_functions[FUNC_MAP_PUT] = org.apache.xpath.functions.map.FuncMapPut.class;
     m_functions[FUNC_MAP_ENTRY] = org.apache.xpath.functions.map.FuncMapEntry.class;
+    m_functions[FUNC_MAP_FOREACH] = org.apache.xpath.functions.map.FuncMapForEach.class;
     
     m_functions[FUNC_PARSE_JSON] = org.apache.xpath.functions.FuncParseJson.class;
     
@@ -1086,6 +1091,8 @@ public class FunctionTable
                          new Integer(FunctionTable.FUNC_MAP_PUT));
          m_functionID.put(Keywords.FUNC_MAP_ENTRY,
                          new Integer(FunctionTable.FUNC_MAP_ENTRY));
+         m_functionID.put(Keywords.FUNC_MAP_FOREACH,
+                         new Integer(FunctionTable.FUNC_MAP_FOREACH));
          
          // XPath 3.1 functions configurations for the array functions 
          // namespace http://www.w3.org/2005/xpath-functions/array
diff --git a/src/org/apache/xpath/compiler/Keywords.java b/src/org/apache/xpath/compiler/Keywords.java
index 70b38dc6..1eb9dca2 100644
--- a/src/org/apache/xpath/compiler/Keywords.java
+++ b/src/org/apache/xpath/compiler/Keywords.java
@@ -584,6 +584,9 @@ public class Keywords
   /** map:entry function string. */
   public static final String FUNC_MAP_ENTRY = "entry";
   
+  /** map:for-each function string. */
+  public static final String FUNC_MAP_FOREACH = "for-each";
+  
   /** parse-json function string. */
   public static final String FUNC_PARSE_JSON = "parse-json";
   
diff --git a/src/org/apache/xpath/compiler/XPathParserImpl.java b/src/org/apache/xpath/compiler/XPathParserImpl.java
index 64575722..427f9b93 100644
--- a/src/org/apache/xpath/compiler/XPathParserImpl.java
+++ b/src/org/apache/xpath/compiler/XPathParserImpl.java
@@ -1075,6 +1075,9 @@ public class XPathParserImpl
     	   else if ("entry".equals(key)) {
       		  id = FunctionTable.FUNC_MAP_ENTRY;  
       	   }
+    	   else if ("for-each".equals(key)) {
+       		  id = FunctionTable.FUNC_MAP_FOREACH;  
+       	   }
     	}
     	else if ((FunctionTable.XPATH_BUILT_IN_ARRAY_FUNCS_NS_URI).equals(nsUri)) {
      	   if ("size".equals(key)) {
diff --git a/src/org/apache/xpath/functions/map/FuncMapForEach.java b/src/org/apache/xpath/functions/map/FuncMapForEach.java
new file mode 100644
index 00000000..f5121dc1
--- /dev/null
+++ b/src/org/apache/xpath/functions/map/FuncMapForEach.java
@@ -0,0 +1,222 @@
+/*
+ * 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.map;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.utils.QName;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.functions.Function2Args;
+import org.apache.xpath.functions.WrongNumberArgsException;
+import org.apache.xpath.objects.InlineFunction;
+import org.apache.xpath.objects.InlineFunctionParameter;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.objects.XPathMap;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Implementation of map:for-each function.
+ * 
+ * @author Mukul Gandhi <mu...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+public class FuncMapForEach extends Function2Args {
+
+	private static final long serialVersionUID = 4474670594915534994L;
+	
+	// The following two fields of this class, are used during 
+	// XPath.fixupVariables(..) action as performed within object of 
+	// this class.    
+	private Vector fVars;    
+	private int fGlobalsSize;
+
+	/**
+	 * Implementation of the function. The function must return a valid object.
+	 * 
+	 * @param xctxt The current execution context.
+	 * 
+	 * @return A valid XObject.
+	 *
+	 * @throws javax.xml.transform.TransformerException
+	 */
+	public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException {
+		
+		XObject result = null;
+
+		SourceLocator srcLocator = xctxt.getSAXLocator();
+
+		Expression arg0 = getArg0();
+		Expression arg1 = getArg1();
+
+		XObject arg0XsObject = null;
+
+		if (fVars != null) {
+		   arg0.fixupVariables(fVars, fGlobalsSize);
+		}
+
+		if (arg0 instanceof XPathMap) {
+		   arg0XsObject = (XPathMap)arg0;              
+		}
+		else if (arg0 instanceof Variable) {
+		   arg0XsObject = ((Variable)arg0).execute(xctxt);
+		   if (!(arg0XsObject instanceof XPathMap)) {
+			   throw new javax.xml.transform.TransformerException("FORG0006 : The 1st argument to function call map:for-each, "
+                                                                                            + "is not a map.", xctxt.getSAXLocator()); 
+		   }
+		}
+		else {
+		   throw new javax.xml.transform.TransformerException("FORG0006 : The 1st argument to function call map:for-each, "
+                                                                                        + "is not a map.", xctxt.getSAXLocator());
+		}
+
+		if (arg1 instanceof InlineFunction) {
+			InlineFunction inlineFuncArg = (InlineFunction)arg1;
+			verifyInlineFunctionParamCardinality(inlineFuncArg, srcLocator);
+			result = evaluateFnMapforEach(xctxt, (XPathMap)arg0XsObject, inlineFuncArg); 
+		}
+		else if (arg1 instanceof Variable) {
+			if (fVars != null) {
+				arg1.fixupVariables(fVars, fGlobalsSize);
+			}
+
+			XObject arg1VarValue = arg1.execute(xctxt);
+			if (arg1VarValue instanceof InlineFunction) {
+				InlineFunction inlineFuncArg = (InlineFunction)arg1VarValue;
+				verifyInlineFunctionParamCardinality(inlineFuncArg, srcLocator);
+				result = evaluateFnMapforEach(xctxt, (XPathMap)arg0XsObject, inlineFuncArg);   
+			}
+			else {
+				throw new javax.xml.transform.TransformerException("FORG0006 : The 2nd argument to function call map:for-each, "
+						                                                               + "is not a function item.", xctxt.getSAXLocator());    
+			}
+		}
+		else {
+			throw new javax.xml.transform.TransformerException("FORG0006 : The 2nd argument to function call map:for-each, "
+					                                                                   + "is not a function item.", xctxt.getSAXLocator());               
+		}
+
+		return result;
+	}
+
+	/**
+	 * Check that the number of arguments passed to this function is correct.
+	 *
+	 * @param argNum The number of arguments that is being passed to the function.
+	 *
+	 * @throws WrongNumberArgsException
+	 */
+	public void checkNumberArgs(int argNum) throws WrongNumberArgsException
+	{
+		if (argNum != 2) {
+		   reportWrongNumberArgs();
+		}
+	}
+
+	/**
+	 * Constructs and throws a WrongNumberArgException with the appropriate
+	 * message for this function object.
+	 *
+	 * @throws WrongNumberArgsException
+	 */
+	protected void reportWrongNumberArgs() throws WrongNumberArgsException {
+		throw new WrongNumberArgsException(XSLMessages.createXPATHMessage(
+																	XPATHErrorResources.ER_TWO, null)); //"2"
+	}
+
+	public void fixupVariables(java.util.Vector vars, int globalsSize)
+	{
+		fVars = (Vector)(vars.clone());
+		fGlobalsSize = globalsSize; 
+	}
+
+	/*
+	 * Verify the number of function parameters, that the inline function is allowed to 
+	 * have for map:for-each function call.
+	 */
+	private void verifyInlineFunctionParamCardinality(InlineFunction inlineFuncArg, SourceLocator srcLocator) throws 
+																								javax.xml.transform.TransformerException {
+		List<InlineFunctionParameter> funcParamList = inlineFuncArg.getFuncParamList();
+		if (funcParamList.size() != 2) {
+			throw new javax.xml.transform.TransformerException("XPTY0004 : The supplied function map:for-each's function item has " + 
+																			 funcParamList.size() + " parameters. Expected 2.", srcLocator);   
+		}
+	}
+
+	/*
+	 * Construct result of map:for-each function call.
+	 */
+	private ResultSequence evaluateFnMapforEach(XPathContext xctxt, XPathMap xpathMap, InlineFunction funcItem) 
+					                                                                                     throws TransformerException {
+		ResultSequence resultSeq = new ResultSequence();
+		
+		List<InlineFunctionParameter> funcParamList = funcItem.getFuncParamList();
+        QName fiParam0 = new QName((funcParamList.get(0)).getParamName());
+        QName fiParam1 = new QName((funcParamList.get(1)).getParamName());				
+        
+        String funcBodyXPathExprStr = funcItem.getFuncBodyXPathExprStr();
+        
+        if (funcBodyXPathExprStr == null || "".equals(funcBodyXPathExprStr)) {
+           return resultSeq;
+        }
+        
+        SourceLocator srcLocator = xctxt.getSAXLocator();
+        XPath inlineFnXpath = new XPath(funcBodyXPathExprStr, srcLocator, null, XPath.SELECT, null);
+		
+		Map<XObject, XObject> nativeMap = xpathMap.getNativeMap();
+		Set<Entry<XObject, XObject>> mapEntrySet = nativeMap.entrySet();
+		
+		Iterator<Entry<XObject, XObject>> iter = mapEntrySet.iterator();
+		
+		XPathContext xpathContextNew = new XPathContext(false);
+        Map<QName, XObject> inlineFunctionVarMap = xpathContextNew.getXPathVarMap();
+        
+		while (iter.hasNext()) {
+		   Entry<XObject, XObject> mapEntry = iter.next();
+		   XObject key = mapEntry.getKey();
+		   XObject value = mapEntry.getValue();
+
+           if (fiParam0 != null) {
+              inlineFunctionVarMap.put(fiParam0, key);
+           }
+           if (fiParam1 != null) {
+              inlineFunctionVarMap.put(fiParam1, value);
+           }
+           
+           XObject resultObj = inlineFnXpath.execute(xpathContextNew, DTM.NULL, null);
+           resultSeq.add(resultObj);
+		}
+		
+		inlineFunctionVarMap.clear();
+
+		return resultSeq;
+	}
+}
diff --git a/tests/org/apache/xalan/xpath3/XPathMapTests.java b/tests/org/apache/xalan/xpath3/XPathMapTests.java
index f6d1a39b..b0aae9a3 100644
--- a/tests/org/apache/xalan/xpath3/XPathMapTests.java
+++ b/tests/org/apache/xalan/xpath3/XPathMapTests.java
@@ -187,5 +187,25 @@ public class XPathMapTests extends XSLTransformTestsUtil {
         
         runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
     }
+    
+    @Test
+    public void xslMapTest15() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test15.xsl"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test15.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test15.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
+    
+    @Test
+    public void xslMapTest16() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test16.xsl"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test16.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test16.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
 
 }
diff --git a/tests/xpath_maps/gold/test15.out b/tests/xpath_maps/gold/test15.out
new file mode 100644
index 00000000..b23e43d3
--- /dev/null
+++ b/tests/xpath_maps/gold/test15.out
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <one itemCount="2">
+    <ok>true ### true</ok>
+  </one>
+</result>
diff --git a/tests/xpath_maps/gold/test16.out b/tests/xpath_maps/gold/test16.out
new file mode 100644
index 00000000..b23e43d3
--- /dev/null
+++ b/tests/xpath_maps/gold/test16.out
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <one itemCount="2">
+    <ok>true ### true</ok>
+  </one>
+</result>
diff --git a/tests/xpath_maps/test15.xsl b/tests/xpath_maps/test15.xsl
new file mode 100644
index 00000000..21fed8b0
--- /dev/null
+++ b/tests/xpath_maps/test15.xsl
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:xs="http://www.w3.org/2001/XMLSchema"
+                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
+				exclude-result-prefixes="xs map"
+                version="3.0">
+                
+   <!-- Author: mukulg@apache.org -->                 
+				
+   <xsl:output method="xml" indent="yes"/>
+   
+   <!-- An XSLT test case, to test XPath 3.1 function map:for-each. --> 
+  
+   <xsl:template match="/">
+     <result>
+	   <xsl:variable name="map1" select="map{1 : 'yes', 2 : 'no'}"/>
+	   <xsl:variable name="action1" select="function($k, $v) { $k }"/>
+	   <xsl:variable name="seq1" select="map:for-each($map1, $action1)"/>
+	   <one itemCount="{count($seq1)}">
+	     <ok>
+		   <xsl:call-template name="exists">
+		     <xsl:with-param name="seq1" select="$seq1"/>
+			 <xsl:with-param name="value1" select="1"/>
+		   </xsl:call-template>
+		   <xsl:text> ### </xsl:text>
+		   <xsl:call-template name="exists">
+		     <xsl:with-param name="seq1" select="$seq1"/>
+			 <xsl:with-param name="value1" select="2"/>
+		   </xsl:call-template>
+		 </ok>
+	   </one>
+	 </result>
+   </xsl:template>
+   
+   <!-- An XSL named template, to check whether a sequence 
+        contains a specific value. -->
+   <xsl:template name="exists" as="xs:boolean">
+     <xsl:param name="seq1" as="xs:integer*"/>
+	 <xsl:param name="value1" as="xs:integer"/>
+	 <xsl:variable name="temp1">
+	   <xsl:for-each select="$seq1">
+	      <xsl:variable name="x1" select="."/>
+	      <xsl:if test="$x1 eq $value1">
+		    <yes/>
+		  </xsl:if>
+	   </xsl:for-each>
+	 </xsl:variable>
+	 <xsl:choose>
+	   <xsl:when test="$temp1/yes">
+	      <xsl:value-of select="xs:boolean(1)"/>
+	   </xsl:when>
+	   <xsl:otherwise>
+	      <xsl:value-of select="xs:boolean(0)"/>
+	   </xsl:otherwise>
+	 </xsl:choose>
+   </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>
diff --git a/tests/xpath_maps/test16.xsl b/tests/xpath_maps/test16.xsl
new file mode 100644
index 00000000..6d23b024
--- /dev/null
+++ b/tests/xpath_maps/test16.xsl
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:xs="http://www.w3.org/2001/XMLSchema"
+                xmlns:map="http://www.w3.org/2005/xpath-functions/map"
+				exclude-result-prefixes="xs map"
+                version="3.0">
+				
+   <!-- Author: mukulg@apache.org -->                 
+				
+   <xsl:output method="xml" indent="yes"/>
+   
+   <!-- An XSLT test case, to test XPath 3.1 function map:for-each. -->
+  
+   <xsl:template match="/">
+     <result>
+	   <xsl:variable name="map1" select="map{1 : 'yes', 2 : 'no'}"/>
+	   <xsl:variable name="action1" select="function($k, $v) { $v }"/>
+	   <xsl:variable name="seq1" select="distinct-values(map:for-each($map1, $action1))"/>
+	   <one itemCount="{count($seq1)}">
+	     <ok>
+		   <xsl:call-template name="exists">
+		     <xsl:with-param name="seq1" select="$seq1"/>
+			 <xsl:with-param name="value1" select="'yes'"/>
+		   </xsl:call-template>
+		   <xsl:text> ### </xsl:text>
+		   <xsl:call-template name="exists">
+		     <xsl:with-param name="seq1" select="$seq1"/>
+			 <xsl:with-param name="value1" select="'no'"/>
+		   </xsl:call-template>
+		 </ok>
+	   </one>
+	 </result>
+   </xsl:template>
+   
+   <!-- An XSL named template, to check whether a sequence 
+        contains a specific value. -->
+   <xsl:template name="exists" as="xs:boolean">
+     <xsl:param name="seq1" as="xs:string*"/>
+	 <xsl:param name="value1" as="xs:string"/>
+	 <xsl:variable name="temp1">
+	   <xsl:for-each select="$seq1">
+	      <xsl:variable name="x1" select="."/>
+	      <xsl:if test="$x1 eq $value1">
+		    <yes/>
+		  </xsl:if>
+	   </xsl:for-each>
+	 </xsl:variable>
+	 <xsl:choose>
+	   <xsl:when test="$temp1/yes">
+	      <xsl:value-of select="xs:boolean(1)"/>
+	   </xsl:when>
+	   <xsl:otherwise>
+	      <xsl:value-of select="xs:boolean(0)"/>
+	   </xsl:otherwise>
+	 </xsl:choose>
+   </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