Return-Path: Delivered-To: apmail-jakarta-commons-dev-archive@apache.org Received: (qmail 44834 invoked from network); 27 Nov 2002 17:21:38 -0000 Received: from unknown (HELO nagoya.betaversion.org) (192.18.49.131) by daedalus.apache.org with SMTP; 27 Nov 2002 17:21:38 -0000 Received: (qmail 11921 invoked by uid 97); 27 Nov 2002 17:22:29 -0000 Delivered-To: qmlist-jakarta-archive-commons-dev@jakarta.apache.org Received: (qmail 11837 invoked by uid 97); 27 Nov 2002 17:22:27 -0000 Mailing-List: contact commons-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Jakarta Commons Developers List" Reply-To: "Jakarta Commons Developers List" Delivered-To: mailing list commons-dev@jakarta.apache.org Received: (qmail 11821 invoked by uid 97); 27 Nov 2002 17:22:27 -0000 X-Antivirus: nagoya (v4218 created Aug 14 2002) Date: 27 Nov 2002 17:21:16 -0000 Message-ID: <20021127172116.84985.qmail@icarus.apache.org> From: jstrachan@apache.org To: jakarta-commons-sandbox-cvs@apache.org Subject: cvs commit: jakarta-commons-sandbox/jelly/src/test/org/apache/commons/jelly/xml xpathSortExample.jelly suite.jelly X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N 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 . 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 * . * * $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 Jason Horman * @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 '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 * . * * $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 Jason Horman * @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 @@ + + + + + + + + + + + + + + + + 31 + 211 + 1 + 11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 1.1 jakarta-commons-sandbox/jelly/src/test/org/apache/commons/jelly/xml/xpathSortExample.jelly Index: xpathSortExample.jelly =================================================================== 31 211 1 11 ${result} ${result} ${result} ${result} ${result} ${result} ${result} -- To unsubscribe, e-mail: For additional commands, e-mail: