View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.beanutils;
19  
20  import java.lang.reflect.InvocationTargetException;
21  import java.io.Serializable;
22  import java.util.Comparator;
23  import org.apache.commons.collections.comparators.ComparableComparator;
24  
25  /***
26   * <p>
27   * This comparator compares two beans by the specified bean property. 
28   * It is also possible to compare beans based on nested, indexed, 
29   * combined, mapped bean properties. Please see the {@link PropertyUtilsBean} 
30   * documentation for all property name possibilities. 
31   *
32   * </p><p>
33   * <strong>Note:</strong> The BeanComparator passes the values of the specified 
34   * bean property to a ComparableComparator, if no comparator is 
35   * specified in the constructor. If you are comparing two beans based 
36   * on a property that could contain "null" values, a suitable <code>Comparator</code> 
37   * or <code>ComparatorChain</code> should be supplied in the constructor. 
38   * </p>
39   *
40   * @author     <a href"mailto:epugh@upstate.com">Eric Pugh</a>
41   * @author Tim O'Brien 
42   */
43  public class BeanComparator implements Comparator, Serializable {
44  
45      private String property;
46      private Comparator comparator;
47  
48      /*** 
49       * <p>Constructs a Bean Comparator without a property set.
50       * </p><p>
51       * <strong>Note</strong> that this is intended to be used 
52       * only in bean-centric environments.
53       * </p><p>
54       * Until {@link #setProperty} is called with a non-null value.
55       * this comparator will compare the Objects only.
56       * </p>
57       */
58      public BeanComparator() {
59          this( null );
60      }
61  
62      /***
63       * <p>Constructs a property-based comparator for beans.
64       * This compares two beans by the property 
65       * specified in the property parameter. This constructor creates 
66       * a <code>BeanComparator</code> that uses a <code>ComparableComparator</code>
67       * to compare the property values. 
68       * </p>
69       * 
70       * <p>Passing "null" to this constructor will cause the BeanComparator 
71       * to compare objects based on natural order, that is 
72       * <code>java.lang.Comparable</code>. 
73       * </p>
74       *
75       * @param property String Name of a bean property, which may contain the 
76       * name of a simple, nested, indexed, mapped, or combined 
77       * property. See {@link PropertyUtilsBean} for property query language syntax. 
78       * If the property passed in is null then the actual objects will be compared
79       */
80      public BeanComparator( String property ) {
81          this( property, ComparableComparator.getInstance() );
82      }
83  
84      /***
85       * Constructs a property-based comparator for beans.
86       * This constructor creates 
87       * a BeanComparator that uses the supplied Comparator to compare 
88       * the property values. 
89       * 
90       * @param property Name of a bean property, can contain the name 
91       * of a simple, nested, indexed, mapped, or combined 
92       * property. See {@link PropertyUtilsBean} for property query language  
93       * syntax. 
94       * @param comparator BeanComparator will pass the values of the 
95       * specified bean property to this Comparator. 
96       * If your bean property is not a comparable or 
97       * contains null values, a suitable comparator 
98       * may be supplied in this constructor.
99       */
100     public BeanComparator( String property, Comparator comparator ) {
101         setProperty( property );
102         if (comparator != null) {
103             this.comparator = comparator;
104         } else {
105             this.comparator = ComparableComparator.getInstance();
106         }
107     }
108 
109     /***
110      * Sets the method to be called to compare two JavaBeans
111      *
112      * @param property String method name to call to compare 
113      * If the property passed in is null then the actual objects will be compared
114      */
115     public void setProperty( String property ) {
116         this.property = property;
117     }
118 
119 
120     /***
121      * Gets the property attribute of the BeanComparator
122      *
123      * @return String method name to call to compare. 
124      * A null value indicates that the actual objects will be compared
125      */
126     public String getProperty() {
127         return property;
128     }
129 
130 
131     /***
132      * Gets the Comparator being used to compare beans.
133      *
134      * @return the Comparator being used to compare beans 
135      */
136     public Comparator getComparator() {
137         return comparator;
138     }
139 
140 
141     /***
142      * Compare two JavaBeans by their shared property.
143      * If {@link #getProperty} is null then the actual objects will be compared.
144      *
145      * @param  o1 Object The first bean to get data from to compare against
146      * @param  o2 Object The second bean to get data from to compare
147      * @return int negative or positive based on order
148      */
149     public int compare( Object o1, Object o2 ) {
150         
151         if ( property == null ) {
152             // compare the actual objects
153             return comparator.compare( o1, o2 );
154         }
155         
156         try {
157             Object value1 = PropertyUtils.getProperty( o1, property );
158             Object value2 = PropertyUtils.getProperty( o2, property );
159             return comparator.compare( value1, value2 );
160         }
161         catch ( IllegalAccessException iae ) {
162             throw new RuntimeException( "IllegalAccessException: " + iae.toString() );
163         } 
164         catch ( InvocationTargetException ite ) {
165             throw new RuntimeException( "InvocationTargetException: " + ite.toString() );
166         }
167         catch ( NoSuchMethodException nsme ) {
168             throw new RuntimeException( "NoSuchMethodException: " + nsme.toString() );
169         } 
170     }
171     
172     /***
173      * Two <code>BeanComparator</code>'s are equals if and only if
174      * the wrapped comparators and the property names to be compared
175      * are equal.
176      * @param  o Comparator to compare to
177      * @return whether the the comparators are equal or not
178      */
179     public boolean equals(Object o) {
180         if (this == o) {
181             return true;
182         }
183         if (!(o instanceof BeanComparator)) {
184             return false;
185         }
186 
187         final BeanComparator beanComparator = (BeanComparator) o;
188 
189         if (!comparator.equals(beanComparator.comparator)) {
190             return false;
191         }
192         if (property != null)
193         {
194             if (!property.equals(beanComparator.property)) {
195                 return false;
196             }
197         }
198         else
199         {
200             return (beanComparator.property == null);
201         }
202 
203         return true;
204     }
205 
206     /***
207      * Hashcode compatible with equals.
208      * @return the hash code for this comparator
209      */ 
210     public int hashCode() {
211         int result;
212         result = comparator.hashCode();
213         return result;
214     }
215 }