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/10/02 15:20:00 UTC

[xalan-java] branch xalan-j_xslt3.0 updated: committing few improvements to implementation of xsl:for-each-group instruction, along with few new related working test cases. also committing minor codebase comments improvements, on this xalanj dev repos branch.

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 09c47427 committing few improvements to implementation of xsl:for-each-group instruction, along with few new related working test cases. also committing minor codebase comments improvements, on this xalanj dev repos branch.
     new 3dd587c1 Merge pull request #96 from mukulga/xalan-j_xslt3.0_mukul
09c47427 is described below

commit 09c4742766fc58c1d3e1495f332fd821c46cfc6e
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Mon Oct 2 20:45:23 2023 +0530

    committing few improvements to implementation of xsl:for-each-group instruction, along with few new related working test cases. also committing minor codebase comments improvements, on this xalanj dev repos branch.
---
 .../apache/xalan/templates/ElemForEachGroup.java   | 384 ++++++++++-----------
 src/org/apache/xalan/templates/ElemFunction.java   |   4 +-
 tests/grouping/sort/gold/test8.out                 |  28 ++
 tests/grouping/sort/gold/test9.out                 |  31 ++
 tests/grouping/sort/test1_d.xml                    |  27 ++
 tests/grouping/sort/test8.xsl                      |  49 +++
 tests/grouping/sort/test9.xsl                      |  54 +++
 .../apache/xalan/xslt3/GroupingWithSortTests.java  |  20 ++
 8 files changed, 388 insertions(+), 209 deletions(-)

diff --git a/src/org/apache/xalan/templates/ElemForEachGroup.java b/src/org/apache/xalan/templates/ElemForEachGroup.java
index c41aaad1..ec1aae95 100644
--- a/src/org/apache/xalan/templates/ElemForEachGroup.java
+++ b/src/org/apache/xalan/templates/ElemForEachGroup.java
@@ -15,9 +15,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/*
- * $Id$
- */
 package org.apache.xalan.templates;
 
 import java.util.ArrayList;
@@ -51,85 +48,59 @@ import org.apache.xpath.objects.XNumber;
 import org.apache.xpath.objects.XObject;
 import org.apache.xpath.objects.XString;
 import org.apache.xpath.operations.Variable;
+import org.apache.xpath.xs.types.XSBoolean;
+import org.apache.xpath.xs.types.XSNumericType;
+import org.apache.xpath.xs.types.XSString;
 
 /**
- *  XSLT 3.0 for-each-group element.
- *    
- *  <xsl:for-each-group
-              select = expression
-              group-by? = expression
-              group-adjacent? = expression
-              group-starting-with? = pattern
-              group-ending-with? = pattern
-              composite? = boolean
-              collation? = { uri } >
-        <!-- Content: (xsl:sort*, sequence-constructor) -->
-    </xsl:for-each-group>
-    
-    <xsl:sort
-           select? = expression
-           lang? = { language }
-           order? = { "ascending" | "descending" }
-           collation? = { uri }
-           stable? = { boolean }
-           case-order? = { "upper-first" | "lower-first" }
-           data-type? = { "text" | "number" | eqname } >
-        <!-- Content: sequence-constructor -->
-    </xsl:sort>
-     
-    There are following two XSLT 3.0 grouping functions,
-    1) fn:current-group()     
-    2) fn:current-grouping-key()
-    
-    Ref : https://www.w3.org/TR/xslt-30/#xsl-for-each-group
-  
-   @author Mukul Gandhi <mu...@apache.org>
-  
-   @xsl.usage advanced
-*/
-/*
- * Implementation of the XSLT 3.0 xsl:for-each-group instruction. 
+ * Implementation of the XSLT 3.0 xsl:for-each-group instruction.
+ * 
+ * Ref : https://www.w3.org/TR/xslt-30/#grouping
+ * 
+ * @author Mukul Gandhi <mu...@apache.org>
+ *  
+ * @xsl.usage advanced
  */
 public class ElemForEachGroup extends ElemTemplateElement 
                                                 implements ExpressionOwner
 {
   
   private static final long serialVersionUID = -6682554013978812260L;
-  
-  /**
-   * Construct a element representing xsl:for-each-group.
-   */
-  public ElemForEachGroup(){}
 
   /**
    * The "select" expression.
    */
-  protected Expression m_selectExpression = null;
+  private Expression m_selectExpression = null;
   
   /**
    * The "group-by" expression.
    */
-  protected Expression m_GroupByExpression = null;
+  private Expression m_GroupByExpression = null;
   
   /**
    * The "group-adjacent" expression.
    */
-  protected Expression m_GroupAdjacentExpression = null;
+  private Expression m_GroupAdjacentExpression = null;
   
   /**
    * The "group-starting-with" expression.
    */
-  protected Expression m_GroupStartingWithExpression = null;
+  private Expression m_GroupStartingWithExpression = null;
   
   /**
    * The "group-ending-with" expression.
    */
-  protected Expression m_GroupEndingWithExpression = null;
+  private Expression m_GroupEndingWithExpression = null;
   
   /**
    * Vector containing the xsl:sort elements associated with this element.
    */
-  protected Vector m_sortElems = null;
+  private Vector m_sortElems = null;
+  
+  /**
+   * The class constructor.
+   */
+  public ElemForEachGroup() {}
   
   /**
    * Get the count of xsl:sort elements associated with this element.
@@ -358,30 +329,106 @@ public class ElemForEachGroup extends ElemTemplateElement
             transformer.popCurrentTemplateRuleIsNull();
         }
   }
+  
+  /**
+   * Sort given xsl:for-each-group groups.
+   *
+   *
+   * @param xctxt             the XPath runtime state for the sort
+   * @param sortKeys          vector of sort keys
+   * @param forEachGroups     an object representing, groups to be sorted
+   *
+   * @return a list of sorted groups
+   *
+   * @throws TransformerException
+   */
+  public List<GroupingKeyAndGroupPair> sortGroups(XPathContext xctxt, Vector sortKeys, Object forEachGroups)
+                                                            throws TransformerException {
+        List<GroupingKeyAndGroupPair> sortedGroups = null;
+      
+        ForEachGroupXslSortSorter sorter = new ForEachGroupXslSortSorter(xctxt, this);
+        
+        sortedGroups = sorter.sort(forEachGroups, sortKeys, xctxt);
+    
+        return sortedGroups;
+  }
 
   /**
-   * Get template element associated with 'this' symbol, which is a 
-   * reference to the current object of this class.
+   * Add a child to the child list.
+   * <!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
+   * <!ATTLIST xsl:apply-templates
+   *   select %expr; "node()"
+   *   mode %qname; #IMPLIED
+   * >
+   *
+   * @param newChild Child to add to child list
    *
-   * @return template element associated with the, 'this' symbol.
+   * @return Child just added to child list
    */
-  protected ElemTemplateElement getTemplateMatch()
+  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
   {
-      return this;
+        int type = ((ElemTemplateElement) newChild).getXSLToken();
+    
+        if (type == Constants.ELEMNAME_SORT)
+        {
+            setSortElem((ElemSort) newChild);
+        
+            return newChild;
+        }
+        else {
+            return super.appendChild(newChild);
+        }
+  }
+  
+  /**
+   * Call the children visitors.
+   * 
+   * @param visitor The visitor whose appropriate method will be called.
+   */
+  public void callChildVisitors(XSLTVisitor visitor, boolean callAttributes)
+  {
+      	if(callAttributes && (null != m_selectExpression))
+      		m_selectExpression.callVisitors(this, visitor);
+      		
+        int length = getSortElemCount();
+    
+        for (int i = 0; i < length; i++)
+        {
+           getSortElem(i).callVisitors(visitor);
+        }
+    
+        super.callChildVisitors(visitor, callAttributes);
   }
 
   /**
-   * Perform the actual XSLT transformation logic, on run-time contents of 
+   * @see ExpressionOwner#getExpression()
+   */
+  public Expression getExpression()
+  {
+      return m_selectExpression;
+  }
+
+  /**
+   * @see ExpressionOwner#setExpression(Expression)
+   */
+  public void setExpression(Expression exp)
+  {
+      exp.exprSetParent(this);
+      m_selectExpression = exp;
+  }
+  
+  /**
+   * This method performs the actual XSLT transformation logic, on XSL contents of 
    * xsl:for-each-group element.
    *
-   * @param transformer            non-null reference to the the current transform-time state.
+   * @param transformer              non-null reference to the the current transform-time state.
    *
-   * @throws TransformerException  thrown in a variety of circumstances.
+   * @throws TransformerException    thrown in a variety of circumstances.
    * 
    * @xsl.usage advanced
    */
-  public void transformSelectedNodes(TransformerImpl transformer)
-                                              throws TransformerException {
+  private void transformSelectedNodes(TransformerImpl transformer)
+                                                               throws TransformerException {
       
         XPathContext xctxt = transformer.getXPathContext();
         
@@ -423,13 +470,13 @@ public class ElemForEachGroup extends ElemTemplateElement
            sourceNodes = m_selectExpression.asIterator(xctxt, sourceNode);
         }
                       
-        // hashmap to store groups formed for, either 'group-by' or 'group-adjacent' attributes.
-        // hashmap's key is the grouping key value, and value of that hashmap item entry is the
-        // content of that group. 
+        // A java.util.Map object to store groups formed for, either 'group-by' or 
+        // 'group-adjacent' attributes. This map stores mappings between, grouping key
+        // value and contents of the group corresponding to a grouping key value. 
         Map<Object, List<Integer>> xslForEachGroupMap = new HashMap<Object, List<Integer>>();
         
-        // list to store groups formed for, either 'group-starting-with' or 'group-ending-with' 
-        // attributes. grouping keys are not available for, these kinds of groups.
+        // List to store groups formed for, either 'group-starting-with' or 'group-ending-with' 
+        // attributes. The grouping keys are not available for, these kind of groups.
         List<List<Integer>> xslForEachGroupStartingWithEndingWith = new ArrayList<List<Integer>>();
         
         if (m_GroupByExpression != null) {
@@ -464,7 +511,7 @@ public class ElemForEachGroup extends ElemTemplateElement
                  Object currValue = xpathRawResult;
                  
                  if (idx == 0) {
-                     // first node within the nodes been traversed, by this loop
+                     // First xdm node within the nodes been traversed, by this loop
                      List<Integer> group = new ArrayList<Integer>();
                      group.add(nextNode);
                      xslForEachGroupMap.put(currValue, group);
@@ -560,8 +607,8 @@ public class ElemForEachGroup extends ElemTemplateElement
                                             ? null : transformer.processSortKeysForEachGroup(
                                                                                     this, sourceNode);            
             if (sortKeys != null) {
-                // there are xsl:sort elements within xsl:for-each-group. sort the groups,
-                // as per these xsl:sort element specifications within an XSLT stylesheet. 
+                // There are xsl:sort elements within xsl:for-each-group. Sort the groups,
+                // as per these xsl:sort element details within an XSLT stylesheet. 
                 Object forEachGroups = null;
                 
                 if (xslForEachGroupMap.size() > 0) {
@@ -581,7 +628,7 @@ public class ElemForEachGroup extends ElemTemplateElement
                     
                     for (ElemTemplateElement templateElem = this.m_firstChild; templateElem != null; 
                                                                       templateElem = templateElem.m_nextSibling) {
-                        // set context item for whole of group contents evaluation, to the initial 
+                        // Set context item for whole of group contents evaluation, to the initial 
                         // item of the group.
                         xctxt.pushCurrentNode((groupNodesDtmHandles.get(0)).intValue());
                         
@@ -594,9 +641,9 @@ public class ElemForEachGroup extends ElemTemplateElement
                 }
             }
             else {
-                // xsl:sort elements are not present within xsl:for-each-group element.                
+                // xsl:sort elements are not present within xsl:for-each-group element               
                 if (xslForEachGroupMap.size() > 0) {
-                    // process the groups, when xsl:for-each-group attributes 'group-by' 
+                    // Process the groups, when xsl:for-each-group attributes 'group-by' 
                     // or 'group-adjacent' are used. 
                     List<GroupingKeyAndNodeHandlePair> groupingKeyAndNodeHandlePairList = new ArrayList
                                                                                  <GroupingKeyAndNodeHandlePair>();
@@ -612,11 +659,11 @@ public class ElemForEachGroup extends ElemTemplateElement
                         groupingKeyAndNodeHandlePairList.add(groupingKeyNodeHandlePair);
                      }
                     
-                     // sort the xsl:for-each-group's groups, using default sorted order
+                     // Sort the xsl:for-each-group's groups, using default sorted order
                      Collections.sort(groupingKeyAndNodeHandlePairList);
                      
-                     // loop through these groups formed by xsl:for-each-group instruction, and process 
-                     // the XSLT contents of each group.
+                     // Loop through these groups formed by xsl:for-each-group instruction, and process 
+                     // the XSL contents of each group.
                      for (int idx = 0; idx < groupingKeyAndNodeHandlePairList.size(); idx++) {
                         GroupingKeyAndNodeHandlePair groupingKeyNodeHandlePair = groupingKeyAndNodeHandlePairList.get(idx);
                         
@@ -625,7 +672,7 @@ public class ElemForEachGroup extends ElemTemplateElement
                         
                         for (ElemTemplateElement templateElem = this.m_firstChild; templateElem != null; 
                                                          templateElem = templateElem.m_nextSibling) {
-                            // set context item for whole of group contents evaluation, to the initial item of the group
+                            // Set context item for whole of group contents evaluation, to the initial item of the group
                             xctxt.pushCurrentNode((groupNodesDtmHandles.get(0)).intValue());
                             
                             templateElem.setGroupingKey(groupingKey);
@@ -637,19 +684,19 @@ public class ElemForEachGroup extends ElemTemplateElement
                      }
                 }
                 else {
-                    // process the groups, when xsl:for-each-group attributes 'group-starting-with' 
+                    // Process the groups, when xsl:for-each-group attributes 'group-starting-with' 
                     // or 'group-ending-with' are used.
-                    // loop through these groups formed by xsl:for-each-group instruction, and process 
-                    // the XSLT contents of each group.
+                    // Loop through these groups formed by xsl:for-each-group instruction, and process 
+                    // the XSL contents of each group.
                     for (int idx = 0; idx < xslForEachGroupStartingWithEndingWith.size(); idx++) {
                        List<Integer> groupNodesDtmHandles = xslForEachGroupStartingWithEndingWith.get(idx);
                        for (ElemTemplateElement templateElem = this.m_firstChild; templateElem != null; 
                                                                           templateElem = templateElem.m_nextSibling) {                   
-                           // set context item for whole of group contents evaluation, to the initial item of the group
+                           // Set context item for whole of group contents evaluation, to the initial item of the group
                            xctxt.pushCurrentNode((groupNodesDtmHandles.get(0)).intValue());
                            
-                           // grouping key is absent when, attributes 'group-starting-with' or 'group-ending-with' 
-                           // are used.                   
+                           // The grouping key is absent when, attributes 'group-starting-with' or 
+                           // 'group-ending-with' are used.                   
                            templateElem.setGroupNodesDtmHandles(groupNodesDtmHandles);
                            xctxt.setSAXLocator(templateElem);
                            transformer.setCurrentElement(templateElem);                   
@@ -674,97 +721,49 @@ public class ElemForEachGroup extends ElemTemplateElement
               sourceNodes.detach();
          }
         
-         // when a particular xsl:for-each-group's evaluation has completed, set the XPath evaluation 
+         // When a particular xsl:for-each-group's evaluation has completed, set the XPath evaluation 
          // context node to the node which was the context node before xsl:for-each-group evaluation 
          // was started. 
          xctxt.pushCurrentNode(sourceNode);
   }
   
-  /**
-   * Sort given xsl:for-each-group groups.
-   *
-   *
-   * @param xctxt             the XPath runtime state for the sort
-   * @param sortKeys          vector of sort keys
-   * @param forEachGroups     an object representing, groups to be sorted
-   *
-   * @return a list of sorted groups
-   *
-   * @throws TransformerException
-   */
-  public List<GroupingKeyAndGroupPair> sortGroups(XPathContext xctxt, Vector sortKeys, Object forEachGroups)
-                                                            throws TransformerException {
-        List<GroupingKeyAndGroupPair> sortedGroups = null;
-      
-        ForEachGroupXslSortSorter sorter = new ForEachGroupXslSortSorter(xctxt, this);
-        
-        sortedGroups = sorter.sort(forEachGroups, sortKeys, xctxt);
-    
-        return sortedGroups;
-  }
+  /* 
+   * A class to support, reordering the xsl:for-each-group's groups as per definition of default 
+   * sorted order (i.e, order of first appearance) when xsl:sort elements are not present within 
+   * xsl:for-each-group.
+  */
+  private class GroupingKeyAndNodeHandlePair implements Comparable<GroupingKeyAndNodeHandlePair> {
+     
+     private Object groupingKey;
+     
+     private Integer nodeHandle;
+     
+     public GroupingKeyAndNodeHandlePair(Object groupingKey, Integer nodeHandle) {
+        this.groupingKey = groupingKey;
+        this.nodeHandle = nodeHandle; 
+     }
+     
+     public Object getGroupingKey() {
+        return groupingKey;
+     }
 
-  /**
-   * Add a child to the child list.
-   * <!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
-   * <!ATTLIST xsl:apply-templates
-   *   select %expr; "node()"
-   *   mode %qname; #IMPLIED
-   * >
-   *
-   * @param newChild Child to add to child list
-   *
-   * @return Child just added to child list
-   */
-  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
-  {
-        int type = ((ElemTemplateElement) newChild).getXSLToken();
-    
-        if (type == Constants.ELEMNAME_SORT)
-        {
-            setSortElem((ElemSort) newChild);
-        
-            return newChild;
-        }
-        else {
-            return super.appendChild(newChild);
-        }
-  }
-  
-  /**
-   * Call the children visitors.
-   * 
-   * @param visitor The visitor whose appropriate method will be called.
-   */
-  public void callChildVisitors(XSLTVisitor visitor, boolean callAttributes)
-  {
-      	if(callAttributes && (null != m_selectExpression))
-      		m_selectExpression.callVisitors(this, visitor);
-      		
-        int length = getSortElemCount();
-    
-        for (int i = 0; i < length; i++)
-        {
-           getSortElem(i).callVisitors(visitor);
-        }
-    
-        super.callChildVisitors(visitor, callAttributes);
-  }
+     public void setGroupingKey(Object groupingKey) {
+        this.groupingKey = groupingKey;
+     }
 
-  /**
-   * @see ExpressionOwner#getExpression()
-   */
-  public Expression getExpression()
-  {
-      return m_selectExpression;
-  }
+     public Integer getNodeHandle() {
+        return nodeHandle;
+     }
 
-  /**
-   * @see ExpressionOwner#setExpression(Expression)
-   */
-  public void setExpression(Expression exp)
-  {
-      exp.exprSetParent(this);
-      m_selectExpression = exp;
+     public void setNodeHandle(Integer nodeHandle) {
+        this.nodeHandle = nodeHandle;
+     }
+
+     @Override
+     public int compareTo(GroupingKeyAndNodeHandlePair obj) {
+        return this.getNodeHandle().compareTo(obj.getNodeHandle());
+     }
+    
   }
   
   /*
@@ -796,6 +795,9 @@ public class ElemForEachGroup extends ElemTemplateElement
   }
   
   /* 
+   * This method, converts initial value of xsl:for-each-group's grouping key value,
+   * into a normalized data typed value.
+   * 
    * For the purpose of, evaluating grouping key XPath expressions for xsl:for-each-group, 
    * the grouping keys are treated as of type string, number or boolean. Any other data 
    * type for grouping key is converted to a string value.
@@ -806,59 +808,30 @@ public class ElemForEachGroup extends ElemTemplateElement
       if (xpathEvalResult instanceof XString) {
           xpathRawResult = xpathEvalResult.str();    
       }
+      else if (xpathEvalResult instanceof XSString) {
+          xpathRawResult = ((XSString)xpathEvalResult).stringValue();  
+      }
       else if (xpathEvalResult instanceof XNumber) {
           xpathRawResult = Double.valueOf(((XNumber)xpathEvalResult).num());  
       }
+      else if (xpathEvalResult instanceof XSNumericType) {
+          String strVal = ((XSNumericType)xpathEvalResult).stringValue();
+          xpathRawResult = Double.valueOf(strVal);
+      }
       else if (xpathEvalResult instanceof XBoolean) {
           xpathRawResult =  Boolean.valueOf(((XBoolean)xpathEvalResult).bool());     
       }
+      else if (xpathEvalResult instanceof XSBoolean) {
+          xpathRawResult = Boolean.valueOf(((XSBoolean)xpathEvalResult).value());
+      }
       else {
-          // any other data type for grouping key, is treated as string
+          // Any other data type for grouping key, is treated as string
           xpathRawResult = XslTransformEvaluationHelper.getStrVal(xpathEvalResult);  
       }
       
       return xpathRawResult;
   }
   
-  /* 
-   * Class to support, reordering the xsl:for-each-group's groups as per definition of default 
-   * sorted order (i.e, order of first appearance) when xsl:sort elements are not present within 
-   * xsl:for-each-group.
-  */
-  class GroupingKeyAndNodeHandlePair implements Comparable<GroupingKeyAndNodeHandlePair> {
-     
-     private Object groupingKey;
-     
-     private Integer nodeHandle;
-     
-     public GroupingKeyAndNodeHandlePair(Object groupingKey, Integer nodeHandle) {
-        this.groupingKey = groupingKey;
-        this.nodeHandle = nodeHandle; 
-     }
-     
-     public Object getGroupingKey() {
-        return groupingKey;
-     }
-
-     public void setGroupingKey(Object groupingKey) {
-        this.groupingKey = groupingKey;
-     }
-
-     public Integer getNodeHandle() {
-        return nodeHandle;
-     }
-
-     public void setNodeHandle(Integer nodeHandle) {
-        this.nodeHandle = nodeHandle;
-     }
-
-     @Override
-     public int compareTo(GroupingKeyAndNodeHandlePair obj) {
-        return this.getNodeHandle().compareTo(obj.getNodeHandle());
-     }
-    
-  }
-  
   /*
    * Get XML document source nodes (represented as an 'DTMIterator' object), from
    * a list of XNodeSet objects contained within a 'ResultSequence' object.    
@@ -869,9 +842,6 @@ public class ElemForEachGroup extends ElemTemplateElement
      
      NodeVector nodeVector = new NodeVector();
      
-     // how to process the sequence object 'resultSeq', when any of
-     // its items are other than XNodeSet object instances?
-     // REVISIT
      for (int idx = 0; idx < resultSeq.size(); idx++) {
          XObject xObject = resultSeq.item(idx);
          xObject = ((XNodeSet)xObject).getFresh();
diff --git a/src/org/apache/xalan/templates/ElemFunction.java b/src/org/apache/xalan/templates/ElemFunction.java
index c268347d..cc3e4078 100644
--- a/src/org/apache/xalan/templates/ElemFunction.java
+++ b/src/org/apache/xalan/templates/ElemFunction.java
@@ -131,7 +131,7 @@ public class ElemFunction extends ElemTemplate
       String funcLocalName = m_name.getLocalName();
       String funcNameSpaceUri = m_name.getNamespaceURI();
       
-      // Validate few of the information of xsl:function's xsl:param declarations.         
+      // Validate few of the information of xsl:function's xsl:param declarations         
       Map<QName, Integer> xslParamMap = new HashMap<QName, Integer>();
       int idx = 0;
       PrefixResolver prefixResolver = xctxt.getNamespaceContext();
@@ -321,7 +321,7 @@ public class ElemFunction extends ElemTemplate
   }
   
   /**
-   * This method helps us to implement xsl:function and xsl:variable instructions,
+   * This method helps to implement xsl:function and xsl:variable instructions,
    * when the XSL child contents of xsl:function or xsl:variable instructions contain 
    * xsl:sequence instruction(s).
    * 
diff --git a/tests/grouping/sort/gold/test8.out b/tests/grouping/sort/gold/test8.out
new file mode 100644
index 00000000..a0d8e615
--- /dev/null
+++ b/tests/grouping/sort/gold/test8.out
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <group score="2">
+    <person>
+      <fName>Joseph</fName>
+      <lName>Kessselman</lName>
+    </person>
+    <person>
+      <fName>Michael</fName>
+      <lName>Glavassevich</lName>
+    </person>
+    <person>
+      <fName>Noah</fName>
+      <lName>Mendelsohn</lName>
+    </person>
+  </group>
+  <group score="5">
+    <person>
+      <fName>Gary</fName>
+      <lName>Gregory</lName>
+    </person>
+  </group>
+  <group score="7">
+    <person>
+      <fName>Mukul</fName>
+      <lName>Gandhi</lName>
+    </person>
+  </group>
+</result>
diff --git a/tests/grouping/sort/gold/test9.out b/tests/grouping/sort/gold/test9.out
new file mode 100644
index 00000000..4b3e0710
--- /dev/null
+++ b/tests/grouping/sort/gold/test9.out
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?><result>
+  <group score="lt_three">
+    <person>
+    <fName>Joseph</fName>
+    <lName>Kessselman</lName>
+    <score>2</score>
+  </person>
+    <person>
+    <fName>Michael</fName>
+    <lName>Glavassevich</lName>
+    <score>2</score>
+  </person>
+    <person>
+    <fName>Noah</fName>
+    <lName>Mendelsohn</lName>
+    <score>2</score>
+  </person>
+  </group>
+  <group score="gt_or_eq_three">
+    <person>
+    <fName>Gary</fName>
+    <lName>Gregory</lName>
+    <score>5</score>
+  </person>
+    <person>
+    <fName>Mukul</fName>
+    <lName>Gandhi</lName>
+    <score>7</score>
+  </person>
+  </group>
+</result>
diff --git a/tests/grouping/sort/test1_d.xml b/tests/grouping/sort/test1_d.xml
new file mode 100644
index 00000000..5546bc41
--- /dev/null
+++ b/tests/grouping/sort/test1_d.xml
@@ -0,0 +1,27 @@
+<info>
+  <person>
+    <fName>Gary</fName>
+    <lName>Gregory</lName>
+    <score>5</score>
+  </person>
+  <person>
+    <fName>Joseph</fName>
+    <lName>Kessselman</lName>
+    <score>2</score>
+  </person>
+  <person>
+    <fName>Michael</fName>
+    <lName>Glavassevich</lName>
+    <score>2</score>
+  </person>
+  <person>
+    <fName>Mukul</fName>
+    <lName>Gandhi</lName>
+    <score>7</score>
+  </person>
+  <person>
+    <fName>Noah</fName>
+    <lName>Mendelsohn</lName>
+    <score>2</score>
+  </person>
+</info>
\ No newline at end of file
diff --git a/tests/grouping/sort/test8.xsl b/tests/grouping/sort/test8.xsl
new file mode 100644
index 00000000..6bec22b7
--- /dev/null
+++ b/tests/grouping/sort/test8.xsl
@@ -0,0 +1,49 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:xs="http://www.w3.org/2001/XMLSchema"
+                exclude-result-prefixes="xs"
+                version="3.0">
+                
+  <!-- Author: mukulg@apache.org -->                
+                
+  <!-- use with test1_d.xml -->
+   
+  <!-- An XSLT stylesheet, to test xsl:for-each-group instruction. -->                 
+                
+  <xsl:output method="xml" indent="yes"/>
+  
+  <xsl:template match="/info">     
+     <result>
+        <xsl:for-each-group select="person" group-by="xs:integer(score)">
+           <xsl:sort select="current-grouping-key()"/>
+           <group score="{current-grouping-key()}">           
+              <xsl:apply-templates select="current-group()"/>
+           </group>
+        </xsl:for-each-group>
+     </result>
+  </xsl:template>
+  
+  <xsl:template match="person">
+    <person>
+      <xsl:copy-of select="fName | lName"/>
+    </person>
+  </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/grouping/sort/test9.xsl b/tests/grouping/sort/test9.xsl
new file mode 100644
index 00000000..e3c78d7b
--- /dev/null
+++ b/tests/grouping/sort/test9.xsl
@@ -0,0 +1,54 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:xs="http://www.w3.org/2001/XMLSchema"
+                xmlns:fn0="http://fn0"
+                exclude-result-prefixes="xs fn0"
+                version="3.0">
+                
+  <!-- Author: mukulg@apache.org -->                
+                
+  <!-- use with test1_d.xml -->
+  
+  <!-- An XSLT stylesheet, to test xsl:for-each-group instruction. -->                
+                
+  <xsl:output method="xml" indent="yes"/>
+  
+  <xsl:template match="/info">     
+     <result>
+       <xsl:for-each-group select="person" group-by="xs:integer(score) lt xs:integer(3)">
+         <xsl:sort select="current-grouping-key()" order="descending"/>
+         <group score="{fn0:trfGroupingKey(current-grouping-key())}">           
+            <xsl:copy-of select="current-group()"/>
+         </group>
+       </xsl:for-each-group>
+     </result>
+  </xsl:template>
+  
+  <!-- A stylesheet function, to transform the grouping key value into a 
+       string value, to display on output. --> 
+  <xsl:function name="fn0:trfGroupingKey" as="xs:string">
+    <xsl:param name="groupingKey"/>
+    
+    <xsl:sequence select="if (string($groupingKey) eq 'true') then 
+                                                    xs:string('lt_three') else 
+                                                    xs:string('gt_or_eq_three')"/>
+  </xsl:function>
+  
+  <!--
+      * 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/xslt3/GroupingWithSortTests.java b/tests/org/apache/xalan/xslt3/GroupingWithSortTests.java
index a1393ee4..b666059a 100644
--- a/tests/org/apache/xalan/xslt3/GroupingWithSortTests.java
+++ b/tests/org/apache/xalan/xslt3/GroupingWithSortTests.java
@@ -116,5 +116,25 @@ public class GroupingWithSortTests extends XslTransformTestsUtil {
         
         runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
     }
+    
+    @Test
+    public void xslGroupingWithSortTest8() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1_d.xml"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test8.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test8.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
+    
+    @Test
+    public void xslGroupingWithSortTest9() {
+        String xmlFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test1_d.xml"; 
+        String xslFilePath = XSL_TRANSFORM_INPUT_DIRPATH + "test9.xsl";
+        
+        String goldFilePath = XSL_TRANSFORM_GOLD_DIRPATH + "test9.out";                
+        
+        runXslTransformAndAssertOutput(xmlFilePath, xslFilePath, goldFilePath, null);
+    }
 
 }


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