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/06/04 11:11:03 UTC

(xalan-java) branch xalan-j_xslt3.0 updated: committing implementation of xpath3.1 arrow operator(=>), along with few working related 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 0a7d5184 committing implementation of xpath3.1 arrow operator(=>), along with few working related test cases
0a7d5184 is described below

commit 0a7d5184591f0e774b12e9533052b0973d99f8c6
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Tue Jun 4 16:40:49 2024 +0530

    committing implementation of xpath3.1 arrow operator(=>), along with few working related test cases
---
 src/org/apache/xalan/templates/ElemVariable.java   |   5 +-
 src/org/apache/xpath/XPath.java                    |  39 ++++-
 src/org/apache/xpath/compiler/Compiler.java        |  45 ++++--
 src/org/apache/xpath/compiler/Lexer.java           |  10 +-
 src/org/apache/xpath/compiler/OpCodes.java         |   4 +-
 src/org/apache/xpath/compiler/XPathParserImpl.java |  65 +++++---
 src/org/apache/xpath/functions/Function.java       |  18 ++-
 src/org/apache/xpath/functions/FunctionOneArg.java |   7 +-
 src/org/apache/xpath/operations/ArrowOp.java       | 164 +++++++++++++++++++++
 src/org/apache/xpath/operations/Operation.java     |  88 ++++++-----
 .../org/apache/xalan/xpath3/XPathArrowOpTests.java |  80 ++++++++++
 tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java   |   4 +-
 tests/xpath_arrow_op/gold/test1.out                |   9 ++
 tests/xpath_arrow_op/gold/test2.out                |   1 +
 tests/xpath_arrow_op/gold/test3.out                |   4 +
 tests/xpath_arrow_op/test1.xsl                     |  50 +++++++
 tests/xpath_arrow_op/test2.xsl                     |  35 +++++
 tests/xpath_arrow_op/test3.xsl                     |  41 ++++++
 18 files changed, 575 insertions(+), 94 deletions(-)

diff --git a/src/org/apache/xalan/templates/ElemVariable.java b/src/org/apache/xalan/templates/ElemVariable.java
index f8dce265..b35f0302 100644
--- a/src/org/apache/xalan/templates/ElemVariable.java
+++ b/src/org/apache/xalan/templates/ElemVariable.java
@@ -49,6 +49,7 @@ import org.apache.xpath.objects.XPathMap;
 import org.apache.xpath.objects.XRTreeFrag;
 import org.apache.xpath.objects.XRTreeFragSelectWrapper;
 import org.apache.xpath.objects.XString;
+import org.apache.xpath.operations.ArrowOp;
 import org.apache.xpath.operations.Operation;
 import org.apache.xpath.operations.Range;
 import org.apache.xpath.operations.SimpleMapOperator;
@@ -404,8 +405,8 @@ public class ElemVariable extends ElemTemplateElement
              
             return evalResult;
         }
-        else if (selectExpression instanceof Range) {
-            XObject evalResult = ((Range)selectExpression).execute(xctxt);
+        else if ((selectExpression instanceof Range) || (selectExpression instanceof ArrowOp)) {
+            XObject evalResult = selectExpression.execute(xctxt);
             
             if (m_asAttr != null) {
                evalResult = SequenceTypeSupport.convertXdmValueToAnotherType(evalResult, m_asAttr, null, xctxt);  
diff --git a/src/org/apache/xpath/XPath.java b/src/org/apache/xpath/XPath.java
index 297ab2cb..b54067e9 100644
--- a/src/org/apache/xpath/XPath.java
+++ b/src/org/apache/xpath/XPath.java
@@ -44,6 +44,7 @@ import org.apache.xpath.functions.Function;
 import org.apache.xpath.objects.ResultSequence;
 import org.apache.xpath.objects.XNumber;
 import org.apache.xpath.objects.XObject;
+import org.apache.xpath.operations.ArrowOp;
 import org.apache.xpath.res.XPATHErrorResources;
 
 import xml.xpath31.processor.types.XSNumericType;
@@ -71,6 +72,12 @@ public class XPath implements Serializable, ExpressionOwner
   // this class.    
   private Vector fVars;    
   private int fGlobalsSize;
+  
+  /**
+   * This class field, is used when evaluating an 
+   * XPath arrow operator, "=>".
+   */
+  private String fArrowOpRemainingXPathExprStr = null;
 
   /**
    * initial the function table
@@ -179,17 +186,22 @@ public class XPath implements Serializable, ExpressionOwner
     m_patternString = exprString;
 
     XPathParserImpl parser = new XPathParserImpl(errorListener, locator);
-    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
-
-    if (SELECT == type)
+    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);    
+    
+    if (SELECT == type) {
       parser.initXPath(compiler, exprString, prefixResolver, false);
+      fArrowOpRemainingXPathExprStr = parser.getArrowOpRemainingXPathExprStr();
+    }
     else if (MATCH == type)
       parser.initMatchPattern(compiler, exprString, prefixResolver);
     else
       throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, new Object[]{Integer.toString(type)})); //"Can not deal with XPath type: " + type);
 
     Expression expr = compiler.compile(0);
-
+    if (expr instanceof ArrowOp) {
+       ((ArrowOp)expr).setArrowOpRemainingXPathExprStr(fArrowOpRemainingXPathExprStr);
+    }
+    
     this.setExpression(expr);
     
     if((null != locator) && locator instanceof ExpressionNode)
@@ -226,10 +238,12 @@ public class XPath implements Serializable, ExpressionOwner
     m_patternString = exprString;
 
     XPathParserImpl parser = new XPathParserImpl(errorListener, locator);
-    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
-
-    if (SELECT == type)
+    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);    
+    
+    if (SELECT == type) {
       parser.initXPath(compiler, exprString, prefixResolver, false);
+      fArrowOpRemainingXPathExprStr = parser.getArrowOpRemainingXPathExprStr();
+    }
     else if (MATCH == type)
       parser.initMatchPattern(compiler, exprString, prefixResolver);
     else
@@ -238,6 +252,9 @@ public class XPath implements Serializable, ExpressionOwner
             new Object[]{Integer.toString(type)})); 
 
     Expression expr = compiler.compile(0);
+    if (expr instanceof ArrowOp) {
+      ((ArrowOp)expr).setArrowOpRemainingXPathExprStr(fArrowOpRemainingXPathExprStr);
+    }
 
     this.setExpression(expr);
     
@@ -776,4 +793,12 @@ public class XPath implements Serializable, ExpressionOwner
    */
   public static final double MATCH_SCORE_OTHER = 0.5;
 
+  public String getArrowOpRemainingXPathExprStr() {
+	 return fArrowOpRemainingXPathExprStr;
+  }
+
+  public void setArrowOpRemainingXPathExprStr(String arrowOpRemainingXPathExprStr) {
+	 this.fArrowOpRemainingXPathExprStr = arrowOpRemainingXPathExprStr;
+  }
+
 }
diff --git a/src/org/apache/xpath/compiler/Compiler.java b/src/org/apache/xpath/compiler/Compiler.java
index 0f28468f..eb0ee2f8 100644
--- a/src/org/apache/xpath/compiler/Compiler.java
+++ b/src/org/apache/xpath/compiler/Compiler.java
@@ -41,6 +41,7 @@ import org.apache.xpath.functions.WrongNumberArgsException;
 import org.apache.xpath.objects.XNumber;
 import org.apache.xpath.objects.XString;
 import org.apache.xpath.operations.And;
+import org.apache.xpath.operations.ArrowOp;
 import org.apache.xpath.operations.CastAs;
 import org.apache.xpath.operations.CastableAs;
 import org.apache.xpath.operations.Div;
@@ -201,6 +202,10 @@ public class Compiler extends OpMap
       expr = range(opPos); break;
     case OpCodes.OP_STR_CONCAT :
       expr = strConcat(opPos); break;
+    case OpCodes.OP_ARROW :
+      fIsCompileFuncPrecededByCompileArrow = true;
+      expr = arrowOp(opPos);
+      break;
     case OpCodes.OP_MINUS :
       expr = minus(opPos); break;
     case OpCodes.OP_MULT :
@@ -257,7 +262,7 @@ public class Compiler extends OpMap
     }
 //    if(null != expr)
 //      expr.setSourceLocator(m_locator);
-
+    
     return expr;
   }
 
@@ -653,6 +658,18 @@ public class Compiler extends OpMap
   {
     return compileOperation(new StrConcat(), opPos);   
   }
+  
+  /**
+   * Compile an XPath 3.1 arrow "=>" operation.
+   * 
+   * @param opPos The current position in the m_opMap array.
+   * 
+   * @throws TransformerException if a error occurs creating the Expression.
+   */
+  protected Expression arrowOp(int opPos) throws TransformerException
+  {
+	return compileOperation(new ArrowOp(), opPos);
+  }
 
   /**
    * Compile a '-' operation.
@@ -1334,13 +1351,18 @@ private static final boolean DEBUG = false;
         int i = 0;
 
         for (int p = opPos; p < endFunc; p = getNextOpPos(p), i++)
+        {          
+           func.setArg(compile(p), i);
+        }
+        
+        if (fIsCompileFuncPrecededByCompileArrow) 
         {
-
-          // System.out.println("argPos: "+ p);
-          // System.out.println("argCode: "+ m_opMap[p]);
-          func.setArg(compile(p), i);
+           // This allows us to, permit the absence of XPath function's 1st 
+           // argument when evaluating with operator "=>".
+           i++;
+           fIsCompileFuncPrecededByCompileArrow = false;
         }
-
+        
         func.checkNumberArgs(i);
       }
       catch (WrongNumberArgsException wnae)
@@ -1348,9 +1370,8 @@ private static final boolean DEBUG = false;
         java.lang.String name = m_functionTable.getFunctionName(funcID);
 
         m_errorHandler.fatalError( new TransformerException(
-                  XSLMessages.createXPATHMessage(XPATHErrorResources.ER_ONLY_ALLOWS, 
-                      new Object[]{name, wnae.getMessage()}), m_locator)); 
-              //"name + " only allows " + wnae.getMessage() + " arguments", m_locator));
+                                             XSLMessages.createXPATHMessage(XPATHErrorResources.ER_ONLY_ALLOWS, 
+                                             new Object[]{name, wnae.getMessage()}), m_locator));
       }
 
       return func;
@@ -1693,4 +1714,10 @@ private static final boolean DEBUG = false;
    * The FunctionTable for all xpath build-in functions
    */
   private FunctionTable m_functionTable;
+  
+  /**
+   * We store within this class field, the fact that, there is
+   * an XPath expression of kind "... => functionCall()".
+   */
+  private boolean fIsCompileFuncPrecededByCompileArrow;
 }
diff --git a/src/org/apache/xpath/compiler/Lexer.java b/src/org/apache/xpath/compiler/Lexer.java
index cd4e85c8..b93901f5 100644
--- a/src/org/apache/xpath/compiler/Lexer.java
+++ b/src/org/apache/xpath/compiler/Lexer.java
@@ -258,7 +258,7 @@ class Lexer
         if ((pat.length() > (i + 1)) && (pat.charAt(i + 1) == '|')) {
           // added for XPath 3.1.
           // to recognize the character sequence "||", as an XPath 
-          // language token.
+          // token.
           addToTokenQueue(pat.substring(i, i + 2));
           i += 1;
           break;
@@ -267,6 +267,14 @@ class Lexer
       case '*' :
       case '+' :
       case '=' :
+    	if ((pat.length() > (i + 1)) && (pat.charAt(i + 1) == '>')) {
+           // added for XPath 3.1.
+           // to recognize the character sequence "=>", as an XPath 
+           // token.
+           addToTokenQueue(pat.substring(i, i + 2));
+           i += 1;
+           break;
+        }
       case ',' :      
       case '\\' :  // Unused at the moment
       case '^' :   // Unused at the moment
diff --git a/src/org/apache/xpath/compiler/OpCodes.java b/src/org/apache/xpath/compiler/OpCodes.java
index 9105c9dc..159350fe 100644
--- a/src/org/apache/xpath/compiler/OpCodes.java
+++ b/src/org/apache/xpath/compiler/OpCodes.java
@@ -736,8 +736,10 @@ public class OpCodes
   public static final int OP_CAST_AS = 77;
   
   public static final int OP_CASTABLE_AS = 78;
+  
+  public static final int OP_ARROW = 79;
 
   /** The next free ID. Please keep this up to date. */
-  private static final int NEXT_FREE_ID = 79;
+  private static final int NEXT_FREE_ID = 80;
   
 }
diff --git a/src/org/apache/xpath/compiler/XPathParserImpl.java b/src/org/apache/xpath/compiler/XPathParserImpl.java
index 38960213..5e1e26e3 100644
--- a/src/org/apache/xpath/compiler/XPathParserImpl.java
+++ b/src/org/apache/xpath/compiler/XPathParserImpl.java
@@ -47,8 +47,8 @@ import org.apache.xpath.composite.SequenceTypeFunctionTest;
 import org.apache.xpath.composite.SequenceTypeKindTest;
 import org.apache.xpath.composite.SequenceTypeMapTest;
 import org.apache.xpath.composite.SequenceTypeSupport;
-import org.apache.xpath.composite.XPathSequenceConstructor;
 import org.apache.xpath.composite.XPathArrayConstructor;
+import org.apache.xpath.composite.XPathSequenceConstructor;
 import org.apache.xpath.composite.XPathSequenceTypeExpr;
 import org.apache.xpath.domapi.XPathStylesheetDOM3Exception;
 import org.apache.xpath.functions.DynamicFunctionCall;
@@ -119,8 +119,8 @@ public class XPathParserImpl
                                                       "eq", "ne", "lt", "gt", "le", "ge", 
                                                       "for", "in", "return", "if", "then", 
                                                       "else", "some", "every", "satisfies", 
-                                                      "let", ":=", "-", "||", "instance", "of", 
-                                                      "as" };
+                                                      "let", ":=", "-", "||", "=>", "instance", 
+                                                      "of", "as" };
   
   // If an XPath expression is () (i.e, representing an xdm empty sequence),
   // we translate that within this XPath parser implementation, to an XPath
@@ -158,6 +158,8 @@ public class XPathParserImpl
   
   static XPathSequenceTypeExpr fXpathSequenceTypeExpr = null;
   
+  private String fArrowOpRemainingXPathExprStr = null;
+  
   /**
    * The parser constructor.
    */
@@ -2095,7 +2097,7 @@ public class XPathParserImpl
         }
         else 
         {
-           // Support for XPath 3.1 language simple map operator '!'.
+           // Support for XPath 3.1 simple map operator '!'.
         	
            // An XPath expression SEQ!Expr evaluates an expression Expr once for every 
            // item in the sequence obtained by evaluating SEQ. An XPath language operator "!" 
@@ -2130,7 +2132,7 @@ public class XPathParserImpl
       }
       else if (tokenIs("eq"))
       {
-        // Support for XPath 3.1 language value comparison operator "eq"
+        // Support for XPath 3.1 value comparison operator "eq"
           
         nextToken();
         insertOp(addPos, 2, OpCodes.OP_VC_EQUALS);
@@ -2144,7 +2146,7 @@ public class XPathParserImpl
       }
       else if (tokenIs("ne"))
       {
-        // Support for XPath 3.1 language value comparison operator "ne"
+        // Support for XPath 3.1 value comparison operator "ne"
           
         nextToken();
         insertOp(addPos, 2, OpCodes.OP_VC_NOT_EQUALS);
@@ -2211,7 +2213,7 @@ public class XPathParserImpl
         }
         else if (tokenIs('<'))
         {
-          // Support for XPath 3.1 language node comparison operator "<<"
+          // Support for XPath 3.1 node comparison operator "<<"
           
           nextToken();
           insertOp(addPos, 2, OpCodes.OP_NC_PRECEDE); 
@@ -2239,7 +2241,7 @@ public class XPathParserImpl
         }
         else if (tokenIs('>'))
         {
-          // Support for XPath 3.1 language node comparison operator ">>"
+          // Support for XPath 3.1 node comparison operator ">>"
           
           nextToken();
           insertOp(addPos, 2, OpCodes.OP_NC_FOLLOWS);
@@ -2258,7 +2260,7 @@ public class XPathParserImpl
       }
       else if (tokenIs("to"))
       {
-          // Support for XPath 3.1 language range expressions.
+          // Support for XPath 3.1 range expression (i.e, an XPath operator "to").
     	  
     	  // An XPath language range expressions are used to construct a 
     	  // sequence of consecutive integers. For e.g, an XPath language range expression
@@ -2279,7 +2281,7 @@ public class XPathParserImpl
       }
       else if (tokenIs("||"))
       {
-          // Support for XPath 3.1 language string concatenation operator "||".
+          // Support for XPath 3.1 string concatenation operator "||".
     	  
     	  // An XPath language string concatenation operator "||" may be used as an
     	  // alternative means, for XPath string concatenation function 'concat'.
@@ -2295,9 +2297,28 @@ public class XPathParserImpl
             m_ops.getOp(addPos + op1 + 1) + op1);
           addPos += 2; 
       }
+      else if (tokenIs("=>"))
+      {
+          // Support for XPath 3.1 arrow operator "=>"
+    	  
+          consumeExpected("=>");
+
+          insertOp(addPos, 2, OpCodes.OP_ARROW);
+          
+          FunctionCall();
+          
+          if (m_token != null) {
+        	 fArrowOpRemainingXPathExprStr = "";  
+          }
+          
+          while (m_token != null) {
+        	 fArrowOpRemainingXPathExprStr += m_token;
+        	 nextToken();
+          }
+      }
       else if (tokenIs("lt"))
       {
-          // Support for XPath 3.1 language value comparison operator "lt"
+          // Support for XPath 3.1 value comparison operator "lt"
           
           nextToken();
         
@@ -2312,7 +2333,7 @@ public class XPathParserImpl
       }
       else if (tokenIs("gt"))
       {
-          // Support for XPath 3.1 language value comparison operator "gt"
+          // Support for XPath 3.1 value comparison operator "gt"
           
           nextToken();
         
@@ -2327,7 +2348,7 @@ public class XPathParserImpl
       }
       else if (tokenIs("le"))
       {
-          // Support for XPath 3.1 language value comparison operator "le"
+          // Support for XPath 3.1 value comparison operator "le"
           
           nextToken();
         
@@ -2342,7 +2363,7 @@ public class XPathParserImpl
       }
       else if (tokenIs("ge"))
       {
-          // Support for XPath 3.1 language value comparison operator "ge"
+          // Support for XPath 3.1 value comparison operator "ge"
           
           nextToken();
         
@@ -2357,7 +2378,7 @@ public class XPathParserImpl
       }
       else if (tokenIs("is"))
       {
-          // Support for XPath 3.1 language node comparison operator "is"
+          // Support for XPath 3.1 node comparison operator "is"
           
           nextToken();
         
@@ -2371,7 +2392,7 @@ public class XPathParserImpl
           addPos += 2;
       }
       else if (tokenIs("instance") && lookahead("of", 1)) {
-          // Support for XPath 3.1 language "instance of" expression.
+          // Support for XPath 3.1 "instance of" expression.
     	  
     	  // An XPath language, "instance of" operator requires two operands. An 
     	  // XPath evaluation for "instance of" expression returns value 'true',
@@ -2392,7 +2413,7 @@ public class XPathParserImpl
           addPos += 2;
       }      
       else if (tokenIs("cast") && lookahead("as", 1)) {
-          // Support for XPath 3.1 language "cast as" expression.
+          // Support for XPath 3.1 "cast as" expression.
     	  
     	  // An XPath expression "cast as" converts an input value, 
     	  // to another value of a specific datatype.
@@ -2413,7 +2434,7 @@ public class XPathParserImpl
           addPos += 2;
       }
       else if (tokenIs("castable") && lookahead("as", 1)) {
-          // Support for XPath 3.1 language "castable as" expression.
+          // Support for XPath 3.1 "castable as" expression.
     	  
     	  // An XPath expression "castable as" tests (i.e, returns true, or false) 
     	  // whether a given value is castable into another datatype.
@@ -2870,7 +2891,7 @@ public class XPathParserImpl
       matchFound = true;
     }
     else if ((m_tokenChar == '$') && (lookahead('(', 2))) {
-       // Support for XPath 3.1 language dynamic function calls.
+       // Support for XPath 3.1 'dynamic function call' expression.
     	
        // For example, if an XPath inline function expression is bound
        // to an XSLT variable as follows : <xsl:variable name="sqr" select="function($val) { $val * $val }"/>,
@@ -2975,7 +2996,7 @@ public class XPathParserImpl
       matchFound = true;
     }
     else if (tokenIs("function") && lookahead('(', 1)) {
-      // Support for XPath 3.1 language "inline function" expressions
+      // Support for XPath 3.1 "inline function" expression
     	
       // Ref : https://www.w3.org/TR/xpath-31/#id-inline-func
       
@@ -4803,4 +4824,8 @@ public class XPathParserImpl
 	  }	  
    }
   
+   public String getArrowOpRemainingXPathExprStr() {
+	   return fArrowOpRemainingXPathExprStr; 
+   }
+  
 }
diff --git a/src/org/apache/xpath/functions/Function.java b/src/org/apache/xpath/functions/Function.java
index 6e89ce37..7b157d08 100644
--- a/src/org/apache/xpath/functions/Function.java
+++ b/src/org/apache/xpath/functions/Function.java
@@ -40,9 +40,14 @@ import org.apache.xpath.objects.XObject;
  */
 public abstract class Function extends Expression
 {
-    static final long serialVersionUID = 6927661240854599768L;
+   static final long serialVersionUID = 6927661240854599768L;
+    
+   /** 
+    * The first argument passed to the function (at index 0).
+    */
+   protected Expression m_arg0;
 
-  /**
+   /**
    * Set an argument expression for a function.  This method is called by the 
    * XPath compiler.
    *
@@ -141,4 +146,13 @@ public abstract class Function extends Expression
   {
     // no default action
   }
+  
+  public Expression getArg0()
+  {
+    return m_arg0;
+  }
+  
+  public void setArg0(Expression arg0) {
+	m_arg0 = arg0; 
+  }
 }
diff --git a/src/org/apache/xpath/functions/FunctionOneArg.java b/src/org/apache/xpath/functions/FunctionOneArg.java
index dd9a9bab..124e9d87 100644
--- a/src/org/apache/xpath/functions/FunctionOneArg.java
+++ b/src/org/apache/xpath/functions/FunctionOneArg.java
@@ -31,12 +31,7 @@ import org.apache.xpath.XPathVisitor;
  */
 public class FunctionOneArg extends Function implements ExpressionOwner
 {
-    static final long serialVersionUID = -5180174180765609758L;
-
-  /** The first argument passed to the function (at index 0).
-   *
-   */
-  Expression m_arg0;
+  static final long serialVersionUID = -5180174180765609758L;
   
   private boolean fArgCountErr = false;
 
diff --git a/src/org/apache/xpath/operations/ArrowOp.java b/src/org/apache/xpath/operations/ArrowOp.java
new file mode 100644
index 00000000..a465fe26
--- /dev/null
+++ b/src/org/apache/xpath/operations/ArrowOp.java
@@ -0,0 +1,164 @@
+/*
+ * 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.operations;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xalan.xslt.util.XslTransformEvaluationHelper;
+import org.apache.xpath.Expression;
+import org.apache.xpath.XPath;
+import org.apache.xpath.XPathContext;
+import org.apache.xpath.functions.Function;
+import org.apache.xpath.functions.Function2Args;
+import org.apache.xpath.functions.Function3Args;
+import org.apache.xpath.functions.FunctionOneArg;
+import org.apache.xpath.functions.WrongNumberArgsException;
+import org.apache.xpath.objects.XObject;
+
+/**
+ * Implementation of XPath 3.1 arrow operator, "=>". 
+ * 
+ * Ref : https://www.w3.org/TR/xpath-31/#id-arrow-operator
+ * 
+ * @author Mukul Gandhi <mu...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+public class ArrowOp extends Operation
+{
+
+	private static final long serialVersionUID = 4577709194385888770L;
+	
+	private java.lang.String fArrowOpRemainingXPathExprStr;
+
+  /**
+   * Evaluate an XPath arrow operator, and return the result.
+   *
+   * @param left non-null reference to the evaluated left operand.
+   * @param right non-null reference to the evaluated right operand.
+   *
+   * @return non-null reference to the XObject that represents the result of the operation.
+   *
+   * @throws javax.xml.transform.TransformerException
+   */
+    public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException {
+        
+      XObject result = null;
+      
+      SourceLocator srcLocator = xctxt.getSAXLocator();
+      
+      Function function = (Function)m_right;
+      
+      if (function instanceof Function3Args) {
+     	 Function3Args funcThreeArgs = (Function3Args)function;
+     	 Expression arg0 = funcThreeArgs.getArg0();
+     	 Expression arg1 = funcThreeArgs.getArg1();
+     	 funcThreeArgs.setArg0(m_left);
+     	 
+     	 try {
+     		if (arg0 != null) {
+     	       funcThreeArgs.setArg(arg0, 1);
+     		}
+     		if (arg1 != null) {
+     	       funcThreeArgs.setArg(arg1, 2);
+     		}
+     	 }
+     	 catch (WrongNumberArgsException ex) {
+  		    throw new javax.xml.transform.TransformerException("FORX0003 : An error occured, during evaluation for "
+  		    		                                                                      + "operator =>.", srcLocator);
+	     }
+     	 
+     	 result = funcThreeArgs.execute(xctxt);
+      }
+      else if (function instanceof Function2Args) {
+    	 Function2Args funcTwoArgs = (Function2Args)function;
+    	 Expression arg1 = funcTwoArgs.getArg1();
+    	 
+    	 if (arg1 != null) {
+     	    throw new javax.xml.transform.TransformerException("FORX0003 : The function's 2nd argument cannot be provided "
+     	    		                                                   + "lexically for an XPath function of arity 2 with evaluation using "
+     	    		                                                   + "operator =>.", srcLocator); 
+     	 }
+     	 else {     		
+     		Expression arg0 = funcTwoArgs.getArg0();
+     		funcTwoArgs.setArg0(m_left);
+     		try {     		   
+			   funcTwoArgs.setArg(arg0, 1);
+			} 
+     		catch (WrongNumberArgsException ex) {
+     		   throw new javax.xml.transform.TransformerException("FORX0003 : An error occured, during evaluation for "
+     		   		                                                      + "operator =>.", srcLocator);
+			}
+     		result = funcTwoArgs.execute(xctxt);
+     	 }
+      }
+      else if (function instanceof FunctionOneArg) {
+     	 FunctionOneArg funcOneArg = (FunctionOneArg)function;
+     	 Expression arg0 = funcOneArg.getArg0();
+     	 
+     	 if (arg0 != null) {
+     	    throw new javax.xml.transform.TransformerException("FORX0003 : The function's 1st argument cannot be provided lexically "
+     	    		                                                   + "for an XPath function of arity 1 with evaluation using "
+     	    		                                                   + "operator =>.", srcLocator); 
+     	 }
+     	 else {
+     		funcOneArg.setArg0(m_left);
+     		result = funcOneArg.execute(xctxt);
+     	 }
+      }
+      
+      if (fArrowOpRemainingXPathExprStr != null) {         
+          result = getFinalResult(result, fArrowOpRemainingXPathExprStr, xctxt);
+      }
+      
+      return result;
+    }
+
+    public java.lang.String getArrowOpRemainingXPathExprStr() {
+	   return fArrowOpRemainingXPathExprStr;
+    }
+
+    public void setArrowOpRemainingXPathExprStr(java.lang.String arrowOpRemainingXPathExprStr) {
+	   this.fArrowOpRemainingXPathExprStr = arrowOpRemainingXPathExprStr;
+    }
+    
+    /**
+     * This method handles more than one '=>' operator occurrences, within an XPath expression. 
+     */
+    private XObject getFinalResult(XObject prevResult, java.lang.String arrowOpRemainingXPathExprStr, 
+    		                                                                               XPathContext xctxt) throws TransformerException {
+       XObject result = null;
+       
+       SourceLocator srcLocator = xctxt.getSAXLocator(); 
+       
+       java.lang.String resultStr = XslTransformEvaluationHelper.getStrVal(prevResult); 
+  	   java.lang.String xpathStr = "'" + resultStr + "'" + arrowOpRemainingXPathExprStr;
+  	   XPath xpath = new XPath(xpathStr, srcLocator, xctxt.getNamespaceContext(), XPath.SELECT, null);
+       int contextNode = xctxt.getCurrentNode();
+       result = xpath.execute(xctxt, contextNode, xctxt.getNamespaceContext());
+       
+       java.lang.String xpathStr2 = xpath.getArrowOpRemainingXPathExprStr();
+       if (xpathStr2 != null) {         
+          result = getFinalResult(result, xpathStr2, xctxt); 
+       }
+       
+       return result;
+    }
+    
+}
diff --git a/src/org/apache/xpath/operations/Operation.java b/src/org/apache/xpath/operations/Operation.java
index da2508be..426d4a92 100644
--- a/src/org/apache/xpath/operations/Operation.java
+++ b/src/org/apache/xpath/operations/Operation.java
@@ -118,51 +118,49 @@ public class Operation extends Expression implements ExpressionOwner
     
     XObject right = null;
     
-        if (m_left instanceof FuncExtFunction) {
-           FuncExtFunction extFunction = (FuncExtFunction)m_left;
-           if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(extFunction.getNamespace())) {
-              left = XSConstructorFunctionUtil.processFuncExtFunctionOrXPathOpn(
-                                                                             xctxt, m_left, null); 
-           }
-           else {
-              left = m_left.execute(xctxt, true);  
-           }
-        }
-        else if (m_left instanceof SelfIteratorNoPredicate) {
-           XObject xpath3ContextItem = xctxt.getXPath3ContextItem();
-           if (xpath3ContextItem != null) {
-              left = xpath3ContextItem;     
-           }
-           else {
-              left = m_left.execute(xctxt, true);   
-           }
-        }
-        else {
-           left = m_left.execute(xctxt, true); 
-        }
-        
-        if (m_right instanceof FuncExtFunction) {
-           FuncExtFunction extFunction = (FuncExtFunction)m_right;
-           if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(extFunction.getNamespace())) {
-              right = XSConstructorFunctionUtil.processFuncExtFunctionOrXPathOpn(
-                                                                            xctxt, m_right, null); 
-           }
-           else {
-              right = m_right.execute(xctxt, true);  
-           }
-        }
-        else if (m_right instanceof SelfIteratorNoPredicate) {
-            XObject xpath3ContextItem = xctxt.getXPath3ContextItem();
-            if (xpath3ContextItem != null) {
-               right = xpath3ContextItem;     
-            }
-            else {
-               right = m_right.execute(xctxt, true);   
-            }
-        }
-        else {
-           right = m_right.execute(xctxt, true); 
-        }
+    if (m_left instanceof FuncExtFunction) {
+    	FuncExtFunction extFunction = (FuncExtFunction)m_left;
+    	if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(extFunction.getNamespace())) {
+    		left = XSConstructorFunctionUtil.processFuncExtFunctionOrXPathOpn(xctxt, m_left, null); 
+    	}
+    	else {
+    		left = m_left.execute(xctxt, true);  
+    	}
+    }
+    else if (m_left instanceof SelfIteratorNoPredicate) {
+    	XObject xpath3ContextItem = xctxt.getXPath3ContextItem();
+    	if (xpath3ContextItem != null) {
+    		left = xpath3ContextItem;     
+    	}
+    	else {
+    		left = m_left.execute(xctxt, true);   
+    	}
+    }
+    else {
+    	left = m_left.execute(xctxt, true); 
+    }
+
+    if (m_right instanceof FuncExtFunction) {
+    	FuncExtFunction extFunction = (FuncExtFunction)m_right;
+    	if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(extFunction.getNamespace())) {
+    		right = XSConstructorFunctionUtil.processFuncExtFunctionOrXPathOpn(xctxt, m_right, null); 
+    	}
+    	else {
+    		right = m_right.execute(xctxt, true);  
+    	}
+    }
+    else if (m_right instanceof SelfIteratorNoPredicate) {
+    	XObject xpath3ContextItem = xctxt.getXPath3ContextItem();
+    	if (xpath3ContextItem != null) {
+    		right = xpath3ContextItem;     
+    	}
+    	else {
+    		right = m_right.execute(xctxt, true);   
+    	}
+    }
+    else {
+    	right = m_right.execute(xctxt, true); 
+    }
 
     XObject result = operate(left, right);
     
diff --git a/tests/org/apache/xalan/xpath3/XPathArrowOpTests.java b/tests/org/apache/xalan/xpath3/XPathArrowOpTests.java
new file mode 100644
index 00000000..6012bd81
--- /dev/null
+++ b/tests/org/apache/xalan/xpath3/XPathArrowOpTests.java
@@ -0,0 +1,80 @@
+/*
+ * 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 arrow operator "=>", test cases.
+ * 
+ * @author Mukul Gandhi <mu...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+public class XPathArrowOpTests extends XSLTransformTestsUtil {        
+    
+    private static final String XSL_TRANSFORM_INPUT_DIRPATH = XSLConstants.XSL_TRANSFORM_INPUT_DIRPATH_PREFIX + "xpath_arrow_op/";
+    
+    private static final String XSL_TRANSFORM_GOLD_DIRPATH = XSLConstants.XSL_TRANSFORM_GOLD_DIRPATH_PREFIX + "xpath_arrow_op/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 xslXPathArrowOpTest1() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xsl"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test1.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
+    
+    @Test
+    public void xslXPathArrowOpTest2() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test2.xsl"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test2.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test2.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
+    
+    @Test
+    public void xslXPathArrowOpTest3() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test3.xsl"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test3.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test3.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 76130ea0..444f0e47 100644
--- a/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java
+++ b/tests/org/apache/xalan/xslt3/Xsl3TestSuite1.java
@@ -34,6 +34,7 @@ import org.apache.xalan.xpath3.InlineFunctionItemExprTests;
 import org.apache.xalan.xpath3.StringTests;
 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.XPathMapTests;
 import org.apache.xalan.xpath3.XsConstructorFunctionTests;
@@ -64,7 +65,8 @@ import org.junit.runners.Suite.SuiteClasses;
                 XmlDotComHigherOrderFunctionsTests.class, FnDefaultCollation.class,
                 FnBaseUriTests.class, FnDocumentUriTests.class, FnResolveQNameTests.class,
                 FnQNameTests.class, XPathMapTests.class, XPathArrayTests.class, FnLangTests.class, 
-                FnRootTests.class, FnParseJsonTests.class, XPathCastTests.class })
+                FnRootTests.class, FnParseJsonTests.class, XPathCastTests.class,
+                XPathArrowOpTests.class })
 public class Xsl3TestSuite1 {
 
 }
diff --git a/tests/xpath_arrow_op/gold/test1.out b/tests/xpath_arrow_op/gold/test1.out
new file mode 100644
index 00000000..02701672
--- /dev/null
+++ b/tests/xpath_arrow_op/gold/test1.out
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <one>ABC</one>
+  <two>true</two>
+  <three>false</three>
+  <four>
+    <item>abc</item>
+    <item>pqr</item>
+  </four>
+</result>
diff --git a/tests/xpath_arrow_op/gold/test2.out b/tests/xpath_arrow_op/gold/test2.out
new file mode 100644
index 00000000..03d7c615
--- /dev/null
+++ b/tests/xpath_arrow_op/gold/test2.out
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><result>ABCDEFGHI</result>
diff --git a/tests/xpath_arrow_op/gold/test3.out b/tests/xpath_arrow_op/gold/test3.out
new file mode 100644
index 00000000..2d5077b9
--- /dev/null
+++ b/tests/xpath_arrow_op/gold/test3.out
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <one>CD</one>
+  <two>ABCDEF, hi there</two>
+</result>
diff --git a/tests/xpath_arrow_op/test1.xsl b/tests/xpath_arrow_op/test1.xsl
new file mode 100644
index 00000000..1066f358
--- /dev/null
+++ b/tests/xpath_arrow_op/test1.xsl
@@ -0,0 +1,50 @@
+<?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 -->
+    
+    <!-- An XSLT 3.0 test case, to test XPath 3.1 operator "=>" -->                              
+
+    <xsl:output method="xml" indent="yes"/>
+
+    <xsl:template match="/">
+       <result>
+         <one>
+           <xsl:value-of select="'abc' => upper-case()"/>
+         </one>
+         <two>
+           <xsl:value-of select="'mnopqr abc' => contains('abc')"/>
+         </two>
+         <three>
+           <xsl:value-of select="'mnopqr' => contains('abc')"/>
+         </three>
+         <four>
+           <xsl:for-each select="'abc pqr' => tokenize('\s+')">
+             <item>
+               <xsl:value-of select="."/>
+             </item>
+           </xsl:for-each>
+         </four>
+       </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_arrow_op/test2.xsl b/tests/xpath_arrow_op/test2.xsl
new file mode 100644
index 00000000..bd4b4b35
--- /dev/null
+++ b/tests/xpath_arrow_op/test2.xsl
@@ -0,0 +1,35 @@
+<?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 -->
+    
+    <!-- An XSLT 3.0 test case, to test XPath 3.1 operator "=>" -->                              
+
+    <xsl:output method="xml" indent="yes"/>
+
+    <xsl:template match="/">
+       <result>
+         <xsl:value-of select="concat('abc', 'def', 'ghi') => upper-case()"/>
+       </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_arrow_op/test3.xsl b/tests/xpath_arrow_op/test3.xsl
new file mode 100644
index 00000000..478c19e9
--- /dev/null
+++ b/tests/xpath_arrow_op/test3.xsl
@@ -0,0 +1,41 @@
+<?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 -->
+    
+    <!-- An XSLT 3.0 test case, to test XPath 3.1 operator "=>" -->                              
+
+    <xsl:output method="xml" indent="yes"/>
+
+    <xsl:template match="/">
+       <result>
+         <one>
+           <xsl:value-of select="concat('abcde', 'fghij', 'klmno') => upper-case() => substring(3, 2)"/>
+         </one>
+         <two>
+           <xsl:variable name="string" select="'abcdef'"/>
+           <xsl:value-of select="$string => upper-case() => concat(', hi there')"/>
+         </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>


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xalan.apache.org
For additional commands, e-mail: commits-help@xalan.apache.org