View Javadoc

1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.jelly.xpath;
17  
18  import java.util.Comparator;
19  import java.util.List;
20  
21  import org.apache.commons.jelly.util.NestedRuntimeException;
22  import org.dom4j.Node;
23  import org.jaxen.JaxenException;
24  import org.jaxen.XPath;
25  
26  /***
27   * Compares xml nodes by extracting the value at xpath and
28   * comparing it.
29   *
30   * @author <a href="mailto:jason@jhorman.org">Jason Horman</a>
31   * @version $Id: XPathComparator.java 155420 2005-02-26 13:06:03Z dirkv $
32   */
33  
34  public class XPathComparator implements Comparator {
35  
36      /*** The xpath to use to extract value from nodes to compare */
37      private XPath xpath = null;
38  
39      /*** Sort descending or ascending */
40      private boolean descending = false;
41  
42      public XPathComparator() {
43  
44      }
45  
46      public XPathComparator(XPath xpath, boolean descending) {
47          this.xpath = xpath;
48          this.descending = descending;
49      }
50  
51      public void setXpath(XPath xpath) {
52          this.xpath = xpath;
53      }
54  
55      public XPath getXpath() {
56          return xpath;
57      }
58  
59      public void setDescending(boolean descending) {
60          this.descending = descending;
61      }
62  
63      public int compare(Object o1, Object o2) {
64          return compare((Node)o1, (Node)o2);
65      }
66  
67      public int compare(Node n1, Node n2) {
68          try {
69  
70              // apply the xpaths. not using stringValueOf since I don't
71              // want all of the child nodes appended to the strings
72              Object val1 = xpath.evaluate(n1);
73              Object val2 = xpath.evaluate(n2);
74  
75              // return if null
76              if (val1 == null || val2 == null) {
77                  return val1 == null ? (val2 == null ? 1 : -1) : 1;
78              }
79  
80              Comparable c1 = getComparableValue(val1);
81              Comparable c2 = getComparableValue(val2);
82  
83              // compare descending or ascending
84              if (!descending) {
85                  return c1.compareTo(c2);
86              } else {
87                  return c2.compareTo(c1);
88              }
89  
90          } catch (JaxenException e) {
91  
92              throw new XPathSortException("error sorting nodes", e);
93  
94          }
95      }
96  
97      /***
98       * Turns the XPath result value into a Comparable object.
99       */
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         }
114         else if (value instanceof Node) {
115             Node node = (Node) value;
116             return node.getStringValue();
117         }
118         return value.toString();
119     }
120 
121     /***
122      * My own runtime exception in case something goes wrong with sort.
123      */
124     public static class XPathSortException extends NestedRuntimeException {
125         public XPathSortException(String message, Throwable cause) {
126             super(message, cause);
127         }
128     }
129 }