001: /*
002: * Copyright 2006-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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.opensource.org/licenses/ecl1.php
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.kuali.core.util;
017:
018: import java.beans.PropertyDescriptor;
019: import java.io.Serializable;
020: import java.lang.reflect.InvocationTargetException;
021: import java.util.Collections;
022: import java.util.Comparator;
023: import java.util.Iterator;
024: import java.util.List;
025:
026: import org.apache.commons.beanutils.PropertyUtils;
027: import org.apache.commons.collections.comparators.ComparableComparator;
028: import org.kuali.core.exceptions.BeanComparisonException;
029:
030: /**
031: * This class compares the two beans using multiple property names.
032: *
033: *
034: */
035: public class BeanPropertyComparator implements Comparator, Serializable {
036: private static final long serialVersionUID = -2675700473766186018L;
037: boolean ignoreCase;
038: private List propertyNames;
039: private Comparator stringComparator;
040: private Comparator booleanComparator;
041: private Comparator genericComparator;
042:
043: /**
044: * Constructs a PropertyComparator for comparing beans using the properties named in the given List; if the List is null, the
045: * beans will be compared directly (by Properties will be compared in the order in which they are listed. Case will be ignored
046: * in String comparisons.
047: *
048: * @param propertyNames List of property names (as Strings) used to compare beans
049: */
050: public BeanPropertyComparator(List propertyNames) {
051: this (propertyNames, true);
052: }
053:
054: /**
055: * Constructs a PropertyComparator for comparing beans using the properties named in the given List. Properties will be compared
056: * in the order in which they are listed. Case will be ignored if ignoreCase is true.
057: *
058: * @param propertyNames List of property names (as Strings) used to compare beans
059: * @param ignoreCase if true, case will be ignored during String comparisons
060: */
061: public BeanPropertyComparator(List propertyNames, boolean ignoreCase) {
062: if (propertyNames == null) {
063: throw new IllegalArgumentException(
064: "invalid (null) propertyNames list");
065: }
066: if (propertyNames.size() == 0) {
067: throw new IllegalArgumentException(
068: "invalid (empty) propertyNames list");
069: }
070: this .propertyNames = Collections
071: .unmodifiableList(propertyNames);
072: this .ignoreCase = ignoreCase;
073:
074: if (ignoreCase) {
075: this .stringComparator = String.CASE_INSENSITIVE_ORDER;
076: } else {
077: this .stringComparator = ComparableComparator.getInstance();
078: }
079: this .booleanComparator = new Comparator() {
080: public int compare(Object o1, Object o2) {
081: int compared = 0;
082:
083: Boolean b1 = (Boolean) o1;
084: Boolean b2 = (Boolean) o2;
085:
086: if (!b1.equals(b2)) {
087: if (b1.equals(Boolean.FALSE)) {
088: compared = -1;
089: } else {
090: compared = 1;
091: }
092: }
093:
094: return compared;
095: }
096:
097: };
098: this .genericComparator = ComparableComparator.getInstance();
099: }
100:
101: /**
102: * Compare two JavaBeans by the properties given to the constructor. If no propertues
103: *
104: * @param o1 Object The first bean to get data from to compare against
105: * @param o2 Object The second bean to get data from to compare
106: * @return int negative or positive based on order
107: */
108: public int compare(Object o1, Object o2) {
109: int compared = 0;
110:
111: try {
112: for (Iterator i = propertyNames.iterator(); (compared == 0)
113: && i.hasNext();) {
114: String currentProperty = i.next().toString();
115:
116: // choose appropriate comparator
117: Comparator currentComparator = null;
118: try {
119: PropertyDescriptor propertyDescriptor = PropertyUtils
120: .getPropertyDescriptor(o1, currentProperty);
121: Class propertyClass = propertyDescriptor
122: .getPropertyType();
123: if (propertyClass.equals(String.class)) {
124: currentComparator = this .stringComparator;
125: } else if (TypeUtils.isBooleanClass(propertyClass)) {
126: currentComparator = this .booleanComparator;
127: } else {
128: currentComparator = this .genericComparator;
129: }
130: } catch (NullPointerException e) {
131: throw new BeanComparisonException(
132: "unable to find property '"
133: + o1.getClass().getName() + "."
134: + currentProperty + "'", e);
135: }
136:
137: // compare the values
138: Object value1 = PropertyUtils.getProperty(o1,
139: currentProperty);
140: Object value2 = PropertyUtils.getProperty(o2,
141: currentProperty);
142: if (value1 == null)
143: value1 = "";
144: if (value2 == null)
145: value2 = "";
146: compared = currentComparator.compare(value1, value2);
147: }
148: } catch (IllegalAccessException e) {
149: throw new BeanComparisonException(
150: "unable to compare property values", e);
151: } catch (NoSuchMethodException e) {
152: throw new BeanComparisonException(
153: "unable to compare property values", e);
154: } catch (InvocationTargetException e) {
155: throw new BeanComparisonException(
156: "unable to compare property values", e);
157: }
158:
159: return compared;
160: }
161: }
|