You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by js...@apache.org on 2002/11/27 18:21:16 UTC

cvs commit: jakarta-commons-sandbox/jelly/src/test/org/apache/commons/jelly/xml xpathSortExample.jelly suite.jelly

jstrachan    2002/11/27 09:21:16

  Modified:    jelly/src/java/org/apache/commons/jelly/tags/xml SetTag.java
                        XMLTagLibrary.java ForEachTag.java
               jelly/src/test/org/apache/commons/jelly/xml suite.jelly
  Added:       jelly/src/java/org/apache/commons/jelly/tags/xml
                        XPathComparator.java SortTag.java
               jelly/src/test/org/apache/commons/jelly/xml
                        xpathSortExample.jelly
  Log:
  Applied Jason Horman's patch to support XPath sorting when navigating over XML data via <x:forEach/>. Thanks for your patch Jason!
  
  Revision  Changes    Path
  1.10      +47 -11    jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/SetTag.java
  
  Index: SetTag.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/SetTag.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- SetTag.java	30 Oct 2002 19:16:23 -0000	1.9
  +++ SetTag.java	27 Nov 2002 17:21:16 -0000	1.10
  @@ -61,18 +61,17 @@
    */
   package org.apache.commons.jelly.tags.xml;
   
  -import java.io.Writer;
  -
  -import org.apache.commons.jelly.JellyContext;
   import org.apache.commons.jelly.MissingAttributeException;
  -import org.apache.commons.jelly.Script;
  -import org.apache.commons.jelly.TagSupport;
   import org.apache.commons.jelly.XMLOutput;
   
   import org.apache.commons.logging.Log;
   import org.apache.commons.logging.LogFactory;
   
   import org.jaxen.XPath;
  +import org.jaxen.JaxenException;
  +
  +import java.util.List;
  +import java.util.Collections;
   
   /** A tag which defines a variable from an XPath expression 
     *
  @@ -90,7 +89,11 @@
       /** The XPath expression to evaluate. */    
       private XPath select;
       
  +    /** Xpath comparator for sorting */
  +    private XPathComparator xpCmp = null;
  +
       public SetTag() {
  +
       }
       
       // Tag interface
  @@ -105,7 +108,14 @@
           
           Object xpathContext = getXPathContext();        
           Object value = select.evaluate(xpathContext);
  -        
  +
  +        if (value instanceof List) {
  +            // sort the list if xpCmp is set.
  +            if (xpCmp != null && (xpCmp.getXpath() != null)) {
  +                Collections.sort((List)value, xpCmp);
  +            }
  +        }
  +
           //log.info( "Evaluated xpath: " + select + " as: " + value + " of type: " + value.getClass().getName() );
           
           context.setVariable(var, value);
  @@ -123,5 +133,31 @@
       /** Sets the XPath expression to evaluate. */
       public void setSelect(XPath select) {
           this.select = select;
  +    }
  +
  +
  +    /** Sets the xpath expression to use to sort selected nodes.
  +     */
  +    public void setSort(XPath sortXPath) throws JaxenException {
  +        if (xpCmp == null) xpCmp = new XPathComparator();
  +        xpCmp.setXpath(sortXPath);
  +    }
  +
  +    /**
  +     * Set whether to sort ascending or descending.
  +     */
  +    public void setDescending(boolean descending) {
  +        if (xpCmp == null) xpCmp = new XPathComparator();
  +        xpCmp.setDescending(descending);
  +    }
  +
  +    /**
  +     * Set the data type to convert nodes being sorted on into before sorting.
  +     * This should be the name of a class that commons.beanutils knows how to convert strings
  +     * into.
  +     */
  +    public void setSortDataType(String sortType) throws ClassNotFoundException {
  +        if (xpCmp == null) xpCmp = new XPathComparator();
  +        xpCmp.setType(Class.forName(sortType));
       }
   }
  
  
  
  1.17      +3 -2      jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/XMLTagLibrary.java
  
  Index: XMLTagLibrary.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/XMLTagLibrary.java,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- XMLTagLibrary.java	27 Nov 2002 12:43:19 -0000	1.16
  +++ XMLTagLibrary.java	27 Nov 2002 17:21:16 -0000	1.17
  @@ -100,6 +100,7 @@
           registerTag("copy", CopyTag.class);
           registerTag("copyOf", CopyOfTag.class);
           registerTag("doctype", DoctypeTag.class);
  +        registerTag("sort", SortTag.class);
       }
   
       public Expression createExpression(
  @@ -110,7 +111,7 @@
   
           // #### may need to include some namespace URI information in the XPath instance?
   
  -        if (attributeName.equals("select")) {
  +        if (attributeName.equals("select") || attributeName.equals("sort")) {
               if ( log.isDebugEnabled() ) {
                   log.debug( "Parsing XPath expression: " + attributeValue );
               }
  
  
  
  1.11      +46 -12    jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/ForEachTag.java
  
  Index: ForEachTag.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/ForEachTag.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- ForEachTag.java	30 Oct 2002 19:16:23 -0000	1.10
  +++ ForEachTag.java	27 Nov 2002 17:21:16 -0000	1.11
  @@ -63,13 +63,12 @@
   
   import java.util.Iterator;
   import java.util.List;
  +import java.util.Collections;
   
  -import org.apache.commons.jelly.JellyContext;
  -import org.apache.commons.jelly.Script;
  -import org.apache.commons.jelly.TagSupport;
   import org.apache.commons.jelly.XMLOutput;
   
   import org.jaxen.XPath;
  +import org.jaxen.JaxenException;
   
   /** A tag which performs an iteration over the results of an XPath expression
     *
  @@ -81,14 +80,17 @@
       /** Holds the XPath selector. */
       private XPath select;
   
  +    /** Xpath comparator for sorting */
  +    private XPathComparator xpCmp = null;
  +
       /** If specified then the current item iterated through will be defined
         * as the given variable name. */
       private String var;
   
       /** The current iteration value */
       private Object iterationValue;
  -    
  -    
  +
  +
       public ForEachTag() {
       }
       
  @@ -96,7 +98,14 @@
       //------------------------------------------------------------------------- 
       public void doTag(XMLOutput output) throws Exception {
           if (select != null) {
  -            Iterator iter = select.selectNodes( getXPathContext() ).iterator();
  +            List nodes = select.selectNodes( getXPathContext() );
  +
  +            // sort the list if xpCmp is set.
  +            if (xpCmp != null && (xpCmp.getXpath() != null)) {
  +                Collections.sort(nodes, xpCmp);
  +            }
  +
  +            Iterator iter = nodes.iterator();
               while (iter.hasNext()) {
                   iterationValue = iter.next();
                   if (var != null) {
  @@ -125,10 +134,35 @@
       public void setSelect(XPath select) {
           this.select = select;
       }
  +
       /** Sets the variable name to export for the item being iterated over
        */
       public void setVar(String var) {
           this.var = var;
       }
  -    
  +
  +    /** Sets the xpath expression to use to sort selected nodes.
  +     */
  +    public void setSort(XPath sortXPath) throws JaxenException {
  +        if (xpCmp == null) xpCmp = new XPathComparator();
  +        xpCmp.setXpath(sortXPath);
  +    }
  +
  +    /**
  +     * Set whether to sort ascending or descending.
  +     */
  +    public void setDescending(boolean descending) {
  +        if (xpCmp == null) xpCmp = new XPathComparator();
  +        xpCmp.setDescending(descending);
  +    }
  +
  +    /**
  +     * Set the data type to convert nodes being sorted on into before sorting. This
  +     * should be the name of a class that commons.beanutils knows how to convert strings
  +     * into.
  +     */
  +    public void setSortDataType(String sortType) throws ClassNotFoundException {
  +        if (xpCmp == null) xpCmp = new XPathComparator();
  +        xpCmp.setType(Class.forName(sortType));
  +    }
   }
  
  
  
  1.1                  jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/XPathComparator.java
  
  Index: XPathComparator.java
  ===================================================================
  /*
   * $Header: /home/cvspublic/jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/ForEachTag.java,v 1.10 2002/10/30 19:16:23 jstrachan Exp $
   * $Revision: 1.10 $
   * $Date: 2002/10/30 19:16:23 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * $Id$
   */
  package org.apache.commons.jelly.tags.xml;
  
  import java.util.Comparator;
  
  import org.dom4j.Node;
  
  import org.jaxen.XPath;
  import org.jaxen.JaxenException;
  
  import org.apache.commons.beanutils.ConvertUtils;
  
  import org.apache.commons.jelly.util.NestedRuntimeException;
  
  /**
   * Compares xml nodes by extracting the value at xpath and
   * comparing it.
   *
   * @author <a href="mailto:jason@jhorman.org">Jason Horman</a>
   * @version $Id$
   */
  
  public class XPathComparator implements Comparator {
  
      /** The xpath to use to extract value from nodes to compare */
      private XPath xpath = null;
  
      /** If set then the value extracted will be cast into this type and compared. */
      private Class type = null;
  
      /** Sort descending or ascending */
      private boolean descending = false;
  
      public XPathComparator() {
  
      }
  
      public XPathComparator(XPath xpath, boolean descending) {
          this.xpath = xpath;
          this.descending = descending;
      }
  
      public XPathComparator(XPath xpath, Class type, boolean descending) {
          this.xpath = xpath;
          this.type = type;
          this.descending = descending;
      }
  
      public void setXpath(XPath xpath) {
          this.xpath = xpath;
      }
  
      public XPath getXpath() {
          return xpath;
      }
  
      public void setType(Class type) {
          this.type = type;
      }
  
      public void setDescending(boolean descending) {
          this.descending = descending;
      }
  
      public int compare(Object o1, Object o2) {
          return compare((Node)o1, (Node)o2);
      }
  
      public int compare(Node n1, Node n2) {
          try {
  
              // apply the xpaths. not using stringValueOf since I don't
              // want all of the child nodes appended to the strings
              Node val1 = (Node)xpath.selectSingleNode(n1);
              Node val2 = (Node)xpath.selectSingleNode(n2);
  
              // return if null
              if (val1 == null || val2 == null) {
                  return val1 == null ? (val2 == null ? 1 : -1) : 1;
              }
  
              // these are what will be compared
              Comparable c1, c2;
  
              // extract the string values
              String s1 = val1.getText();
              String s2 = val2.getText();
  
              // if type is set convert to it and compare
              // if it is not set try to infer types
              if (type != null) {
                  c1 = (Comparable)ConvertUtils.convert(s1, type);
                  c2 = (Comparable)ConvertUtils.convert(s2, type);
              } else {
                  // check if numeric type
                  if (isNumeric(s1) && isNumeric(s2)) {
                      // if either is a double, cast to doubles
                      if (isDouble(s1) || isDouble(s2)) {
                          c1 = (Double)ConvertUtils.convert(s1, Double.class);
                          c2 = (Double)ConvertUtils.convert(s2, Double.class);
                      } else {
                          c1 = (Integer)ConvertUtils.convert(s1, Integer.class);
                          c2 = (Integer)ConvertUtils.convert(s2, Integer.class);
                      }
                  } else {
                      // nope, leave as strings
                      c1 = s1;
                      c2 = s2;
                  }
              }
  
              // compare descending or ascending
              if (!descending) {
                  return c1.compareTo(c2);
              } else {
                  return c2.compareTo(c1);
              }
  
          } catch (JaxenException e) {
  
              throw new XPathSortException("error sorting nodes", e);
  
          }
      }
  
      /**
       * Check to see if a string is a number. Negative and decimals supported.
       * @param str String to check
       * @return True if the string is numeric. Empty strings are not numeric.
       */
      private static final boolean isNumeric(String str) {
          final int strLen = str.length();
          // empty strings are not numbers
          if (strLen == 0) return false;
          // start at pos 1 if the 1st char is '-' to support negatives
          final int startPos = (str.charAt(0) == '-') ? 1 : 0;
          if (startPos == strLen) return false;
          for (int i=startPos;i<strLen;i++) {
              char ch = str.charAt(i);
              if ((ch < '0' || ch > '9') && (ch != '.')) {
                  return false;
              }
          }
  
          return true;
      }
  
      /**
       * Check to see if the number has a period.
       */
      private static final boolean isDouble(String str) {
          return str.indexOf(".") != -1;
      }
  
      /**
       * My own runtime exception in case something goes wrong with sort.
       */
      public static class XPathSortException extends NestedRuntimeException {
          public XPathSortException(String message, Throwable cause) {
              super(message, cause);
          }
      }
  }
  
  
  
  1.1                  jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/SortTag.java
  
  Index: SortTag.java
  ===================================================================
  /*
   * $Header: /home/cvspublic/jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/tags/xml/ForEachTag.java,v 1.10 2002/10/30 19:16:23 jstrachan Exp $
   * $Revision: 1.10 $
   * $Date: 2002/10/30 19:16:23 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * $Id: ForEachTag.java,v 1.10 2002/10/30 19:16:23 jstrachan Exp $
   */
  
  package org.apache.commons.jelly.tags.xml;
  
  import org.apache.commons.jelly.XMLOutput;
  import org.apache.commons.jelly.MissingAttributeException;
  import org.jaxen.XPath;
  import org.jaxen.JaxenException;
  
  import java.util.List;
  import java.util.Collections;
  
  /** A tag that can sort a list of xml nodes via an xpath expression.
    *
    * @author <a href="mailto:jason@jhorman.org">Jason Horman</a>
    * @version $Id$
    */
  
  public class SortTag extends XPathTagSupport {
  
      /** The list to sort */
      private List list = null;
  
      /** Xpath comparator for sorting */
      private XPathComparator xpCmp = null;
  
      public void doTag(XMLOutput output) throws Exception {
          if (xpCmp == null) {
              throw new MissingAttributeException( "xpCmp" );
          }
          if (list == null) {
              throw new MissingAttributeException( "list" );
          }
  
          Collections.sort(list, xpCmp);
      }
  
      /** Set the list to sort. */
      public void setList(List list) {
          this.list = list;
      }
  
      /** Sets the xpath expression to use to sort selected nodes.
       */
      public void setSort(XPath sortXPath) throws JaxenException {
          if (xpCmp == null) xpCmp = new XPathComparator();
          xpCmp.setXpath(sortXPath);
      }
  
      /**
       * Set whether to sort ascending or descending.
       */
      public void setDescending(boolean descending) {
          if (xpCmp == null) xpCmp = new XPathComparator();
          xpCmp.setDescending(descending);
      }
  
      /**
       * Set the data type to convert nodes being sorted on into before sorting. This
       * should be the name of a class that commons.beanutils knows how to convert strings
       * into.
       */
      public void setSortDataType(String sortType) throws ClassNotFoundException {
          if (xpCmp == null) xpCmp = new XPathComparator();
          xpCmp.setType(Class.forName(sortType));
      }
  }
  
  
  
  1.4       +77 -1     jakarta-commons-sandbox/jelly/src/test/org/apache/commons/jelly/xml/suite.jelly
  
  Index: suite.jelly
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/jelly/src/test/org/apache/commons/jelly/xml/suite.jelly,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- suite.jelly	13 Nov 2002 19:35:14 -0000	1.3
  +++ suite.jelly	27 Nov 2002 17:21:16 -0000	1.4
  @@ -94,6 +94,82 @@
   		<test:assert xpath="$doc/*[local-name()='foo']"/>
     </test:case>
   
  +    <!-- test xpath sorting -->
  +    <test:case name="testXpathSorting">
  +        <x:parse var="nums">
  +            <a>
  +                <b v="3"/>
  +                <b v="2"/>
  +                <b v="1"/>
  +                <b v="11"/>
  +                <b v="1.4"/>
  +                <b v="1.2"/>
  +            </a>
  +        </x:parse>
  +
  +        <x:parse var="deeper">
  +            <a>
  +                <b><c><d>3<e>1</e></d></c></b>
  +                <b><c><d>2<e>11</e></d></c></b>
  +                <b><c><d>1</d></c></b>
  +                <b><c><d>11</d></c></b>
  +            </a>
  +        </x:parse>
  +
  +        <!-- test ascending -->
  +        <j:set var="result" value=""/>
  +        <x:forEach select="$nums/a/b" var="x" sort="@v">
  +            <x:set var="num" select="$x/@v"/>
  +            <j:set var="result" value="${result} ${num.get(0).getText()}"/>
  +        </x:forEach>
  +
  +        <test:assertEquals expected=" 1 1.2 1.4 2 3 11" actual="${result}"/>
  +
  +        <!-- test descending -->
  +        <j:set var="result" value=""/>
  +        <x:forEach select="$nums/a/b" var="x" sort="@v" descending="true">
  +            <x:set var="num" select="$x/@v"/>
  +            <j:set var="result" value="${result} ${num.get(0).getText()}"/>
  +        </x:forEach>
  +
  +        <test:assertEquals expected=" 11 3 2 1.4 1.2 1" actual="${result}"/>
  +
  +        <!-- test deeper nesting -->
  +        <j:set var="result" value=""/>
  +        <x:forEach select="$deeper/a/b" var="x" sort="c/d">
  +            <j:set var="result" value="${result} ${x.getStringValue()}"/>
  +        </x:forEach>
  +
  +        <test:assertEquals expected=" 1 211 31 11" actual="${result}"/>
  +
  +        <!-- test sort as strings -->
  +        <j:set var="result" value=""/>
  +        <x:forEach select="$nums/a/b" var="x" sort="@v" sortDataType="java.lang.String">
  +            <x:set var="num" select="$x/@v"/>
  +            <j:set var="result" value="${result} ${num.get(0).getText()}"/>
  +        </x:forEach>
  +
  +        <test:assertEquals expected=" 1 1.2 1.4 11 2 3" actual="${result}"/>
  +
  +        <!-- test x:set with sort -->
  +        <j:set var="result" value=""/>
  +        <x:set var="rset" select="$nums/a/b" sort="@v"/>
  +        <j:forEach var="num" items="${rset.iterator()}">
  +            <j:set var="result" value="${result} ${num.attributeValue('v')}"/>
  +        </j:forEach>
  +
  +        <test:assertEquals expected=" 1 1.2 1.4 2 3 11" actual="${result}"/>
  +
  +        <!-- test x:set with sort -->
  +        <j:set var="result" value=""/>
  +        <x:set var="rset" select="$nums/a/b"/>
  +        <x:sort list="${rset}" sort="@v"/>
  +        <j:forEach var="num" items="${rset.iterator()}">
  +            <j:set var="result" value="${result} ${num.attributeValue('v')}"/>
  +        </j:forEach>
  +
  +        <test:assertEquals expected=" 1 1.2 1.4 2 3 11" actual="${result}"/>
  +
  +    </test:case>
   
  -  
   </test:suite>
  
  
  
  1.1                  jakarta-commons-sandbox/jelly/src/test/org/apache/commons/jelly/xml/xpathSortExample.jelly
  
  Index: xpathSortExample.jelly
  ===================================================================
  <?xml version="1.0"?>
  <j:jelly xmlns:j="jelly:core" xmlns:test="jelly:junit" xmlns:x="jelly:xml">
      <x:parse var="nums">
          <a>
              <b v="3"/>
              <b v="2"/>
              <b v="1"/>
              <b v="11"/>
              <b v="1.4"/>
              <b v="1.2"/>
          </a>
      </x:parse>
  
      <x:parse var="strs">
          <a>
              <b v="z"/>
              <b v="g"/>
              <b v="d"/>
              <b v="a"/>
          </a>
      </x:parse>
  
      <x:parse var="deeper">
          <a>
              <b><c><d>3<e>1</e></d></c></b>
              <b><c><d>2<e>11</e></d></c></b>
              <b><c><d>1</d></c></b>
              <b><c><d>11</d></c></b>
          </a>
      </x:parse>
  
      <j:set var="result" value=""/>
      <x:forEach select="$nums/a/b" var="x" sort="@v">
          <x:set var="num" select="$x/@v"/>
          <j:set var="result" value="${result} ${num.get(0).getText()}"/>
      </x:forEach>
      ${result}
  
      <j:set var="result" value=""/>
      <x:forEach select="$nums/a/b" var="x" sort="@v" descending="true">
          <x:set var="num" select="$x/@v"/>
          <j:set var="result" value="${result} ${num.get(0).getText()}"/>
      </x:forEach>
      ${result}
  
      <j:set var="result" value=""/>
      <x:forEach select="$nums/a/b" var="x" sort="@v" sortDataType="java.lang.String">
          <x:set var="num" select="$x/@v"/>
          <j:set var="result" value="${result} ${num.get(0).getText()}"/>
      </x:forEach>
      ${result}
  
      <j:set var="result" value=""/>
      <x:forEach select="$strs/a/b" var="x" sort="@v">
          <x:set var="str" select="$x/@v"/>
          <j:set var="result" value="${result} ${str.get(0).getText()}"/>
      </x:forEach>
      ${result}
  
      <j:set var="result" value=""/>
      <x:forEach select="$deeper/a/b" var="x" sort="c/d">
          <j:set var="result" value="${result} ${x.getStringValue()}"/>
      </x:forEach>
      ${result}
  
      <j:set var="result" value=""/>
      <x:set var="rset" select="$nums/a/b" sort="@v"/>
      <j:forEach var="num" items="${rset.iterator()}">
          <j:set var="result" value="${result} ${num.attributeValue('v')}"/>
      </j:forEach>
      ${result}
  
      <j:set var="result" value=""/>
      <x:set var="rset" select="$nums/a/b"/>
      <x:sort list="${rset}" sort="@v"/>
      <j:forEach var="num" items="${rset.iterator()}">
          <j:set var="result" value="${result} ${num.attributeValue('v')}"/>
      </j:forEach>
      ${result}
  
  </j:jelly>
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>