001: /*
002: * Copyright 2002,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jelly.xpath;
017:
018: import java.util.Comparator;
019: import java.util.List;
020:
021: import org.apache.commons.jelly.util.NestedRuntimeException;
022: import org.dom4j.Node;
023: import org.jaxen.JaxenException;
024: import org.jaxen.XPath;
025:
026: /**
027: * Compares xml nodes by extracting the value at xpath and
028: * comparing it.
029: *
030: * @author <a href="mailto:jason@jhorman.org">Jason Horman</a>
031: * @version $Id: XPathComparator.java 155420 2005-02-26 13:06:03Z dirkv $
032: */
033:
034: public class XPathComparator implements Comparator {
035:
036: /** The xpath to use to extract value from nodes to compare */
037: private XPath xpath = null;
038:
039: /** Sort descending or ascending */
040: private boolean descending = false;
041:
042: public XPathComparator() {
043:
044: }
045:
046: public XPathComparator(XPath xpath, boolean descending) {
047: this .xpath = xpath;
048: this .descending = descending;
049: }
050:
051: public void setXpath(XPath xpath) {
052: this .xpath = xpath;
053: }
054:
055: public XPath getXpath() {
056: return xpath;
057: }
058:
059: public void setDescending(boolean descending) {
060: this .descending = descending;
061: }
062:
063: public int compare(Object o1, Object o2) {
064: return compare((Node) o1, (Node) o2);
065: }
066:
067: public int compare(Node n1, Node n2) {
068: try {
069:
070: // apply the xpaths. not using stringValueOf since I don't
071: // want all of the child nodes appended to the strings
072: Object val1 = xpath.evaluate(n1);
073: Object val2 = xpath.evaluate(n2);
074:
075: // return if null
076: if (val1 == null || val2 == null) {
077: return val1 == null ? (val2 == null ? 1 : -1) : 1;
078: }
079:
080: Comparable c1 = getComparableValue(val1);
081: Comparable c2 = getComparableValue(val2);
082:
083: // compare descending or ascending
084: if (!descending) {
085: return c1.compareTo(c2);
086: } else {
087: return c2.compareTo(c1);
088: }
089:
090: } catch (JaxenException e) {
091:
092: throw new XPathSortException("error sorting nodes", e);
093:
094: }
095: }
096:
097: /**
098: * Turns the XPath result value into a Comparable object.
099: */
100: protected Comparable getComparableValue(Object value) {
101: if (value instanceof List) {
102: List list = (List) value;
103: if (list.isEmpty()) {
104: value = "";
105: }
106: value = list.get(0);
107: if (value == null) {
108: value = "";
109: }
110: }
111: if (value instanceof Comparable) {
112: return (Comparable) value;
113: } else if (value instanceof Node) {
114: Node node = (Node) value;
115: return node.getStringValue();
116: }
117: return value.toString();
118: }
119:
120: /**
121: * My own runtime exception in case something goes wrong with sort.
122: */
123: public static class XPathSortException extends
124: NestedRuntimeException {
125: public XPathSortException(String message, Throwable cause) {
126: super(message, cause);
127: }
128: }
129: }
|