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/08/09 16:49:59 UTC

(xalan-java) branch xalan-j_xslt3.0 updated: committing implementation of xpath path expression having function call suffix, with few conformance tests

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 b5342ad5f committing implementation of xpath path expression having function call suffix, with few conformance tests
b5342ad5f is described below

commit b5342ad5fb1e4aec2946db23ce68ea42c7dd1021
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Fri Aug 9 22:19:29 2024 +0530

    committing implementation of xpath path expression having function call suffix, with few conformance tests
---
 src/org/apache/xalan/templates/ElemForEach.java    |  27 ++++
 src/org/apache/xalan/templates/ElemIterate.java    |  42 +++++-
 src/org/apache/xalan/templates/ElemValueOf.java    |  24 +++-
 src/org/apache/xalan/templates/ElemVariable.java   |  28 +++-
 src/org/apache/xpath/XPath.java                    |  43 +++++-
 src/org/apache/xpath/axes/WalkingIterator.java     |  17 +++
 src/org/apache/xpath/compiler/XPathParser.java     | 153 ++++++++++++++++++---
 .../xpath/composite/XPathExprFunctionSuffix.java   |  55 ++++++++
 .../xpath3/XPathExprFunctionCallSuffixTests.java   |  91 ++++++++++++
 tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java   |   4 +-
 tests/xpath_function_call_suffix/gold/test1.out    |   4 +
 tests/xpath_function_call_suffix/gold/test2.out    |   1 +
 tests/xpath_function_call_suffix/gold/test3.out    |   7 +
 tests/xpath_function_call_suffix/gold/test4.out    |   7 +
 tests/xpath_function_call_suffix/test1.xml         |   8 ++
 tests/xpath_function_call_suffix/test1.xsl         |  44 ++++++
 tests/xpath_function_call_suffix/test2.xsl         |  40 ++++++
 tests/xpath_function_call_suffix/test3.xsl         |  43 ++++++
 tests/xpath_function_call_suffix/test4.xsl         |  43 ++++++
 19 files changed, 647 insertions(+), 34 deletions(-)

diff --git a/src/org/apache/xalan/templates/ElemForEach.java b/src/org/apache/xalan/templates/ElemForEach.java
index b05a7cc69..d7327d17e 100644
--- a/src/org/apache/xalan/templates/ElemForEach.java
+++ b/src/org/apache/xalan/templates/ElemForEach.java
@@ -41,6 +41,7 @@ import org.apache.xpath.ExpressionOwner;
 import org.apache.xpath.XPath;
 import org.apache.xpath.XPathContext;
 import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.axes.WalkingIterator;
 import org.apache.xpath.composite.XPathForExpr;
 import org.apache.xpath.composite.XPathSequenceConstructor;
 import org.apache.xpath.functions.Function;
@@ -448,6 +449,13 @@ public class ElemForEach extends ElemTemplateElement implements ExpressionOwner
     else if (m_selectExpression instanceof LocPathIterator) {
         LocPathIterator locPathIterator = (LocPathIterator)m_selectExpression;
         
+        Function func = null;
+        
+        if (locPathIterator instanceof WalkingIterator) {
+            WalkingIterator walkingIter = (WalkingIterator)locPathIterator;
+            func = walkingIter.getFuncExpr();
+        }                
+        
         boolean isProcessAsNodeset = true;
         DTMIterator dtmIter = null;                     
         try {
@@ -457,6 +465,25 @@ public class ElemForEach extends ElemTemplateElement implements ExpressionOwner
            isProcessAsNodeset = false;
         }
         
+        if (dtmIter != null) {
+        	ResultSequence rSeq = null;
+        	if (func != null) {
+        		rSeq = new ResultSequence();
+        		int nextNode;
+        		while ((nextNode = dtmIter.nextNode()) != DTM.NULL)
+        		{
+        			XNodeSet singletonXPathNode = new XNodeSet(nextNode, xctxt);
+        			xctxt.setXPath3ContextItem(singletonXPathNode);                              
+        			XObject funcEvalResult = func.execute(xctxt);
+        			rSeq.add(funcEvalResult);
+        		}
+        		
+        		processSequenceOrArray(transformer, xctxt, rSeq);
+                
+                return;
+        	}
+        }
+        
         if (!isProcessAsNodeset) {        	
         	String xpathPatternStr = m_xpath.getPatternString();
             
diff --git a/src/org/apache/xalan/templates/ElemIterate.java b/src/org/apache/xalan/templates/ElemIterate.java
index 211a5fe07..87ea932d7 100644
--- a/src/org/apache/xalan/templates/ElemIterate.java
+++ b/src/org/apache/xalan/templates/ElemIterate.java
@@ -32,12 +32,14 @@ import org.apache.xpath.Expression;
 import org.apache.xpath.ExpressionOwner;
 import org.apache.xpath.XPath;
 import org.apache.xpath.XPathContext;
+import org.apache.xpath.axes.WalkingIterator;
+import org.apache.xpath.functions.Function;
 import org.apache.xpath.objects.ResultSequence;
 import org.apache.xpath.objects.XNodeSet;
 import org.apache.xpath.objects.XObject;
 import org.apache.xpath.objects.XPathArray;
 
-/*
+/**
  * Implementation of the XSLT 3.0 xsl:iterate instruction.
  * 
  * Ref : https://www.w3.org/TR/xslt-30/#element-iterate
@@ -180,7 +182,43 @@ public class ElemIterate extends ElemTemplateElement implements ExpressionOwner
            
            verifyXslElemIterateChildElementsSequence(xctxt);
            
-           XObject evalResult = m_selectExpression.execute(xctxt);
+           XObject evalResult = null;
+           
+           Function func = null;
+           
+           if (m_selectExpression instanceof WalkingIterator) {
+               WalkingIterator walkingIter = (WalkingIterator)m_selectExpression;
+               func = walkingIter.getFuncExpr();
+               
+               DTMIterator dtmIter = null;
+               try {
+                   dtmIter = walkingIter.asIterator(xctxt, sourceNode);
+               }
+               catch (ClassCastException ex) {
+                   // no op
+               }
+               
+               if (dtmIter != null) {
+            	   ResultSequence rSeq = null;
+            	   if (func != null) {
+            		   rSeq = new ResultSequence();
+            		   int nextNode;
+            		   while ((nextNode = dtmIter.nextNode()) != DTM.NULL)
+            		   {
+            			   XNodeSet singletonXPathNode = new XNodeSet(nextNode, xctxt);
+            			   xctxt.setXPath3ContextItem(singletonXPathNode);                              
+            			   XObject funcEvalResult = func.execute(xctxt);
+            			   rSeq.add(funcEvalResult);
+            		   }
+            		   
+            		   evalResult = rSeq;
+            	   }
+               }
+           }
+           
+           if (evalResult == null) {
+              evalResult = m_selectExpression.execute(xctxt);
+           }
            
            List<XObject> itemsToBeProcessed = null;
                
diff --git a/src/org/apache/xalan/templates/ElemValueOf.java b/src/org/apache/xalan/templates/ElemValueOf.java
index 20f45cee5..d45fdccb6 100644
--- a/src/org/apache/xalan/templates/ElemValueOf.java
+++ b/src/org/apache/xalan/templates/ElemValueOf.java
@@ -33,12 +33,13 @@ import org.apache.xpath.Expression;
 import org.apache.xpath.XPath;
 import org.apache.xpath.XPathContext;
 import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.axes.WalkingIterator;
 import org.apache.xpath.composite.XPathIfExpr;
 import org.apache.xpath.composite.XPathLetExpr;
-import org.apache.xpath.functions.XSLConstructorStylesheetOrExtensionFunction;
 import org.apache.xpath.functions.Function;
-import org.apache.xpath.functions.XSLFunctionService;
 import org.apache.xpath.functions.XPathDynamicFunctionCall;
+import org.apache.xpath.functions.XSLConstructorStylesheetOrExtensionFunction;
+import org.apache.xpath.functions.XSLFunctionService;
 import org.apache.xpath.objects.ResultSequence;
 import org.apache.xpath.objects.XNodeSet;
 import org.apache.xpath.objects.XNumber;
@@ -460,6 +461,13 @@ public class ElemValueOf extends ElemTemplateElement {
                   else if (expr instanceof LocPathIterator) {
                      LocPathIterator locPathIterator = (LocPathIterator)expr;
                      
+                     Function func = null;
+                     
+                     if (locPathIterator instanceof WalkingIterator) {
+                         WalkingIterator walkingIter = (WalkingIterator)locPathIterator;
+                         func = walkingIter.getFuncExpr();
+                     }
+                     
                      DTMIterator dtmIter = null;                     
                      try {
                         dtmIter = locPathIterator.asIterator(xctxt, current);
@@ -474,8 +482,16 @@ public class ElemValueOf extends ElemTemplateElement {
                         while ((nextNode = dtmIter.nextNode()) != DTM.NULL)
                         {
                            XNodeSet singletonXPathNode = new XNodeSet(nextNode, xctxt);
-                           String nodeStrVal = singletonXPathNode.str();
-                           strBuff.append(nodeStrVal + " ");
+                           String resultStr = "";
+                           if (func != null) {
+                              xctxt.setXPath3ContextItem(singletonXPathNode);                              
+                              XObject funcEvalResult = func.execute(xctxt);
+                              resultStr = XslTransformEvaluationHelper.getStrVal(funcEvalResult);                               
+                           }
+                           else {
+                              resultStr = singletonXPathNode.str();
+                           }
+                           strBuff.append(resultStr + " ");
                         }
                          
                         String nodeSetStrValue = strBuff.toString();
diff --git a/src/org/apache/xalan/templates/ElemVariable.java b/src/org/apache/xalan/templates/ElemVariable.java
index 6d057102c..11393093e 100644
--- a/src/org/apache/xalan/templates/ElemVariable.java
+++ b/src/org/apache/xalan/templates/ElemVariable.java
@@ -28,6 +28,7 @@ import org.apache.xalan.res.XSLTErrorResources;
 import org.apache.xalan.transformer.TransformerImpl;
 import org.apache.xalan.xslt.util.XslTransformEvaluationHelper;
 import org.apache.xalan.xslt.util.XslTransformSharedDatastore;
+import org.apache.xml.dtm.DTM;
 import org.apache.xml.dtm.DTMIterator;
 import org.apache.xml.utils.PrefixResolver;
 import org.apache.xml.utils.QName;
@@ -36,6 +37,7 @@ import org.apache.xpath.XPath;
 import org.apache.xpath.XPathContext;
 import org.apache.xpath.axes.LocPathIterator;
 import org.apache.xpath.axes.SelfIteratorNoPredicate;
+import org.apache.xpath.axes.WalkingIterator;
 import org.apache.xpath.composite.SequenceTypeData;
 import org.apache.xpath.composite.SequenceTypeSupport;
 import org.apache.xpath.functions.XSLConstructorStylesheetOrExtensionFunction;
@@ -484,6 +486,13 @@ public class ElemVariable extends ElemTemplateElement
             
             LocPathIterator locPathIterator = (LocPathIterator)selectExpression;
             
+            Function func = null;
+            
+            if (locPathIterator instanceof WalkingIterator) {
+                WalkingIterator walkingIter = (WalkingIterator)locPathIterator;
+                func = walkingIter.getFuncExpr();
+            }
+            
             DTMIterator dtmIter = null;                     
             try {
                 dtmIter = locPathIterator.asIterator(xctxt, contextNode);
@@ -492,8 +501,23 @@ public class ElemVariable extends ElemTemplateElement
                 // no op
             }
             
-            if (dtmIter != null) {               
-               var = new XNodeSet(dtmIter);
+            if (dtmIter != null) {
+               ResultSequence rSeq = null;
+               if (func != null) {
+            	  rSeq = new ResultSequence();
+            	  int nextNode;
+            	  while ((nextNode = dtmIter.nextNode()) != DTM.NULL)
+                  {
+                      XNodeSet singletonXPathNode = new XNodeSet(nextNode, xctxt);
+                      xctxt.setXPath3ContextItem(singletonXPathNode);                              
+                      XObject funcEvalResult = func.execute(xctxt);
+                      rSeq.add(funcEvalResult);
+                  }
+            	  var = rSeq; 
+               }
+               else {
+                  var = new XNodeSet(dtmIter);
+               }
                
                if (m_asAttr != null) {
                   var = SequenceTypeSupport.castXdmValueToAnotherType(var, m_asAttr, null, xctxt);  
diff --git a/src/org/apache/xpath/XPath.java b/src/org/apache/xpath/XPath.java
index e3ff7aa70..8c132ca78 100644
--- a/src/org/apache/xpath/XPath.java
+++ b/src/org/apache/xpath/XPath.java
@@ -34,9 +34,11 @@ import org.apache.xml.dtm.DTMIterator;
 import org.apache.xml.utils.PrefixResolver;
 import org.apache.xml.utils.SAXSourceLocator;
 import org.apache.xpath.axes.LocPathIterator;
+import org.apache.xpath.axes.WalkingIterator;
 import org.apache.xpath.compiler.Compiler;
 import org.apache.xpath.compiler.FunctionTable;
 import org.apache.xpath.compiler.XPathParser;
+import org.apache.xpath.composite.XPathExprFunctionSuffix;
 import org.apache.xpath.functions.Function;
 import org.apache.xpath.objects.ResultSequence;
 import org.apache.xpath.objects.XNumber;
@@ -249,22 +251,51 @@ public class XPath implements Serializable, ExpressionOwner
     m_patternString = exprString;
 
     XPathParser parser = new XPathParser(errorListener, locator);
-    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);    
+    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
+    
+    Expression expr = null;
     
     if (SELECT == type) {
       parser.initXPath(compiler, exprString, prefixResolver, false);
       m_arrowop_remaining_xpath_expr_str = parser.getArrowOpRemainingXPathExprStr();
+      
+      XPathExprFunctionSuffix pathExprFunctionSuffix = parser.getPathExprFunctionSuffix();
+      if (pathExprFunctionSuffix != null) {
+    	 parser = new XPathParser(errorListener, locator);
+    	 compiler = new Compiler(errorListener, locator, m_funcTable);
+    	 String xpathOneExprStr = pathExprFunctionSuffix.getXPathOneStr();    	     	 
+    	 parser.initXPath(compiler, xpathOneExprStr, prefixResolver, false);
+    	 Expression expr1 = compiler.compile(0);
+    	 
+    	 parser = new XPathParser(errorListener, locator);
+    	 compiler = new Compiler(errorListener, locator, m_funcTable);
+    	 String xpathTwoExprStr = pathExprFunctionSuffix.getXPathTwoStr();    	 
+    	 parser.initXPath(compiler, xpathTwoExprStr, prefixResolver, false);
+    	 Expression expr2 = compiler.compile(0);
+    	 
+    	 parser.setPathExprFunctionSuffix(null);
+    	 
+    	 expr = expr1;
+    	 
+    	 if ((expr instanceof WalkingIterator) && (expr2 instanceof Function)) {
+    		WalkingIterator walkingIter = (WalkingIterator)expr;
+    		walkingIter.setFuncExpr((Function)expr2);
+    	 }
+      }
     }
     else if (MATCH == type)
-      parser.initMatchPattern(compiler, exprString, prefixResolver);
+      parser.initMatchPattern(compiler, exprString, prefixResolver);    
     else
       throw new RuntimeException(XSLMessages.createXPATHMessage(
             XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, 
-            new Object[]{Integer.toString(type)})); 
-
-    Expression expr = compiler.compile(0);
+            new Object[]{Integer.toString(type)}));
+    
+    if (expr == null) {
+       expr = compiler.compile(0);
+    }
+    
     if (expr instanceof ArrowOp) {
-      ((ArrowOp)expr).setArrowOpRemainingXPathExprStr(m_arrowop_remaining_xpath_expr_str);
+       ((ArrowOp)expr).setArrowOpRemainingXPathExprStr(m_arrowop_remaining_xpath_expr_str);
     }
 
     this.setExpression(expr);
diff --git a/src/org/apache/xpath/axes/WalkingIterator.java b/src/org/apache/xpath/axes/WalkingIterator.java
index 10765f349..caebd75cc 100644
--- a/src/org/apache/xpath/axes/WalkingIterator.java
+++ b/src/org/apache/xpath/axes/WalkingIterator.java
@@ -28,6 +28,7 @@ import org.apache.xpath.VariableStack;
 import org.apache.xpath.XPathVisitor;
 import org.apache.xpath.compiler.Compiler;
 import org.apache.xpath.compiler.OpMap;
+import org.apache.xpath.functions.Function;
 
 /**
  * Location path iterator that uses Walkers.
@@ -35,6 +36,14 @@ import org.apache.xpath.compiler.OpMap;
 public class WalkingIterator extends LocPathIterator implements ExpressionOwner
 {
     static final long serialVersionUID = 9110225941815665906L;
+    
+    /**
+     * If there's a function call suffix at an end of XPath expression 
+     * similar to /a/b/funcCall(..), we keep a compiled Function 
+     * object for funcCall(..) within this class field.  
+     */
+    private Function m_func_expr = null;
+    
   /**
    * Create a WalkingIterator iterator, including creation
    * of step walkers from the opcode list, and call back
@@ -357,4 +366,12 @@ public class WalkingIterator extends LocPathIterator implements ExpressionOwner
       return true;
     }
 
+	public void setFuncExpr(Function expr) {		
+		m_func_expr = expr;
+	}
+	
+	public Function getFuncExpr() {
+		return m_func_expr;
+	}
+
 }
diff --git a/src/org/apache/xpath/compiler/XPathParser.java b/src/org/apache/xpath/compiler/XPathParser.java
index 195a1f258..b7745844b 100644
--- a/src/org/apache/xpath/compiler/XPathParser.java
+++ b/src/org/apache/xpath/compiler/XPathParser.java
@@ -50,6 +50,7 @@ import org.apache.xml.utils.PrefixResolver;
 import org.apache.xpath.XPathProcessorException;
 import org.apache.xpath.composite.ForQuantifiedExprVarBinding;
 import org.apache.xpath.composite.LetExprVarBinding;
+import org.apache.xpath.composite.XPathExprFunctionSuffix;
 import org.apache.xpath.composite.SequenceTypeArrayTest;
 import org.apache.xpath.composite.SequenceTypeData;
 import org.apache.xpath.composite.SequenceTypeFunctionTest;
@@ -252,7 +253,9 @@ public class XPathParser
   
   private String m_arrowOpRemainingXPathExprStr = null;
   
-  private boolean isFunctionArgumentParse;
+  private XPathExprFunctionSuffix m_pathExprFunctionSuffix = null;
+  
+  private boolean m_isFunctionArgumentParse;
   
   /**
    * The parser constructor.
@@ -606,7 +609,7 @@ public class XPathParser
 
     if (m_queueMark < m_ops.getTokenQueueSize())
     {
-      m_token = (String) m_ops.m_tokenQueue.elementAt(m_queueMark++);
+      m_token = (String) m_ops.m_tokenQueue.elementAt(m_queueMark++);      
       m_tokenChar = m_token.charAt(0);
     }
     else
@@ -956,7 +959,7 @@ public class XPathParser
                                                                                  OccurrenceIndicator.ONE_OR_MANY);
          nextToken();
       }
-      else if ((m_token != null) && !isXPathInlineFunctionParse && !isFunctionArgumentParse) {
+      else if ((m_token != null) && !isXPathInlineFunctionParse && !m_isFunctionArgumentParse) {
          throw new javax.xml.transform.TransformerException("XPST0051 A sequence type occurence indicator '" + m_token + "', is not recognized."); 
       }
   }
@@ -3425,7 +3428,7 @@ public class XPathParser
   protected void Argument() throws javax.xml.transform.TransformerException
   {
 	
-    isFunctionArgumentParse = true;
+    m_isFunctionArgumentParse = true;
     
 	int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
     
@@ -3622,7 +3625,7 @@ public class XPathParser
     
     m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
                                            m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-    isFunctionArgumentParse = false;
+    m_isFunctionArgumentParse = false;
     
   }
 
@@ -4026,7 +4029,9 @@ public class XPathParser
   {
 
     int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-    int axesType;
+    int axesType = 0;
+    
+    boolean funcMatchFound = false;
 
     // The next blocks guarantee that a FROM_XXX will be added.
     if (lookahead("::", 1))
@@ -4041,23 +4046,98 @@ public class XPathParser
       axesType = OpCodes.FROM_ATTRIBUTES;
 
       appendOp(2, axesType);
-      nextToken();
+      nextToken();            
     }
-    else
+    else if (!isXPathPatternExcludeTrailingNodeFunctions() && (lookahead('(', 1) || (lookahead(':', 1) && lookahead('(', 3))))
     {
-      axesType = OpCodes.FROM_CHILDREN;
-
-      appendOp(2, axesType);
+    	TokenQueueScanPosition prevTokQueueScanPosition = new TokenQueueScanPosition(
+    			                                                                 m_queueMark, m_tokenChar, m_token);
+    	funcMatchFound = FunctionCall();
+    	if (funcMatchFound && (m_token != null)) {    		
+    	   restoreTokenQueueScanPosition(prevTokQueueScanPosition);
+    	   
+    	   axesType = OpCodes.FROM_CHILDREN;
+
+           appendOp(2, axesType);
+    	}
+    	else if (funcMatchFound) {    	   
+    	   m_queueMark = 0;
+    	   m_tokenChar = 0;
+    	   m_token = null;
+    	   
+    	   nextToken();
+    	   
+    	   int scanOneEndPos = 0;
+    	   int scanTwoStartPos = 0;
+    	   if (prevTokQueueScanPosition.getQueueMark() > 2) {
+    		   scanOneEndPos = (prevTokQueueScanPosition.getQueueMark() - 2);
+    		   scanTwoStartPos = prevTokQueueScanPosition.getQueueMark();
+    	   }
+    	   else if (prevTokQueueScanPosition.getQueueMark() > 1) {
+    		   scanOneEndPos = (prevTokQueueScanPosition.getQueueMark() - 1);
+    		   scanTwoStartPos = prevTokQueueScanPosition.getQueueMark();
+    	   }
+    	   
+    	   String xpathOneStr = "";
+    	   String xpathTwoStr = "";
+    	   xpathOneStr = m_token;
+    	   while (m_queueMark != scanOneEndPos) {
+    		   nextToken();
+    		   xpathOneStr += m_token; 
+    	   }
+    	   
+    	   for (int idx = scanOneEndPos; idx < scanTwoStartPos; idx++) {
+    		  nextToken(); 
+    	   }
+    	   
+    	   while (m_token != null) {
+    		   xpathTwoStr += m_token;
+    		   // The following is copy of the code of nextToken() 
+    		   // function, with minor modification for this need.
+    		   if (m_queueMark < m_ops.getTokenQueueSize())
+    		   {
+    			   Object tokenObj = m_ops.m_tokenQueue.elementAt(m_queueMark++);
+    			   if (tokenObj instanceof XString) {
+    				   m_token = "'" + ((XString)tokenObj).str() + "'";  
+    			   }
+    			   else {
+    				   m_token = (String)tokenObj; 
+    			   }
+    			   m_tokenChar = m_token.charAt(0);
+    		   }
+    		   else
+    		   {
+    			   m_token = null;
+    			   m_tokenChar = 0;
+    		   }
+    	   }
+    	   
+    	   m_pathExprFunctionSuffix = new XPathExprFunctionSuffix();    	   
+    	   m_pathExprFunctionSuffix.setXPathOneStr(xpathOneStr);
+    	   m_pathExprFunctionSuffix.setXPathTwoStr(xpathTwoStr);
+    	   
+    	   m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
+                                            m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+    	   
+    	}
     }
+    else
+    {       
+        axesType = OpCodes.FROM_CHILDREN;
 
-    // Make room for telling how long the step is without the predicate
-    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
-    NodeTest(axesType);
+        appendOp(2, axesType);
+    }
 
-    // Tell how long the step is without the predicate
-    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
-      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+    if (!funcMatchFound) {
+	    // Make room for telling how long the step is without the predicate
+	    m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
+	
+	    NodeTest(axesType);
+	
+	    // Tell how long the step is without the predicate
+	    m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
+	      m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+    }
    }
 
   /**
@@ -5158,7 +5238,7 @@ public class XPathParser
 	   XSTypeDefinition xsTypeDefinition = xsModel.getTypeDefinition(typeName, typeNamespace);
 	   
 	   if (xsTypeDefinition != null) {
-		   if (!isFunctionArgumentParse && tokenIs(')')) {
+		   if (!m_isFunctionArgumentParse && tokenIs(')')) {
 			   consumeExpected(')');
 		   }
 		   xpathSequenceTypeExpr.setXsSequenceTypeDefinition(xsTypeDefinition);
@@ -5641,5 +5721,40 @@ public class XPathParser
 	  m_tokenChar = tokQueueScanPosition.getTokenChar();
 	  m_token = tokQueueScanPosition.getToken();	
    }
+
+   public XPathExprFunctionSuffix getPathExprFunctionSuffix() {
+	   return m_pathExprFunctionSuffix;
+   }
+
+   public void setPathExprFunctionSuffix(XPathExprFunctionSuffix pathExprFunctionSuffix) {
+	   this.m_pathExprFunctionSuffix = pathExprFunctionSuffix;
+   }
+   
+   /**
+    * This method checks, whether there's a function call suffix at an 
+    * end of XPath expression similar to /a/b/funcCall(..).
+    */
+   private boolean isXPathPatternExcludeTrailingNodeFunctions() {
+	  boolean isExclude = false;
+	  
+	  String currXPathPattern = m_ops.m_currentPattern;
+	  if (currXPathPattern.endsWith("node()") || currXPathPattern.endsWith("text()") || currXPathPattern.endsWith("comment()")) {
+		  isExclude = true; 
+	  }
+	  else {
+		  String[] spltResult = currXPathPattern.split("node\\(\\)[\\s]*[\\|][\\s]*");
+		  if (spltResult.length > 1) {
+			 isExclude = true; 
+		  }
+		  if (!isExclude) {
+			 spltResult = currXPathPattern.split("comment\\(\\)\\[.*\\],");
+			 if (spltResult.length > 1) {
+				isExclude = true; 
+			 }
+		  }
+	  }
+	  
+	  return isExclude;
+   }
   
 }
diff --git a/src/org/apache/xpath/composite/XPathExprFunctionSuffix.java b/src/org/apache/xpath/composite/XPathExprFunctionSuffix.java
new file mode 100644
index 000000000..b93c2fc36
--- /dev/null
+++ b/src/org/apache/xpath/composite/XPathExprFunctionSuffix.java
@@ -0,0 +1,55 @@
+/*
+ * 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.composite;
+
+/**
+ * An object of this class, stores few run-time information
+ * for evaluation of XPath path expressions having function
+ * call suffix.
+ * 
+ * For e.g, an XPath expression /a/b/funcCall(.) is split into
+ * two XPath strings /a/b and funcCall(.). The function call 
+ * funcCall(.) is invoked on each node of the nodeset resulting 
+ * from evaluation of XPath expression /a/b.    
+ *
+ * @author Mukul Gandhi <mu...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+public class XPathExprFunctionSuffix {
+
+	private String m_xpathOneStr = null;
+	
+	private String m_xpathTwoStr = null;
+
+	public String getXPathOneStr() {
+		return m_xpathOneStr;
+	}
+
+	public void setXPathOneStr(String xpathOneStr) {
+		this.m_xpathOneStr = xpathOneStr;
+	}
+
+	public String getXPathTwoStr() {
+		return m_xpathTwoStr;
+	}
+
+	public void setXPathTwoStr(String xpathTwoStr) {
+		this.m_xpathTwoStr = xpathTwoStr;
+	}
+
+}
diff --git a/tests/org/apache/xalan/xpath3/XPathExprFunctionCallSuffixTests.java b/tests/org/apache/xalan/xpath3/XPathExprFunctionCallSuffixTests.java
new file mode 100644
index 000000000..fb226a281
--- /dev/null
+++ b/tests/org/apache/xalan/xpath3/XPathExprFunctionCallSuffixTests.java
@@ -0,0 +1,91 @@
+/*
+ * 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.xalan.xpath3;
+
+import org.apache.xalan.util.XSLConstants;
+import org.apache.xalan.util.XSLTransformTestsUtil;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * XPath 3.1 test cases to test, XPath path expressions 
+ * having function call suffix.
+ * 
+ * @author Mukul Gandhi <mu...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+public class XPathExprFunctionCallSuffixTests extends XSLTransformTestsUtil {        
+    
+    private static final String XSL_TRANSFORM_INPUT_DIRPATH = XSLConstants.XSL_TRANSFORM_INPUT_DIRPATH_PREFIX + "xpath_function_call_suffix/";
+    
+    private static final String XSL_TRANSFORM_GOLD_DIRPATH = XSLConstants.XSL_TRANSFORM_GOLD_DIRPATH_PREFIX + "xpath_function_call_suffix/gold/";
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+        // no op
+    }
+
+    @AfterClass
+    public static void tearDownAfterClass() throws Exception {        
+        xmlDocumentBuilderFactory = null;
+        xmlDocumentBuilder = null;
+        xslTransformerFactory = null;
+    }
+
+    @Test
+    public void xpathExprFunctionCallSuffixTest1() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xml"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test1.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
+    
+    @Test
+    public void xpathExprFunctionCallSuffixTest2() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xml"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test2.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test2.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
+    
+    @Test
+    public void xpathExprFunctionCallSuffixTest3() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xml"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test3.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test3.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
+    
+    @Test
+    public void xpathExprFunctionCallSuffixTest4() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xml"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test4.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test4.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
+
+}
diff --git a/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java b/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java
index 8c44b9ef5..9f8351b84 100644
--- a/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java
+++ b/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java
@@ -46,6 +46,7 @@ import org.apache.xalan.xpath3.ValueComparisonTests;
 import org.apache.xalan.xpath3.XPathArrayTests;
 import org.apache.xalan.xpath3.XPathArrowOpTests;
 import org.apache.xalan.xpath3.XPathCastTests;
+import org.apache.xalan.xpath3.XPathExprFunctionCallSuffixTests;
 import org.apache.xalan.xpath3.XPathGeneralComparisonOpTests;
 import org.apache.xalan.xpath3.XPathMapTests;
 import org.apache.xalan.xpath3.XPathNamedFunctionReferenceTests;
@@ -84,7 +85,8 @@ import org.junit.runners.Suite.SuiteClasses;
                 JiraIssuesTests.class, XPathNamedFunctionReferenceTests.class, FnApplyTests.class,
                 XPathGeneralComparisonOpTests.class, FnMapFindTests.class, XPathNumericArithmeticTests.class,
                 XslTunnelParameterTests.class, FuncBooleanValuesTests.class, XslElementValidationTests.class,
-                XslLiteralResultElementValidationTests.class, XslAttributeValidationTests.class })
+                XslLiteralResultElementValidationTests.class, XslAttributeValidationTests.class,
+                XPathExprFunctionCallSuffixTests.class })
 public class Xsl3TestSuite1 {
 
 }
diff --git a/tests/xpath_function_call_suffix/gold/test1.out b/tests/xpath_function_call_suffix/gold/test1.out
new file mode 100644
index 000000000..bac2334e7
--- /dev/null
+++ b/tests/xpath_function_call_suffix/gold/test1.out
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <one>2 4 10 20 30</one>
+  <two>hello 2 hello -4 hello 10 hello -20 hello 30</two>
+</result>
diff --git a/tests/xpath_function_call_suffix/gold/test2.out b/tests/xpath_function_call_suffix/gold/test2.out
new file mode 100644
index 000000000..e5ea57ec0
--- /dev/null
+++ b/tests/xpath_function_call_suffix/gold/test2.out
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><result>2 4 10 20 30</result>
diff --git a/tests/xpath_function_call_suffix/gold/test3.out b/tests/xpath_function_call_suffix/gold/test3.out
new file mode 100644
index 000000000..a96be1149
--- /dev/null
+++ b/tests/xpath_function_call_suffix/gold/test3.out
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <item>2</item>
+  <item>4</item>
+  <item>10</item>
+  <item>20</item>
+  <item>30</item>
+</result>
diff --git a/tests/xpath_function_call_suffix/gold/test4.out b/tests/xpath_function_call_suffix/gold/test4.out
new file mode 100644
index 000000000..a96be1149
--- /dev/null
+++ b/tests/xpath_function_call_suffix/gold/test4.out
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <item>2</item>
+  <item>4</item>
+  <item>10</item>
+  <item>20</item>
+  <item>30</item>
+</result>
diff --git a/tests/xpath_function_call_suffix/test1.xml b/tests/xpath_function_call_suffix/test1.xml
new file mode 100644
index 000000000..3c8f7c3ac
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test1.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<info>
+   <a val="2">a1</a>
+   <a val="-4">a2</a>
+   <a val="10">a3</a>
+   <a val="-20">a4</a>
+   <a val="30">a5</a>
+</info>
\ No newline at end of file
diff --git a/tests/xpath_function_call_suffix/test1.xsl b/tests/xpath_function_call_suffix/test1.xsl
new file mode 100644
index 000000000..54121cc64
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test1.xsl
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                				
+                version="3.0">
+                
+   <!-- Author: mukulg@apache.org -->                
+
+   <!-- use with test1.xml -->
+   
+   <!-- An XSLT test case to test, XPath path expression
+        having a function call suffix.
+   -->				
+
+   <xsl:output method="xml" indent="yes"/>
+
+   <xsl:template match="/info">
+	  <result>
+	     <one>
+            <xsl:value-of select=".//@val/abs(.)"/>
+		 </one>
+		 <two>
+           <xsl:value-of select=".//@val/concat('hello', ' ', .)"/>
+		 </two>
+      </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>
diff --git a/tests/xpath_function_call_suffix/test2.xsl b/tests/xpath_function_call_suffix/test2.xsl
new file mode 100644
index 000000000..028be9aae
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test2.xsl
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"				
+                version="3.0">
+                
+   <!-- Author: mukulg@apache.org -->                
+
+   <!-- use with test1.xml -->
+   
+   <!-- An XSLT test case to test, XPath path expression
+        having a function call suffix.
+   -->					
+
+   <xsl:output method="xml" indent="yes"/>
+
+   <xsl:template match="/info">
+	  <result>
+         <xsl:variable name="seq1" select=".//@val/abs(.)"/>
+		 <xsl:value-of select="$seq1"/>
+      </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>
diff --git a/tests/xpath_function_call_suffix/test3.xsl b/tests/xpath_function_call_suffix/test3.xsl
new file mode 100644
index 000000000..af1016809
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test3.xsl
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"				
+                version="3.0">
+                
+   <!-- Author: mukulg@apache.org -->                
+
+   <!-- use with test1.xml -->
+   
+   <!-- An XSLT test case to test, XPath path expression
+        having a function call suffix.
+   -->					
+
+   <xsl:output method="xml" indent="yes"/>
+
+   <xsl:template match="/info">
+	  <result>
+         <xsl:for-each select=".//@val/abs(.)">
+		   <item>
+		      <xsl:value-of select="."/>
+		   </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>
diff --git a/tests/xpath_function_call_suffix/test4.xsl b/tests/xpath_function_call_suffix/test4.xsl
new file mode 100644
index 000000000..ed3ad2448
--- /dev/null
+++ b/tests/xpath_function_call_suffix/test4.xsl
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"				
+                version="3.0">
+                
+   <!-- Author: mukulg@apache.org -->                
+
+   <!-- use with test1.xml -->
+   
+   <!-- An XSLT test case to test, XPath path expression
+        having a function call suffix.
+   -->					
+
+   <xsl:output method="xml" indent="yes"/>
+
+   <xsl:template match="/info">
+	  <result>
+         <xsl:iterate select=".//@val/abs(.)">
+		   <item>
+		      <xsl:value-of select="."/>
+		   </item>
+		 </xsl:iterate>
+      </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