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 2023/08/13 15:48:12 UTC

[xalan-java] branch xalan-j_xslt3.0 updated: committing implementation of xpath 3.1 fn:sort function, and few new working related test cases as well. with this commit, there's an implementation of xpath sequence expression () as well, which is an xpath syntax used to denote an empty sequence.

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 d8b535ac committing implementation of xpath 3.1 fn:sort function, and few new working related test cases as well. with this commit, there's an implementation of xpath sequence expression () as well, which is an xpath syntax used to denote an empty sequence.
     new 52054a5a Merge pull request #50 from mukulga/xalan-j_xslt3.0_mukul
d8b535ac is described below

commit d8b535ac36509d727a281159f635877f575f6b4e
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Sun Aug 13 21:13:49 2023 +0530

    committing implementation of xpath 3.1 fn:sort function, and few new working related test cases as well. with this commit, there's an implementation of xpath sequence expression () as well, which is an xpath syntax used to denote an empty sequence.
---
 src/org/apache/xpath/compiler/FunctionTable.java |   9 +-
 src/org/apache/xpath/compiler/Keywords.java      |   3 +
 src/org/apache/xpath/compiler/XPathParser.java   |  42 ++-
 src/org/apache/xpath/functions/FuncSort.java     | 327 +++++++++++++++++++++++
 src/org/apache/xpath/objects/XObject.java        |  44 ++-
 tests/fn_fold_left/test1.xsl                     |   4 +-
 tests/fn_sort/gold/test1.out                     |   6 +
 tests/fn_sort/gold/test2.out                     |   4 +
 tests/fn_sort/gold/test3.out                     |  22 ++
 tests/fn_sort/test1.xsl                          |  49 ++++
 tests/fn_sort/test1_a.xml                        |  10 +
 tests/fn_sort/test1_b.xml                        |  22 ++
 tests/fn_sort/test2.xsl                          |  41 +++
 tests/fn_sort/test3.xsl                          |  45 ++++
 tests/org/apache/xalan/xpath3/FnSortTests.java   |  82 ++++++
 tests/org/apache/xalan/xslt3/AllXsl3Tests.java   |   3 +-
 16 files changed, 671 insertions(+), 42 deletions(-)

diff --git a/src/org/apache/xpath/compiler/FunctionTable.java b/src/org/apache/xpath/compiler/FunctionTable.java
index b1cd5c39..2b27e26a 100644
--- a/src/org/apache/xpath/compiler/FunctionTable.java
+++ b/src/org/apache/xpath/compiler/FunctionTable.java
@@ -260,6 +260,9 @@ public class FunctionTable
   
   /** The 'for-each-pair()' id. */
   public static final int FUNC_FOR_EACH_PAIR = 78;
+  
+  /** The 'sort()' id. */
+  public static final int FUNC_SORT = 79;
 
   // Proprietary
 
@@ -317,7 +320,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 = 79;
+  private static final int NUM_BUILT_IN_FUNCS = 80;
 
   /**
    * Number of built-in functions that may be added.
@@ -428,6 +431,8 @@ public class FunctionTable
       org.apache.xpath.functions.FuncFoldRight.class;
     m_functions[FUNC_FOR_EACH_PAIR] = 
       org.apache.xpath.functions.FuncForEachPair.class;
+    m_functions[FUNC_SORT] = 
+      org.apache.xpath.functions.FuncSort.class;
     
     // XPath 3.1 functions configurations for the math functions 
     // namespace http://www.w3.org/2005/xpath-functions/math.
@@ -592,6 +597,8 @@ public class FunctionTable
                           new Integer(FunctionTable.FUNC_FOLD_RIGHT));
           m_functionID.put(Keywords.FUNC_FOR_EACH_PAIR,
                           new Integer(FunctionTable.FUNC_FOR_EACH_PAIR));
+          m_functionID.put(Keywords.FUNC_SORT,
+                          new Integer(FunctionTable.FUNC_SORT));
           
           // XPath 3.1 functions configurations for the math functions 
           // namespace http://www.w3.org/2005/xpath-functions/math.
diff --git a/src/org/apache/xpath/compiler/Keywords.java b/src/org/apache/xpath/compiler/Keywords.java
index 8b639a32..2ace7df2 100644
--- a/src/org/apache/xpath/compiler/Keywords.java
+++ b/src/org/apache/xpath/compiler/Keywords.java
@@ -278,6 +278,9 @@ public class Keywords
   /** for-each-pair function string. */
   public static final String FUNC_FOR_EACH_PAIR = "for-each-pair";
   
+  /** sort function string. */
+  public static final String FUNC_SORT = "sort";
+  
   /** math:pi function string. */
   public static final String FUNC_MATH_PI = "pi";
   
diff --git a/src/org/apache/xpath/compiler/XPathParser.java b/src/org/apache/xpath/compiler/XPathParser.java
index e4bffc99..4aced74a 100644
--- a/src/org/apache/xpath/compiler/XPathParser.java
+++ b/src/org/apache/xpath/compiler/XPathParser.java
@@ -98,6 +98,12 @@ public class XPathParser
                                                       "else", "some", "every", "satisfies", 
                                                       "let", ":=", "-", "||"};
   
+  // If the XPath expression is () (i.e, representing an xdm empty sequence), we
+  // translate that internally within this XPath parser implementation, to an
+  // XPath range "to" expression using this class field (this equivalently produces
+  // an xdm empty sequence).
+  private static final String XPATH_EXPR_STR_EMPTY_SEQUENCE = "1 to 0";
+  
   private static final List<String> fXpathOpArrTokensList = Arrays.asList(XPATH_OP_ARR);
   
   private boolean fDynamicFunctionCallArgumentMarker = false;
@@ -659,7 +665,7 @@ public class XPathParser
       boolean isXpathDynamicFuncCallParseAhead = false;
 
       if (lookahead('(', 1)) {
-         // Handles the case, where the function call argument
+         // handles the case, where the function call argument
          // is itself another function call.
          fDynamicFunctionCallArgumentMarker = true;
          argDetailsStrPartsList.add(m_token);
@@ -981,10 +987,31 @@ public class XPathParser
           //    parse an XPath expression delimited by braces '(' and ')'.
           // 2) The second pass, is XPath parsing as usual, using the result
           //    determined during step 1) as mentioned above.
-          nextToken();                  
+          nextToken();
           
           List<String> sequenceConstructorXPathParts = new ArrayList<String>();
           
+          if (tokenIs(')') && (getTokenRelative(0) == null)) {
+              // handles the case, where the XPath expression is ()
+              
+              int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
+              
+              nextToken();                            
+              
+              insertOp(opPos, 2, OpCodes.OP_SEQUENCE_CONSTRUCTOR_EXPR);
+              
+              sequenceConstructorXPathParts.add(XPATH_EXPR_STR_EMPTY_SEQUENCE);
+              
+              fSimpleSequenceConstructor = new SimpleSequenceConstructor();              
+              fSimpleSequenceConstructor.setSequenceConstructorXPathParts(
+                                                                    sequenceConstructorXPathParts);
+              
+              m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, 
+                                                  m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
+              
+              return;
+          }                    
+          
           while (m_token != null)
           {                            
               if (tokenIs("function")) {
@@ -1090,7 +1117,7 @@ public class XPathParser
           }
       }
       else {
-         ExprSingle();    
+         ExprSingle();  
       }
   }
 
@@ -2263,9 +2290,6 @@ public class XPathParser
    * | Number
    * | FunctionCall
    * | FunctionItemExpr
-   * 
-   * The XPath grammar option 'VarRef ArgumentList' mentioned above
-   * denotes, dynamic function call.
    *
    * @return true if this method successfully matched a PrimaryExpr
    *
@@ -2290,7 +2314,7 @@ public class XPathParser
       matchFound = true;
     }
     else if ((m_tokenChar == '$') && (lookahead('(', 2))) {
-       // support for XPath 3.1 dynamic function calls
+       // support for XPath 3.1 dynamic function call
                      
        appendOp(2, OpCodes.OP_DYNAMIC_FUNCTION_CALL);
        
@@ -2498,7 +2522,7 @@ public class XPathParser
 
     appendOp(2, OpCodes.OP_ARGUMENT);
     Expr();
-
+    
     m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
       m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
   }
@@ -2603,7 +2627,7 @@ public class XPathParser
     {
       int funcTok = getFunctionToken(m_token);
 
-      if (-1 == funcTok)
+      if (funcTok == -1)
       {
           error(XPATHErrorResources.ER_COULDNOT_FIND_FUNCTION,
               new Object[]{"{" + FunctionTable.XPATH_BUILT_IN_FUNCS_NS_URI + "}" + m_token + "()"});
diff --git a/src/org/apache/xpath/functions/FuncSort.java b/src/org/apache/xpath/functions/FuncSort.java
new file mode 100644
index 00000000..975351e9
--- /dev/null
+++ b/src/org/apache/xpath/functions/FuncSort.java
@@ -0,0 +1,327 @@
+/*
+ * 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.
+ */
+/*
+ * $Id$
+ */
+package org.apache.xpath.functions;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.SourceLocator;
+
+import org.apache.xalan.res.XSLMessages;
+import org.apache.xalan.templates.ElemTemplateElement;
+import org.apache.xalan.templates.XMLNSDecl;
+import org.apache.xalan.xslt.util.XslTransformEvaluationHelper;
+import org.apache.xml.dtm.DTM;
+import org.apache.xml.dtm.DTMIterator;
+import org.apache.xml.dtm.DTMManager;
+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.objects.InlineFunction;
+import org.apache.xpath.objects.ResultSequence;
+import org.apache.xpath.objects.XNodeSet;
+import org.apache.xpath.objects.XObject;
+import org.apache.xpath.operations.Variable;
+import org.apache.xpath.res.XPATHErrorResources;
+
+/**
+ * Implementation of the sort() function.
+ * 
+ * @author Mukul Gandhi <mu...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+/*
+ * fn:sort is one of XPath 3.1's higher-order function
+ * (ref, https://www.w3.org/TR/xpath-functions-31/#higher-order-functions).
+ *  
+ * The fn:sort function, sorts a supplied sequence, based on the value 
+ * of a sort key supplied as a function.
+ */
+public class FuncSort extends FunctionMultiArgs
+{
+
+    private static final long serialVersionUID = -7037457505611880167L;
+    
+    /**
+     * The number of arguments passed to the fn:sort function 
+     * call.
+     */
+    private int numOfArgs = 0;
+
+    /**
+     * Execute 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
+    {
+        ResultSequence evalResultSeq = new ResultSequence();
+        
+        SourceLocator srcLocator = xctxt.getSAXLocator();
+        
+        Expression arg0 = m_arg0;        
+        Expression arg1 = null;        
+        Expression arg2 = null;
+        
+        List<InpSeqItemWithSortKeyValue> inpSeqItemWithSortKeyValueList = new 
+                                                                   ArrayList<InpSeqItemWithSortKeyValue>();
+        
+        if ((numOfArgs == 3) || (numOfArgs == 2)) {
+           arg1 = m_arg1;           
+           XObject XObjArg1 = arg1.execute(xctxt);
+            
+           if (!((XObjArg1 instanceof ResultSequence) && 
+                                                (((ResultSequence)XObjArg1).size() == 0))) {
+               throw new javax.xml.transform.TransformerException("FORG0006 : The collation corresponding to supplied value of "
+                                                                                     + "second argument to function call fn:sort, is "
+                                                                                     + "not supported.", srcLocator);  
+           }
+            
+           arg2 = m_arg2; 
+        }
+        
+        XObject xObjArg0 = null;
+        
+        if (arg0 instanceof Variable) {
+           xObjArg0 = ((Variable)arg0).execute(xctxt);           
+        }
+        else {
+           xObjArg0 = arg0.execute(xctxt); 
+        }                
+        
+        if (xObjArg0 instanceof XNodeSet) {
+           DTMManager dtmMgr = (DTMManager)xctxt;
+            
+           XNodeSet xNodeSet = (XNodeSet)xObjArg0;           
+           DTMIterator sourceNodes = xNodeSet.iter();
+           
+           int nextNodeDtmHandle;
+           
+           Map<QName, XObject> inlineFunctionVarMap = xctxt.getXPathVarMap();
+           
+           ElemTemplateElement elemTemplateElement = (ElemTemplateElement)xctxt.getNamespaceContext();
+           List<XMLNSDecl> prefixTable = null;
+           if (elemTemplateElement != null) {
+               prefixTable = (List<XMLNSDecl>)elemTemplateElement.getPrefixTable();
+           }
+
+           while ((nextNodeDtmHandle = sourceNodes.nextNode()) != DTM.NULL) {
+              XNodeSet xNodeSetItem = new XNodeSet(nextNodeDtmHandle, dtmMgr);
+              
+              if (numOfArgs == 3) {                                   
+                  InlineFunction arg2InlineFunc = (InlineFunction)arg2;
+                  
+                  List<String> funcParamNameList = arg2InlineFunc.getFuncParamNameList();
+                  inlineFunctionVarMap.put(new QName(funcParamNameList.get(0)), xNodeSetItem);
+                  
+                  String sortKeyXPathStr = arg2InlineFunc.getFuncBodyXPathExprStr();
+                  
+                  if (prefixTable != null) {
+                      sortKeyXPathStr = XslTransformEvaluationHelper.replaceNsUrisWithPrefixesOnXPathStr(sortKeyXPathStr, 
+                                                                                                                   prefixTable);
+                  }
+                  
+                  XPath sortKeyXPathExpr = new XPath(sortKeyXPathStr, srcLocator, xctxt.getNamespaceContext(), 
+                                                                                       XPath.SELECT, null);
+                  XObject sortKeyVal = sortKeyXPathExpr.execute(xctxt, nextNodeDtmHandle, xctxt.getNamespaceContext());
+                  
+                  // reset the function item argument reference value
+                  inlineFunctionVarMap.put(new QName(funcParamNameList.get(0)), null);
+                  
+                  inpSeqItemWithSortKeyValueList.add(new InpSeqItemWithSortKeyValue(xNodeSetItem, sortKeyVal));
+               }
+               else if ((numOfArgs == 2) || (numOfArgs == 1)) {
+                  inpSeqItemWithSortKeyValueList.add(new InpSeqItemWithSortKeyValue(xNodeSetItem, xNodeSetItem));
+               }
+           }
+           
+           inpSeqItemWithSortKeyValueList.sort(new FnSortComparator(srcLocator));
+           
+           for (int idx = 0; idx < inpSeqItemWithSortKeyValueList.size(); idx++) {
+              InpSeqItemWithSortKeyValue inpSeqItemWithSortKeyValue = inpSeqItemWithSortKeyValueList.get(idx);
+              evalResultSeq.add(inpSeqItemWithSortKeyValue.getInpSeqItem()); 
+           }
+           
+        } 
+        else if (xObjArg0 instanceof ResultSequence) {
+           ResultSequence arg0ResultSeq = (ResultSequence)xObjArg0;
+           
+           Map<QName, XObject> inlineFunctionVarMap = xctxt.getXPathVarMap();
+           
+           ElemTemplateElement elemTemplateElement = (ElemTemplateElement)xctxt.getNamespaceContext();
+           List<XMLNSDecl> prefixTable = null;
+           if (elemTemplateElement != null) {
+               prefixTable = (List<XMLNSDecl>)elemTemplateElement.getPrefixTable();
+           }
+           
+           for (int idx = 0; idx < arg0ResultSeq.size(); idx++) {
+              XObject inputSeqItem = arg0ResultSeq.item(idx);
+              if (numOfArgs == 3) {                                   
+                 InlineFunction arg2InlineFunc = (InlineFunction)arg2;
+                 
+                 List<String> funcParamNameList = arg2InlineFunc.getFuncParamNameList();
+                 inlineFunctionVarMap.put(new QName(funcParamNameList.get(0)), inputSeqItem);
+                 
+                 String sortKeyXPathStr = arg2InlineFunc.getFuncBodyXPathExprStr();
+                 
+                 if (prefixTable != null) {
+                     sortKeyXPathStr = XslTransformEvaluationHelper.replaceNsUrisWithPrefixesOnXPathStr(sortKeyXPathStr, 
+                                                                                                         prefixTable);
+                 }
+                 
+                 XPath sortKeyXPathExpr = new XPath(sortKeyXPathStr, srcLocator, xctxt.getNamespaceContext(), 
+                                                                                                     XPath.SELECT, null);
+                 XObject sortKeyVal = sortKeyXPathExpr.execute(xctxt, xctxt.getContextNode(), xctxt.getNamespaceContext());
+                 
+                 // reset the function item argument reference value
+                 inlineFunctionVarMap.put(new QName(funcParamNameList.get(0)), null);
+                 
+                 inpSeqItemWithSortKeyValueList.add(new InpSeqItemWithSortKeyValue(inputSeqItem, sortKeyVal));
+              }
+              else if ((numOfArgs == 2) || (numOfArgs == 1)) {
+                 inpSeqItemWithSortKeyValueList.add(new InpSeqItemWithSortKeyValue(inputSeqItem, inputSeqItem));  
+              }
+           }
+           
+           inpSeqItemWithSortKeyValueList.sort(new FnSortComparator(srcLocator));
+               
+           for (int idx = 0; idx < inpSeqItemWithSortKeyValueList.size(); idx++) {
+              InpSeqItemWithSortKeyValue inpSeqItemWithSortKeyValue = inpSeqItemWithSortKeyValueList.get(idx);
+              evalResultSeq.add(inpSeqItemWithSortKeyValue.getInpSeqItem()); 
+           }           
+        }
+        else {           
+           evalResultSeq.add(xObjArg0); 
+        }
+        
+        return evalResultSeq;
+    }
+    
+    /**
+     * 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 > 0 && argNum <= 3)) {
+          reportWrongNumberArgs();
+       }
+       else {
+          numOfArgs = argNum;   
+       }
+    }
+
+    /**
+     * 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_ONE_TWO_OR_THREE, 
+                                                                     null));
+    }
+    
+    /*
+     * An object of this class, contains a pair of an input 
+     * sequence xdm item and its corresponding sort key value.
+     */
+    class InpSeqItemWithSortKeyValue {
+        
+       private XObject inpSeqItem;
+       
+       private XObject inpSeqItemSortKeyVal;
+       
+       public InpSeqItemWithSortKeyValue(XObject inpSeqItem, 
+                                                      XObject inpSeqItemSortKeyVal) {
+          this.inpSeqItem = inpSeqItem;
+          this.inpSeqItemSortKeyVal = inpSeqItemSortKeyVal; 
+       }
+
+       public XObject getInpSeqItem() {
+          return inpSeqItem;
+       }
+
+       public void setInpSeqItem(XObject inpSeqItem) {
+          this.inpSeqItem = inpSeqItem;
+       }
+
+       public XObject getInpSeqItemSortKeyVal() {
+          return inpSeqItemSortKeyVal;
+       }
+
+       public void setInpSeqItemSortKeyVal(XObject inpSeqItemSortKeyVal) {
+          this.inpSeqItemSortKeyVal = inpSeqItemSortKeyVal;
+       }
+
+    }
+    
+    /*
+     * An object of this class, is used to support sorting of an 
+     * xdm input sequence. 
+     */
+    class FnSortComparator implements Comparator {
+        
+        SourceLocator srcLocator;
+        
+        /*
+         * The class constructor.
+         */
+        public FnSortComparator(SourceLocator srcLocator) {
+           this.srcLocator = srcLocator;    
+        }
+
+        @Override
+        public int compare(Object obj1, Object obj2) {
+           int comparisonResult = 0;
+           
+           XObject sortKeyVal1 = ((InpSeqItemWithSortKeyValue)obj1).getInpSeqItemSortKeyVal();
+           XObject sortKeyVal2 = ((InpSeqItemWithSortKeyValue)obj2).getInpSeqItemSortKeyVal();
+           
+           try {
+              if (sortKeyVal1.vcGreaterThan(sortKeyVal2, null, true)) {
+                 comparisonResult = 1;  
+              }
+              else if (sortKeyVal1.vcLessThan(sortKeyVal2, null, false)) {
+                 comparisonResult = -1; 
+              }
+           }
+           catch (javax.xml.transform.TransformerException ex) {
+              // NO OP
+              // REVISIT
+           }
+           
+           return comparisonResult;           
+        }        
+    }
+    
+}
diff --git a/src/org/apache/xpath/objects/XObject.java b/src/org/apache/xpath/objects/XObject.java
index 86d6de40..189d0db9 100644
--- a/src/org/apache/xpath/objects/XObject.java
+++ b/src/org/apache/xpath/objects/XObject.java
@@ -574,14 +574,7 @@ public class XObject extends Expression implements Serializable, Cloneable
    *
    * @return True if this object is less than the given object
    *
-   * @throws javax.xml.transform.TransformerException
-   * 
-   * Notes : Currently, we haven't implemented following XPath 3.1 spec definitions for
-   *         value comparison operator "lt"/"ge",
-   *         1) XPath 3.1 spec, requires atomizing the operands of XPath operator "lt"/"ge",
-   *            before applying operator "lt"/"ge" to the operands.
-   *         2) If any of the operands of operator "lt"/"ge", after atomization is an empty
-   *            sequence, the result of operation "lt"/"ge" should be an empty sequence.              
+   * @throws javax.xml.transform.TransformerException             
    */
   public boolean vcLessThan(XObject obj2, ExpressionNode expressionOwner, boolean isLtTest) throws 
                                                                     javax.xml.transform.TransformerException {
@@ -613,6 +606,11 @@ public class XObject extends Expression implements Serializable, Cloneable
        else if ((this instanceof XNumber) && (obj2 instanceof XNumber)) {
            return ((XNumber)this).num() < ((XNumber)obj2).num(); 
        }
+       else if ((this instanceof XString) && (obj2 instanceof XString)) {
+           String lStr = (((XString)this)).str();
+           String rStr = (((XString)obj2)).str();
+           return (lStr.compareTo(rStr) < 0) ? true : false;
+        }
        
        boolean isOperandNodeSet1 = false;
        boolean isOperandNodeSet2 = false;
@@ -655,14 +653,7 @@ public class XObject extends Expression implements Serializable, Cloneable
    *
    * @return True if this object is greater than the given object
    *
-   * @throws javax.xml.transform.TransformerException
-   * 
-   * Notes : Currently, we haven't implemented following XPath 3.1 spec definitions for
-   *         value comparison operator "gt"/"le",
-   *         1) XPath 3.1 spec, requires atomizing the operands of XPath operator "gt"/"le",
-   *            before applying operator "gt"/"le" to the operands.
-   *         2) If any of the operands of operator "gt"/"le", after atomization is an empty
-   *            sequence, the result of operation "gt"/"le" should be an empty sequence.              
+   * @throws javax.xml.transform.TransformerException            
    */
   public boolean vcGreaterThan(XObject obj2, ExpressionNode expressionOwner, boolean isGtTest) throws 
                                                                     javax.xml.transform.TransformerException {
@@ -689,7 +680,12 @@ public class XObject extends Expression implements Serializable, Cloneable
           return ((XSInt)this).gt((XSInt)obj2);    
        }
        else if ((this instanceof XSDate) && (obj2 instanceof XSDate)) {
-           return ((XSDate)this).gt((XSDate)obj2);    
+          return ((XSDate)this).gt((XSDate)obj2);    
+       }
+       else if ((this instanceof XString) && (obj2 instanceof XString)) {
+          String lStr = (((XString)this)).str();
+          String rStr = (((XString)obj2)).str();
+          return (lStr.compareTo(rStr) > 0) ? true : false;
        }
        
        boolean isOperandNodeSet1 = false;
@@ -712,10 +708,10 @@ public class XObject extends Expression implements Serializable, Cloneable
        }
        
        if (isOperandNodeSet1 || this instanceof XNumber) {
-           return this.num() > obj2.num();    
+          return this.num() > obj2.num();    
        }    
        else if (isOperandNodeSet2 || obj2 instanceof XNumber) {
-           return obj2.num() > this.num();    
+          return obj2.num() > this.num();    
        }
        
        return true;
@@ -863,15 +859,7 @@ public class XObject extends Expression implements Serializable, Cloneable
    *
    * @return True if this object is equal to the given object
    *
-   * @throws javax.xml.transform.TransformerException
-   * 
-   * Notes : Currently, we haven't implemented following XPath 3.1 spec definitions for
-   *         value comparison operator "eq"/"ne",
-   *         1) XPath 3.1 spec, requires atomizing the operands of XPath operator "eq"/"ne",
-   *            before applying operator "eq"/"ne" to the operands.
-   *         2) If any of the operands of operator "eq"/"ne", after atomization is an empty
-   *            sequence, the result of operation "eq"/"ne" should be an empty sequence. 
-   *            Instead, we return the result as false for such cases.  
+   * @throws javax.xml.transform.TransformerException 
    */
   public boolean vcEquals(XObject obj2, ExpressionNode expressionOwner, boolean isEqTest) 
                                                                throws javax.xml.transform.TransformerException
diff --git a/tests/fn_fold_left/test1.xsl b/tests/fn_fold_left/test1.xsl
index 92b53aef..dd3f7569 100644
--- a/tests/fn_fold_left/test1.xsl
+++ b/tests/fn_fold_left/test1.xsl
@@ -14,9 +14,7 @@
    
    <xsl:variable name="fnAdd" select="function($arg1, $arg2) { $arg1 + $arg2 }"/>
    
-   <!-- the XPath evaluation of select expression of this variable,
-        produces an empty sequence. -->
-   <xsl:variable name="emptySeq" select="1 to 0"/>
+   <xsl:variable name="emptySeq" select="()"/>
       
    <xsl:template match="/">      
       <result>
diff --git a/tests/fn_sort/gold/test1.out b/tests/fn_sort/gold/test1.out
new file mode 100644
index 00000000..8ace53be
--- /dev/null
+++ b/tests/fn_sort/gold/test1.out
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <one>1 3 4 5 6</one>
+  <two>1 -2 5 8 10 -10 10</two>
+  <three>-10 -2 1 5 8 10 10</three>
+  <four>-10 -2 1 5 8 10 10</four>
+</result>
diff --git a/tests/fn_sort/gold/test2.out b/tests/fn_sort/gold/test2.out
new file mode 100644
index 00000000..be526f93
--- /dev/null
+++ b/tests/fn_sort/gold/test2.out
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <one>1 -2 5 8 10 -10 10</one>
+  <two>1 2 3 -4 -7</two>
+</result>
diff --git a/tests/fn_sort/gold/test3.out b/tests/fn_sort/gold/test3.out
new file mode 100644
index 00000000..2c4ead41
--- /dev/null
+++ b/tests/fn_sort/gold/test3.out
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?><document>
+  <person>
+    <fName>Mukul</fName>
+    <lName>Gandhi</lName>
+  </person>
+  <person>
+    <fName>Michael</fName>
+    <lName>Glavassevich</lName>
+  </person>
+  <person>
+    <fName>Neil</fName>
+    <lName>Graham</lName>
+  </person>
+  <person>
+    <fName>Gary</fName>
+    <lName>Gregory</lName>
+  </person>
+  <person>
+    <fName>Joseph</fName>
+    <lName>Kesselman</lName>
+  </person>
+</document>
diff --git a/tests/fn_sort/test1.xsl b/tests/fn_sort/test1.xsl
new file mode 100644
index 00000000..4c7b8c72
--- /dev/null
+++ b/tests/fn_sort/test1.xsl
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version="3.0">
+                
+   <!-- Author: mukulg@apache.org -->
+   
+   <!-- An XSLT stylesheet test case, for the XPath 3.1 fn:sort
+        function. The fn:sort function examples used within this
+        stylesheet, are borrowed from XPath 3.1 spec, with slight
+        modifications. -->                
+
+   <xsl:output method="xml" indent="yes"/>
+   
+   <xsl:variable name="seq1" select="(1, 4, 6, 5, 3)"/>
+   <xsl:variable name="seq2" select="(1, -2, 5, 10, -10, 10, 8)"/>
+   
+   <xsl:variable name="emptySeq" select="()"/>
+   
+   <xsl:template match="/">
+      <result>
+        <one><xsl:value-of select="sort($seq1)"/></one>
+                 
+        <two><xsl:value-of select="sort($seq2, $emptySeq, function($a) { abs($a) })"/></two>
+        
+        <three><xsl:value-of select="sort($seq2, $emptySeq)"/></three>
+        
+        <four><xsl:value-of select="sort($seq2)"/></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>
\ No newline at end of file
diff --git a/tests/fn_sort/test1_a.xml b/tests/fn_sort/test1_a.xml
new file mode 100644
index 00000000..74a29ec0
--- /dev/null
+++ b/tests/fn_sort/test1_a.xml
@@ -0,0 +1,10 @@
+<temp>
+   <a>1</a>
+   <a>-2</a>
+   <a>5</a>
+   <a>10</a>
+   <a>-10</a>
+   <a>10</a>
+   <a>8</a>
+   <data val1="3" val2="-4" val3="1" val4="-7" val5="2"/>
+</temp>
\ No newline at end of file
diff --git a/tests/fn_sort/test1_b.xml b/tests/fn_sort/test1_b.xml
new file mode 100644
index 00000000..a77b436c
--- /dev/null
+++ b/tests/fn_sort/test1_b.xml
@@ -0,0 +1,22 @@
+<document>
+  <person>
+    <fName>Gary</fName>
+    <lName>Gregory</lName>
+  </person>
+  <person>
+    <fName>Michael</fName>
+    <lName>Glavassevich</lName>
+  </person>
+  <person>
+    <fName>Joseph</fName>
+    <lName>Kesselman</lName>
+  </person>
+  <person>
+    <fName>Mukul</fName>
+    <lName>Gandhi</lName>
+  </person>
+  <person>
+    <fName>Neil</fName>
+    <lName>Graham</lName>
+  </person>
+</document>
\ No newline at end of file
diff --git a/tests/fn_sort/test2.xsl b/tests/fn_sort/test2.xsl
new file mode 100644
index 00000000..7c63f689
--- /dev/null
+++ b/tests/fn_sort/test2.xsl
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version="3.0">
+                
+   <!-- Author: mukulg@apache.org -->
+   
+   <!-- use with test1_a.xml -->
+   
+   <!-- An XSLT stylesheet test case, to test XPath 3.1 fn:sort function,
+        by reading input data from an XML external source document. -->                
+
+   <xsl:output method="xml" indent="yes"/>
+   
+   <xsl:variable name="emptySeq" select="()"/>
+   
+   <xsl:template match="/temp">
+      <result>
+         <one><xsl:value-of select="sort(a, $emptySeq, function($a) { abs($a) })"/></one>
+         <two><xsl:value-of select="sort(data/@*, $emptySeq, function($val) { abs($val) })"/></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>
\ No newline at end of file
diff --git a/tests/fn_sort/test3.xsl b/tests/fn_sort/test3.xsl
new file mode 100644
index 00000000..5818703b
--- /dev/null
+++ b/tests/fn_sort/test3.xsl
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version="3.0">
+                
+   <!-- Author: mukulg@apache.org -->
+   
+   <!-- use with test1_b.xml -->
+   
+   <!-- An XSLT stylesheet test case, to test XPath 3.1 fn:sort function,
+        by reading input data from an XML external source document. 
+        
+        This stylesheet, sorts a sequence of XML person elements by last name 
+        as the major sort key and first name as the minor sort key, using the 
+        default collation.
+    -->                
+
+   <xsl:output method="xml" indent="yes"/>
+   
+   <xsl:variable name="emptySeq" select="()"/>
+   
+   <xsl:template match="/document">
+      <document>       
+         <xsl:copy-of select="sort(person, $emptySeq, function($person) { $person/lName || ':' || $person/fName })"/>
+      </document>
+   </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>
\ No newline at end of file
diff --git a/tests/org/apache/xalan/xpath3/FnSortTests.java b/tests/org/apache/xalan/xpath3/FnSortTests.java
new file mode 100644
index 00000000..3fb17ee9
--- /dev/null
+++ b/tests/org/apache/xalan/xpath3/FnSortTests.java
@@ -0,0 +1,82 @@
+/*
+ * 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.XslTransformTestsUtil;
+import org.apache.xalan.xslt3.XSLConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * XPath 3.1 function fn:sort test cases.
+ * 
+ * @author Mukul Gandhi <mu...@apache.org>
+ * 
+ * @xsl.usage advanced
+ */
+public class FnSortTests extends XslTransformTestsUtil {        
+    
+    private static final String XSL_TRANSFORM_INPUT_DIRPATH = XSLConstants.XSL_TRANSFORM_INPUT_DIRPATH_PREFIX + 
+                                                                                                           "fn_sort/";
+    
+    private static final String XSL_TRANSFORM_GOLD_DIRPATH = XSLConstants.XSL_TRANSFORM_GOLD_DIRPATH_PREFIX + 
+                                                                                                           "fn_sort/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 xslFnSortTest1() {
+        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 xslFnSortTest2() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1_a.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 xslFnSortTest3() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1_b.xml"; 
+        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/AllXsl3Tests.java b/tests/org/apache/xalan/xslt3/AllXsl3Tests.java
index 0cd26d09..2faca9c2 100644
--- a/tests/org/apache/xalan/xslt3/AllXsl3Tests.java
+++ b/tests/org/apache/xalan/xslt3/AllXsl3Tests.java
@@ -27,6 +27,7 @@ import org.apache.xalan.xpath3.FnFoldRightTests;
 import org.apache.xalan.xpath3.FnForEachPairTests;
 import org.apache.xalan.xpath3.FnForEachTests;
 import org.apache.xalan.xpath3.FnIndexOfTests;
+import org.apache.xalan.xpath3.FnSortTests;
 import org.apache.xalan.xpath3.FnStringJoinTests;
 import org.apache.xalan.xpath3.FnTokenizeTests;
 import org.apache.xalan.xpath3.FnUnparsedTextTests;
@@ -76,7 +77,7 @@ import org.junit.runners.Suite.SuiteClasses;
                 SequenceConstructorTests.class, StringConcatExprTests.class, 
                 XsDurationComponentExtractionFunctionTests.class, XPathArithmeticOnDurationValuesTests.class,
                 NodeComparisonTests.class, SimpleMapOperatorTests.class, FnFoldLeftTests.class,
-                FnFoldRightTests.class, FnForEachPairTests.class })
+                FnFoldRightTests.class, FnForEachPairTests.class, FnSortTests.class })
 public class AllXsl3Tests {
 
 }


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