001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.commons.beanutils;
019:
020: import java.lang.reflect.InvocationTargetException;
021: import java.io.Serializable;
022: import java.util.Comparator;
023: import org.apache.commons.collections.comparators.ComparableComparator;
024:
025: /**
026: * <p>
027: * This comparator compares two beans by the specified bean property.
028: * It is also possible to compare beans based on nested, indexed,
029: * combined, mapped bean properties. Please see the {@link PropertyUtilsBean}
030: * documentation for all property name possibilities.
031: *
032: * </p><p>
033: * <strong>Note:</strong> The BeanComparator passes the values of the specified
034: * bean property to a ComparableComparator, if no comparator is
035: * specified in the constructor. If you are comparing two beans based
036: * on a property that could contain "null" values, a suitable <code>Comparator</code>
037: * or <code>ComparatorChain</code> should be supplied in the constructor.
038: * </p>
039: *
040: * @author <a href"mailto:epugh@upstate.com">Eric Pugh</a>
041: * @author Tim O'Brien
042: */
043: public class BeanComparator implements Comparator, Serializable {
044:
045: private String property;
046: private Comparator comparator;
047:
048: /**
049: * <p>Constructs a Bean Comparator without a property set.
050: * </p><p>
051: * <strong>Note</strong> that this is intended to be used
052: * only in bean-centric environments.
053: * </p><p>
054: * Until {@link #setProperty} is called with a non-null value.
055: * this comparator will compare the Objects only.
056: * </p>
057: */
058: public BeanComparator() {
059: this (null);
060: }
061:
062: /**
063: * <p>Constructs a property-based comparator for beans.
064: * This compares two beans by the property
065: * specified in the property parameter. This constructor creates
066: * a <code>BeanComparator</code> that uses a <code>ComparableComparator</code>
067: * to compare the property values.
068: * </p>
069: *
070: * <p>Passing "null" to this constructor will cause the BeanComparator
071: * to compare objects based on natural order, that is
072: * <code>java.lang.Comparable</code>.
073: * </p>
074: *
075: * @param property String Name of a bean property, which may contain the
076: * name of a simple, nested, indexed, mapped, or combined
077: * property. See {@link PropertyUtilsBean} for property query language syntax.
078: * If the property passed in is null then the actual objects will be compared
079: */
080: public BeanComparator(String property) {
081: this (property, ComparableComparator.getInstance());
082: }
083:
084: /**
085: * Constructs a property-based comparator for beans.
086: * This constructor creates
087: * a BeanComparator that uses the supplied Comparator to compare
088: * the property values.
089: *
090: * @param property Name of a bean property, can contain the name
091: * of a simple, nested, indexed, mapped, or combined
092: * property. See {@link PropertyUtilsBean} for property query language
093: * syntax.
094: * @param comparator BeanComparator will pass the values of the
095: * specified bean property to this Comparator.
096: * If your bean property is not a comparable or
097: * contains null values, a suitable comparator
098: * may be supplied in this constructor.
099: */
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: * Gets the property attribute of the BeanComparator
121: *
122: * @return String method name to call to compare.
123: * A null value indicates that the actual objects will be compared
124: */
125: public String getProperty() {
126: return property;
127: }
128:
129: /**
130: * Gets the Comparator being used to compare beans.
131: *
132: * @return the Comparator being used to compare beans
133: */
134: public Comparator getComparator() {
135: return comparator;
136: }
137:
138: /**
139: * Compare two JavaBeans by their shared property.
140: * If {@link #getProperty} is null then the actual objects will be compared.
141: *
142: * @param o1 Object The first bean to get data from to compare against
143: * @param o2 Object The second bean to get data from to compare
144: * @return int negative or positive based on order
145: */
146: public int compare(Object o1, Object o2) {
147:
148: if (property == null) {
149: // compare the actual objects
150: return comparator.compare(o1, o2);
151: }
152:
153: try {
154: Object value1 = PropertyUtils.getProperty(o1, property);
155: Object value2 = PropertyUtils.getProperty(o2, property);
156: return comparator.compare(value1, value2);
157: } catch (IllegalAccessException iae) {
158: throw new RuntimeException("IllegalAccessException: "
159: + iae.toString());
160: } catch (InvocationTargetException ite) {
161: throw new RuntimeException("InvocationTargetException: "
162: + ite.toString());
163: } catch (NoSuchMethodException nsme) {
164: throw new RuntimeException("NoSuchMethodException: "
165: + nsme.toString());
166: }
167: }
168:
169: /**
170: * Two <code>BeanComparator</code>'s are equals if and only if
171: * the wrapped comparators and the property names to be compared
172: * are equal.
173: * @param o Comparator to compare to
174: * @return whether the the comparators are equal or not
175: */
176: public boolean equals(Object o) {
177: if (this == o) {
178: return true;
179: }
180: if (!(o instanceof BeanComparator)) {
181: return false;
182: }
183:
184: final BeanComparator beanComparator = (BeanComparator) o;
185:
186: if (!comparator.equals(beanComparator.comparator)) {
187: return false;
188: }
189: if (property != null) {
190: if (!property.equals(beanComparator.property)) {
191: return false;
192: }
193: } else {
194: return (beanComparator.property == null);
195: }
196:
197: return true;
198: }
199:
200: /**
201: * Hashcode compatible with equals.
202: * @return the hash code for this comparator
203: */
204: public int hashCode() {
205: int result;
206: result = comparator.hashCode();
207: return result;
208: }
209: }
|